← 中文资源

简体中文版本

添加幂等 Webhook 处理器

对原始请求体验证签名,用事务去重事件 id,并快速返回,使提供方重试不会重复应用同一事件。

阅读英文原文

重试是常态

提供方会因为网络失败和进程重启而重试 Webhook。假设恰好一次投递的处理器迟早会重复扣款、重复发送或重复创建。提供方的事件 id 就是幂等键。把它与唯一约束一起存储,并让业务更新和去重插入在同一事务中提交。

CREATE TABLE processed_events (
  provider text NOT NULL,
  event_id text NOT NULL,
  processed_at timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (provider, event_id)
);

如果插入因唯一冲突失败,说明事件已处理过。应返回成功而不是错误,让提供方停止重试。如果业务更新失败,要同时回滚更新和插入,使后续重试可以再次尝试。

先验签再解析

签名验证必须使用原始请求体。框架先解析 JSON 再序列化会改变空白和键顺序,导致 HMAC 不再匹配。只在 Webhook 路由上挂载原始体中间件,验签后再解析。使用常量时间比较,并拒绝时间戳超出小窗口的请求以限制重放。

快速确认,安全处理

提供方超时很快。在请求内只做最小的持久化工作,把其余工作入队,然后返回 2xx。如果确认后队列发布可能失败,就用与去重记录同一事务的 outbox 行。这样事件要么被完整接受,要么可以在不重复业务效果的情况下重试。

测试重试

发送两次相同的已签名负载,断言只发生一次状态变更且两次都返回 2xx。发送签名错误的负载,断言 401 且没有写入。如果提供方不保证顺序,还要测试乱序事件,验证业务逻辑会拒绝或缓冲过期更新。