Webhooks
收款状态变化时发送到你系统的通知。
您的系统无需反复轮询"付款了吗?",有事件发生时 PayZu 会主动调用您:收款状态变化、反欺诈状态更新、拒付(chargeback)或新的循环扣款周期。
如何配置
在创建收款时(POST /charges)提供 postbackUrl。每当有事件发生,PayZu 都会向该 URL 发送一个 JSON 格式的 POST 请求。
事件
| 事件类型 | 说明 |
|---|---|
charge.update | 支付状态变化 |
antifraud.update | 反欺诈分析完成,结果体现在收款的 status 和 reasonCode 中 |
chargeback | 拒付通知 |
recurrence.cycle | 新的循环扣款周期已扣款 |
Payload 结构
{
"event": "charge.update",
"data": {}
}data 对象的格式与查询收款的响应完全一致。
请求 Header
每个 POST 请求都带有以下 header:
| Header | 说明 |
|---|---|
Content-Type | 始终为 application/json |
X-Webhook-Signature | Payload 的 HMAC SHA-256 签名,十六进制格式(64 个字符) |
X-Webhook-Timestamp | 发送时刻,自 Unix 纪元起的毫秒数 |
X-Webhook-Nonce | 请求的唯一标识符(32 个十六进制字符) |
重试
事件发生后会立即进行首次投递。只有当您的 URL 在 5 秒内返回 HTTP 2xx 状态码,投递才视为成功:任何其他状态码或超时的响应均视为失败。
投递失败后,webhook 最多进行 5 次重试。每次失败后,距下一次尝试的间隔逐步增大:各次重试分别在 1 分钟、10 分钟、1 小时、6 小时和 24 小时后进行。此后不再重试。
请尽快响应 webhook(返回一个简单的 200 即可),并异步处理 payload,以免超出 5 秒的限制。由于超时可能导致已处理过的事件被重新投递,消费必须是幂等的:请使用收款的 id 加上状态变化作为去重键。不要用 X-Webhook-Nonce 去重,它标识的是 HTTP 请求,每次重新投递都会变化。
HMAC 验证
每个 webhook 都使用您的 webhook secret 签名,该密钥由 PayZu 随您的 API 凭据一同提供。您的 API 应在处理 payload 之前先验证签名:
提取 x-webhook-timestamp、x-webhook-nonce 和 x-webhook-signature 三个 header。
将 timestamp、nonce 和 payload 的值用 . 连接,得到验证基础字符串:timestamp.nonce.payload。
使用您的 webhook secret,对该字符串以 SHA-256 算法生成 HMAC 签名。
将生成的签名与 x-webhook-signature header 的值比较。不一致则拒绝该 webhook。
Node.js 示例,使用 crypto.timingSafeEqual 以恒定时间比较签名:
const crypto = require("node:crypto");
function verifyWebhookSignature(request, webhookSecret) {
const timestamp = request.headers["x-webhook-timestamp"];
const nonce = request.headers["x-webhook-nonce"];
const signature = request.headers["x-webhook-signature"];
if (typeof signature !== "string" || !/^[0-9a-f]{64}$/i.test(signature)) {
return false;
}
const baseString = `${timestamp}.${nonce}.${request.rawBody}`;
const expectedSignature = crypto
.createHmac("sha256", webhookSecret)
.update(baseString)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, "hex"),
Buffer.from(signature, "hex"),
);
}HMAC 必须基于请求的原始报文体(raw body)计算,即收到的原样内容,在任何 JSON 解析之前。
Nonce 验证(可选)
x-webhook-nonce header 的值是每个请求唯一且临时的标识符。提取后,检查该 nonce 是否已被记录过:
- 若该值已被使用过,则拒绝请求,以防范重放攻击(replay attacks)。
- 若 nonce 是新的,则将其记录为已使用,确保后续调用无法重复使用。
Timestamp 验证(可选)
x-webhook-timestamp header 的值是发送时刻自 Unix 纪元起的毫秒数。将其与当前时间比较:若差值超过 5 分钟,则拒绝请求。此项校验可丢弃已过期的 webhook,避免处理陈旧或潜在恶意的消息。