本文へ移動
開発者リソースWebhook

Webhook エンドポイントで Tokenz のイベントを受信する

Tokenz アカウントのイベントを Webhook (ウェブフック) エンドポイントで検知し、連携において自動的にリアクションをトリガーできるようにします。

金額フォーマット: Webhook ペイロード内の金額値(amount フィールドなど)は各通貨の最小単位で表現されます。通貨ごとの正確なエンコーディングについては、サポートされている通貨を参照してください。

Webhook を使用する理由

Tokenz Checkout (チェックアウト) の連携を構築する際に、Tokenz アカウントで発生するイベントをリアルタイムで受信し、バックエンドシステムがそれに応じてアクションを実行できるようにする必要があります。

Webhook イベントを有効にするには、Webhook エンドポイントを登録する必要があります。 登録後、Tokenz は Tokenz アカウントでイベントが発生すると、リアルタイムでイベントデータをアプリケーションの Webhook エンドポイントにプッシュできるようになります。 Tokenz は、HTTPS を使用して Webhook イベントをイベントオブジェクトを含む JSON ペイロードとしてアプリに送信します。

Checkout の結果通知を常に受け取れるよう、リダイレクト処理に加えて Webhook を実装することを強くお勧めします。

イベントオブジェクト

イベントが発生すると、Tokenz は新しいイベントオブジェクトを生成します。 1つの API リクエストで複数のイベントが作成される場合もあります。

Tokenz アカウントに Webhook エンドポイントを登録すると、Tokenz はイベントオブジェクトを登録された Webhook エンドポイントに POST リクエストの一部として自動的に送信できるようになります。 Webhook エンドポイントがイベントを受信すると、アプリはバックエンドアクション (order.succeeded イベントを受信したらお客様に購入商品のダウンロードリンクを送るなど) を実行できます。

当社が Webhook エンドポイントに送信するイベントオブジェクトには、変更されたオブジェクトのスナップショットが含まれます。 Webhook には常に発生したイベントの全オブジェクトとイベントのメタデータが含まれます。

イベントペイロードの例

以下は、注文が成功した場合に送信されるイベントの例です。

json
{
    "id": "a1b2c3d4-e5f6-7g8h-9i0j-k1l2m3n4o5p6",
    "object": "order.succeeded",
    "createdAt": "2025-09-24T03:22:20.297Z",
    "test": true,
    "eventData": {
        "type": "order",
        "version": "v2",
        "data": {
            "order": {
                "id": "order_1D5xA9BnKfv_t",
                "object": "order",
                "status": "succeeded",
                "amount": {
                  "amount": 3700,
                  "currency": "JPY"
                },
                "items": [
                  {
                    "id": "item_1D6F8mR5TnK_t",
                    "detail": {
                      "product": {
                        "price": {
                          "amount": 1200,
                          "currency": "JPY"
                        },
                        "quantity": 3,
                        "label": "ひとにぎりのエメラルド",
                        "description": "80+8",
                        "images": [
                          "https://images.ctfassets.net/z82qbo7cv7ia/1dWPbk5Qx2M1Qikj6Knyuc/e37b2c26829c0d30793a348ae3adb3b0/fake-pass.webp"
                        ]
                      }
                    }
                  },
                  {
                    "id": "item_1xejV7puGeK_t",
                    "detail": {
                      "product": {
                        "price": {
                          "amount": 100,
                          "currency": "JPY"
                        },
                        "quantity": 1,
                        "label": "Diamond",
                        "description": "",
                        "images": [],
                        "sku": "sku_103843dfjhdfgiu",
                        "taxCategory": "VIRTUAL_CURRENCY"
                      }
                    }
                  }
                ],
                "description": "Game items purchase",
                "reference": "ORDER_2024_001",
                "test": true,
                "expiresAt": "2024-12-31T23:59:59Z",
                "createdAt": "2024-01-15T10:00:00Z",
                "updatedAt": "2024-01-15T10:05:30Z"
            }
        }
    }
}

ライブモードとテストモード

1つのエンドポイントをライブモードとテストモードの両方に使用している場合、ライブモードとテストモード両方のイベント配信リクエストをエンドポイントで受け取る場合があります。 テストフラグでオブジェクトがライブモードまたはテストモードのどちらに存在するのか確認し、正しいイベントの処理を判断してください。

バージョン

version はイベントの API のバージョンを示し、含まれる data.object の構造を決定します。

イベントのタイプ

