Stripe webhook 升级 API 版本:先建一个 disabled 端点做双投递,老端点回 400 保留回滚
相关文档:
每个 webhook endpoint 各自固定绑定一个 API 版本,Stripe 按该版本组织发给这个端点的事件 payload。因此升级集成版本时,不只是换 SDK,端点也要一起升级。
版本由谁决定
一次请求最终使用哪个版本,按以下顺序确定:
- 请求里带了
Stripe-Versionheader,就用它; - 否则用服务端 SDK 固定的版本;
- 再否则用账户的默认 API 版本。
账户默认版本在第一次发起 API 请求时设定,可在 Workbench 的 Overview 页查看和升级。通过 API 检索 event 时,返回结构由该事件发生当时的账户默认版本决定。
webhook endpoint 可以固定自己的版本:只要端点显式设置了版本,Stripe 发给它的所有事件都用这个版本。
服务端 SDK 的版本约束:
- 用 Ruby/Python/PHP/Node 时,可以全局或在单次请求上设置
stripe_version。 - 用 Java/Go/.NET 这类强类型语言时,API 调用固定使用 SDK 发布时的 API 版本,无法指定其他版本。这些 SDK 的 webhook 端点版本必须与 SDK 版本一致,否则 SDK 无法反序列化事件。
先判断目标版本有没有 breaking change
2024-09-30.acacia之前的每个 API 版本都带 breaking changes。- 从
2024-09-30.acacia起,Stripe 每月发布新版本,且不含 breaking changes。 - 每年两次发布 major release(Basil、Clover、Dahlia、Endive 等),其首个版本包含 breaking changes,升级到新的 major 可能需要改代码。
- 当前版本是
2026-09-30.endive。Dahlia 的首个版本2026-03-25.dahlia引入了 breaking changes,之后的 Dahlia 版本只做增量。 - Endive 的 changelog:从 Checkout Sessions 移除 payment method types 参数;从 Payment Intents 和 Setup Intents 移除 payment method types 参数;移除 Elements 中的 payment method types 选项;废弃 payment request button;API v1 资源的 thin events 转为 GA;统一 Subscriptions 与 Invoices 的计费周期锚点格式;支持按需暂停与恢复 Subscriptions。
升级 webhook 端点:按顺序操作
1. 新建一个 disabled 的端点
url:与原端点相同,加一个查询参数区分两者,例如https://example.com/webhooks?version=2024-04-10。enabled_events:与原来相同的事件集合。api_version:要升到的目标版本。升到最新版本可以用 Dashboard 或 API;其他版本只能用 API 显式指定。
创建后先保持 disabled,下一步再启用。
2. 改处理代码,让它忽略发往新端点的事件
判断请求里的查询参数:旧 API 版本就照常处理;新版本就直接忽略并返回 200,避免 Stripe 重投。
3. 启用新端点
此时每个事件会被投递两次:一次用旧 API 版本,一次用新 API 版本。
4. 升级 Stripe library 到与新端点一致的版本
对照 changelog 处理 breaking changes。然后把处理逻辑反过来:查询参数是旧版本就忽略并返回 400,让 Stripe 自动重试;是新版本就正常处理。返回 400 的意义在于:万一需要回滚,事件会被重新发到旧端点。
5. 观察新端点
事件处理不正确时:先把代码回退到旧版本,临时禁用新端点,处理失败的事件(之前返回过 400 的会被 Stripe 自动全部重发),定位并修复问题,再重新启用端点继续观察。
6. 禁用旧端点,停止返回 400
旧端点一直返回 400 会影响依赖 200 响应的集成。禁用之后,Stripe 不再重投那些返回过 400 的事件。
整个灰度期间两个端点同时存活,Stripe 会把订阅的事件同时投给两边,事件处理必须幂等。
集成升级的其他动作
- 先确定目标版本,在代码里显式指定 API 版本,不要依赖账户默认版本。
- 用
Stripe-Versionheader 试跑更新的版本。 - 在 Workbench 的 Overview 页查看集成当前用了哪些 API 版本。
Event destinations 与 snapshot events
- snapshot events 的 payload 渲染版本由 destination 的
snapshot_api_version属性控制,它与 SDK 的 API 版本无关。 - thin events 的 payload 不带版本。
snapshot_api_version只能在创建 event destination 时设置。要用别的版本,先新建一个配好该版本的目的地并测试,再删除原有的。- 迁移期间两个目的地都处于活动状态时,事件处理必须幂等,因为 Stripe 会向两者都投递订阅的事件。
API 版本实际控制什么
- 未设置
Stripe-Version时,你可以发送哪些参数、会收到哪些对象。 - Stripe.js 返回对象的结构。
- 发往 webhook 端点的对象结构。
- Stripe 代你执行的自动化 Billing 操作,例如为新订阅周期生成 invoice。
Stripe 视为向后兼容的改动
- 新增 API 资源。
- 新增可选的请求参数。
- 响应中新增属性。
- 属性顺序变化。
- 不透明字符串的长度或格式变化,包括 charge ID 上
ch_这类前缀的增删。 - Stripe 对象 ID 最长可达 255 字符,MySQL 中应存为
VARCHAR(255) COLLATE utf8_bin以保证大小写敏感。 - 新增事件类型,webhook listener 必须能优雅处理不认识的事件类型。
绕开整套流程:thin events
不想处理 webhook 版本,就用 thin events。API v1 和 v2 资源都会产生 thin events,且 thin event 通知的 payload 不带版本,升级 API 版本或 SDK 时不需要改 event destination 配置。Endive changelog 中,API v1 资源的 thin events 已 GA,此前是 private preview,仅支持 API v2 资源。