Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Stripe リコンサイルの勘所
Search
acomagu
November 10, 2023
0
350
Stripe リコンサイルの勘所
- リコンサイルとは?
- なぜリコンサイルが必要なのか?
- Stripe においてリコンサイルは必要か?
- Stripe とのリコンサイル方法
acomagu
November 10, 2023
Tweet
Share
More Decks by acomagu
See All by acomagu
Stripe SSoT をするべきか否か
acomagu
0
25
JP_Stripes: リコンサイル(突合処理)のテスト
acomagu
0
55
「境界付けられたコンテキスト間の関係」についてもっと語ろう
acomagu
0
51
地方 MaaS 事例: アプリの進化に伴って変化してきた Stripe 利用方法
acomagu
0
190
CDK 一発で全てのエラーログを Slack に流す
acomagu
0
2k
AWS CDK を支える Constructs について
acomagu
0
150
DDDとは結局何なのか
acomagu
0
250
API Gateway HTTP API について
acomagu
0
120
JP_Stripes: 一貫性に寄与する設計
acomagu
0
83
Featured
See All Featured
Statistics for Hackers
jakevdp
796
220k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
10
810
Designing for Performance
lara
604
68k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
How STYLIGHT went responsive
nonsquared
95
5.2k
Bootstrapping a Software Product
garrettdimon
PRO
305
110k
Measuring & Analyzing Core Web Vitals
bluesmoon
4
170
Why Our Code Smells
bkeepers
PRO
335
57k
The Cult of Friendly URLs
andyhume
78
6.1k
Rebuilding a faster, lazier Slack
samanthasiow
79
8.7k
Adopting Sorbet at Scale
ufuk
73
9.1k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
Transcript
Stripe リコンサイルの勘所 The Designium, Inc. 伊藤勇希 @acomagu
リコンサイルとは?
リコンサイルとは? 「突合処理」「整合性チェック」とも言われる。 複数のシステムの状態を突合し、状態のズレが発生しないようにする仕組みのこと このスライドでは「システムの状態」のことを「データ」と呼びます(リコンサイルで考慮す べきシステムの状態は、DB に保存されたデータで決定できることがほとんどなため)。
なぜリコンサイルが必要なのか?
なぜリコンサイルをするのか 2つ以上の永続的なデータがあり、全体として結果整合性が求められるかつ、何らかの 理由で整合性が壊れる可能性がある場合 例: システム A はデータ a を DB
に持っていて、システム B は a から得られるデータ f(a) を DB に持っているが、システム A のデータはユーザーによって変更されることが ある
なぜリコンサイルをするのか 2つ以上の永続的なデータがあり、全体として結果整合性が求められるかつ、何らかの 理由で整合性が壊れる可能性がある場合 例: システム A はデータ a を DB
に持っていて、システム B は a から得られるデータ f(a) を DB に持っているが、システム A のデータはユーザーによって変更されることが ある
整合性が壊れる原因 - 通知なくデータの一部に変更が加えられる場合 - データの一方はそもそも外部システムである場合 - 運用の一環でデータを手動で変更することがある場合 -
異常によって整合性を取ることに失敗した場合 - 外部システムがデータの変更を通知してくれなかった場合 - 外部システムの API コールに失敗した場合 - ソースコードの間違いによって整合性が取れない場合 - 先の図で言う関数 f に間違いが発覚した場合
リコンサイルが必要ない場合 - 古いデータに関しては整合性が必要ない場合 - 整合性が必要なデータのうち、永続性がある(DB やメモリに長期間保存される)も のものは1つしかない場合(SSoT) - システム B
がシステム A からデータ a を都度取得して f(a) を計算しているならこれに当たる - データが完全にシステムの管理下にあるか、データの変更に関して確実に通知を 受け取ることができ、またソースコードの間違いや例外によるリスクが低い(許容で きる)場合 - 別システムでも、分散トランザクションを確実に実行できるならばリコンサイルの必要性が下がる場 合がある
Stripe においてリコンサイルは必要か?
Stripe とのリコンサイルは必要か - Stripe はデータを持つ外部システムであり、このデータと整合性を取る必要がある データが独自システムに永続化されているなら、リコンサイルが必要な可能性が高 い - 例: 独自システムの
Order オブジェクトが Stripe の Charge オブジェクトに依存して いる場合
Stripe との リコンサイルの方法
リコンサイルの方法 基本的には 1. どちらかのデータを起点として、もう一方の想定されるデータを計算し、突合する 2. もし差分があれば、差分を解消するような動作(補償)をする 3. 差分の解消に失敗した場合、例外を投げる ここで二つの疑問 -
想定されるデータはどうやって計算するのか? - 差分の解消は具体的にどうやってやるのか? ここはケースバイケースだが、Stripe に限って考えてみる
Stripe とのリコンサイルで事前に考えること 1. そもそも Stripe にどんな状態があるのか 2. Stripe の状態の中で、独自システムが関心のある状態はどれか 3.
それがどんな状態であるべきで、どんな状態であってはいけないのか
Stripe とのリコンサイルで事前に考えること 1. そもそも Stripe にどんな状態があるのか 2. Stripe の状態の中で、独自システムが関心のある状態はどれか 3.
それがどんな状態であるべきで、どんな状態であってはいけないのか
そもそも Stripe にどんな状態があるのか 「購入」に関係するいくつかのオブジェクトがある - Checkout Session - Payment Intent
- Charge それぞれについてどんな状態があるのかざっくり:
Checkout Session status: “open” | “complete” | “expired” Checkout Session
が支払われたかどうかを表す payment_status: “paid” | “unpaid” | “no_payment_required” 支払いがキャプチャされたかどうかを確認できる payment_intent: { … } 紐づいている Payment Intent
Payment Intent status: "requires_payment_method" | "requires_confirmation" | "requires_action" | "processing"
| "requires_capture" | "canceled" | "succeeded" 支払いの詳細な状態を取得できる。 charges: [ … ] 紐づいている Charge のリスト
Charge status: “succeeded” | “pending” | “failed” 個別の支払いが成功したかどうかが分かる refunded:
boolean Charge が全額返金されたかどうか refunded_amount: number 返金された金額 (部分返金されたかどうかはこの値が 0 以上かどうかで判断する)
主要な状態の表 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”
Stripe とのリコンサイルで事前に考えること 1. そもそも Stripe にどんな状態があるのか 2. Stripe の状態の中で、独自システムが関心のある状態はどれか 3.
それがどんな状態であるべきで、どんな状態であってはいけないのか
Stripe とのリコンサイルで事前に考えること 1. そもそも Stripe にどんな状態があるのか 2. Stripe の状態の中で、独自システムが関心のある状態はどれか 3.
それがどんな状態であるべきで、どんな状態であってはいけないのか ↓ 1. 表を見ながらどのフィールドをチェックすればいいか確認する 2. 独自システムの状態との対応表を作る 例: Order の状態が complete のとき、PaymentIntent.status は succeeded であるべき, 等
None
どうやって状態の差分を解消するのか? 基本的には「どちらかに寄せる」 ただ「全て A or B に寄せる」ようなことはできないことが多い。全ての組み合わせについ て丁寧に考えていくしかない。 IMO: リコンサイル実装のメンテナンス性を維持するためにも、「起こり得なそうな組み合
わせ」については無理に実装しようとせず、(エラーを投げるだけにして)運用で対処する と割り切るのも大切
どうやって状態の差分を解消するのか? 例えば - 独自システム側の Order は支払い完了状態なのに、Stripe 側では返金されていた ら? - もう一度別の決済を作成する?
- Order をキャンセルする? - 独自システム側の Order はキャンセル済みなのに、Stripe 側で返金されていな かったら? - Stripe 側で返金をする? - Order を支払い完了状態に直す?
私達の実装
私達の実装 Purchase というオブジェクトが Stripe の Checkout Session の状態に依存する状態を 持っています。 私達のサービスでは、Stripe
は下記の6つの状態を想定すれば十分であることが分かり ました。 「Checkout Session オープン」 「Checkout Session 期限切れ」 「キャンセル済み(全額返金済み or 失敗)」 「キャプチャ待ち」 「一部キャプチャ待ち」 「成功」 まず Stripe の支払いデータが上記のいずれに当たるのか返却する関数を作成しまし た。
None
私達の実装 Purchase オブジェクトは、その状態ごとにサブクラスがあります。 IncompletedPurchase CompletedPurchase CapturedPurchase RefundedPurchase … それぞれのクラスに reconcileWithStripe()
メソッドがあり、リコンサイルはそこで 実施します。
まとめ
まとめ - リコンサイルとは: システム間の状態の差分を解消する突合処理のこと - Stripe の状態に依存するデータを持つシステムでは、リコンサイルが必要になる場 合が多い - 実装に移る前に、「Stripe
が取りうる状態」の中から独自システムが関心のあるも のを切り分け、独自システムの状態との対応表を作る - 状態の差分の解消については、起こりうるパターンについて一つ一つどちらに寄せ るか考える
ありがとうございました