Webhook 回调怎么设计:签名、重放保护与幂等
回调是对接的最后一公里。本文给出签名校验、重放保护、幂等消费与事件日志的结构化清单。
在虚拟商品自动发货体系里,Webhook 回调承担“交付结果确认”的角色:你把码发出去、把服务开通了,最终需要通过回调把结果交给对接方。 如果回调设计不严谨,最常见的后果是:重复处理、状态不一致、对账困难,甚至被恶意重放请求。
目录
1)回调的最小字段:事件、时间戳、签名
回调建议以“事件”为中心(event),而不是只传一个状态。因为同一订单可能出现多个事件:发货成功、回调成功、补发、退款等。 最小字段通常包括:
- event_id:事件唯一 ID(用于幂等消费)
- order_id:对接方订单号或平台订单号
- event_type:例如 delivered / failed / refunded 等
- timestamp:事件发生时间(用于重放保护)
- signature:基于 body + timestamp 的 HMAC 签名
2)签名校验与重放保护:别只校验一个 token
只校验静态 token 的问题在于:一旦泄露,攻击者可以伪造回调。更稳的做法是: 使用 HMAC(如 SHA256)对请求体与时间戳做签名,并在接收端校验:
- 签名是否正确(防伪造)
- 时间戳是否在可接受窗口内(例如 5 分钟,防重放)
- 是否已处理过相同 event_id(防重复消费)
3)幂等消费:重复回调是常态
回调的失败原因很多:对方网络抖动、接口超时、对方服务短暂不可用。你的系统会重试,同一个事件可能被推送多次。 因此接收端必须做到“重复回调不重复处理”。常见做法:
- 以 event_id 做幂等(最推荐)
- 或以 order_id + event_type 做幂等(次优)
- 幂等记录需有 TTL,避免无限增长
4)回调失败的重试策略:指数退避与死信
回调重试建议采用指数退避,并设置最大次数。超过阈值后进入“死信队列”(dead-letter),由人工或定时任务处理。 官网内容可以公开“重试原则”,但不必公开具体阈值。
指数退避
2s → 5s → 15s → 60s… 避免短时间集中重试放大故障。
最大次数
到达上限后转入死信,记录最后一次响应与错误码。
可追溯日志
回调次数、每次响应、时间线必须可查,才能支撑对账与争议处理。
如果你打算把官网作为长期知识库,Webhook 回调这类内容非常适合做主题聚合:既专业、又可复用,还能自然形成内部链接到“API 对接”与“对账口径”页面。