iPayXXAPI 文档

Webhook

接收实时事件通知与回调

概述

当关键业务事件发生时(如支付完成、授权激活),ipayxx 会向您配置的 Webhook URL 发送 HTTP POST 请求, 携带事件详细信息。您可以使用 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 请求都包含签名头,用于验证请求来自 ipayxx 而非第三方伪造。

验证步骤

  1. 从请求头获取 X-Webhook-Signature
  2. 从请求头获取 X-Webhook-Timestamp
  3. timestamp.body 拼接为待签名字符串
  4. 使用 Webhook Secret 进行 HMAC-SHA256 签名
  5. 比较计算结果与请求头中的签名是否一致

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,确保传输安全