Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungMy fact-checker said CONFIRMED about a group that doesn't exist(23.09.2026 um 02:13 Uhr)
Sichere ProgrammierungZero-Friction Payment Architectures for Global Scalability(23.09.2026 um 02:20 Uhr)
Sichere ProgrammierungYour SaaS launch should have a high-score table(23.09.2026 um 02:24 Uhr)
Sichere ProgrammierungMy fact-checker said CONFIRMED about a group that doesn't exist(23.09.2026 um 02:13 Uhr)
Sichere ProgrammierungZero-Friction Payment Architectures for Global Scalability(23.09.2026 um 02:20 Uhr)
Sichere ProgrammierungYour SaaS launch should have a high-score table(23.09.2026 um 02:24 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Building a Chat Plugin for Shopware 6: Message Queues, Sales Channels, and Service Decoration

I already had webhook sync modules for Drupal Commerce, Sylius, and PrestaShop. Shopware 6 is the most complex of the four: sales channels, multi-currency, Symfony Messenger for async, and a Vue.js admin framework. Shopware's…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

I already had webhook sync modules for Drupal Commerce, Sylius, and PrestaShop. Shopware 6 is the most complex of the four: sales channels, multi-currency, Symfony Messenger for async, and a Vue.js admin framework.






Shopware's Event Subscriber System



Shopware uses event subscribers similar to Sylius (both are Symfony-based), but with Shopware-specific event classes:




class ProductSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'product.written' => 'onProductWritten',
'product.deleted' => 'onProductDeleted',
];
}

public function onProductWritten(EntityWrittenEvent $event): void
{
foreach ($event->getIds() as $productId) {
$this->webhookService->dispatch(
new WebhookMessage('product.updated', $productId)
);
}
}
}






Shopware fires EntityWrittenEvent which contains all entity IDs that were written in a single DAL operation. This is different from Sylius where each entity fires its own event. One Shopware write operation can contain multiple product IDs.



The OrderSubscriber listens for order state changes with configurable states:




public function onOrderStateChanged(StateMachineTransitionEvent $event): void
{
if (!$this->isTrackedOrderState($event->getToPlace()->getTechnicalName())) {
return;
}

// Check transaction state too (configurable)
$this->trackConversion($event->getEntityId());
}






Store owners configure which order states and transaction states trigger conversion events. A store might want to track on "completed" + "paid", while another tracks on "in_progress" + "authorized". This is managed through the admin dashboard, not code.






Message Queue: Symfony Messenger



Shopware uses Symfony Messenger for async processing. Compared to the other platforms I've integrated with, this one required the least custom code for reliable async delivery.




class WebhookMessage
{
public function __construct(
private readonly string $eventType,
private readonly string $entityId,
private readonly array $context = [],
) {}
}

class WebhookMessageHandler
{
public function __invoke(WebhookMessage $message): void
{
$product = $this->productRepository->search(
new Criteria([$message->getEntityId()]),
Context::createDefaultContext()
)->first();

$data = $this->formatter->formatProduct(
$product,
$this->channelContextService->getContexts()
);

$this->webhookClient->send(
$message->getEventType(),
$data
);
}
}






When a product is saved, the subscriber dispatches a WebhookMessage. Symfony Messenger serializes it and puts it on the async transport. A background worker picks it up and the handler sends the webhook.






Comparison with Other Platforms






































Platform Async mechanism Retry handling Worker management
Shopware Symfony Messenger Built-in retry + dead letter bin/console messenger:consume async
Magento DB message queue Built-in retry bin/magento queue:consumers:start
Sylius kernel.terminate None (same process) None needed
PrestaShop register_shutdown_function None (same process) None needed


Shopware and Magento have proper background workers with retry handling. If a webhook fails, the message goes back on the queue and gets retried. With Sylius and PrestaShop, a failed HTTP request during shutdown is just lost.



The tradeoff: Shopware and Magento require a running worker process. Sylius and PrestaShop don't. For production stores, a worker is already running for other tasks, so it's not additional overhead.






Sales Channel Architecture



This is where Shopware gets interesting. A single Shopware instance can have multiple sales channels: a B2C storefront, a B2B portal, an Amazon channel. Each channel has its own:




  • Languages (de-DE, en-GB, fr-FR)

  • Currencies (EUR, CHF, GBP)

  • Product assignments

  • Domains



The plugin needs to handle this. A product might be visible in the B2C channel at €49.99 (EUR) but in the B2B channel at €39.99 (EUR) with different descriptions.




class ProductFormatter implements ProductFormatterInterface
{
public function formatProduct(
ProductEntity $product,
array $channelContexts,
?string $syncSessionId = null,
): array {
$names = [];
$descriptions = [];
$prices = [];
$channelKeys = [];

foreach ($channelContexts as $channelKey => $context) {
$channelKeys[] = $channelKey;

foreach ($this->getChannelLanguages($context) as $language) {
$translated = $this->translateProduct($product, $language);
$lang = $language->getLocaleCode();
$names[$channelKey][$lang] = $translated->getName();
$descriptions[$channelKey][$lang] = $translated->getDescription();
}

foreach ($this->getChannelCurrencies($context) as $currency) {
$price = $this->calculatePrice($product, $currency, $context);
$prices[$channelKey][] = [
'current_price' => $price->getGross(), // or net, configurable
'regular_price' => $price->getListPrice()?->getGross(),
'currency' => $currency->getIsoCode(),
];
}
}

return [
'identification_number' => 'product-' . $product->getId(),
'sku' => $product->getProductNumber(),
'channels' => $channelKeys,
'names' => $names,
'descriptions' => $descriptions,
'prices' => $prices,
// ... categories, attributes, brands, images
];
}
}






