メインコンテンツまでスキップ

冪等性と順序

冪等とは、同じ操作を繰り返しても結果が 1 回分と同じになることです(用語集)。

再送でもイベントのボディと id は変わりません。署名検証済みボディの id に UNIQUE 制約を設けて重複排除します。 Stbl-Pay-Event-Id ヘッダは署名対象外なので、冪等キーに使いません。

テスト通知は先に分ける​

署名の検証が済んだら、inbox に保存する前に、生の本文を JSON.parse した値で test === true かを判定します。テスト通知は注文を変えず 2xx だけを返します(テスト通知)。

受信と処理を分ける​

受信ハンドラは検証したイベントを inbox に永続化し、その後で 2xx を返します。保存したことと業務処理が完了したことを、別の状態として記録してください。

CREATE TABLE stbl_pay_inbox (
event_id TEXT PRIMARY KEY,
payload JSONB NOT NULL,
received_at TIMESTAMPTZ NOT NULL DEFAULT now(),
processed_at TIMESTAMPTZ
);

再送が届いたときに既存の未処理イベントがあっても、新たな行は作りません。ワーカーが未処理行を再試行できるようにします。未知の種別は注文を変更せず無視します。

業務処理のトランザクション​

同じ DB トランザクションで次を行います。

  1. イベントと対応する注文をロックし、処理済みか確認する。
  2. 決済 ID・加盟店・金額と注文の対応を確認する。不一致は調査用に保留する。payment_intent_id に対応する注文が無い通知(店頭の QR コード決済など)は、保留せず、注文を変えずに処理済みにする。
  3. 注文を条件付きで更新し、商品提供などの outbox を一意な注文キーで記録する。
  4. イベントに処理済み時刻を記録する。

外部処理はコミット後にワーカーから呼び、外部サービスにも冪等キーを渡します。状態照会や手動処理も同じ注文更新経路を通し、イベント ID が違っても二重出荷しないようにします。

順序​

再送を含めて到着順序は保証されません。created_at の大小だけで注文を上書きせず、決済の終端状態と注文の対応を確認します。同じ決済に異なる終端結果が届くなどの不整合は、自動処理せず状態照会と調査へ回してください。

完了判定とイベント一覧も参照してください。