RANTANDocs
RANTAN を開く
ホステッドチェックアウト

決済 Webhook

署名付きの決済・チェックアウト・返金イベントをバックエンドで受信します。

RANTAN はホステッド決済、チェックアウト、返金の状態が変わると、署名付き HTTPS イベントを販売者バックエンドへ送信します。Webhook は非同期通知なので、検証して冪等に処理してください。

#Endpoint を登録

販売者 JWT で HTTPS endpoint を登録します。プライベート・loopback・ローカルネットワーク・認証情報付き URL・リダイレクト・非標準ポートは許可されません。

Create endpoint
curl -X POST https://app.rantan.xyz/api/v2/webhooks/endpoints/ \
  -H "Authorization: Bearer $RANTAN_SELLER_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://merchant.example/webhooks/rantan",
    "description": "Production",
    "subscribed_events": [
      "payment.succeeded",
      "checkout.completed",
      "checkout.failed",
      "refund.completed"
    ]
  }'
201 Created
{
  "id": "7d97ad12-7091-43f6-b9bc-707cab54ae29",
  "url": "https://merchant.example/webhooks/rantan",
  "description": "Production",
  "subscribed_events": [
    "payment.succeeded",
    "checkout.completed",
    "checkout.failed",
    "refund.completed"
  ],
  "is_active": true,
  "created_at": "2026-07-30T10:00:00Z",
  "updated_at": "2026-07-30T10:00:00Z",
  "signing_secret": "whsec_store-this-once"
}

#イベントタイプ

イベント送信タイミング
payment.succeededx402 決済が永続的に記録された時
checkout.completedRANTAN のフルフィルメントが完了した時
checkout.failedチェックアウトが最終失敗状態になった時
checkout.canceledセッションまたは購入がキャンセルされた時
checkout.expiredセッションまたは購入が期限切れになった時
refund.completed返金完了が記録された時

#イベント Payload

各イベントには固定 id があります。アクセス、在庫、クレジット、注文状態を変更する前に一意キーとして保存してください。

payment.succeeded
{
  "id": "d22f4de0-4e92-4dc9-af9f-aaadbbdad801",
  "type": "payment.succeeded",
  "api_version": "2026-07-30",
  "created_at": "2026-07-30T10:04:32Z",
  "data": {
    "object": {
      "checkout_session_id": "c8a9d48e-68b2-48dd-a754-75635cae71d6",
      "client_reference_id": "order_20260730_001",
      "product_id": 123,
      "purchase_id": "bfaf2aa4-dd86-4356-92db-ae0431806867",
      "purchase_status": "payment_confirmed",
      "completed": false,
      "buyer_wallet_address": "0x1234...abcd",
      "quantity": 1,
      "currency": "USDC",
      "total_amount": "12.50000000",
      "amount_atomic": "12500000"
    },
    "payment": {
      "provider": "x402",
      "transaction_hash": "0xabcd...",
      "amount_atomic": "12500000",
      "recipient": "0x9876...fedc"
    }
  }
}

#署名を検証

Rantan-Signature は t={Unix timestamp},v1={HMAC-SHA256} 形式です。正確な raw body を `${t}.${rawBody}` として digest を計算し、5分を超えた timestamp を拒否して定数時間で比較します。

Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

export async function handleRantanWebhook(req, res) {
  // Read the raw bytes before JSON parsing.
  const rawBody = await readRawBody(req);
  const signatureHeader = req.headers["rantan-signature"];
  if (typeof signatureHeader !== "string") {
    return res.status(400).end();
  }
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((part) => part.split("=")),
  );

  const timestamp = Number(parts.t);
  if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > 300) {
    return res.status(400).end();
  }

  const expected = createHmac(
    "sha256",
    process.env.RANTAN_WEBHOOK_SECRET,
  )
    .update(`${timestamp}.`)
    .update(rawBody)
    .digest("hex");

  const received = Buffer.from(parts.v1 ?? "", "hex");
  const calculated = Buffer.from(expected, "hex");
  if (
    received.length !== calculated.length ||
    !timingSafeEqual(received, calculated)
  ) {
    return res.status(401).end();
  }

  const event = JSON.parse(rawBody.toString("utf8"));
  await processEventOnce(event.id, event.type, event.data);
  return res.status(204).end();
}

#再試行と配信履歴

10秒以内に 2xx を返してください。リダイレクトは追跡しません。失敗時は1分、5分、30分、2時間、6時間、24時間、48時間後に再試行し、8回失敗すると failed になります。

#手動再試行

直近100件までの配信履歴を確認し、配信済みまたは失敗したイベントを再送できます。イベント id は変わらないため、受信処理は必ず冪等にしてください。

Replay delivery
curl -X POST \
  https://app.rantan.xyz/api/v2/webhooks/deliveries/$DELIVERY_ID/retry/ \
  -H "Authorization: Bearer $RANTAN_SELLER_JWT"

#Webhook API

POST/api/v2/webhooks/endpoints/

Endpoint を作成し、最初の signing_secret を受け取ります。

GET/api/v2/webhooks/endpoints/

販売者の endpoint 一覧を取得します。

PATCH/api/v2/webhooks/endpoints/{endpointId}/

URL、購読イベント、有効状態を変更します。

GET/api/v2/webhooks/endpoints/{endpointId}/

特定 endpoint の設定を取得します。

DELETE/api/v2/webhooks/endpoints/{endpointId}/

Endpoint を無効化し、以降の配信を停止します。

POST/api/v2/webhooks/endpoints/{endpointId}/rotate-secret/

以前の secret を無効化し、新しい signing_secret を取得します。

GET/api/v2/webhooks/deliveries/

配信状態とレスポンス履歴を取得します。

POST/api/v2/webhooks/deliveries/{deliveryId}/retry/

同じ event id で配信を再試行します。