--- title: "集成 Webhook" description: "要在应用程序中接收 webhook 事件,请按照以下步骤创建并注册 webhook 端点::" source: "https://docs.tokenz.one/zh-CN/v1/checkout/webhooks-get-started" api_version: "v1" locale: "zh-CN" version_status: "legacy" docs_stage: "prod" --- # 集成 Webhook 要在应用程序中接收 webhook 事件,请按照以下步骤创建并注册 webhook 端点:: 1. **创建 webhook 端点处理器**,用于接收事件数据的 POST 请求。 2. 使用仪表板 **注册你的端点。** 3. **保护你的 webhook 端点。** 4. 使用[测试付款](https://docs.tokenz.one/zh-CN/v1/checkout/testing) **测试你的 webhook 端点。** ## 1. 创建处理器 设置一个能够通过 POST 方法接受 webhook 请求的 HTTPS 端点功能。你可以使用 [Webhook.site](https://webhook.site/) 来查看有效负载和测试传送。 确保你的端点功能: • 处理带有 JSON 负载的 POST 请求,该负载包含一个 [event 对象](https://docs.tokenz.one/zh-CN/v1/checkout/webhooks#event-%E5%AF%B9%E8%B1%A1)。 • 在执行可能导致超时的复杂逻辑之前,快速返回成功的状态码(2xx)。 ## 2. 注册你的端点 在[仪表板](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` 标头中,包含时间戳 (`t:`) 和一个或多个版本签名 (`v1:`) 需要验证。目前有效的签名版本是 v1。 **Example:** ```plaintext t:1725864981111,v1:zjdrra3...Mh+e6s= ``` ### 验证签名 #### 1. 提取时间戳和签名 将标头按逗号(`,`)分隔,创建元素列表。再使用冒号(`:`)分隔每个元素,得到前缀和值的对。前缀 `t` 代表时间戳,`v1` 为签名。忽略其他元素。 #### 2. 准备用于签名的有效负载 将时间戳(字符串形式)与请求主体(即 JSON 负载)的原始文本连接,组成有效负载字符串。 #### 3. 计算签名 使用 SHA256 哈希函数计算 HMAC,端点的签名密钥作为密钥,有效负载字符串作为消息。 #### 4. 比较签名 将标头中的签名与计算得到的签名进行比对。评估当前时间与接收到的时间戳的差异,防止重放攻击。如果签名有效但时间戳已过期,应用程序可以拒绝该请求。推荐的时间容差为 300 秒(5 分钟)。 重放攻击是指攻击者截获并重传合法有效负载及其签名。Tokenz 通过在 `Tokenz-Signature` 标头中包含时间戳来减少这种风险。 #### 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-CN/v1/checkout/testing)创建并完成一些测试付款。