The sales channel mapping is configured in the admin dashboard as a JSON map: Shopware channel ID → Emporiqa channel key. This lets store owners control which channels sync and how they're identified on the external service.






Service Interfaces and Decoration



Shopware uses Symfony's DI container. I defined interfaces for all core services:




interface ProductFormatterInterface
{
public function formatProduct(
ProductEntity $product, array $channelContexts, ?string $syncSessionId = null,
): array;
}

interface CmsPageFormatterInterface
{
public function formatCmsPage(CmsPageEntity $cmsPage, array $channelContexts, ?string $syncSessionId = null): array;
public function formatLandingPage(LandingPageEntity $landingPage, array $channelContexts, ?string $syncSessionId = null): array;
}

interface WebhookClientInterface
{
public function send(string $eventType, array $data): void;
}

interface ConfigServiceInterface
{
public function get(string $key, ?string $salesChannelId = null): mixed;
}

interface SyncServiceInterface
{
public function syncProducts(?callable $progressCallback = null, bool $dryRun = false): array;
public function syncPages(?callable $progressCallback = null, bool $dryRun = false): array;
}

interface ChannelResolverInterface
{
public function resolveChannelKey(string $salesChannelId): string;
public function getMapping(): array;
}






Any of these can be decorated by another plugin:




<service id="YourPlugin\Service\CustomProductFormatter"
decorates="Emporiqa\ShopwarePlugin\Service\ProductFormatter">
<argument type="service"
id="YourPlugin\Service\CustomProductFormatter.inner" />
</service>






This is the same pattern as Sylius's service decoration. Both platforms use Symfony DI, so the extensibility model is identical. Magento uses DI preferences (di.xml), PrestaShop uses hooks.






Admin Dashboard: Vue.js Module



Shopware's admin is built with Vue.js. The plugin ships a Vue component for the admin dashboard with:




  • Test connection button

  • Sync dashboard with progress tracking (products and pages separately)

  • Sales channel mapping UI

  • Brand source selection (which property group to use as brand)

  • Order state configuration (which states trigger conversion events)

  • Data preview (see formatted product/page data before syncing)



The admin module communicates with 8 custom API endpoints:












































Endpoint Description
POST /api/_action/emporiqa/sync Trigger sync (entity in body: products, pages, or all)
POST /api/_action/emporiqa/test-connection Test webhook connection
POST /api/_action/emporiqa/sync-overview Sync status and statistics
POST /api/_action/emporiqa/data-preview Preview formatted data
POST /api/_action/emporiqa/sales-channels Available sales channels
POST /api/_action/emporiqa/property-groups Property groups for brand mapping
POST /api/_action/emporiqa/order-states Available order states
POST /api/_action/emporiqa/save-settings Save dashboard settings


These are the same operations available via CLI (bin/console emporiqa:sync:products, etc.), just exposed through the admin API.






Storefront: Widget and Cart



The widget is injected via Shopware's storefront JS plugin system:




// emporiqa-widget.plugin.js
export default class EmporiqaWidgetPlugin extends Plugin {
init() {
const config = this.el.dataset;

const script = document.createElement('script');
script.src = `${config.emporiqaUrl}/chat/embed/`;
script.dataset.storeId = config.storeId;
script.dataset.language = config.language;
script.dataset.currency = config.currency;
script.dataset.channel = config.channel;
document.head.appendChild(script);
}
}






Language, currency, and channel are injected from the StorefrontSubscriber, which reads them from the current SalesChannelContext. The widget automatically shows the right language and currency for each storefront domain.



Cart operations use storefront routes:




#[Route(path: '/emporiqa/api/cart/add', name: 'emporiqa.cart.add', methods: ['POST'])]
public function add(Request $request, SalesChannelContext $context): JsonResponse
{
$this->cartService->add(
$request->get('product_id'),
$request->get('quantity', 1),
$context
);

return new JsonResponse(['success' => true]);
}






SEO URLs are registered so routes work with Shopware's URL rewriting.






What Doesn't Work Well



Shopware 6.6+ only: The plugin uses APIs introduced in 6.6.0. Stores on 6.5 or earlier would need significant changes. Shopware 5 is a completely different architecture.



CMS page model: Shopware's CMS pages are structured as sections → blocks → elements. Extracting readable text from this nested structure for a chat assistant requires walking the tree and concatenating element content. Landing pages work well. Regular shopping experiences (category layouts) are less useful because they contain product listings, not prose.



Admin module build process: Shopware's admin build system requires running bin/console plugin:refresh and sometimes a full admin build (bin/build-administration.sh) for Vue component changes to appear. During development, this adds friction compared to Magento's XML-based admin or PrestaShop's Smarty templates.






Wrapping Up



Shopware 6 has a well-structured architecture for this kind of integration. Symfony Messenger for async, service interfaces for extensibility, sales channel contexts for multi-tenant isolation, and a Vue.js admin for configuration. The complexity is in the sales channel model: products, prices, languages, and currencies all vary by channel. The plugin respects all of this, which means the chat assistant on each storefront only knows about that storefront's catalog and prices.



The plugin requires Shopware 6.6 or 6.7 with PHP 8.2+. Install it with Composer (composer require emporiqa/shopware-plugin) or upload the release zip from the GitHub repo via the Extension Manager. The Shopware Store listing is still pending review. Documentation and setup guide. I also wrote about the full Shopware integration on the Emporiqa blog.



Try the live demo at demo.emporiqa.com or create a free Emporiqa account with $25 of signup credit (~100 conversations). No card needed.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Building a Chat Plugin for Shopware 6: Message Queues, Sales Channels, and Service Decoration

Thematisch verwandte Begriffe: Building, Chat, Plugin, Shopware · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-17636 | IBM Financial Transaction Manager (FTM) for RedHat OpenShift could allow…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick