如果整个资源关系图能装进脑子里,凌晨三点也能排障。我们刻意把它做小。
每个事件都带签名、按资源保序,并保留 30 天。发布期间漏收了一段?直接重放该时间区间即可追平状态,无需提工单,也无需导 CSV。
{
"id": "evt_7Qa4Lm20",
"type": "payout.settled",
"created_at": "2026-04-18T06:26:11Z",
"data": {
"payout_id": "pay_31Ax9K",
"amount": 48200,
"currency": "SGD",
"settled_amount": 561238400,
"settled_currency": "IDR",
"rate": 11644.99,
"rail": "id_local_rtgs",
"ledger_entries": ["ent_88f1", "ent_88f2"]
}
}
{
"id": "evt_5Bn9Zt41",
"type": "collection.received",
"created_at": "2026-04-18T06:12:47Z",
"data": {
"collection_id": "col_9F4C21",
"virtual_account": "va_sg_seller_2291",
"amount": 129900,
"currency": "SGD",
"method": "bank_transfer",
"attributed_to": "seller_2291",
"risk_score": 12
}
}
import crypto from "node:crypto"; export function verify(rawBody, header, secret) { const [ts, sig] = header.split(","); const expected = crypto .createHmac("sha256", secret) .update(`${ts}.${rawBody}`) .digest("hex"); const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300; return fresh && crypto.timingSafeEqual( Buffer.from(sig), Buffer.from(expected) ); }
每个错误都带有一个分类,用来回答唯一的问题:重试、改请求,还是上报。不留模糊地带。
合作方或网络的瞬时状况。带同一幂等键重发即可,不会产生重复指令。
请求体或其业务上下文有误。响应会指明具体字段以及它违反的规则。
合规或额度需要人工决策。响应会包含可通过 API 追踪的工单编号。