编程 stripe-node 23.0.0 升级笔记:API 版本被静默抬升、payment_method_types 移除、webhook 容差反转

2026-10-21 21:04:37

stripe-node 23.0.0 升级笔记:API 版本被静默抬升、payment_method_types 移除、webhook 容差反转

版本与 pinned API version

stripe-node 23.0.0 于 2026-10-01 发布于 npm,pinned API version 从 22.6.2 的 2026-08-26.dahlia 改为 2026-09-30.endive。SDK 在未指定 apiVersion 时使用发布时的最新版本,所以单纯改 package.json 里的版本号,等于同时把发给 Stripe 的 API 版本也换掉了。

验证方式是把 SDK 的请求打到本地自建的 HTTP server 上,直接读 Stripe-Version 请求头:

const http = require('http');
const Stripe = require('stripe');

const server = http.createServer((req, res) => {
  console.log(req.headers['stripe-version']);
  res.setHeader('content-type', 'application/json');
  res.end(JSON.stringify({ id: 'cus_test', object: 'customer' }));
});

server.listen(4242, async () => {
  const stripe = Stripe('sk_test_x', { host: '127.0.0.1', port: 4242, protocol: 'http' });
  await stripe.customers.create({});
});

三种组合的输出:

stripe 22.6.2                                   -> Stripe-Version: 2026-08-26.dahlia
stripe 23.0.0                                   -> Stripe-Version: 2026-09-30.endive
stripe 23.0.0 + apiVersion: '2026-08-26.dahlia' -> Stripe-Version: 2026-08-26.dahlia

钉住旧版本用:

const stripe = Stripe(process.env.STRIPE_SECRET_KEY, {
  apiVersion: '2026-08-26.dahlia',
});

README 里有一条容易被略过的说明:类型只反映最新 API 版本。即使 apiVersion 钉在 2026-08-26.dahlia,TypeScript 类型仍按 23.0.0 对应的新版本生成,类型与实际响应会不一致。要么接受这层不一致,要么别把 SDK 和 API 版本拆得太久。

payment_method_types 被移除

23.0.0 的生成代码里明确写了 Remove support for payment_method_types,涉及 Checkout.SessionCreateParams、PaymentIntentConfirmParams、PaymentIntentCreateParams 等;同时在 Checkout.SessionCreateParams 与 Checkout.Session 上新增 allowed_payment_method_types。

TypeScript 下编译直接失败:

error TS2561: Object literal may only specify known properties, but 'payment_method_types' does not exist in type 'SessionCreateParams'. Did you mean to write 'payment_method_data'?
error TS2561: ... but 'payment_method_types' does not exist in type 'PaymentIntentCreateParams'.

22.6.2 下没有这个错误,反过来 allowed_payment_method_types 在 22.6.2 里不存在,所以两个版本的代码不能共用同一份类型定义。

纯 JavaScript 不报错。23.0.0 仍会把 payment_method_types[0]=card 原样放进请求体发出去,拦在编译期之外的问题就落到运行期。按 API changelog 的 2026-09-30.endive breaking changes,创建 Checkout Session 时传 payment_method_types 返回 400,错误码 payment_method_types_no_longer_supported;Payment Intent 的 create/update/confirm、Setup Intent 的 create/update 同理。

改法:

 await stripe.checkout.sessions.create({
-  payment_method_types: ['card'],
+  allowed_payment_method_types: ['card'],
   line_items: [...],
   mode: 'payment',
 });

注意语义也变了:不匹配币种/金额的支付方式会被排除,而不是返回错误。错误路径变成静默少几种支付方式,测试时要看返回对象里的支付方式列表,而不是只看有没有抛异常。另外新增了 paypay 支持。

webhook 签名容差语义反转

两处变化方向相反:

  • stripe.webhooks.signature.verifyHeader() / verifyHeaderAsync() 此前省略 tolerance 参数时跳过时间戳容差校验,现在默认使用 Webhook.DEFAULT_TOLERANCE(300 秒)。
  • constructEvent 第四参传 0 以前仍按默认 300 秒校验,现在正确跳过时间戳校验。

后果分成两类:直接调用 verifyHeader 的代码从 23.0.0 起会拒绝旧签名,原本「不传 tolerance 就不校验时间戳」的路径没有了;constructEvent(..., 0) 的代码会静默变成接受重放签名。签名本身仍然验证,反转的只是时间戳这层防重放。显式传参最稳:

stripe.webhooks.signature.verifyHeader(payload, sigHeader, secret, 300);

先把调用点找出来:

grep -rnE "constructEvent(Async)?\(|verifyHeader(Async)?\(" \
  --include=*.js --include=*.ts --include=*.mjs --include=*.cjs \
  src/ server/ api/ 2>/dev/null

其他变更

  • Node.js 18 支持结束,engines 从 >=18 改为 >=20,最低 Node 20。
  • 响应体被截断时,错误类型由 StripeAPIError 改为 StripeConnectionError。依赖错误类型判断是否重试的逻辑要跟着改,否则这类瞬时错误不会被重试。
  • ErrorType 不再导出,改用 Stripe.errors。
  • Account.business_profile 新增 specified_commercial_transactions_act_url(日本专用)。

升级顺序

  1. 先 pin apiVersion 到 2026-08-26.dahlia,把 SDK 升级与 API 版本升级拆成两步,出问题时能区分是哪一层引起的。
  2. 升到 23.0.0,修类型错误(payment_method_types → allowed_payment_method_types)与 webhook 调用点(verifyHeader 补 tolerance、constructEvent(..., 0) 逐个确认意图)。
  3. 在 sandbox 端到端跑一遍支付,重点看被剔除的支付方式和 webhook 重放校验。
  4. 最后升 API 版本到 2026-09-30.endive,并检查 dashboard 里 webhook 投递设置使用的版本。

webhook 端点的独立版本

webhook 端点有独立的 api_version,而 snapshot_api_version 只能在创建目标时设置,事后改不了。所以升级要新建端点并行接收,处理程序必须幂等——同一事件可能从新旧两条路径各到一次。轻量事件(thin events)的 payload 不带版本,可以避开这一层。

复制全文 生成海报 支付 Stripe 接口对接 webhook

推荐文章

程序员茄子在线接单