Webhook
Webhook 把收款(收單)訂單事件推送到你控制的 HTTPS 地址,後端不用輪詢就能知道付款到了。它按帳戶在 開發者 → Webhook(/home/developer/webhooks)裡設定:儲存回呼地址、複製簽名金鑰、傳送測試事件,並在那裡查看每一次投遞。該頁需先通過你本人的個人身份認證(KYC)才能打開,儲存設定、傳送測試事件、重放投遞也都需要。
對回呼地址的要求
- 標準連接埠的公網 HTTPS 地址;內網或本機地址儲存時會被拒絕。
- 10 秒內回傳任意
2xx。不跟隨重新導向(3xx),重新導向按失敗計。 - 投遞是 至少一次:用信封裡的
id去重。
信封
json
{
"id": "3f0d7a2e-…",
"type": "acquiring.order.paid",
"createdAt": "2026-10-09T08:12:33.000Z",
"dataVersion": 1,
"data": {
"orderId": "…",
"orderNo": "…",
"status": "paid",
"amount": "120.00",
"receivedAmount": "120.00",
"currency": "USDT",
"paymentMethod": "usdt-trc20",
"custodyMode": "platform",
"verification": null,
"metadata": {}
}
}| 欄位 | 含義 |
|---|---|
id | 事件 id(UUID);重試與重放都是同一個 id |
type | 事件類型,見下表 |
createdAt | 事實發生的時間(ISO 8601) |
dataVersion | data 的結構版本,目前為 1 |
data | 訂單事實,金額為十進位字串;metadata 是你建單時附帶的物件 |
事件類型
type | 時機 |
|---|---|
acquiring.order.paid | 客戶付款已確認 |
acquiring.order.settled | 資金已結算到你的餘額 |
acquiring.order.funds_reorg_compensated | 鏈上重組影響了這筆付款,平台已為訂單補償 |
merchant.test | 「傳送測試事件」按鈕發出的事件,不重試 |
兌換訂單目前不投遞 Webhook,請輪詢 GET /cash/v1/merchant/exchange/orders/{orderId}。
驗簽
每次投遞帶 X-Cash-Signature: t=<unix 秒>,v1=<hex>,其中 v1 = HMAC-SHA256(secret, "<t>.<原始請求內容>")。請對 原始 請求內容位元組計算 HMAC(不要重新序列化 JSON),用恆定時間比較,並拒絕幾分鐘以前的時間戳以防重放。
js
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyCashWebhook(rawBody, signatureHeader, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(signatureHeader.split(',').map((part) => part.split('=')));
const expected = createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) <= toleranceSeconds;
return fresh && expected.length === parts.v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}在主控台輪換簽名金鑰後,舊簽名立即失效——先部署新金鑰,再輪換。
重試、死信與重放
失敗的投遞(網路錯誤、逾時、非 2xx、重新導向)按指數退避重試:從 60 秒起、上限 1 小時,最多 8 次。最後一次仍失敗後事件成為 死信,出現在 開發者 → Webhook 頁(/home/developer/webhooks)的「Webhook 投遞紀錄」裡並帶「重放」按鈕,重放會用同一個 id 再發一次。每次嘗試的結果、HTTP 狀態與原因碼都列在那裡;請求內容與金鑰不會顯示。