2023.10.10
クレカ決済にみる外部システム連携のための堅牢な設計
こんにちは、Y.C.です。今携わっているサービスでは決済処理の開発を担当することが多く、その度により良い設計について頭を悩ませてきました。外部システムと連携する際、どうすればデータの整合性を保ち、変更に強いコードにすることができるでしょうか?今回は、そのために今まで考えてきたことを言語化して、これから外部連携サービスを実装される方の判断材料を提供すると共に、未来の自分がさらなる改善をおこなっていくための礎としたいと思います。
結論ファースト
外部システム連携の難しさ
シーケンス図
- トランザクションを長時間維持することの問題
- DB との connection を、API がタイムアウトするまでの長時間占有してしまう恐れがあります。決済リクエストごとに connection が必要となると、同時に捌けるリクエスト数が大きく制限されます。
- 予期せずレコードのロックをとってしまう可能性があります。 例えば MySQL の場合、トランザクション中 に insert したレコードに外部キーがあると、その参照先のレコードに共有ロックがかかってしまい更新できなくなります。
- 整合性の問題
- 決済に成功したら 成功処理をして commit、失敗したら失敗処理をして commit すべきですが、決済 API がタイムアウトしたりレスポンスが異常な場合は成功したのか失敗したのか不明なため、どちらも行ってはいけません。
これを解決するため、外部とやり取りをする度に都度 レコードを update して commit します。こうするといつシステム異常やネットワーク異常が発生してもどこまで処理を進めたか分かるため、後から処理を続けることができます。
状態遷移図
必要な状態を状態遷移図で表現しました。決済に失敗すると初期状態に戻るのではなく、決済失敗状態に遷移します。状態が循環しないようにすることで、ある状態から別の状態へ遷移が必ず 1 度だけ実行されます。つまり、API コールやリダイレクトといった外部とのやりとりが exactly once であることを保証します。決済成功状態なのに決済 API がコール出来ていないという事態や、逆に決済 API をコールしたのに初期状態のままであるような事態にはならないわけです。
黒字で示した状態は、通常の操作がされる限り長時間その状態であり続ける可能性のある安定した状態です。ここでは定常状態と呼びたいと思います。3DS 認証中が定常状態というのは違和感があるかもしれませんが、ユーザが 3DS 認証を諦めて離脱するとここで停止してしまいます。またこの段階では決済は確実に成功していないので、このまま放置して問題ありません。
赤字で示したのが API コール中しか存在しない状態で、過渡状態と呼びたいと思います。通常であれば、過渡状態は 1 秒も持続しないでしょう。もしタイムアウト時間以上経過している場合は、本当にタイムアウトしたか、どこかで異常が発生し処理が停止した可能性があります。こうなると、決済が成功したのか、失敗したのか、あるいは止まっているのか分かりません。ここでユーザが再度支払おうとすると、初期状態ではないので reject します。二重払いされないという意味では安全ですが、可能な限り早急に追加の対応を行い、定常状態に遷移する必要があります。
異常への対応
-
手動で実際の決済状態を確認して状態遷移させる
-
頻度が多いと大変ですし、人がすばやく反応できないと長時間過渡状態が維持されてしまいます。
-
-
ユーザが再度支払おうとしたら決済状態確認 API をコールする
-
先ほどは reject するとしましたが、決済状態確認 API をコールし決済の成否を確定させることができれば、それに従い定常状態に遷移することができます。
-
ユーザが再度支払おうとしなければ意味がないです。
-
-
決済 API がタイムアウトしたり異常なレスポンスが返った場合に自動リトライする
-
すぐ決済処理を続行できる可能性があります。
-
決済 API に冪等性が必要です。ない場合は代わりに決済状態確認 API を利用します。
-
こちらがシステムダウンした場合は意味がないです。
-
決済代行サービス側やネットワーク上で問題があった場合、短時間のうちにリトライしても意味がないです。
-
-
バッチで決済状態確認 API をコールする
-
過渡状態のままタイムアウト時間経過している場合に決済状態確認 API をコールし、結果に応じて定常状態に遷移させます。
-
即時性はありませんが確実性は高いです。
-
API 異常レスポンスの判定の罠
実装パターン
- 購入対象に依存する処理(ex. 在庫を減らす)
- 決済方法に依存する処理(ex. 後続の API コールに必要なパラメータを記録する)
- 両方に依存する処理(ex. 決済手数料や消費税といったお金の流れを記録する)
愚直
継承
移譲
まとめ
宣伝
グループ研究開発本部 次世代システム研究室では、最新のテクノロジーを調査・検証しながらインターネット上の高度なアプリケーション開発を行うエンジニア・アーキテクトを募集しています。募集職種一覧 からご応募をお待ちしています。
グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。
Follow @GMO_RD