--- title: "整合 Webhook" description: "要開始在你的應用程式中接收 webhook 事件,請按照以下步驟創建並註冊 webhook URL 端點:" source: "https://docs.tokenz.one/zh-TW/v1/checkout/webhooks-get-started" api_version: "v1" locale: "zh-TW" version_status: "legacy" docs_stage: "prod" --- # 整合 Webhook 要開始在你的應用程式中接收 webhook 事件,請按照以下步驟創建並註冊 webhook URL 端點: 1. **創建 webhook 端點處理器** 來接收事件數據的 POST 請求。 2. 使用後台 **註冊你的端點。** 3. **保護你的 webhook 端點。** 4. 使用[測試付款](https://docs.tokenz.one/zh-TW/v1/checkout/testing) **測試你的 webhook 端點。** ## 1. 創建處理器 設置一個能夠通過 POST 方法接受 webhook 請求的 HTTPS 端點功能。你可以使用 [Webhook.site](https://webhook.site/) 來查看有效酬載和測試傳送。 確保你的端點功能: • 處理帶有 JSON 酬載的 POST 請求,該酬載包含一個[事件物件](https://docs.tokenz.one/zh-TW/v1/checkout/webhooks#%E4%BA%8B%E4%BB%B6%E6%A6%82%E8%BF%B0)。 • 在執行可能導致超時的複雜邏輯之前,快速返回成功的狀態碼(2xx)。 ## 2. 註冊你的端點 在 [Tokenz Dashboard](https://dashboard.tokenz.one/) 上註冊你的 webhook 端點。 1. 前往你的 `主頁`。 2. 點擊 `創建新 webhook`。 3. 填寫你的 URL 和可選的描述,並選擇你希望接收的事件。我們建議至少選擇 `order.succeeded` 以確保不會錯過成功的客戶訂單。 4. 點擊 `創建 webhook`。 5. 複製並保存返回的簽名金鑰。你需要它來驗證收到的 webhook。基於安全性考量,我們只會顯示一次金鑰。 ![](https://docs.tokenz.one/docs/dashboard_create_webhook_en.png) ## 3. 保護你的 webhook 端點 Tokenz 發送的所有 webhook 都包含簽名,你必須驗證這個簽名以確保 webhook 是由 Tokenz 發送並且沒有被篡改。或者,你可以將 webhook 通知僅作為觸發器,通過 API 檢查訂單狀態變更。我們建議驗證 webhook 簽名以避免不必要的 API 請求。 每個簽名的事件都包含一個 `Tokenz-Signature` header,其中包含時間戳 (`t:`) 和一個或多個版本簽名 (`v1:`) 需要驗證。目前有效的簽名方案是 v1。 **Example:** ```plaintext t:1725864981111,v1:zjdrra3...Mh+e6s= ``` ### 驗證簽名 #### 1. 提取時間戳和簽名 使用逗號(`,`)作為分隔符拆分 header 以創建元素列表。然後使用冒號(`:`)作為分隔符拆分每個元素以得出 前綴 和 值,前綴 `t` 代表時間戳,而 `v1` 包含簽名,其他元素則忽略。 #### 2. 準備用於簽名的有效酬載 將時間戳(作為字串)與請求主體(即 JSON 酬載)的原始文本直接連接起來,組成有效酬載字符串。 #### 3. 計算簽名 使用 SHA256 雜湊函數計算 HMAC。使用端點的簽名金鑰作為金鑰,將有效酬載字符串作為雜湊計算時的訊息本文。 #### 4. 比較簽名 將 HTTP header 中的簽名與預期簽名進行匹配。評估當前時間與接收時間戳之間的差異以防止重放攻擊。如果簽名有效但時間戳已過期,應用程序可以拒絕有效酬載。建議的容許誤差為 300 秒(5 分鐘)。 重放攻擊發生在攻擊者攔截並重新傳輸合法的有效酬載及其簽名時。Tokenz 通過在 `Tokenz-Signature` header 中包含時間戳來減輕這種攻擊。 #### NodeJS ```javascript 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 處理正確運行,請按照我們的[測試指南](https://docs.tokenz.one/zh-TW/v1/checkout/testing)創建並完成一些測試付款。