14个月间我们记录了thMenu上每个Stripe webhook: 100,000个事件中有312个以相同event_id到达两次或三次。这是Stripe "at-least-once delivery"承诺的具体数字——没有幂等性,佣金账本中就有三条重复记录。
为什么同一事件到达三次
如果Stripe在约10秒内没收到2xx响应,就重试。TCP超时、lambda冷启动、DNS瞬时故障,甚至成功处理后响应写入前连接断开——都会触发重试。Stripe在3天内以指数退避重试。
三个常见场景: Cloudflare Workers冷启动超过10秒,D1 batch事务已提交但连接断开,或在Stripe仪表板手动点击"resend"。
INSERT-Claim模式
thMenu的防御是stripe_webhook_events表,使用event_id PRIMARY KEY。第一个动作: INSERT。Unique约束错误23505 = 重复——200 OK no-op。
关键: claim必须先于业务逻辑,否则会有竞态条件。
竞态条件: 乱序事件
真实生产事件: customer.subscription.updated比checkout.session.completed早到800ms。UPDATE影响0行。修复: 回滚claim,返回503,Stripe重试。
FAQ
签名验证应在claim之前还是之后?之前。Verify便宜,DB写入昂贵。
幂等表保留多久?Stripe保证30天重试;我们保留90天。
Stripe会永久重试吗?不会,最多3天。
觉得有用?分享给朋友。