Aufspaltung des Zahlungs-POC: Architektur- und Design-Entscheidungen
Auf dieser Seite werden die architektonischen Optionen hinter dem aufgeteilten Zahlungsnachweis des Konzepts erläutert. Lesen Sie es, bevor Sie die Build-Eingabeaufforderungen in dieser Reihe verwenden, damit Sie verstehen, wie jede Komponente strukturiert ist und wie Sie die Muster in Ihrem eigenen Projekt anpassen können.
Das Grundprinzip
Bei dem Machbarkeitsnachweis geht es nicht um die eleganteste Split-Zahlungsimplementierung. Es geht darum zu zeigen wie man ohne eine Big-Bang-Umschreibung mit der Umstellung der Commerce-Logik auf App Builder.
Die Regel, die durchgängig angewendet wird, lautet:
Wenn etwas synchron im Commerce-Anfragezyklus laufen muss oder Commerce-interne APIs aufrufen muss, die keine saubere externe Oberfläche haben, bleibt es in PHP. Alles andere wandert nach App Builder.
Was lebt in Commerce (PHP) und warum
1. Kreditantrag speichern: PlaceOrderPlugin
Store-Guthaben wird mit Magento\CustomerBalance\Api\BalanceManagementInterface::apply() auf den Warenkorb angewendet. Diese Methode funktioniert nur für einen aktiven Warenkorb. Der Warenkorb wird zum Zeitpunkt der Bestellung inaktiv. App Builder erhält das E/A Ereignis (nachdem die Bestellung aufgegeben wurde. Daher ist es nicht möglich, eine Gutschrift von App Builder zu beantragen.
Die Lektion Alles, was den Warenkorbstatus vor der Bestellplatzierung ändern muss, muss in Commerce ausgeführt werden. Es gibt keine Problemumgehung.
2. Synchroner Schwellenwertwächter: CheckoutPlugin
Die Prüfung des Bestellschwellenwerts von 100 USD muss den Kunden bei der Zahlungsstufe blockieren, bevor er Place Order auswählt. Die Antwort muss im Commerce-Anfragezyklus synchron sein. App Builder ist ereignisgesteuert und asynchron, sodass in diesem Moment kein sofortiger Fehler zurückgegeben werden kann.
App Builder auch validiert den Schwellenwert (als Audit), aber das Kundenerlebnis hängt davon ab, dass die Commerce-Prüfung zuerst ausgeführt wird.
3. Benutzerdefinierte REST-Endpunkte: webapi.xml und SplitPaymentManagement
Die folgenden Endpunkte müssen:
SplitInvoiceServiceaufrufen (Rechnungen, die den internen Rechnungsdienst von Commerce verwenden)ShipOrder::execute()aufrufen (interner Versand-Service von Commerce)- Status und Status der Bestellung mit dem Auftragsstatus-Computer von Commerce aktualisieren
/V1/split-payment/orders/:id/cash-received und /V1/split-payment/orders/:id/cash-decline
Es gibt keine saubere öffentliche REST-Ebene für dieses Verhalten, sodass Commerce die Endpunkte verfügbar macht. App Builder nennt sie.
4. Beträge nach Angebot und Bestellung aufteilen: Beobachter und Plug-ins
Commerce benötigt die aufgeteilten Beträge (split_store_credit_amount, split_cash_amount, split_cash_status) für die Bestellung, sowohl für die REST-Antworten, die App Builder liest, als auch für die Admin. Die Beträge werden mit Erweiterungsattributen angehängt und in einem Beobachter auf sales_model_service_quote_submit_before aus dem Angebot in den Auftrag kopiert.
Was lebt in App Builder und warum
1. Ereignisgesteuerte Auftragsverarbeitung: payment-orchestrator
Nachdem sales_order_place_before ausgelöst wird, erhält App Builder das Ereignis. Er überprüft den Schwellenwert erneut (als Audit), zeichnet einen Kassenantrag Kommentar zur Bestellung auf und aktualisiert den Bestellstatus. Nichts davon erfordert neues PHP, nur REST zurück in Commerce.
2. Barakzept: payment-accept
Wenn ein ERP (oder ein Benutzer im Dashboard) den Empfang von Bargeld bestätigt, werden payment-accept Anrufe POST /V1/split-payment/orders/:id/cash-received. Rechnung, Versand und Auftragsstatus werden in Commerce abgewickelt. App Builder ist der Trigger.
3. Kassenrückgang: payment-decline
payment-decline ruft POST /V1/split-payment/orders/:id/cash-decline auf und Commerce storniert die Bestellung. Das gleiche Muster wie Barakzepte.
4. Benutzer-Dashboard: demo-dashboard
Ein eigenständiges HTML-Dashboard, das von einer App Builder-Web-Aktion bereitgestellt wird. Es ruft Bestellungen, die auf Bargeld warten, aus Commerce REST ab und bietet Accept-/Decline-Aktionen, die die oben genannten App Builder-Aktionen aufrufen. Commerce Admin ist nicht erforderlich.
Der Schwellenwert: zweimal absichtlich erzwungen
Customer at checkout
|
v
[Commerce: CheckoutPlugin] <- Synchronous, blocks immediately, user sees error
|
| (if somehow bypassed: direct API call, and so on)
v
[Order placed] -> I/O Event -> [App Builder: payment-orchestrator]
|
v
[evaluateThreshold()] <- Async audit, records failure comment
Commerce steuert den für Benutzende gerichteten Schutz; App Builder steuert die Prüfung nach der Platzierung. Das ist Absicht.
Der Store-Abspann: Warum es in PHP bleibt
What you might think would work (it does not):
Order placed -> I/O Event -> App Builder -> PUT /V1/carts/:id/store-credit
(Fails: cart is inactive after place order)
What actually works:
AroundPlaceOrder plugin
-> BalanceManagementInterface::apply($cartId, $amount) <- cart is still active
-> place order
-> order placed
-> I/O event: App Builder (store credit is already applied)
Die store-credit.js Datei im Orchestrator dokumentiert dies. Es ist ein No-op-Stub mit Kommentaren, die erklären, warum es nicht verwendet wird.
Erweiterungsattribute: der Kleber
Aufspaltungsbeträge bewegen sich durch das System bei Erweiterungsattributen:
Checkout JavaScript (Knockout)
| POST /V1/split-payment/set
v
SplitPaymentSession (PHP session)
| AroundPlaceOrder reads the session
v
CartInterface extension attributes
| `sales_model_service_quote_submit_before` observer
v
OrderInterface extension attributes -> `sales_order` flat columns
| I/O event payload includes these fields
v
App Builder `payment-orchestrator` reads the split amounts
Datenmodell
sales_orderflache Spalten, die dieses Modul hinzufügt
split_store_credit_amountsplit_cash_amountsplit_cash_statuspending, received oder declinedsplit_sc_invoice_idsplit_cash_invoice_idErweiterungsattribute (in CartInterface, OrderInterface und OrderPaymentInterface)
split_store_credit_amount(float)split_cash_amount(float)split_cash_status(Zeichenfolge)
Payload-Felder des I/O-Ereignisses
observer.sales_order_place_before wird in io_events.xml konfiguriert, um Folgendes in das Ereignis einzuschließen:
entity_id, quote_id, increment_id, subtotal,
split_store_credit_amount, split_cash_amount, split_cash_status
App Builder verwendet entity_id als Auftrags-ID und split_store_credit_amount und split_cash_amount für die Schwellenwertvalidierung.
Die fünf Randfälle des Konzeptnachweises umfassen
1. CapCustomerBalanceCollectPlugin
Der native Customer balance-Gesamt-Collector von Commerce kann zu viel anwenden (er kann den vollständigen verfügbaren Saldo sehen, nicht den sitzungsdeklarierten Aufspaltungsbetrag). Dieses Plug-in begrenzt den Betrag auf den in der Sitzung deklarierten Wert.
2. FixSplitPaymentGrandTotalPlugin
Nachdem das Store-Guthaben angewendet wurde, kann der Grand Total auf den bargeldlosen Betrag fallen. Die Checkout-JavaScript muss die Bestellsumme für die Aufspaltungsvalidierung () diese Änderung berechnen. Das Plug-in wird nach der Gesamterfassung ausgeführt und korrigiert die Anzeige, während der JavaScript grand_total nicht vertraut und den Wert aus Zwischensummensegmenten rekonstruiert.
3. FixInvoiceCustomerBalanceAfterTotalsPlugin
Wenn die Rechnungssummen zurückgezogen werden, kann die Gutschrift des Geschäfts zweimal vorgenommen werden. Dieses Plug-in korrigiert customer_balance_amount auf Rechnungen.
4. SplitPaymentZeroTotalPlugin
Nachdem die Gutschrift für den Store angewendet wurde, kann die Grand Total für den Warenkorb $0 sein (vollständige Gutschrift für den Store). Die Zero subtotal checkout von Commerce kann in diesem Fall Code blockieren. Dieses Plug-in ermöglicht COD, wenn der Barbetrag der Sitzung größer als 0 ist.
5. Erinnerung vor BalanceManagementInterface::apply() zitieren
apply() vergleicht den Betrag mit dem aktuellen Grand Total. Wenn die Summe bereits nur der Cash-Teil ist, kann apply() fehlschlagen oder die Obergrenze einschränken. PlaceOrderPlugin setzt die Gesamtreparatur vorübergehend aus, während der Saldo angewendet wird, mithilfe eines Sitzungs-Flags (beginBalanceApply / endBalanceApply).