Webhookと毎時突合で崩れない同期を作る

通知は「変化のシグナル」としてだけ使い、データは必ず取り直す。信頼性設計の実践

Webhook突合冪等性キャンセル同期日程変更
読了時間: 9分

はじめに

公式サイトから予約を入れるだけなら、話は単純です。難しいのはその後で、OTAから予約が入ったり、運営者が予約管理システム上で日程を1日ずらしたり、キャンセルが発生したりします。これらを公式サイト側にどう反映するか。

この記事では、外部からの変更通知(Webhook)と定期的な照合(突合)を組み合わせて、ずれても必ず戻る同期を作った方法を紹介します。

通知の中身を信用しない

Webhookに署名がないという現実

多くのサービスは、通知が本物かを確認するための電子署名を付けてくれます。しかし今回使った予約管理システムのWebhookには、署名の仕組みがありませんでした。あるのは「好きなヘッダーを1つ付けられる」機能だけです。

そこで合言葉となる文字列をヘッダーに設定し、届いた通知のヘッダーと照合する方式にしました。ただし、これだけでは十分ではありません。合言葉が漏れれば偽の通知を送れてしまいます。

通知は「何かが変わった」だけを伝える合図

そこで根本的に発想を変えました。通知の本文を一切信用せず、データは必ず自分で取りに行くという方針です。

Webhookの扱い方
BEFORE
通知の中身をそのまま保存

偽の通知で予約内容を書き換えられる。通知の形式が変わると壊れる

AFTER
通知は合図としてのみ使う

「何か変わったらしい」を受けて、正規のAPIから正しいデータを取り直す

この方針なら、仮に偽の通知が送られてきても、起きるのは「無駄な照合処理が1回走る」だけです。データが壊れることはありません。通知の本文は保存もログ出力もせず、内容の指紋(ハッシュ値)だけを重複判定用に記録しています。

同じ通知が2回来ても大丈夫にする

ネットワークの都合で同じ通知が複数回届くことは珍しくありません。そこで通知の指紋をデータベースに記録し、同じものが来たら記録をスキップします。

ただし照合処理そのものはスキップしません。理由は、日程を9月10日→12日→10日と戻したとき、1回目と3回目の通知内容が完全に同じになるからです。「同じ通知だから何もしなくてよい」と判断すると、その変更を取りこぼします。

毎時の突合で取りこぼしを拾う

通知が届かない前提で作る

Webhookは便利ですが、届かないことがあります。実際、外部からの通知が100回以上連続で失敗した事例もありました。通知だけに頼る設計は、いつか必ず破綻します。

そこで1時間ごとに動く定期処理を用意し、現在有効な予約をまとめて取得して自社DBと突き合わせるようにしました。通知が届けば即座に、届かなくても1時間以内には必ず追いつきます。

二重の同期経路
予約管理システムでの変更

OTA予約・日程変更・キャンセル

Webhook(速い経路)

数秒で反応。ただし届かないことがある

毎時突合(確実な経路)

最大1時間の遅れ。ただし必ず追いつく

どちらも同じ照合処理を呼ぶ
自社DBの予約状態を更新

日程・キャンセル・復活を反映

速い経路と確実な経路の両方を用意し、どちらも同じ照合処理を呼ぶ。これで速さと確実さを両立できます。

照合は読むだけにする

この照合処理は、予約管理システムに対して読み取りしか行いません。書き込みをすると、その変更がまた通知を呼び、無限ループになる危険があるからです。

「同期」というと双方向を想像しがちですが、片方向に絞ることで構造がぐっと単純になります。

変更の種類ごとに対応を変える

キャンセルは決済状況で扱いを分ける

予約管理システム側でキャンセルされたとき、公式サイト側がどう振る舞うべきかは、決済がすでに済んでいるかで変わります。

決済済みのキャンセルを自動で処理しないのは、返金額の判断がキャンセルポリシーや個別事情に左右されるからです。システムは「これは人が見るべき案件です」と伝えるところまでを担当します。

日程変更は「動かすもの」と「動かさないもの」を決める

運営者が日程を変更したとき、連動して直すべき項目とそうでない項目があります。

宿泊者専用ページの有効期限はチェックアウト日に連動するので、自動で再計算します。一方、有効開始日は絶対に動かしません。すでにお客様に配ったURLが「まだご利用いただけません」に逆戻りしたら、混乱を招くだけだからです。

金額も自動では変えません。お客様が同意した金額を、後から一方的に書き換えるべきではないからです。日程が変わって料金が変わるなら、それは運営者がお客様と話し合って決めることです。

自動課金日が過ぎているときの扱い

日程が前倒しされて、自動課金の予定日がすでに過去になってしまうことがあります。このとき即座に課金すると、運営者がまだ料金を調整している最中かもしれません。

そこで原則は課金せず、運営者に「確認してください」とメールを送る形にしました。ただしチェックインが当日か翌日なら、待っている余裕がないので即座に課金します。過剰に請求するミスは取り返しがつかないので、判断に迷う場面では人に委ねる設計にしています。

変化がないときはメールを送らない

突合の結果は運営者にメールで報告しますが、差分がゼロの回はメールを送りません。1時間ごとに「変更なし」が届いたら、本当に大事な通知が埋もれてしまうからです。

逆に、1日1回動く自動課金処理は対象がゼロ件でも必ず報告メールを送ります。こちらは「今日も仕組みが動いた」という生存確認を兼ねているからです。目的によって、送る・送らないを使い分けています。

まとめ

崩れない同期を作るために大事にしたのは、次の3点です。

  1. 通知の中身を信用しない — 合図としてだけ受け取り、データは正規のAPIで取り直す
  2. 二重の経路を持つ — 速いWebhookと確実な定期突合を組み合わせる
  3. 判断が要る変更は人に渡す — 返金や料金変更は自動化せず、目印を付けて通知する