Plugin-Events oder Webhooks? Dieser Leitfaden erklärt beide Mechanismen in Shopware 6, zeigt die Registrierung in der manifest.xml, den Aufbau der Payload, die Signaturprüfung und die häufigsten Fehlerquellen.
Shopware 6 bietet zwei Wege, auf Ereignisse im Shop zu reagieren, und beide werden leicht verwechselt. Der eine ist das interne PHP-Event-System, in das sich Plugins einklinken und das innerhalb des Shopware-Prozesses läuft. Der andere sind Webhooks: Sie gehören zum App-System und benachrichtigen einen externen Dienst per HTTP.
Die falsche Wahl kostet Zeit. Ein Plugin-Subscriber läuft nicht auf einer Shopware-Cloud-Instanz, und ein Webhook kann nicht verändern, was während eines Requests passiert. Dieser Leitfaden erklärt, was beide Mechanismen leisten, wie ein Webhook registriert und abgesichert wird, welche Verhaltensweisen dokumentiert sind und auf welche Sie sich nicht verlassen sollten.
Alle technischen Angaben beziehen sich auf die offizielle Shopware-Entwicklerdokumentation. Versionsabhängige Funktionen sind mit dem Release markiert, in dem sie eingeführt wurden. Grundlagen zur Plattform finden Sie auf unserer Seite zur Shopware-Entwicklung.
Plugins laufen im Shop. Sie registrieren einen Subscriber, der EventSubscriberInterface implementiert, versehen ihn mit dem Tag kernel.event_subscriber und erhalten Events synchron, während der Request noch verarbeitet wird. Weil der Subscriber das Event-Objekt in der Hand hält, kann er beeinflussen, was als Nächstes passiert: einen Preis anpassen, einen Schreibvorgang blockieren, die Ausgabe der Storefront ändern.
Apps laufen außerhalb des Shops. Shopware kommuniziert mit ihnen ausschließlich über HTTP, und Webhooks sind der Weg, auf dem der Shop der App mitteilt, dass etwas passiert ist. Die App erhält im Nachhinein einen POST-Request und reagiert, indem sie über die Admin-API zurückruft. Den auslösenden Vorgang kann sie nicht mehr verändern.
Eine brauchbare Faustregel: Muss die Logik während des Requests laufen und sein Ergebnis verändern, gehört sie in ein Plugin. Reagiert sie im Nachhinein und lebt auf Ihrer eigenen Infrastruktur, gehört sie in eine App mit Webhooks. Für einfache Benachrichtigungen ohne eigene App reicht oft auch die Aktion „URL aufrufen“ im Flow Builder, die allerdings an den Tarif Evolve gebunden ist.
Bevor der erste Webhook feuert, müssen vier Dinge stehen.
Webhooks gehören zu einer App, nicht zu einem Plugin. Die App wird durch eine manifest.xml beschrieben und durchläuft mit dem Shop einen Registrierungs-Handshake, bei dem Ihre App ein Secret pro Shop erzeugt und speichert. Dieses Secret signiert später jeden Webhook-Request.
Die Zustellung läuft über die Message Queue von Shopware. Läuft kein Consumer, werden die Nachrichten geschrieben, aber nie gesendet, und in der Storefront erscheint kein Fehler. Das ist die mit Abstand häufigste Ursache dafür, dass ein korrekt konfigurierter Webhook scheinbar nichts tut.
Shopware sendet an die im Manifest angegebene URL. Sie muss vom Shop-Server aus erreichbar sein und sollte TLS terminieren. Für die lokale Entwicklung brauchen Sie einen Tunneldienst, damit der Shop Ihren Rechner erreicht.
Die meisten Webhook-Events setzen eine entsprechende Berechtigung im Manifest voraus. Wer order.written abonniert, ohne order:read zu deklarieren, scheitert an der Validierung. Shopware prüft Webhook-Berechtigungen seit Version 6.3.5.0.
Es gibt kein Webhook-Plugin zu installieren. Webhooks werden direkt im App-Manifest in einem <webhooks>-Block deklariert:
<webhooks>
<webhook name="product-changed"
url="https://example.com/event/product-changed"
event="product.written"/>
</webhooks>
Drei Attribute sind Pflicht: name muss innerhalb des Manifests eindeutig sein, url ist der Endpunkt, an den Shopware sendet, und event ist die Kennung des Events, das Sie empfangen möchten.
Nur Events, die Shopware als „hookable“ markiert, lassen sich nutzen. Die dokumentierte Menge deckt die Bereiche ab, die die meisten Integrationen brauchen:
checkout.order.placed, order.written, order.deletedproduct.written, product.deleted, customer.written, category.written, media.written und ihre Gegenstückecheckout.customer.register, checkout.customer.login, checkout.customer.logout, customer.recovery.requeststate_enter.order.state.completed, state_enter.order_transaction.state.paid, state_enter.order_delivery.state.shipped und die passenden state_leave-Eventsapp.installed, app.activated, app.deactivated, app.deleted sowie shopware.updatedSeit Shopware 6.5.7.0 kann ein Webhook das Attribut onlyLiveVersion="true" tragen. Ohne dieses Attribut feuert ein order.written-Webhook auch für versionierte Entwürfe, die beim Bearbeiten einer Bestellung in der Administration entstehen, was eine Integration in der Regel nicht möchte:
<webhook name="order-created"
url="https://example.com/event/order-created"
event="order.written"
onlyLiveVersion="true"/>
Das Attribut wird nur bei Entity-written-Events ausgewertet.
Der Request-Body ist JSON und hat immer dieselben drei Schlüssel auf oberster Ebene:
{
"data": {
"payload": [
{
"entity": "product",
"operation": "delete",
"primaryKey": "7b04ebe416db4ebc93de4d791325e1d9",
"updatedFields": []
}
],
"event": "product.written"
},
"source": {
"url": "http://localhost:8000",
"appVersion": "0.0.1",
"shopId": "dgrH7nLU6tlE",
"eventId": "7b04ebe416db4ebc93de4d791325e1d9"
},
"timestamp": 123123123
}
Bei Entity-written-Events enthält die Payload nicht die vollständige Entität. Shopware begründet das klar: Weil die Zustellung asynchron ist, könnte eine vollständige Entität beim Eintreffen bereits veraltet sein. Sie erhalten den primaryKey, und das erwartete Muster ist, den aktuellen Zustand über die Admin-API nachzuladen.
Integrationen, die die Payload als Quelle der Wahrheit behandeln, schreiben früher oder später veraltete Daten in ein angebundenes System zurück. Diese Entscheidung sollte vor der ersten Codezeile fallen, nicht danach.
source.shopId identifiziert den Shop und bleibt über seine Lebensdauer konstant.source.eventId bleibt über Wiederholungen hinweg gleich und ist damit der natürliche Schlüssel zur Deduplizierung.timestamp ist der Zeitpunkt der Verarbeitung und steht ab 6.4.1.0 zur Verfügung.Zusätzlich sendet Shopware ab 6.4.1.0 den Header sw-version sowie ab 6.4.5.0 sw-context-language und sw-user-language.
Jeder Webhook-Request trägt den Header shopware-shop-signature. Er enthält einen SHA-256-HMAC über den rohen Request-Body, geschlüsselt mit dem Shop-Secret, das Ihre App bei der Registrierung erzeugt hat. Ein Endpunkt, der diese Prüfung auslässt, akzeptiert alles, was irgendjemand an ihn sendet.
Das App-System verwendet zwei verschiedene Secrets, und ihre Verwechslung ist eine häufige Ursache für fehlgeschlagene Prüfungen:
shopware-app-signature signiert den Query-String des Registrierungs-Requests und verwendet das App-Secret, das Sie mit Ihrem Shopware-Account teilen.shopware-shop-signature signiert den Body des Webhook-Requests und verwendet das Shop-Secret, das pro Shop bei der Registrierung entsteht.Hashen Sie den rohen Body, bevor irgendein JSON-Parsing stattfindet. Frameworks, die den Request neu serialisieren, erzeugen eine andere Bytefolge und damit einen anderen HMAC.
Weisen Sie Requests mit zu altem timestamp ab. Ein Angreifer kann den Zeitstempel nicht ändern, ohne die Signatur ungültig zu machen, was ihn zu einem brauchbaren Schutz gegen wiederholte Requests macht.
Shopware empfiehlt, das App PHP SDK oder das Symfony Bundle zu verwenden, statt die Prüfung selbst zu implementieren. Seit 6.7.13.0 wird das Signatur-Secret zum Zeitpunkt der Zustellung aufgelöst, sodass Zustellungen über eine Secret-Rotation hinweg mit dem aktuellen Secret signiert sind.
Webhooks scheitern leise. Nichts in der Storefront deutet darauf hin, dass eine Zustellung ausgeblieben ist. Der erste Schritt ist deshalb immer festzustellen, ob Shopware es überhaupt versucht hat.
Shopware protokolliert Zustellversuche in der Tabelle webhook_event_log. Gibt es keine Zeile für das erwartete Event, wurde der Webhook nie in die Warteschlange gestellt, und das Problem liegt im Manifest oder in den Berechtigungen. Gibt es eine Zeile, die den Status „queued“ nie verlässt, liegt das Problem bei der Message Queue, nicht bei Ihrem Endpunkt.
Eine Aufräumaufgabe entfernt Einträge, die erfolgreich zugestellt wurden oder endgültig fehlgeschlagen sind; laufende oder wiederholte Einträge bleiben erhalten. Seit 6.7.4.0 werden Einträge, die im Status „queued“ hängen, nach der doppelten konfigurierten Aufbewahrungsdauer entfernt.
onlyLiveVersion nicht gesetzt ist.Ein minimaler Endpunkt, der den rohen Body protokolliert und schnell mit 200 antwortet, trennt Zustellprobleme von Verarbeitungsproblemen. Sobald die Zustellungen zuverlässig ankommen, verlagern Sie die Verarbeitung hinter eine eigene Warteschlange.
Die Entscheidungen, die eine Webhook-Integration stabil halten, fallen früh, und die meisten folgen aus einer Eigenschaft: Die Zustellung ist asynchron und kann wiederholt werden.
shopware-shop-signature gegen den rohen Body prüfen, bevor irgendetwas anderes passiert.source.eventId bleibt über Wiederholungen gleich und erkennt einen bereits verarbeiteten Request.primaryKey per Admin-API nachladen.Zwei Verhaltensweisen fehlen in dieser Liste bewusst, weil Shopware sie nicht dokumentiert: das genaue HTTP-Timeout einer Zustellung und welche Statuscodes als Fehler gelten. Eine Integration, die sich auf konkrete Werte verlässt, baut auf einer Annahme, nicht auf einer Spezifikation. Wenn Sie keine eigene App betreiben möchten, lassen sich viele Webhook-Szenarien auch über eine Automatisierungsplattform wie n8n abbilden.
Nein. Webhooks gehören zum App-System und werden im App-Manifest deklariert. Aus dem Shopware Store muss nichts installiert werden.
In den meisten Fällen läuft kein Message-Queue-Consumer. Shopware stellt die Zustellung in die Warteschlange und sendet sie nie. Prüfen Sie in webhook_event_log, ob das Event überhaupt eingereiht wurde.
Weil die Zustellung asynchron ist und eine vollständige Entität beim Eintreffen bereits veraltet sein könnte. Shopware sendet den primaryKey und erwartet, dass Sie den aktuellen Zustand über die Admin-API laden.
Die Webhook-Dokumentation nennt keine webhook-spezifische Zahl. Die allgemeine Message-Queue-Dokumentation sagt, dass Nachrichten dreimal wiederholt werden, bevor sie in den Failure-Transport wandern. Nehmen Sie das als Größenordnung, nicht als Garantie, und deduplizieren Sie über source.eventId.
Nein. Ein Webhook ist eine Benachrichtigung im Nachhinein. Um einen laufenden Request zu beeinflussen, brauchen Sie ein Plugin mit PHP-Event-Subscriber, was wiederum einen selbst gehosteten Shop voraussetzt. Wie wir solche Plugins entwickeln, zeigen wir auf unserer Seite zur Shopware-Plugin-Entwicklung.
Webhooks des App-Systems sind seit der Linie 6.3 Teil des Shopware-Kerns. Einige Details kamen später: das Feld timestamp und der Header sw-version in 6.4.1.0, die Sprach-Header in 6.4.5.0 und onlyLiveVersion in 6.5.7.0. Die aktuelle Hauptlinie ist 6.7.