Webhook 通知
在商户后台或通过 POST /api/v2/webhook-endpoints 配置接收地址后, 平台会将事件以 POST JSON 推送到你的服务器。发货必须以 Webhook(或主动查单)为准, 浏览器同步跳转不可作为发货依据。
事件结构
POST https://your-server.com/webhooks/llp
Content-Type: application/json
X-LLP-Event: payment.succeeded
X-LLP-Signature: t=1787000000,v1=6f3a... # HMAC-SHA256(secret, "{t}.{body}")
{
"id": "evt_cme0...", // 事件 ID:Replay 时保持不变
"type": "payment.succeeded",
"created_at": "2026-08-25T12:00:05.000Z",
"merchant_id": "100001", // 商户号
"api_version": "2026-08-01",
"data": {
"payment_intent_id": "cmt7...",
"system_order_no": "P2026...",
"merchant_order_no": "M2026...",
"amount": "100.00",
"fee": "5.00",
"currency": "CNY",
"status": "SUCCEEDED",
"subject": "VIP 会员",
"paid_at": "2026-08-25T12:00:04.000Z"
}
}事件类型
| 类型 | 时机 |
|---|---|
payment.created / payment.processing | 订单创建 / 出码成功 |
payment.succeeded | 可信 Provider 事件确认支付成功(发货依据) |
payment.failed / payment.expired / payment.canceled | 终态 |
refund.processing / refund.succeeded / refund.failed | 退款生命周期 |
reconciliation.discrepancy | 对账差异(预留) |
验签(Node.js)
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyWebhook(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(
signatureHeader.split(',').map((kv) => kv.split('=')),
);
const expected = createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
// 可选:校验 parts.t 与当前时间差 < 5 分钟,防重放
return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
app.post('/webhooks/llp', express.raw({ type: 'application/json' }), (req, res) => {
if (!verifyWebhook(req.body.toString(), req.headers['x-llp-signature'], WEBHOOK_SECRET)) {
return res.status(400).send('invalid signature');
}
const event = JSON.parse(req.body.toString());
// 幂等:以 event.id 去重(Replay 会复用同一 event.id 且必然重复投递)
if (!alreadyProcessed(event.id) && event.type === 'payment.succeeded') {
ship(event.data.merchant_order_no);
}
res.status(200).send('ok'); // 2xx 视为投递成功
});重试与死信
- 非 2xx 或超时(10s)视为失败,按指数退避最多投递 12 次(约 5.7 小时窗口)
- 重试耗尽进入死信(DEAD),可在商户后台「Webhook」或平台后台手动 Replay
- Replay 生成新的 delivery,但
event.id保持不变 —— 请务必按event.id做幂等 - 处理逻辑应快速返回(先落库再异步处理),避免超时误判
安全提示:回调地址必须为公网可达的 https 地址(生产环境拒绝内网/本机地址); 验签失败的请求直接丢弃,不要输出任何差异化错误信息。