Playbook

Unveränderliches Payment-Snapshot-Design: Der Warenkorb wird eingefroren (Unveraenderliches Payment Snapshot Design)

Den Live-Warenkorb während der Zahlung erneut zu lesen, macht Betrag und Währung unentschieden. Ohne einen eingefrorenen Snapshot ist Finalisierung nicht…

Verteilte Payment Engine (Distributed Payment Engine)

Teil 4 von 22

Serie zur verteilten Payment-Architektur — die Lücke zwischen Capture und Complete.

Distributed payment engine architecture diagram

Der Warenkorb ist ein bewegliches Ziel

Wenn ein Kunde zur Kasse kommt, können sich Preis, Kampagne und Bestandsinformation im Warenkorb noch ändern: Eine Aktion kann ablaufen, ein Preis kann aktualisiert werden, derselbe Kunde kann den Warenkorb in einem anderen Tab bearbeiten. Frieren Sie diese Information nicht in dem Moment ein, in dem der Payment Intent erstellt wird, können der an den PSP gesendete Betrag und der bei der Finalisierung erwartete Betrag zwei verschiedene Wahrheiten darstellen.

Warenkorb (live, veränderlich)  --zum Intent-Zeitpunkt-->  Payment-Snapshot (eingefroren, unveränderlich)
                                                        ↓
                                an den PSP gesendeter Betrag = Betrag im Snapshot

Dieser Teil erklärt, warum der „Rückgriff auf den Live-Warenkorb“ ein Risiko und keine Abkürzung ist, und wie ein unveränderlicher Snapshot das löst.

Begriffe, dort definiert, wo sie zuerst auftauchen

📦 Payment Intent
Ein Datensatz, der die Zahlungsabsicht des Kunden darstellt und den an den PSP zu sendenden Betrag und die Währung trägt.

📦 Snapshot
Eine eingefrorene, unveränderliche Kopie einer Tatsache (Preis, Menge, Steuer, Rabatt), wie sie zu einem bestimmten Zeitpunkt bestand.

📦 Price-at-Intent
Das Prinzip, dass der zum Startzeitpunkt der Zahlung gültige Preis unabhängig von späteren Kampagnen- oder Preisänderungen erhalten bleibt.

📦 Basket Drift
Wenn der Warenkorb sich vom Snapshot entfernt, während der Zahlungsprozess läuft (der Nutzer bearbeitet ihn in einem anderen Tab, oder Hintergrundjobs ändern ihn).

📦 Amount Reconciliation
Der Abgleich des tatsächlich vom PSP gemeldeten eingezogenen Betrags mit dem erwarteten Betrag im Snapshot.

Ein Snapshot ist kein „Foto“ des Warenkorbs — er friert den Zustand des Warenkorbs zu diesem Zeitpunkt ein, so wie man einen rechtlichen Datensatz einfriert, der danach nie mehr bearbeitet werden kann.

Warum das Lesen des Live-Warenkorbs irreführend ist

Manche Systeme schauen, nachdem der Payment Intent erstellt wurde, während der Finalisierung erneut in den Warenkorb, um den „aktuellen Betrag“ zu holen. Das wirkt verlockend, weil es sich anfühlt, als würde man „die frischesten Daten“ nutzen — tatsächlich vermischt es aber zwei Wahrheiten aus zwei verschiedenen Zeitpunkten.

t0: Kunde startet Zahlung  → Warenkorb: 3 Artikel, 450 €, Aktion aktiv
t1: Anfrage über 450 € geht an den PSP
t2: Aktion läuft ab, Warenkorb zeigt jetzt 480 €
t3: Finalisierung schaut erneut in den Warenkorb → erwartet 480 €, aber der PSP hat 450 € eingezogen

Diese Diskrepanz lässt die Finalisierungs-Saga ohne Entscheidung darüber, welcher Betrag als „richtig“ gilt. Das Ergebnis: manuelle Prüfung, eine Kundenbeschwerde, oder ein still falscher Ledger-Eintrag.

Was der Snapshot löst

