Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Stripe リコンサイルの勘所

acomagu
November 10, 2023
300

Stripe リコンサイルの勘所

- リコンサイルとは?
- なぜリコンサイルが必要なのか?
- Stripe においてリコンサイルは必要か?
- Stripe とのリコンサイル方法

acomagu

November 10, 2023
Tweet

Transcript

  1. なぜリコンサイルをするのか 2つ以上の永続的なデータがあり、全体として結果整合性が求められるかつ、何らかの 理由で整合性が壊れる可能性がある場合
 例: システム A はデータ a を DB

    に持っていて、システム B は a から得られるデータ f(a) を DB に持っているが、システム A のデータはユーザーによって変更されることが ある

  2. なぜリコンサイルをするのか 2つ以上の永続的なデータがあり、全体として結果整合性が求められるかつ、何らかの 理由で整合性が壊れる可能性がある場合
 例: システム A はデータ a を DB

    に持っていて、システム B は a から得られるデータ f(a) を DB に持っているが、システム A のデータはユーザーによって変更されることが ある

  3. 整合性が壊れる原因 - 通知なくデータの一部に変更が加えられる場合
 - データの一方はそもそも外部システムである場合 
 - 運用の一環でデータを手動で変更することがある場合 
 -

    異常によって整合性を取ることに失敗した場合
 - 外部システムがデータの変更を通知してくれなかった場合 
 - 外部システムの API コールに失敗した場合 
 - ソースコードの間違いによって整合性が取れない場合
 - 先の図で言う関数 f に間違いが発覚した場合 

  4. リコンサイルが必要ない場合 - 古いデータに関しては整合性が必要ない場合
 - 整合性が必要なデータのうち、永続性がある(DB やメモリに長期間保存される)も のものは1つしかない場合(SSoT)
 - システム B

    がシステム A からデータ a を都度取得して f(a) を計算しているならこれに当たる 
 - データが完全にシステムの管理下にあるか、データの変更に関して確実に通知を 受け取ることができ、またソースコードの間違いや例外によるリスクが低い(許容で きる)場合
 - 別システムでも、分散トランザクションを確実に実行できるならばリコンサイルの必要性が下がる場 合がある

  5. Checkout Session status: “open” | “complete” | “expired” Checkout Session

    が支払われたかどうかを表す
 
 payment_status: “paid” | “unpaid” | “no_payment_required” 支払いがキャプチャされたかどうかを確認できる
 
 payment_intent: { … } 紐づいている Payment Intent
  6. Payment Intent status: "requires_payment_method" | "requires_confirmation" | "requires_action" | "processing"

    | "requires_capture" | "canceled" | "succeeded" 支払いの詳細な状態を取得できる。 charges: [ … ] 紐づいている Charge のリスト
  7. Charge status: “succeeded” | “pending” | “failed” 個別の支払いが成功したかどうかが分かる
 
 refunded:

    boolean Charge が全額返金されたかどうか
 refunded_amount: number 返金された金額
 (部分返金されたかどうかはこの値が 0 以上かどうかで判断する)

  8. 主要な状態の表 CheckoutSession .status CheckoutSession .payment_status PaymentIntent .status Charge.status Checkout Session

    作成直後 “open” “unpaid” “requires_paymen t_method” 無し 支払い情報入力直後 “complete” “unpaid” “processing” “pending” 支払い成功後 (未キャプチャ) “complete” “unpaid” “requires_capture” “succeeded” 支払い成功後(キャプ チャ済み) “complete” “paid” “succeeded” “succeeded” 支払い失敗時 “complete” “unpaid” “requires_paymen t_method” “failed” Checkout Session 期限切れ “expired” “unpaid” “canceled” 無し 返金済み “complete” “paid” “succeeded” “succeeded”
  9. Stripe とのリコンサイルで事前に考えること 1. そもそも Stripe にどんな状態があるのか
 2. Stripe の状態の中で、独自システムが関心のある状態はどれか
 3.

    それがどんな状態であるべきで、どんな状態であってはいけないのか
 ↓
 1. 表を見ながらどのフィールドをチェックすればいいか確認する
 2. 独自システムの状態との対応表を作る
 例: Order の状態が complete のとき、PaymentIntent.status は succeeded であるべき, 等
 

  10. どうやって状態の差分を解消するのか? 例えば
 - 独自システム側の Order は支払い完了状態なのに、Stripe 側では返金されていた ら?
 - もう一度別の決済を作成する?

    
 - Order をキャンセルする? 
 
 - 独自システム側の Order はキャンセル済みなのに、Stripe 側で返金されていな かったら?
 - Stripe 側で返金をする? 
 - Order を支払い完了状態に直す? 

  11. 私達の実装 Purchase というオブジェクトが Stripe の Checkout Session の状態に依存する状態を 持っています。
 私達のサービスでは、Stripe

    は下記の6つの状態を想定すれば十分であることが分かり ました。
 「Checkout Session オープン」
 「Checkout Session 期限切れ」
 「キャンセル済み(全額返金済み or 失敗)」
 「キャプチャ待ち」
 「一部キャプチャ待ち」
 「成功」
 まず Stripe の支払いデータが上記のいずれに当たるのか返却する関数を作成しまし た。

  12. まとめ - リコンサイルとは: システム間の状態の差分を解消する突合処理のこと
 - Stripe の状態に依存するデータを持つシステムでは、リコンサイルが必要になる場 合が多い
 - 実装に移る前に、「Stripe

    が取りうる状態」の中から独自システムが関心のあるも のを切り分け、独自システムの状態との対応表を作る
 - 状態の差分の解消については、起こりうるパターンについて一つ一つどちらに寄せ るか考える