服务端与数据 / 进阶
Webhook 接收端:验签、去重和异步消费缺一不可
Webhook 是外部系统主动调用你的接口。接收端需要对原始字节验签、限制时间窗口、按事件 ID 去重并快速返回。
必须对原始请求体验签
JSON 解析后键顺序和空白可能变化,用重新序列化的对象验签会失败。路由应保留原始 Buffer,并把时间戳与请求体按供应商协议拼接。
EXAMPLE / 01Webhook
import crypto from 'node:crypto';
const signed = `${timestamp}.${rawBody.toString('utf8')}`;
const expected = crypto.createHmac('sha256', secret).update(signed).digest();
const received = Buffer.from(signature, 'hex');
const valid = received.length === expected.length &&
crypto.timingSafeEqual(received, expected);签名正确也可能是旧请求重放
拒绝与服务器时间相差过大的时间戳,例如超过 5 分钟。然后以供应商 eventId 建唯一索引;同一事件重复到达时返回成功,但不重复执行业务副作用。
EXAMPLE / 02Webhook
CREATE TABLE webhook_events (
provider text NOT NULL,
event_id text NOT NULL,
event_type text NOT NULL,
payload jsonb NOT NULL,
received_at timestamptz NOT NULL DEFAULT now(),
processed_at timestamptz,
PRIMARY KEY (provider, event_id)
);持久化后快速返回 2xx
在请求里完成发邮件、更新报表等全部工作,会让上游超时并不断重试。验签后先持久化事件并提交,再交给消费者处理。消费者同样要幂等,因为队列通常是至少投递一次。
保留事件状态、失败原因和重试次数,并提供按 eventId 重放的管理能力。不要依靠日志手工拼回事件。