跳转到正文
使用文档
简
English简体中文繁體中文
工作空间
指南API 参考API v1
指南

接收 Webhook 通知

在购买、续费或余额变化时接收带签名的通知。

点击接口可编辑请求并查看响应。

创建订阅

调用 POST/webhooks,传入名称、你的公网 HTTPS 地址、enabled: true 和所需的 event_types,并发送已保存的 Idempotency-Key。密钥需要写入访问权限及 webhooks:write。

安全保存返回的签名密钥。使用完整的 whsec_… 值,包括前缀。普通订阅读取不会返回密钥。

事件 含义
fulfillment.updated 静态资源或交付状态变化
renewal.updated 续费状态变化
dynamic.purchase.updated 流量购买状态变化
refund.completed 退款完成
wallet.low_balance 钱包余额降至你设置的阈值以下

余额提醒还需传入整数字符串 wallet_low_balance_threshold_fen。订阅只通知创建后的变化。

验证签名

保留原始请求体。使用签名密钥,对时间戳、一个句点和原始请求体字节计算 HMAC-SHA256,再以恒定时间比较 X-Webhook-Signature。

以下 Node.js 函数接收 Buffer 类型的 rawBody 和 Headers 类型的 headers:

import { createHmac, timingSafeEqual } from 'node:crypto';

function verifyWebhook(rawBody, headers, secret) {
  const timestamp = headers.get('X-Webhook-Timestamp') ?? '';
  const signature = headers.get('X-Webhook-Signature') ?? '';
  if (!/^\d+$/.test(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  if (!/^v1=[0-9a-f]{64}$/.test(signature)) return false;
  const expected = createHmac('sha256', secret)
    .update(timestamp + '.')
    .update(rawBody)
    .digest();
  const actual = Buffer.from(signature.slice(3), 'hex');
  return timingSafeEqual(expected, actual);
}

验证通过后再解析 JSON。不要先解析再序列化请求体,否则签名字节会改变。

处理重复与重试

使用事件的 id 避免重复处理。通知可能重复或乱序;需要当前状态时,请读取对应的 API 资源。

安全保存或处理通知后返回 2xx。失败通知每轮最多尝试八次,间隔从 30 秒逐步增加至一小时。通过 GET/webhooks/{id}/deliveries 查看记录,通过 POST/webhooks/{id}/deliveries/{delivery}/retry 重试失败的通知。

更换签名密钥

调用 POST/webhooks/{id}/rotate-secret 更换密钥。发送中的通知可能仍使用旧密钥,请在接收端保留短暂的过渡时间。接收地址必须是使用 443 端口的公网 HTTPS 地址,且不能重定向。