跳至内容
开发者资源集成 Webhook

集成 Webhook

要在应用程序中接收 webhook 事件,请按照以下步骤创建并注册 webhook 端点::

  1. 创建 webhook 端点处理器,用于接收事件数据的 POST 请求。
  2. 使用仪表板 注册你的端点。
  3. 保护你的 webhook 端点。
  4. 使用测试付款 测试你的 webhook 端点。

1. 创建处理器

设置一个能够通过 POST 方法接受 webhook 请求的 HTTPS 端点功能。你可以使用 Webhook.site 来查看有效负载和测试传送。

确保你的端点功能:

• 处理带有 JSON 负载的 POST 请求,该负载包含一个 event 对象。 • 在执行可能导致超时的复杂逻辑之前,快速返回成功的状态码(2xx)。

2. 注册你的端点

在仪表板上注册你的 webhook 端点。

  1. 前往你的 主页。
  2. 点击 创建新 webhook。
  3. 填写你的 URL 和可选描述,并选择你希望接收的事件。我们建议至少选择 order.succeeded 以确保不遗漏成功的客户订单。
  4. 点击 创建 webhook。
  5. 复制并保存返回的签名密钥。你需要它来验证收到的 webhook。出于安全原因,我们只会显示一次密钥。

3. 保护你的 webhook 端点

Tokenz 所发送的所有 webhook 都包含签名,你必须验证这个签名以确保 webhook 来自 Tokenz 且未被篡改。或者,你可以将 webhook 通知仅作为触发器,通过 API 检查订单状态变更。不过,我们建议验证 webhook 签名,以减少不必要的 API 调用。

每个事件的签名包含在 Tokenz-Signature 标头中,包含时间戳 (t:<unix-timestamp-in-milliseconds>) 和一个或多个版本签名 (v1:<HMAC signature>) 需要验证。目前有效的签名版本是 v1。

Example:

plaintext
t:1725864981111,v1:zjdrra3...Mh+e6s=

验证签名

1. 提取时间戳和签名

将标头按逗号(,)分隔,创建元素列表。再使用冒号(:)分隔每个元素,得到前缀和值的对。前缀 t 代表时间戳,v1 为签名。忽略其他元素。

2. 准备用于签名的有效负载

将时间戳(字符串形式)与请求主体(即 JSON 负载)的原始文本连接,组成有效负载字符串。

3. 计算签名

使用 SHA256 哈希函数计算 HMAC,端点的签名密钥作为密钥,有效负载字符串作为消息。

4. 比较签名

将标头中的签名与计算得到的签名进行比对。评估当前时间与接收到的时间戳的差异,防止重放攻击。如果签名有效但时间戳已过期,应用程序可以拒绝该请求。推荐的时间容差为 300 秒(5 分钟)。

重放攻击是指攻击者截获并重传合法有效负载及其签名。Tokenz 通过在 Tokenz-Signature 标头中包含时间戳来减少这种风险。

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 处理正确,请按照我们的测试指南创建并完成一些测试付款。