Der richtige Ansatz friert den Zustand des Warenkorbs zum Zeitpunkt der Erstellung des Payment Intent ein — Betrag, Währung, Positionen, Steuer, Rabatte — als separaten, unveränderlichen Datensatz. Dieser Datensatz ist unabhängig von der Warenkorb-Tabelle; selbst wenn sich der Warenkorb ändert, die Aktion endet oder ein Produktpreis aktualisiert wird, bleibt der Snapshot exakt gleich.

PaymentSnapshot
  amount: 450.00
  currency: EUR
  lines: [...]
  createdAt: t0
  (auch wenn sich der Warenkorb bei t1, t2, t3 ändert, ändert sich dieser Datensatz nie)

Der an den PSP gesendete Betrag wird immer aus dem Snapshot gelesen, nie aus dem Live-Warenkorb. Auch die Finalisierungs-Saga verweist bei jedem Schritt — Bestandsreduzierung, Ledger-Eintrag, Bestellbestätigung — auf denselben Snapshot. So beruhen alle zahlungsbezogenen Entscheidungen auf derselben festen Tatsache, egal was später mit dem Warenkorb passiert.

Betrag und Währung validieren

Einfrieren allein reicht nicht — Sie müssen auch den tatsächlich vom PSP gemeldeten eingezogenen Betrag validieren. Stimmt der Betrag in der Antwort des PSP nicht mit dem Betrag im Snapshot überein (eine Rundungsdifferenz, ein Fehler bei der Währungsumrechnung, ein Integrationsdefekt), darf dieses Ereignis nicht automatisch als „abgeschlossen“ markiert werden — es muss in eine Abgleichs-Warteschlange fallen.

PSP-Antwort: amount=450.00, currency=EUR
Snapshot:    amount=450.00, currency=EUR
             → stimmt überein, Verarbeitung geht weiter

PSP-Antwort: amount=449.99, currency=EUR
Snapshot:    amount=450.00, currency=EUR
             → stimmt nicht überein, Finalisierung WIRD GESTOPPT, geht in die Prüf-Warteschlange

Dieser Validierungsschritt erkennt sowohl Integrationsfehler als auch mögliche Manipulationsversuche frühzeitig.

Die Versuchung, auf den Live-Warenkorb zurückzugreifen

Manche Teams greifen selbst nach dem Aufbau des Snapshot-Mechanismus während der Finalisierung wieder auf den Warenkorb zurück und sagen: „Aber der Bestand könnte sich geändert haben, schauen wir auf aktuelle Daten.“ Das unterläuft den ganzen Sinn des Snapshots: Die Bestandsprüfung sollte ein eigener Schritt mit eigener Fehler- und Kompensationslogik sein — die Entscheidung über Betrag und Währung sollte aber niemals auf Live-Daten zurückgreifen. Beides zu vermischen ist, als würde man einen bereits eingefrorenen rechtlichen Datensatz wieder zur Debatte stellen.

Die am häufigsten verwechselten Zuordnungen

❌ Erneutes Lesen des Warenkorbs bei der Finalisierung bedeutet „die frischesten Daten“ nutzen
✓ Erneutes Lesen des Warenkorbs bei der Finalisierung vermischt zwei verschiedene Zeitpunkte

❌ Ein Snapshot ist nur ein Logging-/Audit-Datensatz
✓ Ein Snapshot ist die einzige Quelle der Wahrheit für Zahlungs- und Finalisierungsentscheidungen

❌ Der Betrag vom PSP stimmt immer mit dem erwarteten Betrag überein, Validierung ist unnötig
✓ Eine Betrags-/Währungsabweichung sollte ein Ereignis in einer Warteschlange sein, nicht stillschweigend akzeptiert werden

❌ Bestandsaktualität und Zahlungsbetrag können mit demselben Mechanismus geprüft werden
✓ Die Bestandsprüfung ist ein eigener Schritt; die Betragsentscheidung sollte nie auf Live-Daten zurückgreifen

