RANTANDocs
RANTAN 열기
호스티드 체크아웃

결제 웹훅

서명된 결제·체크아웃·환불 이벤트를 판매자 백엔드에서 수신합니다.

RANTAN은 호스티드 결제, 체크아웃 또는 환불 상태가 바뀌면 판매자 백엔드로 서명된 HTTPS 이벤트를 전송합니다. 웹훅은 비동기 신호이므로 이벤트를 검증하고 멱등하게 처리하세요.

#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/

기존 비밀키를 폐기하고 새 signing_secret을 받습니다.

GET/api/v2/webhooks/deliveries/

전송 상태와 응답 이력을 조회합니다.

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

동일한 event id로 전송을 다시 시도합니다.