Webhook
接收实时事件通知与回调
概述
当关键业务事件发生时(如支付完成、授权激活),ipayxx 会向您配置的 Webhook URL 发送 HTTP POST 请求, 携带事件详细信息。您可以使用 Webhook 来自动化业务流程。
配置方式
Webhook URL 可在控制台「开发者设置」中配置。每个应用最多可设置 5 个 Webhook 端点。
事件类型
| 事件 | 触发时机 | 说明 |
|---|---|---|
order.paid | 订单支付成功 | 包含订单详情与生成的授权密钥 |
license.activated | 授权首次激活 | 包含授权详情与设备信息 |
license.revoked | 授权被吊销 | 包含吊销原因与授权信息 |
负载结构
所有 Webhook 请求均使用 POST 方法, Content-Type 为 application/json。
通用结构
{
"id": "evt_20260308_001",
"type": "order.paid",
"created_at": "2026-03-08T10:05:32Z",
"data": {
// 事件特定数据
}
}order.paid 示例
{
"id": "evt_20260308_001",
"type": "order.paid",
"created_at": "2026-03-08T10:05:32Z",
"data": {
"order_no": "ORD20260308100001",
"product_id": "prod_pro_license",
"amount": 29900,
"currency": "CNY",
"buyer_email": "[email protected]",
"license_key": "LIC-ABCD-EFGH-IJKL-MNOP",
"paid_at": "2026-03-08T10:05:32Z"
}
}license.activated 示例
{
"id": "evt_20260308_002",
"type": "license.activated",
"created_at": "2026-03-08T10:10:00Z",
"data": {
"license_key": "LIC-ABCD-EFGH-IJKL-MNOP",
"product_id": "prod_pro_license",
"device_id": "hw_fingerprint_abc123",
"device_name": "MacBook Pro 16\"",
"activated_at": "2026-03-08T10:10:00Z"
}
}license.revoked 示例
{
"id": "evt_20260308_003",
"type": "license.revoked",
"created_at": "2026-03-08T12:00:00Z",
"data": {
"license_key": "LIC-ABCD-EFGH-IJKL-MNOP",
"product_id": "prod_pro_license",
"reason": "refund",
"revoked_at": "2026-03-08T12:00:00Z"
}
}重试策略
如果您的服务器未在 5 秒内返回 2xx 状态码, ipayxx 会按以下间隔自动重试:
| 重试次数 | 等待时间 |
|---|---|
| 第 1 次重试 | 1 分钟后 |
| 第 2 次重试 | 5 分钟后 |
| 第 3 次重试 | 30 分钟后 |
| 第 4 次重试 | 2 小时后 |
| 第 5 次重试 | 24 小时后 |
幂等性
由于重试机制,您的 Webhook 处理程序可能收到重复事件。请使用事件 ID 进行去重处理。
签名验证
每个 Webhook 请求都包含签名头,用于验证请求来自 ipayxx 而非第三方伪造。
验证步骤
- 从请求头获取
X-Webhook-Signature - 从请求头获取
X-Webhook-Timestamp - 将
timestamp.body拼接为待签名字符串 - 使用 Webhook Secret 进行 HMAC-SHA256 签名
- 比较计算结果与请求头中的签名是否一致
Node.js 验证示例
const crypto = require('crypto');
function verifyWebhookSignature(req, webhookSecret) {
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
const body = JSON.stringify(req.body);
// 检查时间戳,防止重放攻击(5 分钟有效)
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - parseInt(timestamp)) > 300) {
return false;
}
// 计算签名
const signString = `${timestamp}.${body}`;
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(signString)
.digest('hex');
// 安全比较
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}最佳实践
- 始终验证 Webhook 签名,防止伪造请求
- 使用事件 ID 实现幂等处理,避免重复操作
- 快速返回
200响应,异步处理业务逻辑 - 记录所有收到的 Webhook 事件,便于调试
- 为 Webhook 端点配置 HTTPS,确保传输安全