Webhook
Webhook 是履约的唯一可信来源。每当发生重要事件——支付、退款、订阅、结算——FastStar 会向你的回调地址 POST 带签名的 JSON 事件,即使买家从未回到你的网站,你的系统也能保持同步。
配置
在商户后台配置 Webhook:设置回调地址(endpoint)与签名密钥(secret),并可选择只订阅部分事件类型(不选则接收全部事件)。你的端点必须返回 2xx 状态码才算投递成功。
投递格式
每次投递为一个 HTTP POST,携带以下请求头:
| 请求头 | 说明 |
|---|---|
X-Webhook-Signature | 签名:t=<unix 时间戳>,v1=<hex HMAC-SHA256> |
X-Webhook-ID | 事件唯一 id——用于幂等去重 |
X-Webhook-Timestamp | 事件创建时间(unix 秒) |
消息体为 JSON 事件包:
{
"id": "evt_xxx",
"type": "payment.succeeded",
"created": 1769900000,
"data": { "payment_id": "pi_xxx", "amount": 1999, "currency": "USD", "status": "succeeded" },
"livemode": true,
"api_version": "v1"
} 事件类型
| 事件 | 触发时机 |
|---|---|
payment.succeeded | 支付成功完成——据此履约 |
payment.failed | 支付尝试失败 |
payment.canceled | 支付被取消 |
payment.refunded | 扣款被退款 |
refund.succeeded | 退款完成 |
subscription.created / subscription.updated / subscription.canceled | 订阅生命周期变化 |
invoice.paid / invoice.payment_failed | 订阅账单结果 |
dispute.created / dispute.updated / dispute.closed | 拒付 / 争议生命周期 |
settlement.succeeded | 结算(余额出款)完成 |
验证签名
签名头的格式为 t=<时间戳>,v1=<签名>。签名是用你的 Webhook
密钥对字符串 <时间戳>.<原始消息体> 计算的
HMAC-SHA256(hex 编码)。验签必须基于原始请求体,在任何 JSON 解析之前进行:
const crypto = require('crypto');
function verifyWebhook(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(
signatureHeader.split(',').map((p) => p.split('='))
);
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
return (
parts.v1 &&
parts.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))
);
}
// Express:保留原始消息体用于验签
app.post('/webhooks/faststar', express.raw({ type: 'application/json' }), (req, res) => {
if (!verifyWebhook(req.body.toString(), req.get('X-Webhook-Signature'), process.env.WEBHOOK_SECRET)) {
return res.status(400).send('bad signature');
}
const event = JSON.parse(req.body);
if (event.type === 'payment.succeeded') {
// 履约(按 event.id 做幂等处理)
}
res.sendStatus(200); // 尽快确认,重活异步处理
});
建议拒绝时间戳过旧的事件以防重放。也可以通过 API 验证签名:
POST /public/verify-signature
({ provider_name, payload, signature } →
{ valid: true }),但推荐优先在本地做 HMAC 验签。
重试与幂等
- 只有你的端点返回 2xx 才算投递成功。其他状态码或网络错误会进入重试队列。
- 重试按递增退避执行(大致按 1 分钟、5 分钟、30 分钟、2 小时、24 小时的节奏),最多重试到你配置的次数(默认 3 次)。
-
重试可能导致同一事件被投递多次,请幂等处理——以事件
id(X-Webhook-ID)为处理键。 - 尽快响应(先确认、再异步处理),避免超时被判定为失败。
前端事件不等于履约。checkout.success 等 SDK
回调只用于确认买家的界面体验。履约一律以 payment.succeeded Webhook 为准。