Wenn Mollie doppelt kassiert: Preisdiskrepanzen bei Shopware-Bundles lösen

Preisdiskrepanzen bei Shopware 6 Bundles mit SwkwebProductSet und Mollie Payments — und wie ein Decorator-Plugin das Problem sauber löst.

March 2026 6 Min. Lesezeit Feinwerk Software

Das Problem

In einem Kundenprojekt mit Shopware 6, Mollie Payments und dem SwkwebProductSet-Plugin (Bundle-Plugin für Produktsets) stießen wir auf ein Problem, das sich erst beim Bezahlvorgang zeigte: Mollie meldete eine Preisdiskrepanz und lehnte die Bestellung ab.

Die Fehlermeldung war deutlich — der Gesamtbetrag der einzelnen Positionen stimmte nicht mit dem Bestellwert überein. Aber im Shopware-Backend sah alles korrekt aus. Was war passiert?

Die Ursache: Doppelte Buchungspositionen

Das SwkwebProductSet-Plugin erzeugt beim Hinzufügen eines Bundles zum Warenkorb eine verschachtelte Struktur von Line Items:

  • swkweb-product-set — der Eltern-Eintrag mit dem korrekten Gesamtpreis des Bundles
  • swkweb-product-set-slot — Slots, die die einzelnen Positionen im Bundle gruppieren
  • swkweb-product-set-option — Optionen innerhalb der Slots
  • product — die tatsächlichen Produkte als Kind-Elemente

Das Problem: Der Mollie Line Item Builder flacht diese verschachtelte Struktur ab und sendet alle Positionen als separate Buchungsposten an Mollie — inklusive der Kind-Elemente, deren Preise bereits im Eltern-Element enthalten sind. Das Ergebnis: Der Gesamtbetrag wurde doppelt berechnet.

Beispiel: Ein Bundle kostet 49,90 €. Mollie erhält den Eltern-Eintrag (49,90 €) plus die einzelnen Produkte (z.B. 19,90 € + 15,00 € + 15,00 €). Summe bei Mollie: 99,80 € statt 49,90 €.

Die Lösung: Ein Decorator-Plugin

Statt den Code von Mollie oder SwkwebProductSet zu verändern, haben wir uns für das Decorator Pattern entschieden — ein bewährtes Architektur-Muster, das in Shopware 6 über den Symfony Service Container nativ unterstützt wird.

Die Idee: Wir wrappen den bestehenden MollieLineItemBuilder in einen eigenen Decorator, der vor dem Aufruf des Original-Builders die problematischen Kind-Elemente herausfiltert.

Der Decorator im Detail

Der MollieLineItemBuilderDecorator erbt vom originalen Builder und überschreibt die Methode buildLineItemPayload(). Vor dem Aufruf des inneren Builders passiert folgendes:

  1. Erkennung: Prüfen, ob swkweb-product-set Line Items in der Bestellung vorhanden sind
  2. Filterung: Rekursives Entfernen aller swkweb-product-set-slot und swkweb-product-set-option Einträge
  3. Bereinigung: Beim Eltern-Set-Item werden die Kinder entfernt, damit der Original-Builder sie nicht erneut abflacht
  4. Delegation: Die bereinigten Line Items werden an den Original-Builder übergeben
  5. Wiederherstellung: Nach dem Aufruf werden die Original-Line-Items auf der Bestellung wiederhergestellt

Service-Registrierung

Die Einbindung erfolgt über Symfonys decorates-Attribut in der services.xml:

<service id="...MollieLineItemBuilderDecorator"
         decorates="Kiener\MolliePayments\...\MollieLineItemBuilder"
         decoration-priority="-1">
    <argument type="service" id="...MollieLineItemBuilderDecorator.inner"/>
    <argument type="service" id="logger"/>
</service>

Der Decorator wird mit Priorität -1 registriert und erhält automatisch den Original-Service als .inner-Referenz. Shopware und Symfony erledigen den Rest — kein einziger Eingriff in fremden Code nötig.

Warum Decorator statt Event-Subscriber?

Shopware 6 bietet verschiedene Erweiterungsmechanismen. Für diesen Fall war der Decorator die beste Wahl:

  • Keine Abhängigkeit von Events: Der Mollie-Builder feuert kein geeignetes Event vor dem Aufbau der Line Items
  • Vollständige Kontrolle: Wir können die Eingabedaten vor dem Original-Aufruf manipulieren und nach dem Aufruf wiederherstellen
  • Saubere Trennung: Der Decorator ist ein eigenständiges Plugin — bei Deinstallation wird der Original-Service automatisch wiederhergestellt
  • Zukunftssicher: Wenn Mollie oder SwkwebProductSet Updates liefern, funktioniert der Decorator weiterhin, solange sich die Methodensignatur nicht ändert

Logging für Transparenz

Das Plugin loggt jeden Filtervorgang mit der Bestell-ID, der Anzahl der originalen und gefilterten Line Items. Im Produktionsbetrieb hilft das bei der Fehlersuche, ohne den normalen Betrieb zu beeinflussen:

SwkwebMollieLineItemFix: Filtering SwkwebProductSet child line items
  orderId: 018f...
  orderNumber: 10042
  originalCount: 7
SwkwebMollieLineItemFix: Filtered line items
  filteredCount: 3

Ergebnis

Nach der Installation des Plugins werden Bestellungen mit SwkwebProductSet-Bundles korrekt an Mollie übermittelt. Die Preise stimmen, die Zahlung geht durch, und im Shopware-Backend bleibt alles unverändert.

Das Plugin ist mit Shopware 6.6 und 6.7 kompatibel und kann über Composer installiert werden. Der gesamte Quellcode umfasst eine einzige Klasse mit rund 120 Zeilen — klein, fokussiert und wartbar.

Fazit: Nicht jedes Problem braucht eine große Lösung. Manchmal reicht ein gezielter Decorator, der zur richtigen Zeit die richtigen Daten filtert. Das Decorator Pattern in Symfony/Shopware 6 ist dafür ein mächtiges Werkzeug — sauber, minimal-invasiv und rückstandsfrei entfernbar.

Ähnliches Problem in Ihrem Shop?

Wir helfen bei Plugin-Konflikten, Payment-Integrationen und allem, was in Shopware 6 nicht so läuft wie es soll.