APIリファレンス:Webhook
Webhookは、イベントが起きたあとに自分のサーバーへ知らせを送ります。 アプリのWebhook URL(任意)をその他 → Merchant APIで設定すると、受け取る設定にしたイベントごとに、署名付きのJSONボディがPOSTされます。
ボディの形
{
"update_id": 123,
"update_type": "invoice_paid",
"request_date": "2026-08-11T12:00:00Z",
"payload": { … }
}
update_idは、同じイベントを送り直しても変わりません。
重複の排除はこの値を基準にしてください。
request_dateは、配信を試みるたびに記録されます。
payloadは、そのイベント種別の完全なオブジェクトです。
請求書のイベントなら請求書オブジェクト、ほかに送金リンク、送金、サブスクリプションのオブジェクトがあります(それぞれの形は各リファレンスのページにあります)。
イベントの種別
update_type | 発火する場面 | ペイロード |
|---|---|---|
invoice_paid | 請求書が支払われたとき。個別の受信設定は不要です | 請求書オブジェクト |
invoice_expired | 請求書が未払いのまま期限を過ぎたとき | 請求書オブジェクト |
check_activated | 送金リンク、または指定された受取人の1人分が受け取られたとき | 送金リンクオブジェクト。受取人一覧のあるリンクではactivated_slotも含まれます |
check_expired | 受取人一覧のあるリンクが期限切れになり、未受取分がアプリに戻ったとき | recipientsと各受取人のclaimed_atを含む送金リンクオブジェクト |
transfer_completed | 送金が完了したとき、またはオンチェーンの出金が承認されたとき | 送金または出金オブジェクト |
transfer_returned | 未受取の保留中の送金がアプリに戻ったとき、またはオンチェーンの出金が失敗して保留額が戻ったとき | 送金または出金オブジェクト |
refund_completed | 返金が実行されたとき | refunded_*のフィールドが入った請求書オブジェクト |
subscription_activated | ユーザーがプランを承認したとき | サブスクリプションオブジェクト |
subscription_charged | 期間分が課金されたとき。課金の種別はcharge.kindが示します(initial・renewal・resubscribe) | サブスクリプションオブジェクトとcharge({period_no, kind, asset, amount, fee, paid_at}) |
subscription_cancelled | どちらかの側がサブスクリプションをキャンセルしたとき | サブスクリプションオブジェクト |
subscription_expired | 猶予期間が未払いのまま終わったとき | サブスクリプションオブジェクト |
アプリで作成したオンチェーンの出金にも、同じ送金イベントが使われます。そのペイロードにはkind: "withdrawal"、network・address・txidなどのチェーン関連フィールド、completedまたはfailedのステータスが含まれます。transfer_idは通常の送金とは別に採番されるため、使う前にkindでペイロードの種類を区別してください。ダウンロードできるOpenAPIリファレンスには、両方の形式を記載しています。
invoice_paid以外はすべて、選んで受け取る方式です(Crypto Botに対する拡張です。Crypto Botの形に厳密に合わせた実装は、自分で選ばないかぎり知らないupdate_typeに出会いません)。
アプリごとに、Merchant API画面の追加のWebhookイベントから選びます。
Webhookの項目にあるトグルには、上の表の識別子が表示されます。イベントを受信するにはWebhook URLを保存してください。
署名を検証する
配信には必ず**TgPayCrypto-API-Signatureヘッダーが付きます(TgCryptoPay-API-SignatureとCrypto-Pay-API-Signatureは同じ値の互換の別名です)。
値は、生のリクエストボディのHMAC-SHA256を16進数にしたもので、鍵はアプリのメインAPIトークン**のSHA-256ダイジェストです。Crypto Botと同じ方式なので、既存の検証コードはそのまま動きます。
import hashlib, hmac
secret = hashlib.sha256(API_TOKEN.encode()).digest()
expected = hmac.new(secret, raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, headers["TgPayCrypto-API-Signature"])
検証は受け取った生のバイト列に対して行ってください。 一度読み込んで書き出し直したものはバイト単位で変わることがあり、検証に失敗します。 署名するのはメイントークンだけで、制限付きトークンが署名することはありません。 メイントークンを再発行すると、Webhookの署名の鍵もその場で切り替わるので、サーバー側の値も同じタイミングで更新してください。
配信と再送
- 試行がタイムアウトする前に2xxのレスポンスが返れば、配信は成功です。速やかに応答し、時間のかかる処理は別に実行してください。
- エラーステータス、タイムアウト、接続失敗の場合は、間隔を延ばしながら上限回数まで再送されます。配信は保証されません。障害のあとには、
getInvoicesで支払い状況を照合してください。 - 最後の試行が終わると、そのイベントの配信は打ち切られます。Webhook URL自体が自動で無効になることはありません。 応答の不安定なエンドポイントでも、アプリの配信設定が自動で解除されることはありません。
- 送り直しがある以上、ハンドラーはべき等でなければなりません。
処理に入る前に、
update_idで重複を排除してください。
⚠️ 注文を処理する前に検証する
Webhook URLには誰でもPOSTできます。
署名の検証が通るまで、ボディは信用できない入力として扱ってください。
注文を出荷せず、ユーザーへの入金もせず、支払い済みとして記録もしないでください。
安全な順序は、署名を検証する、update_idで重複を排除する、そのうえで処理する、です。
この記事は役に立ちましたか?
ご意見ありがとうございます。