整合 Webhook
要開始在你的應用程式中接收 webhook 事件,請按照以下步驟創建並註冊 webhook URL 端點:
- 創建 webhook 端點處理器 來接收事件數據的 POST 請求。
- 使用後台 註冊你的端點。
- 保護你的 webhook 端點。
- 使用測試付款 測試你的 webhook 端點。
1. 創建處理器
設置一個能夠通過 POST 方法接受 webhook 請求的 HTTPS 端點功能。你可以使用 Webhook.site 來查看有效酬載和測試傳送。
確保你的端點功能:
• 處理帶有 JSON 酬載的 POST 請求,該酬載包含一個事件物件。 • 在執行可能導致超時的複雜邏輯之前,快速返回成功的狀態碼(2xx)。
2. 註冊你的端點
在 Tokenz Dashboard 上註冊你的 webhook 端點。
- 前往你的
主頁。 - 點擊
創建新 webhook。 - 填寫你的 URL 和可選的描述,並選擇你希望接收的事件。我們建議至少選擇
order.succeeded以確保不會錯過成功的客戶訂單。 - 點擊
創建 webhook。 - 複製並保存返回的簽名金鑰。你需要它來驗證收到的 webhook。基於安全性考量,我們只會顯示一次金鑰。

3. 保護你的 webhook 端點
Tokenz 發送的所有 webhook 都包含簽名,你必須驗證這個簽名以確保 webhook 是由 Tokenz 發送並且沒有被篡改。或者,你可以將 webhook 通知僅作為觸發器,通過 API 檢查訂單狀態變更。我們建議驗證 webhook 簽名以避免不必要的 API 請求。
每個簽名的事件都包含一個 Tokenz-Signature header,其中包含時間戳 (t:<unix-timestamp-in-milliseconds>) 和一個或多個版本簽名 (v1:<HMAC signature>) 需要驗證。目前有效的簽名方案是 v1。
Example:
t:1725864981111,v1:zjdrra3...Mh+e6s=
驗證簽名
1. 提取時間戳和簽名
使用逗號(,)作為分隔符拆分 header 以創建元素列表。然後使用冒號(:)作為分隔符拆分每個元素以得出 前綴 和 值,前綴 t 代表時間戳,而 v1 包含簽名,其他元素則忽略。
2. 準備用於簽名的有效酬載
將時間戳(作為字串)與請求主體(即 JSON 酬載)的原始文本直接連接起來,組成有效酬載字符串。
3. 計算簽名
使用 SHA256 雜湊函數計算 HMAC。使用端點的簽名金鑰作為金鑰,將有效酬載字符串作為雜湊計算時的訊息本文。
4. 比較簽名
將 HTTP header 中的簽名與預期簽名進行匹配。評估當前時間與接收時間戳之間的差異以防止重放攻擊。如果簽名有效但時間戳已過期,應用程序可以拒絕有效酬載。建議的容許誤差為 300 秒(5 分鐘)。
重放攻擊發生在攻擊者攔截並重新傳輸合法的有效酬載及其簽名時。Tokenz 通過在 Tokenz-Signature header 中包含時間戳來減輕這種攻擊。
const crypto = require('crypto');
const express = require('express');
const app = express();
const port = 3000;
// Use express.raw() so the signature is computed over the exact bytes
// Tokenz sent; re-serializing a parsed body can fail verification.
// If your app registers express.json() or another body parser globally,
// mount this webhook route BEFORE it so the raw body is preserved.
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
// The signing secret shown once on webhook creation is Base64-encoded.
const secret = 'webhook-endpoint-secret';
const sig = req.headers['tokenz-signature'];
let timestamp, signature;
for (let sigPart of sig.split(',')) {
let [key, value] = sigPart.split(':');
if (key === 't') {
timestamp = value;
} else if (key === 'v1') {
signature = value;
}
}
const checkSignature = crypto.createHmac('sha256', Buffer.from(secret, 'base64'))
.update(`${timestamp}${req.body.toString('utf8')}`)
.digest('base64');
// The `t` value in Tokenz-Signature is a Unix timestamp in milliseconds.
const tolerance = 5 * 60 * 1000; // 5 minutes
if (
signature === checkSignature &&
Math.abs(Date.now() - Number(timestamp)) <= tolerance
) {
return res.status(200).end();
} else {
return res.status(400).end();
}
});
4. 測試你的 webhook 端點
為了確保你的 webhook 處理正確運行,請按照我們的測試指南創建並完成一些測試付款。