Webhook 概览
Webhook 用于向商户系统异步推送 Payin、Payout 的状态变化。创建订单或发起出金的同步响应只代表请求已被接收,不代表资金已经完成处理。
商户系统应同时接入:
- Webhook:实时接收状态变化
- 查询接口:在 Webhook 丢失、超时或对账时做补偿确认
标准处理流程
- 接收 Beyounger 的 HTTP POST 请求
- 读取原始请求体
raw_body - 使用 Webhook secret 校验
X-Beyounger-Signature - 按
event_id做事件幂等 - 按业务 ID 做业务幂等,例如
order_id或payout_id - 根据
event_type更新本地业务状态 - 返回 HTTP
2xx
如果验签失败,不要更新业务状态。
请求头
| Header | 说明 |
|---|---|
Content-Type | 固定为 application/json |
X-Beyounger-Webhook-Id | Webhook 配置 ID |
X-Beyounger-Event | 事件类型 |
X-Beyounger-Delivery-Id | 本次投递 ID,用于排查重复投递 |
X-Beyounger-Signature | 请求签名 |
签名规则
签名使用原始请求体和 Webhook secret 计算:
hex(HMAC_SHA256(raw_body, webhook_secret))
注意:
- 必须使用原始请求体,不要使用重新序列化后的 JSON。
- 签名比较应使用 timing-safe compare。
- 当前签名不包含时间戳和 nonce,商户必须通过
event_id做幂等和防重放。
Node.js 示例:
import crypto from 'crypto';
export function verifyWebhook(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected.toLowerCase()),
Buffer.from(String(signature).trim().toLowerCase()),
);
}
事件分类
| 产品 | 事件 | 用途 |
|---|---|---|
| Payin | acquiring.order.updated | 收款订单状态变化 |
| Payout | payout.verify.request | 请求商户验证出金信息 |
| Payout | payout.review.approved / payout.review.rejected / payout.review.returned | 出金审核状态变化 |
| Payout | payout.execution.processing / payout.execution.completed / payout.execution.failed | 出金执行状态变化 |
响应要求
标准状态通知 Webhook:
- 商户处理成功后返回任意 HTTP
2xx - 非
2xx、网络错误、超时都会被视为投递失败 - Beyounger 可能重试投递,商户必须保证幂等
验证类 Webhook:
payout.verify.request需要返回明确 JSON- 通过:
{"verified": true} - 拒绝:
{"verified": false} - 任何不明确响应都不会被当作通过
幂等建议
建议商户至少保存:
| 字段 | 用途 |
|---|---|
event_id | 防止同一事件重复处理 |
event_type | 判断事件语义 |
business_id | 对应业务对象 ID,例如 order_id 或 payout_id |
occurred_at | 排查事件发生时间 |
X-Beyounger-Delivery-Id | 排查投递和重试 |
重复事件建议直接返回 2xx,不要重复发货、重复入账或重复通知用户。
查询补偿
Webhook 用于实时通知,但最终对账建议仍调用查询接口确认:
| 场景 | 查询接口 |
|---|---|
| Payin 收款订单 | GET /payment/acquiring/orders/{order_id} |
| Payin 支付记录排查 | GET /payment/acquiring/orders/{order_id}/payments |
| Payout 出金 | GET /payment/payouts/{payout_id} |
最佳实践
- Webhook URL 必须使用 HTTPS。
- 不要在 Webhook 请求中执行耗时任务,建议先落库再异步处理。
- 验签失败直接拒绝,不更新业务状态。
- 对重复事件返回
2xx。 - 对终态事件再调用查询接口做补偿确认。
- 不要在日志中打印 Webhook secret。