再試行と冪等性
Webhook配信失敗時の再試行の仕組みと、イベントIDを用いた重複排除の方法について説明します。
エンドポイントがWebhookを2xxレスポンスで確認応答しない場合、Jammは再試行します。これを安全に処理するため、エンドポイントは冪等である必要があります — 同じイベントを2回受信しても、重複した処理が発生してはいけません。
再試行
エンドポイントが2xx以外のレスポンスを返すか、2分以内に応答しない場合、Jammは指数バックオフで配信を再試行します:
- 最大試行回数: 最大20回の試行。
- バックオフ: 1時間から始まり、試行ごとに2倍になります(上限3時間)。
- 期間: 再試行はJammが中止するまで約2日間にわたります。
- IDの安定性: Webhookの
idはすべての試行で変わらないため、重複排除に利用できます。
イベントへの応答
HTTPレスポンスのステータスコードで、Jammが再試行するかどうかが決まります:
| 状況 | 応答 | 理由 |
|---|---|---|
| イベントを正常に処理できた | 2xx | 確認応答 — 再試行なし。 |
| 一時的な失敗(DB停止、タイムアウト、参照先レコードが未作成など) | 2xx以外(またはタイムアウト) | Jammが再送するため、一時的な問題は自然に解消します。 |
| 恒久的な失敗(不明なID、不正なデータ、処理対象外のイベント) | 2xx + 自身でアラート | 恒久的なエラーは再試行しても解決しません。確認応答したうえで、内部でアラートを出して調査してください。 |
重要なガイドライン
- 再試行は競合の解決策ではありません: 再試行は数時間間隔です。
2xx以外は、ミリ秒単位の競合の解決策にはなりません。自身のレコードが作成される前にイベントが届き得る場合は、確認応答してAPIで整合を取ってください — 1時間後の再送を待たないでください。 - 永続化してから確認応答する: 理想的には、イベントを受信・保存できない場合にのみ
2xx以外を返してください。保存できたら2xxを返し、処理は自社インフラで独自の再試行により行ってください — Jammの再送をジョブキュー代わりに使わないでください。 - 上限に達する前にエスカレーションする: 一時的な失敗で
2xx以外を返す場合は、同じidが約5回配信失敗したら、2xx+ アラート + 整合に切り替えます。
冪等性
再試行により、同じイベントがエンドポイントに複数回届く場合があります。Webhookペイロードのidフィールドを使って重複を排除してください。
id(mwh-*形式)は:
- イベントごとに一意 — 異なるイベントは異なるIDを持ちます。
- 再試行をまたいで不変 — 同じイベントIDがすべての再試行で送信されます。
シンプルな重複排除パターン
async function handleWebhook(payload) {
if (await alreadyProcessed(payload.id)) {
return { ok: true }; // 確認応答するが処理はスキップ
}
await processEvent(payload);
await markProcessed(payload.id);
} ⏱️ 保存期間: 処理済みのIDは、少なくともJammの再試行期間(約2日間)の間は保存してください。
取りこぼしたイベントの整合
Webhookの配信を保証されたものとして扱わないでください。確定した状態は常にAPIにあります。そこで確認してください:
- 決済の結果 —
GetCharge(またはGetCharges)を呼び出してcharge_status(CHARGE_STATUS_SUCCESS、CHARGE_STATUS_FAILUREなど)を読み取ります。 - 返金 —
GetChargeを呼び出してcharge_status = CHARGE_STATUS_REFUNDED(当日キャンセルの場合はCHARGE_STATUS_CANCELLED)を確認します。amount_refundedはbuyerに返金された合計額、initial_amountは元の決済額であり返金可能な上限です。 - カスタマー/契約の有効化 —
GetCustomerを呼び出してactivatedを確認するか、GetContractで契約状況を確認します。
対象オブジェクトでの冪等性
APIルックアップを行う場合は、Webhook IDではなく対象オブジェクトのIDをキーにしてください。APIルックアップはmwh-*の識別子を持ちません。
- 決済・返金イベントは決済ID(
trx-*)をキーにします。 - 契約・アカウントイベントはカスタマーID(
cus-*)をキーにします。
ベストプラクティス
- 速やかに確認応答する — イベントを永続化したらすぐに
2xxを返し、重い処理は非同期で行う。 - IDで重複排除する — 下流の処理を実行する前に
mwh-*IDを確認する。 - 署名を検証する — ペイロードのデータに基づいて処理する前に署名を検証する(Signature Verificationを参照)。
- 恒久的な失敗ではアラートを出し、再試行させない —
2xxを返して内部でログ/アラートを出す。 - 重要な状態の整合を取る — 配信だけに依存せず、
GetCustomer/GetChargeで確認する。 - イベント順序に寛容になる — 再試行ではイベントが順不同で配信される場合があります。到着順ではなく
created_atとevent_typeに依拠してください。
Was this page helpful?
Need help? Coming Soon
Support Center Slack community Contact usSign up for our developer newsletter: Coming Soon
You can unsubscribe at any time. Read our
privacy policy and terms and conditions.
privacy policy and terms and conditions.