INSIGHT

決済は一回、通知は二回——連携を重複処理しないための確認

ACTION|次の確認へ AI / 活用のヒント

同じ出来事の通知が再送されても、業務処理を一度にできるか。

注文画面では、支払いは一回しか行われていない。ところが連携記録には、支払い完了の通知が二件届いている。通知を受けるたびに出荷依頼を作る仕組みなら、同じ注文から二つの仕事が始まるかもしれない。

ここで「通知を一回だけ送ってもらう」と頼むだけでは、設計条件にならない。外部サービスは、届かなかった可能性がある通知を再送する場合がある。必要なのは、通知の回数を業務の回数へ直結させず、同じ出来事を見分ける仕組みである。

冪等性とは、再送をなくすことではない。同じ出来事を繰り返し受けても、決めた業務結果を一回に保つ性質である。

通知、出来事、業務処理を三つに分ける

通知は「外部で何かが起きた」と知らせる封筒のようなものだ。配達が二回でも、支払いという出来事が二回起きたとは限らない。そして、支払いを受けて自社が行う注文確定、在庫引当、出荷依頼、メール送信は、さらに別の業務処理である。

記録も三段に分ける。

  1. 受信記録:いつ、どの通知IDを受け、署名を確認したか
  2. 出来事の記録:どの支払いや注文に関する、何の状態変化か
  3. 業務処理の記録:注文確定や出荷依頼を実行済みか、保留か

通知IDだけで同じ出来事を識別できるかは、連携先の仕様による。別の通知IDでも、同じ支払いや注文に関する重複のことがある。そこで、通知の識別子に加え、対象となる支払い・注文の識別子、通知の種類、自社側の現在状態を照合する。

「二件届いたから一件を削除する」のではなく、受信記録は残したまま、同じ業務を二度進めない。後から再送の理由や処理結果を追うためである。

連携先の仕様を読んでから、識別方法を決める

StripeのWebhook公式資料 は、同じイベントが複数回届く場合があること、イベントの配信順序を保証しないこと、署名を検証すること、処理済みのイベントを記録することなどを説明している。また、場合によっては別々のEventオブジェクトが生成されるため、関連するオブジェクトIDとイベント種別で重複を識別する考え方も示している。

これはStripeの仕様であり、すべての外部サービスが同じID、再送条件、順序を使うという意味ではない。利用先ごとに、通知IDは一意か、同じ業務事象に別IDが付くことがあるか、順番は保証されるか、署名をどう確かめるか、現在状態をどこへ照会できるかを確認する。

資料から、二重請求が実際に起きたとも、通知が重複すれば必ず二重出荷になるとも言えない。本稿の注文と出荷は、設計を確かめるための業務例である。

提供元の再送仕様は、障害ではなく入力条件である。その条件を自社の一回限りの業務へどう変換するかを設計する。

処理済み判定は、「見つけたら無視」だけでは足りない

最初の通知を受け、注文は確定したが、出荷依頼の直前で処理が止まったとする。二回目の通知を単に「受信済み」として無視すれば、出荷されない。反対に最初から全部やり直せば、すでに完了した処理を重複する可能性がある。

そこで業務の状態を、「受信」「確認済み」「注文確定済み」「出荷依頼済み」「完了」「要照合」のように分ける。重複通知を受けたら、通知IDだけでなく現在状態を見る。完了なら結果を再利用する。途中なら、完了済みの工程を繰り返さず、次へ進めるかを判定する。外部での成立状態が分からなければ、自動再実行せず照合へ送る。

実装を担当する人は、同じ識別子で二つの処理を同時に始められない制約と、状態更新を一まとまりに扱う方法を選ぶ。具体的な方式は使うデータベースや連携先で異なる。大切なのは、「チェックしてから実行する」二つの動作の間に別処理が入り、両方が未処理と判断する余地まで確認することだ。

受信先が「受け取りました」と返す時点も決める。後で処理するための記録がまだ残っていないのに受信完了を返すと、その直後の停止で仕事を取り落とす可能性がある。受信の受付と業務の完了を別の状態にし、受付済みの仕事が最後まで進んだかを追う。

冪等にする範囲を、業務ごとに切る

同じ通知を二回処理しても、すべての工程を同じ方法で一回にできるとは限らない。注文記録の更新は一意な識別子で抑えられても、外部倉庫への依頼、会計への計上、利用者へのメールは、それぞれ別の受付記録と照会方法を持つことがある。

工程ごとに次を決める。

  • 同じ業務を識別するキーは何か
  • 二回呼ばれたとき、前回の結果を返せるか
  • 外部へ依頼済みかを照会できるか
  • 状態が分からないとき、誰の確認へ送るか
  • 取消しや訂正が必要なら、誰が実行するか

一つの「処理済み」印で全工程をまとめない。出荷依頼済みでもメール未送信という状態はあり得る。利用者に見せる結果も、「成功」だけではなく、完了した工程と保留中の工程が分かるようにする。

冪等性の境界はコードの関数ではなく、二度起きてはいけない業務結果ごとに決める。

テスト用の一件を再送し、二回目の処理結果まで確かめる

許可されたテスト環境と架空の注文で、同じ通知を二回送る試験を開発担当と用意する。実際の課金・出荷につながらないことを先に確かめる。最初は最後まで完了させ、二回目で新しい注文や出荷が作られないことを見る。途中停止の試験も本番では行わず、完了済みの仕事を繰り返さないかを確認する。

順不同も別に試す。取消しや返金に関する通知を先に受けた後、支払い完了が届いたとき、最後に届いた通知だけで状態を前へ戻さないことを確認する。通知ID、対象ID、受信順、署名確認、処理前後の状態、外部依頼、最終判断を一件の記録で追う。

利用開始前に、重複通知を処理済み、継続可能、要照合へ分けられ、二度起きてはいけない業務結果が一回に保たれることを確認する。仕様や状態が分からない間は、自動確定せず、人が連携先の記録と照合する。

再送をなくすのではなく、同じ仕事を重複させない記録を作る

通知が二回来たことは、決済が二回行われた証拠ではない。けれども、通知のたびに同じ業務を始める設計なら、重複実行の入口にはなる。

自分がつなぐ一件について、通知の識別子と業務の状態を、どこで見られるかを確かめたい。実装方法が分からなくても、二度起きてほしくない結果と、試したい条件は担当へ渡せる。テストの記録を残し、完了・途中・不明を分けるところから、連携の確認を進める。

参考資料