Skip to content

Webhook 与签名校验

这页属于高级接入参考。Taria Pay 会把支付状态变化以 POST JSON 的形式发送到商户 webhook endpoint,用于把支付结果回写到你的系统。

如果你是从 Dashboard 的 /developers 开始接入,这页对应的就是:

  • Webhooks:创建 endpoint、保存 signing secret
  • Delivery Logs:查看投递状态、重试次数和失败原因

如何创建 webhook endpoint

当前仓库支持两种入口:

  • Dashboard Developers 页面
  • POST /internal/merchant-api/webhook-endpoints

示例:

bash
curl -X POST "http://localhost:8080/internal/merchant-api/webhook-endpoints" \
  -H "Authorization: Bearer $INTERNAL_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "merchantId":"merchant_demo",
    "url":"https://merchant.example/webhooks/payments",
    "events":["payment_intent.confirmed","payment_intent.failed"]
  }'

如果不传 events,当前实现默认只订阅:

text
payment_intent.confirmed

如果你通过 Dashboard 的 Developers 页面创建 endpoint,建议至少先订阅:

  • payment_intent.confirmed
  • payment_intent.failed

请求头

Header说明
x-tariapay-signatureHMAC-SHA256 签名
x-tariapay-timestampUnix 秒级时间戳
x-tariapay-event事件名
x-tariapay-delivery-id单次投递 ID

兼容期内,服务端也会继续发送旧的 x-pay-* 头。

验签算法

签名原文:

text
${timestamp}.${raw_body}

推荐直接用 SDK:

ts
import { TariaPay } from "@tariapay/sdk";

const tariapay = new TariaPay({
  secretKey: process.env.TARIAPAY_SECRET_KEY!,
});

const event = tariapay.webhooks.constructEvent(
  rawBody,
  headers,
  process.env.TARIAPAY_WEBHOOK_SECRET!,
);

Next.js Route Handler 示例:

ts
import { TariaPay } from "@tariapay/sdk";

const tariapay = new TariaPay({
  secretKey: process.env.TARIAPAY_SECRET_KEY!,
  baseUrl: process.env.TARIAPAY_API_BASE_URL,
});

export async function POST(req: Request) {
  const rawBody = await req.text();

  const event = tariapay.webhooks.constructEvent(
    rawBody,
    req.headers,
    process.env.TARIAPAY_WEBHOOK_SECRET!,
  );

  const paymentIntent = event.data.paymentIntent;

  switch (event.type) {
    case "payment_intent.confirmed":
      // 用 paymentId 或 orderId 幂等更新订单
      break;
    case "payment_intent.failed":
      // 标记订单失败,允许用户重试
      break;
    default:
      break;
  }

  return Response.json({ received: true });
}

如果你不用 SDK,Node.js 原始实现是:

ts
const payloadToVerify = `${headers["x-tariapay-timestamp"]}.${rawBody}`;
const signature = crypto
  .createHmac("sha256", signingSecret)
  .update(payloadToVerify)
  .digest("hex");

const isValid = signature === headers["x-tariapay-signature"];

Payload 结构

json
{
  "id": "evt_123",
  "type": "payment_intent.confirmed",
  "createdAt": "2026-03-29T12:00:00.000Z",
  "data": {
    "paymentIntent": {
      "paymentId": "pi_123",
      "id": "pi_123",
      "orderId": "order_1001",
      "status": "confirmed",
      "currency": "USDC",
      "amount": "49.99",
      "checkoutUrl": "https://checkout.tariapay.com/checkout/payment-intents/pi_123",
      "txHash": "0x..."
    }
  }
}

投递与重试语义

  • endpoint 返回 2xx 视为成功
  • 2xx 或超时进入重试
  • 当前实现最多尝试 5 次
  • Cloud Run 部署推荐每分钟由调度器调用一次 POST /internal/process-webhooks
  • 手动排查时也可以直接调用该接口推进队列

接入建议

  1. 先验签,再解析 JSON
  2. paymentIntent.paymentIdorderId 做幂等写入
  3. webhook 处理尽量快速返回 200
  4. 用查询接口做兜底,不要把前端页面状态当唯一真相
  5. 先在 Developers 的 Delivery Logs 看失败原因,再决定是否升级排查

常见排查顺序

  1. 先确认 endpoint URL 是否可公网访问
  2. 再确认 signing secret 是否和 Dashboard 里保存的一致
  3. 看 Delivery Logs 里的响应码、attempt 次数和 last error
  4. 最后再用 payment intent 查询接口或 txHash 做状态补偿

相关文档: