手册
不変のチェックアウト スナップショットの設計: カートを凍結する決定 (Immutable Payment Snapshot Design)
支払い開始時にカートをライブで読み取ると、金額と通貨は未定のままになります。インテントの瞬間にフリーズするスナップショットがなければ、ファイナライゼーションは確実に機能しません。
分散型決済エンジン
部分 4 的 22
取得と完了の間のギャップを埋める一連の分散型支払いアーキテクチャ。
バスケットは動くターゲットです
顧客が支払い画面にアクセスすると、カート内の価格、キャンペーン、在庫情報は変更される可能性があります。キャンペーンが期限切れになったり、価格が更新されたり、同じ顧客が別のタブでカートを変更したりする可能性があります。支払い意図の作成時にこの情報を凍結しないと、PSP に送られる金額と最終処理時に予想される金額が 2 つの異なる現実を表す可能性があります。```text Sepet (canlı, değişken) --intent anında--> Payment Snapshot (donmuş, değişmez) ↓ PSP'ye gönderilen tutar = snapshot'taki tutar
## 最初に説明した概念```text
📦 Payment Intent
Müşterinin ödeme yapma niyetini temsil eden, PSP'ye gönderilecek tutarı ve para birimini taşıyan kayıt.
📦 Snapshot
Bir anlık gerçeğin (fiyat, miktar, vergi, indirim) o an olduğu haliyle donmuş, sonradan değişmeyen kopyası.
📦 Price-at-Intent
Ödeme başlatıldığı anda geçerli olan fiyatın, sonraki kampanya veya fiyat değişikliklerinden bağımsız olarak korunması ilkesi.
📦 Basket Drift
Sepetin, ödeme süreci ilerlerken (kullanıcı başka bir sekmede değişiklik yaparak veya arka plan işleriyle) snapshot'tan farklılaşması.
📦 Amount Reconciliation
PSP'den gelen gerçek tahsilat tutarının, snapshot'taki beklenen tutarla karşılaştırılması.
```スナップショットはカートの「写真」ではありません。バスケットの現在の状態は法的記録として凍結され、後で変更することはできません。
## ライブバスケットを読むと誤解を招く理由
一部のシステムでは、支払い意図が作成された後の最終段階でカートを再度調べて「現在の金額」を取得します。このアプローチは、「最新のデータ」が使用されているという感覚を与えるため、魅力的に見えます。しかし実際には、それは 2 つの異なる時点での 2 つの異なる現実を混同します。```text
t0: Müşteri ödeme başlatır → Sepet: 3 ürün, 450 TL, kampanya aktif
t1: PSP'ye 450 TL isteği gider
t2: Kampanya süresi dolar, sepet artık 480 TL gösterir
t3: Finalization, sepete tekrar bakar → 480 TL bekler ama PSP 450 TL tahsil etmiştir
```この非互換性により、ファイナライゼーションの物語は、どの金額を「正しい」とみなすべきかを決定できない状況に陥ります。その結果、手動によるレビュー、顧客からの苦情、または暗黙のうちに不正確な財務記録が発生します。
## スナップショットによって問題が解決されました
正しいアプローチは、支払い意図が作成された時点のカートの現在の状態 (金額、通貨、品目、税、割引) を別個の不変の記録として凍結することです。このレコードはバスケット テーブルから独立しています。バスケットが変更されたり、キャンペーンが終了したり、製品価格が更新されたりしても、スナップショットは同じままです。```text
PaymentSnapshot
amount: 450.00
currency: TRY
lines: [...]
createdAt: t0
(sepet t1, t2, t3'te değişse de bu kayıt asla değişmez)
```PSP に送信される金額は、ライブ カートからではなく、常にスナップショットから読み取られます。最終処理では、在庫の値下げ、財務記録、注文確認のステップで常にこのスナップショットが参照されます。このようにして、カートの将来が何であれ、支払い関連のすべての決定は同じ不変の真実に基づいて行われます。
## 金額と通貨の確認
スナップショットはただフリーズするだけではありません。 PSP から返される実際の回収量も確認する必要があります。 PSP の応答の金額がスナップショットの金額と一致しない場合 (丸めの違い、通貨換算エラー、統合の欠陥など)、このイベントは自動的に「完了」としてマークされるべきではありません。紛争キューに入る必要があります。```text
PSP cevabı: amount=450.00, currency=TRY
Snapshot: amount=450.00, currency=TRY
→ eşleşti, işleme devam
PSP cevabı: amount=449.99, currency=TRY
Snapshot: amount=450.00, currency=TRY
→ eşleşmedi, finalization DURDURULUR, inceleme kuyruğuna düşer
```この検証手順では、統合エラーと改ざんの可能性のあるシナリオの両方を早期に検出します。
## 「ライブカートに戻る」ルアー
スナップショットの仕組みが確立された後でも、「でも株価は変わっているかもしれないから、現在のデータを見てみよう」と言ってファイナライズの途中でバスケットに戻るチームもあります。これはスナップショットの目的を損なうものです。在庫管理は別のステップとして (そして独自の失敗/補償ロジックを使用して) 扱う必要があります。金額と通貨の決定は、ライブデータに頼ってはいけません。この 2 つを混同することは、凍結された法的記録を議論のために再度開くようなものです。
## このエピソードで最も混乱を招く対戦```text
❌ Finalization'da sepete tekrar bakmak, “en güncel veri” kullanmaktır
✓ Finalization'da sepete tekrar bakmak, iki farklı zaman noktasındaki gerçeği karıştırmaktır
❌ Snapshot sadece bir loglama/audit kaydıdır
✓ Snapshot, ödeme ve finalization kararlarının tek referans kaynağıdır
❌ PSP'den gelen tutar her zaman beklenen tutarla eşleşir, doğrulama gereksizdir
✓ Tutar/para birimi uyuşmazlığı otomatik değil, kuyruğa düşen bir olay olmalıdır
❌ Stok güncelliği ile ödeme tutarı aynı mekanizmayla kontrol edilebilir
✓ Stok kontrolü ayrı bir adımdır; tutar kararı asla canlı veriye dönmemelidir
設計チェックリストのスナップショットを作成する
- PSP に送信した金額は、ライブ カートから読み取られたものですか、それとも凍結されたスナップショット レコードから読み取られたものですか?
- ファイナライゼーションの各ステップでは、どのソース (スナップショットまたはライブ テーブル) から金額が読み取られますか?
- PSP から返された量がスナップショットの量と一致しない場合はどうなりますか?自動的に通過しますか、それとも停止しますか?
- スナップショットの作成後にバスケット テーブルが変更されても、スナップショットが変更されないことをテストしましたか?
- スナップショットの通貨フィールドは、PSP に送信される通貨と常に同じですか。変換があった場合、このステップはどこに記録されますか?
これら 5 つの質問のうち 1 つでも明確に答えられない場合は、システム内に「サイレント 金額ドリフト」が発生するリスクがある可能性があります。
このセクションで覚えておくべきこと
- 支払い意図が作成されたらすぐに、カートの金額、明細、通貨を別の不変のスナップショットとして凍結する必要があります。
- ファイナライゼーション サーガは、どの段階でもライブ カートに戻ってはなりません。すべての決定はスナップショットを参照する必要があります。
- PSP から返される実際の量は、スナップショット内の予想される量と比較する必要があります。紛争は自動的に解決されるべきではなく、再検討されるべきです。
- 在庫の最新の管理は別の責任となります。金額/通貨の決定と混同しないでください。
バスケットは未来の青写真です。支払いは過去に凍結された決定です。同じ記録からこれら 2 つを読み取ることは、2 つの異なる時代を 1 つの現実のように見せることです。
FAQ
Frequently asked questions
支払い意図とは何ですか?
顧客の支払いの意思を表し、PSP に送信される金額と通貨を記録するレコード。
スナップショットとは何ですか?
瞬間的な現実 (価格、数量、税金、割引) をその瞬間のまま冷凍コピーしたもので、後で変更されることはありません。
「ファイナライズ時にカートをもう一度見るということは、「最新のデータ」を使用することを意味します」は本当ですか?
ファイナライゼーションでバスケットをもう一度見ると、2 つの異なる時点で現実が混乱します
このセクションでは何を修正しますか?
このセクションでは、「ライブカートに戻す」ことが近道ではなくリスクである理由と、不変スナップショットがそれをどのように解決するかについて説明します。支払い意図が作成されたらすぐに、カートの金額、明細、通貨を別の不変のスナップショットとして凍結する必要があります。顧客が支払い画面にアクセスすると、カート内の価格、キャンペーン、在庫情報は変更される可能性があります。キャンペーンが期限切れになったり、価格が更新されたり、同じ顧客が別のタブでカートを変更したりする可能性があります。支払い意図の作成時にこの情報を凍結しないと、PSP に送られる金額と最終処理時に予想される金額が 2 つの異なる現実を表す可能性があります。
学到的工程原理
- 支払い意図が発生するとすぐに、バスケットの金額、明細、通貨を不変のスナップショットとして凍結する必要があります。
- 最終決定の決定は決してライブカートに戻すべきではありません。常にスナップショットを参照する必要があります。
- PSP から返された量とスナップショットが一致しない場合、これは自動的なイベントではなく、レビューの対象となる必要があります。
继续阅读
继续阅读
系列中的下一个
API (アプリケーション プログラミング インターフェイス) リクエストを超えた冪等性 (反復可能で安全なトランザクション)
冪等性は単一のヘッダーではありません。これは、API キーからステップ ポインターまで、5 つの異なるレイヤーで個別に設定する必要がある防御スタックです。
系列中的下一个
収集(キャプチャ)は簡単だが、ファイナライズはなぜ難しいのか?
PSPが資金を得るのはほんの一歩だ。注文を完了してください。これは、在庫、財務、報告、清掃の各ステップをすべて成功させる必要がある物語です。
同系列
決済システムにおける Webhook の信頼性
Webhook が繰り返されたり、消えたり、順序が乱れたり、遅れて到着したりします。署名を検証し、迅速な ACK を返し、同期的な重労働を実行しないでください。