BuffMoneyBuffMoney
构建

Webhooks。

订阅 BuffMoney 异步事件。HMAC-SHA256 签名校验、自动重试、幂等递送。

事件类型

事件触发时机
invoice.paidInvoice transitioned to paid after a successful CNY collection.
invoice.refundedInvoice was refunded (manual or sandbox auto-refund).
payment_order.paidA specific payment order succeeded — fires before invoice.paid.
payment_order.failedA payment order failed or expired.
payout.completedSettlement payout for the period was wired.
subscription.activatedMerchant activated a paid subscription.
subscription.cancelledMerchant cancelled a subscription.

请求格式

我们 POST 一条 JSON body 到你配置的 URL,带上 4 个 BuffMoney 头:

  • x-bm-event事件类型(见上表)
  • x-bm-delivery本次投递的 ID(用于去重)
  • x-bm-signatureHMAC-SHA256(body, endpointSecret)
  • x-bm-timestamp发送时的 Unix 时间戳
POST /your-endpoint
content-type: application/json
x-bm-event: invoice.paid
x-bm-delivery: dlv_8f2a...
x-bm-signature: 5f3a09c84d9e...
x-bm-timestamp: 1779850000

{
  "id": "dlv_8f2a...",
  "event": "invoice.paid",
  "createdAt": "2026-05-25T10:50:00Z",
  "data": {
    "invoiceId": "...",
    "invoiceNumber": "INV-...",
    "merchantId": "...",
    "collectionCurrency": "CNY",
    "collectionAmountMinor": "72",
    "settlementCurrency": "USD",
    "netSettlementMinor": "10",
    "paidAt": "2026-05-25T10:50:00Z",
    "channel": "wechat"
  }
}

签名校验

用你的 endpoint secret 对 raw request body 做 HMAC-SHA256,跟 x-bm-signature 头比对。constant-time 比对避免 timing attack。

// Node.js — verify x-bm-signature
import crypto from "crypto";

export function verifyBuffMoneyWebhook(
  rawBody: string,
  signatureHeader: string,
  endpointSecret: string
): boolean {
  const computed = crypto
    .createHmac("sha256", endpointSecret)
    .update(rawBody, "utf8")
    .digest("hex");
  // constant-time compare
  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader, "hex"),
    Buffer.from(computed, "hex")
  );
}

响应要求

你的 endpoint 必须 5 秒内返回 2xx。失败(4xx / 5xx / 超时)会触发重试。

重试策略

失败递送指数退避重试:1m, 5m, 15m, 1h, 6h, 24h, 72h(共 7 次)。每次重试携带相同的 x-bm-delivery 头,用它去重。72 小时后投递放弃,事件标记 dead,可在后台手动重发。

幂等性

你的 handler 必须能处理重复递送。最简单的方式:在你侧建一个 deliveries 表,存 x-bm-delivery,处理前先查是否已存在。