Checkliste zur Prüfung Ihres Snapshot-Designs

  1. Wird der an den PSP gesendete Betrag aus dem Live-Warenkorb oder aus einem eingefrorenen Snapshot-Datensatz gelesen?
  2. Aus welcher Quelle (Snapshot oder Live-Tabelle) liest jeder Schritt Ihrer Finalisierungs-Saga Betrag/Menge?
  3. Was passiert, wenn der Betrag vom PSP nicht mit dem Snapshot-Betrag übereinstimmt? Läuft es automatisch durch, oder stoppt es?
  4. Haben Sie getestet, dass der Snapshot unverändert bleibt, selbst wenn sich die Warenkorb-Tabelle nach der Snapshot-Erstellung ändert?
  5. Stimmt das Währungsfeld des Snapshots immer mit der tatsächlich an den PSP gesendeten Währung überein; wo wird eine Umrechnung protokolliert?

Können Sie nicht einmal eine dieser fünf Fragen klar beantworten, hat Ihr System wahrscheinlich ein stilles Betrags-Drift-Risiko.

Was aus diesem Teil bleiben sollte

  1. In dem Moment, in dem ein Payment Intent erstellt wird, müssen Betrag, Positionen und Währung des Warenkorbs in einem separaten, unveränderlichen Snapshot eingefroren werden.
  2. Die Finalisierungs-Saga sollte in keinem Schritt auf den Live-Warenkorb zurückgreifen; jede Entscheidung sollte sich auf den Snapshot beziehen.
  3. Der tatsächliche, vom PSP gemeldete Betrag sollte mit dem erwarteten Betrag im Snapshot verglichen werden; eine Abweichung sollte nicht automatisch durchlaufen, sondern zur Prüfung gehen.
  4. Die Bestandsaktualitätsprüfung ist eine eigene Verantwortlichkeit und sollte nicht mit der Betrags-/Währungsentscheidung vermischt werden.

Der Warenkorb ist ein Entwurf, offen für die Zukunft; die Zahlung ist eine in der Vergangenheit eingefrorene Entscheidung. Beide aus demselben Datensatz zu lesen, lässt zwei verschiedene Zeitpunkte wie eine Wahrheit aussehen.

FAQ

Häufige Fragen

Was ist Payment Intent?

Ein Datensatz, der die Zahlungsabsicht des Kunden darstellt und den an den PSP zu sendenden Betrag und die Währung trägt.

Was ist Snapshot?

Eine eingefrorene, unveränderliche Kopie einer Tatsache (Preis, Menge, Steuer, Rabatt), wie sie zu einem bestimmten Zeitpunkt bestand.

Stimmt es, dass „Erneutes Lesen des Warenkorbs bei der Finalisierung bedeutet „die frischesten Daten“ nutzen“?

Erneutes Lesen des Warenkorbs bei der Finalisierung vermischt zwei verschiedene Zeitpunkte

Was legt dieser Teil fest?

Dieser Teil erklärt, warum der „Rückgriff auf den Live-Warenkorb“ ein Risiko und keine Abkürzung ist, und wie ein unveränderlicher Snapshot das löst. In dem Moment, in dem ein Payment Intent erstellt wird, müssen Betrag, Positionen und Währung des Warenkorbs in einem separaten, unveränderlichen Snapshot eingefroren werden. Wenn ein Kunde zur Kasse kommt, können sich Preis, Kampagne und Bestandsinformation im Warenkorb noch ändern: Eine Aktion kann ablaufen, ein Preis kann aktualisiert werden, derselbe Kunde kann den Warenkorb in einem anderen Tab bearbeiten. Frieren Sie diese Information nicht in dem Moment ein, in dem der Payment Intent erstellt wird, können der an den PSP gesendete Betrag und der bei der Finalisierung erwartete Betrag zwei verschiedene Wahrheiten darstellen.

Gelernte Engineering-Prinzipien

  • In dem Moment, in dem ein Payment Intent erstellt wird, müssen Betrag, Positionen und Währung des Warenkorbs in einem unveränderlichen Snapshot eingefroren werden.
  • Finalisierungsentscheidungen sollten nie auf den Live-Warenkorb zurückgreifen, sondern immer auf den Snapshot verweisen.
  • Eine Abweichung zwischen dem vom PSP gemeldeten Betrag und dem Snapshot sollte nie automatisch durchlaufen, sondern ein prüfbares Ereignis werden.

Weiterlesen

Weiterlesen

Nachster Teil der Serie

Nachster Teil der Serie

Aus derselben Serie

Paylaş