再送・順序・冪等性の設計
レシートローラーは、受信側が受け取れなかった配信を自動で再送します。その結果、同じイベントが2回以上届くことや、順序が前後することがあり、まれに届かないこともあります。この記事では再送の規則と、それを前提にした受信側の作り方を説明します。
成功と失敗
- 受信側が
2xxを返せば成功です - それ以外はすべて失敗として再送されます。
4xx(401や404を含む)も5xxと同じ扱いです - 10秒以内に応答がない場合や、DNS・TLS・接続のエラーも失敗です。配信ログでは
httpStatusが0になります
再送のタイミング
| 試行 | 送るタイミング |
|---|---|
| 1回目 | イベントの発生直後 |
| 2回目 | 1回目の失敗の1分後 |
| 3回目 | 2回目の失敗の10分後 |
| 4回目 | 3回目の失敗の1時間後 |
- 最大4回で、最後の試行は最初の送信から約71分後です(各試行の待ち時間10秒を除く)
- 再送のボディ、署名、
X-RR-Deliveryは最初の送信と同じです - 4回とも失敗すると、そのWebhookは無効(
active: false)になり、以後のイベントは送られません。1つのイベントで4回失敗しただけで、Webhook全体が止まります - 無効になっている間に起きたイベントは保留されず、有効に戻してもあとから届きません。過去のイベントを手動で再送する機能もありません
- 一時停止(
active: false)にしても、すでに再送待ちになっているイベントの再送は止まりません
再送は保存されていない
再送の待ち時間はレシートローラーのサーバー上で保持されており、キューやデータベースには保存されていません。待っている間にサーバーが再起動すると(デプロイなど)、そのイベントの残りの再送は行われず、Webhookも無効になりません。配信ログには最後に失敗した試行が残るだけです。
そのため「再送があるから必ず届く」とは考えず、取りこぼしが困る連携では定期的にAPIで取り直して突き合わせてください。売上なら取引一覧APIで期間を指定して取得できます。
順序は保証されない
イベントはそれぞれ独立に送られ、再送はあとから届きます。次のようなことが起こり得ます。
- 受注の
order.updatedがorder.createdより先に届く - 1回目に失敗した売上が、あとの売上より遅れて届く
順序が大事な処理では、Webhookを「変更があった」という合図として扱い、APIで最新の状態を取り直すのが安全です。ボディの timestamp はレシートローラーがイベントを送り出した時刻で、再送でも変わらないので、同じ受注について古い通知で新しい状態を上書きしないための比較に使えます。
重複の除き方
同じイベントが2回以上届くのは、主に受信側が処理を終えたのに 2xx を返せなかったとき(10秒を超えた、途中でエラーになった)です。どの配信にも、イベントごとに一意な X-RR-Delivery ヘッダー(ボディの id と同じ値)が付いているので、これをキーにします。
async function handle(deliveryId, event) {
// 処理済みならそのまま 2xx を返す
const inserted = await db.processedDeliveries.insertIfAbsent({
delivery_id: deliveryId,
received_at: new Date(),
});
if (!inserted) return;
await processEvent(event);
}
- 処理済みの記録は、再送が終わる約71分より長く、少なくとも数時間は残します
- 処理済みの記録と業務の更新を同じトランザクションで行えると、途中で失敗したときに記録だけが残ることを防げます
- 同じイベントを購読しているWebhookを複数登録している場合も、どのWebhookからの配信にも同じ
X-RR-Deliveryが付くので、まとめて1回だけ処理できます
業務上のキーで書き込みを冪等にする方法も併用できます。売上なら data.id({terminal_id}|{yyyy-MM}|{transaction_id} の形)、受注なら data.order_id をキーにして上書き保存すれば、同じ内容が2回届いても結果は変わりません。
おすすめの受け方
- 署名を検証する
X-RR-Deliveryで重複を確認し、処理済みならすぐ200を返す- ボディを自分の側のキューやテーブルに保存する
- すぐに
200を返す(10秒を大きく下回るように) - 重い処理や外部サービスへの送信は、保存したデータから別の処理で行い、失敗したら自分の側でやり直す
こうすると、外部サービスが止まっても受信は失敗せず、4回の失敗でWebhookが無効になることも避けられます。
よくある失敗
| 作り | 起きること |
|---|---|
| 重い処理を終えてから応答する | 10秒を超えて失敗になり、同じイベントが再送される |
一時的な失敗のたびに 5xx を返し続ける | 1つのイベントで4回失敗するとWebhookが無効になり、以後のイベントがすべて止まる |
| 重複を除かずに数量や金額を加算する | 再送で二重に計上される |
| 届いた順に状態を上書きする | 古い通知が新しい状態を上書きする |
| Webhookだけを頼りにする | 無効化中やサーバー再起動時のイベントに気づけない |
関連ガイド
-
Webhookの登録方法開発者ポータルの画面とAPIでWebhookを登録する手順、購読イベントの選び方、テスト送信、変更・一時停止・削除の方法を説明します。
-
SNS Webhookバイパス(LINEなど外部SNSのWebhook転送)レシートローラーがLINE等のSNSプラットフォームから受け取ったWebhookを、店舗側の同意のもとで開発者アプリへ転送する「SNS Webhookバイパス」機能の仕組み、設定方法、署名の取り扱い、注意点を解説します。
-
Webhookが届かないWebhookが届かないときに、テスト送信と配信ログで原因を切り分け、無効化・購読イベント・接続・署名検証を順に確認する手順です。
-
監視と失敗時の対応Webhookの有効状態・最終送信日時・配信ログの見方と、自動で無効になったことに気づいて復旧し、抜けた期間をAPIで補う方法を説明します。
-
開発者向けヘルプ目次レシートローラー開発者向けヘルプ目次です。開発者申請、アプリケーション登録、OAuth認証とスコープ、実装ガイド(ウォレットアプリ・店舗向けWebhook・Survey API)、データ領域別ガイド、運用とセキュリティ、コミュニティ、トラブルシューティングまでをまとめています。