必要に応じて横にスクロール
タイプ説明
order.created新しい注文が Checkout Session の作成 エンドポイントから作成されました。
order.succeededお客様が注文の支払いに成功しました。 お客様に商品を配送してください。
order.expired注文の有効期限が切れました。
order.canceled注文がキャンセルされました。
order.failed注文の支払いが失敗しました。
refund.created返金が作成されました。
refund.failed返金が失敗しました。
dispute.created異議申し立てが作成されました。
dispute.closed異議申し立てがクローズされました。
redemption.completedギフトコードの利用、または無料アイテムの受け取りが完了しました。kind で種類を識別します(source は非推奨)。無料アイテムでは code は省略されます。

再送とべき等性

Tokenz は、Webhook に対してエンドポイントが 2xx レスポンスで受領を通知することを想定しています。それ以外のステータスコードを返した場合、タイムアウトした場合、または到達できなかった場合、Tokenz は最大 3 日間、自動的に配信を再試行します。最初の再試行は、配信失敗から約 5 秒後、15 秒後、30 秒後に行われます。その後は遅延時間を増やし、再試行の頻度を下げます。再試行の時刻は多少変わることがあるため、後続の再試行の正確な時刻に依存しないでください。テストモードの Webhook 配信では異なる再試行ポリシーを使用する場合があるため、ライブモードの再試行時刻の測定または検証には使用しないでください。エンドポイントが 2xx を返すと、そのイベントの再試行は停止します。同じイベントを複数回受信する可能性があり、イベントの到着順序は保証されません。

配信はべき等に処理してください:

  • イベントを安全に受信したら、できるだけ早く 2xx を返し、重い処理は非同期で行ってください。
  • イベントのトップレベルの id をべき等キーとして使用してください。処理済みのイベント ID を記録し、すでに処理したイベントはスキップすることで、再送によって注文が二重に履行されることを防げます。

トラブルシューティング:エンドポイントがイベントを受信しない

「Webhook が届かない」場合、そのほとんどは Tokenz が実際に送信しており、エンドポイントがそれを拒否していることを意味します。Tokenz は 2xx 以外のレスポンス(またはタイムアウトや到達できないエンドポイント)を配信の失敗とみなし、最大 3 日間自動的に再試行します。そのため、あなたの側では何も届いていないように見える一方で、Tokenz は同じ配信が繰り返し失敗しているのを見ています(例えば HTTP 400 が連続するなど)。

Tokenz はエンドポイントごとの配信ログを公開していないため、ご自身のエンドポイントから診断してください。

1. エンドポイントが返した HTTP ステータスを確認する。 これは Tokenz が見ているものとまったく同じです。サーバーのアクセスログ/エラーログで受信した POST を確認してください。

  • 2xx — イベントは配信され、受領が通知されました。それでもシステムが処理を実行しなかった場合、問題は配信ではなく、レスポンス後のハンドラーにあります。
  • 400、401、403 — エンドポイントはイベントを受信しましたが、拒否しました(下記を参照)。
  • 5xx またはタイムアウト — ハンドラーがエラーになったか、応答が遅すぎました。

2. 400 を返している場合は、まず署名の検証を確認してください。 これが最も一般的な原因です。

  • 生のリクエストボディに対して検証する。 ほとんどのフレームワークは JSON を解析して再シリアライズするため、バイト列が変わり HMAC が一致しなくなります。正確な生のバイト列を(例えば express.raw() で)読み取り、検証してから解析してください。Webhook エンドポイントを保護する を参照してください。
  • 正しい署名用シークレットを使用する。 シークレットはエンドポイントを登録するときに一度だけ表示され、そのエンドポイント固有のものです。ローテーションされた、または入力を誤ったシークレットは、すべてのイベントで検証に失敗します。
  • テストモードとライブモードを一致させる。 テストモードのイベントをライブの署名用シークレットで検証する(またはその逆)と、決して一致しません。1 つのエンドポイントで両方を処理する場合は、イベントの test フラグを使ってシークレットを選択してください。
  • クロックのずれに注意する。 5 分のタイムスタンプ許容時間で拒否している場合、サーバーのクロックが大きくずれていると、本来は有効なイベントを拒否してしまう可能性があります。

3. 2xx 以外のレスポンスになるその他の原因:

  • エンドポイントの前段にあるファイアウォール、WAF、または IP 許可リストが Tokenz のリクエストをブロックしている。
  • エンドポイントが HTTPS で公開されていない、またはダッシュボードに登録された URL が誤っているか、間違った環境を指している。
  • リクエスト処理の途中で重い処理を行ってタイムアウトしている。まず 2xx で受領を通知し、その後に非同期で処理してください。

4. 配信を独立して確認する。 エンドポイント(またはそのコピー)を Webhook.site に向けて、Tokenz が配信していることを確認し、正確なペイロードとヘッダーを検査してから、テストモードの支払いでイベントを発生させてください。

エンドポイントが 2xx を返すと、再試行は停止します。再試行はすでに処理したイベントを再送する可能性があるため、ハンドラーはべき等に保ってください(上記を参照)。