news 2026/9/11 6:33:57

Medusa Stripe 支付提供者 @medusajs/payment-stripe:从 2.0 到 2.20 的能力演进与源码级解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Medusa Stripe 支付提供者 @medusajs/payment-stripe:从 2.0 到 2.20 的能力演进与源码级解析

Medusa Stripe 支付提供者 @medusajs/payment-stripe:从 2.0 到 2.20 的能力演进与源码级解析

【免费下载链接】medusaThe world's most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusa

@medusajs/payment-stripe 是 Medusa 官方支付模块(Modules.PAYMENT)的 Stripe 实现,负责将 Medusa 的支付会话生命周期(发起、授权、捕获、退款、Webhook 对账)映射到 Stripe PaymentIntent 与 Customer API。本文以该包的 CHANGELOG.md 为主线骨架,结合 packages/modules/providers/payment-stripe 下的源码、类型定义与测试,梳理 2.0 大版本重写以来的关键能力演进,并给出配置参数、provider 注册方式和底层调用链的完整解析。读完本文,你将掌握该模块的 8 个支付方式服务、全部配置项及其作用,以及 initiatePayment 到 Webhook 对账的完整实现路径。

一、模块概览:一个包,八种支付方式

从 package.json 可以看到,该包名称为@medusajs/payment-stripe,当前版本 2.20.1,运行时要求 Node.js >= 20,核心依赖为stripe@^15.5.0@medusajs/framework@2.20.1(peer 与 dev 依赖同版本)。

模块入口 src/index.ts 通过ModuleProvider(Modules.PAYMENT, { services })向支付模块注册了 8 个服务:

Provider Key服务类对应支付方式说明
stripeStripeProviderService通用 Stripe 支付默认 provider,payment method 由请求上下文决定
stripe-oxxoOxxoProviderServiceOXXO墨西哥现金支付,支持过期天数配置
stripe-bancontactStripeBancontactServiceBancontact比利时本地支付
stripe-blikStripeBlikServiceBLIK波兰本地支付
stripe-giropayStripeGiropayServicegiropay德国银行转账
stripe-idealStripeIdealServiceiDEAL荷兰本地支付
stripe-przelewy24StripePrzelewy24ServicePrzelewy24波兰银行转账
stripe-promptpayPromptpayProviderServicePromptPay泰国本地支付,2.0.3 版本加入

这些 Key 集中定义在 src/types/index.ts 的PaymentProviderKeys常量中。其中stripe-idealstripe-promptpay等服务通过覆盖paymentIntentOptionspayment_method_types固定各自的支付方式,例如 PromptPay 服务固定为["promptpay"]capture_method: "automatic"(见 src/services/stripe-promptpay.ts)。

二、配置参数:StripeOptions 完整说明

所有 provider 共享同一份选项类型StripeOptions,定义于 src/types/index.ts:

参数类型是否必填默认值作用
apiKeystringStripe 账户 API 密钥;缺失时validateOptions直接抛错
webhookSecretstring推荐用于 Webhook 签名校验;缺失时启动阶段仅告警(见下文 2.16 演进)
capturebooleanfalse是否立即捕获(automatic capture),默认手动捕获(capture_method: "manual"
automaticPaymentMethodsbooleanfalse为 true 时在 intent 请求上设置automatic_payment_methods: { enabled: true }
paymentMethodConfigurationstring传入 Stripe Payment Method Configurations 的 ID(PMC ID),由 Dashboard 托管可用支付方式集合
paymentDescriptionstring当请求上下文未提供时,给 intent 设置的默认描述
oxxoExpiresDaysnumber3OXXO 支付过期天数,映射到payment_method_options.oxxo.expires_after_days
asyncPaymentMethodTypesStripe.PaymentMethod.Type[]异步支付方式类型列表;未配置时所有支付方式按同步处理。异步支付方式在 Stripe 状态为pending时也允许生成订单

此外 PaymentIntentOptions 定义了各支付方式服务可以覆写的 intent 参数:capture_methodautomatic/manual)、setup_future_usageon_session/off_session)、payment_method_types,以及payment_method_options.oxxo.expires_after_days

配置校验:缺失 apiKey 直接报错,缺失 webhookSecret 启动告警

src/core/stripe-base.ts 中的静态方法validateOptions定义了配置校验规则:

  • apiKey缺失时抛出"Required option apiKey is missing in Stripe plugin",阻止 provider 初始化;
  • webhookSecret缺失时打印console.warn,并通过静态标志hasWarnedMissingWebhookSecret保证在 8 个 provider 服务各自被 loader 校验时只告警一次

三、版本演进时间线:CHANGELOG 中的关键能力节点

以下是 CHANGELOG 记录的、对功能有实质影响的版本节点(依赖同步更新如@medusajs/framework的例行升级不再赘述):

2.0.0:Medusa 2.0 大版本重构

2.0.0 条目 标记为Major Changes,对应 Medusa 2.0 发布(PR #7341)。这一代将支付逻辑收敛为AbstractPaymentProvider<StripeOptions>抽象基类StripeBase,统一实现 Stripe 的 PaymentIntent / Customer / Refund / Webhook 调用,各支付方式服务仅需通过paymentIntentOptions描述差异。此前在 0.0.2 版本(PR #6700)中所有模块被打上初始版本号以支持 monorepo 联测。

2.0.3:加入 PromptPay

PR #9789(CHANGELOG 2.0.3 条目)为泰国市场新增promptpay支付方式,注册StripePromptpayService。该服务固定payment_method_types: ["promptpay"]并采用自动捕获。

2.11.x:共享支付令牌、PromptPay 注册修复、metadata 合并

  • 2.11.0feat(payment-stripe): Allow passing shared payment token in Stripe,允许在发起支付时透传 Stripe 共享支付令牌;同时修复了StripePromptPayService未在模块 provider 中注册导致 PromptPay 无法工作的问题(这也解释了为何 2.0.3 加入 PromptPay 后仍需在 2.11.0 修正注册)。
  • 2.11.1:PR #13801,发起支付时将自定义 metadata 与session_id合并写入 intent 的 metadata,而不是覆盖。对应源码见 initiatePayment:
metadata: { ...(data?.metadata ?? {}), session_id: data?.session_id as string, }

其中session_id是 Webhook 对账的关键锚点(下文详述)。

2.12.0:OXXO 支付方式与可配置过期时间

PR #13805(CHANGELOG 2.12.0 条目)新增 OXXO provider 支持,并支持配置过期时间。OxxoProviderService通过paymentIntentOptions固定payment_method_types: ["oxxo"]capture_method: "automatic",并将oxxoExpiresDays(默认 3 天)映射为payment_method_options.oxxo.expires_after_days(见 src/services/stripe-oxxo.ts)。

2.13.2:账户持有人删除保护与外部退款同步

该版本包含两项行为修正(CHANGELOG 2.13.2 条目):

  • PR #14112 修改deleteAccountHolder实现,避免永久删除底层的 Stripe Customer。从源码看 deleteAccountHolder 当前仍调用stripe_.customers.del,社区修正是对删除语义的收敛——确保只在明确上下文下删除,避免误删用户 Stripe 档案;实施时建议结合实际使用场景评估 account holder 删除策略。
  • PR #14746:处理在 Medusa 外部发起退款的情况。对应 refundPayment 中捕获CHARGE_ALREADY_REFUNDEDErrorCodes.CHARGE_ALREADY_REFUNDED)错误并静默放行,避免外部已退款时内部重复退款抛错。

2.16.0:webhookSecret 缺失告警与支付方式删除

(CHANGELOG 2.16.0 条目)两项改动:

  1. webhookSecret缺失时在 provider 初始化阶段告警:此前该配置缺失会被静默接受,导致后续 Webhook 签名校验失败,依赖 Webhook 的支付流程(如 3D Secure、异步捕获)一直卡在pending。告警文案与validateOptions实现一一对应(见上文配置校验小节)。
  2. 新增删除支付方式能力deletePaymentMethod通过stripe_.paymentMethods.detach将支付方式从客户档案解绑(源码),并配合listPaymentMethods(默认limit: 100列出客户全部支付方式)与savePaymentMethod(基于setupIntents保存)形成完整的保存-列表-删除闭环。

2.17.2:异步支付方式支持

PR #15085(CHANGELOG 2.17.2 条目)在paymentpayment-stripecore-flowsmedusadashboardjs-sdkutilstypes等多个包中引入异步支付方式(async payment methods)支持,同时修复了 Webhook 中对异步支付方式检查的优雅降级。

异步支付方式(如银行转账类)的特点是:Stripe 状态为pending时订单即可被创建。其判定逻辑在 isAsyncPaymentMethod:只有options_.asyncPaymentMethodTypes中列出的类型才按异步处理。该判定同时影响两处:

  • getStatusprocessing状态映射(源码):异步方式返回PENDING_AUTHORIZATION,否则返回PENDING
  • Webhook 处理payment_intent.created/payment_intent.processing事件时(源码):异步方式返回PENDING_AUTHORIZATION动作,同步方式返回PENDING

2.18.0:payment_method_configuration 支持

(CHANGELOG 2.18.0 条目)新增payment_method_configuration支持:通过传入 Stripe Payment Method Configurations(PMC ID),即可在 Stripe Dashboard 上集中管理可用支付方式集合,而无需改代码。

从源码看该参数有明确的优先级与互斥逻辑(normalizePaymentIntentParameters):

if (!paymentMethodTypes?.length) { res.payment_method_configuration = (extra?.payment_method_configuration as string | undefined) ?? this.options_?.paymentMethodConfiguration }

即:仅当没有显式指定payment_method_types时才应用payment_method_configuration(请求上下文优先于全局选项)。这与 stripe-base.spec.ts 中 5 个用例逐一对应:

  • extra 中的payment_method_configuration优先于 options;
  • 未传 extra 时使用 options 中的配置;
  • 固定了payment_method_types的专用 provider(如 iDEAL)不设置 PMC;
  • 同时传入payment_method_types与 PMC 时,PMC 被忽略;
  • 全部未配置时 PMC 为undefined,且默认capture_methodmanual

四、核心实现:StripeBase 与支付会话生命周期

所有支付方式服务继承自抽象类StripeBase(src/core/stripe-base.ts),它是该模块的"心脏"。

4.1 支付会话生命周期方法

StripeBase完整实现AbstractPaymentProvider<StripeOptions>的全部接口,与 Medusa Payment 模块的调用关系如下:

方法Stripe 底层调用说明
initiatePaymentpaymentIntents.create创建 PaymentIntent,金额换算为最小货币单位,写入session_id等 metadata
authorizePayment复用getPaymentStatus本质是paymentIntents.retrieve并映射状态
capturePaymentpaymentIntents.capture手动捕获;遇PAYMENT_INTENT_UNEXPECTED_STATE且 intent 已succeeded时视为捕获成功返回
cancelPayment/deletePaymentpaymentIntents.cancel取消 intent;若 Stripe 已返回canceled状态则容错返回
refundPaymentrefunds.create按最小货币单位退款;对CHARGE_ALREADY_REFUNDED幂等放行
retrievePaymentpaymentIntents.retrieve查询并将金额从最小单位转回标准单位
updatePaymentpaymentIntents.update金额变更时更新 intent,金额未变则直接返回当前状态
createAccountHolder/updateAccountHolder/deleteAccountHoldercustomers.create/customers.update/customers.del将 Medusa 客户映射为 Stripe Customer,账单地址映射为 Stripe Shipping 地址
listPaymentMethods/savePaymentMethod/deletePaymentMethodcustomers.listPaymentMethods/setupIntents.create/paymentMethods.detach客户支付方式管理

所有写操作都透传idempotencyKey(来自context.idempotency_key),保证网络重试下的幂等性。

4.2 金额换算:getSmallestUnit

Stripe 要求金额以最小货币单位(如分为单位)传入。工具函数 src/utils/get-smallest-unit.ts 维护了一张货币幂表:

  • 幂 0(无小数位):BIF、CLP、DJF、GNF、JPY、KMF、KRW、MGA、PYG、RWF、UGX、VND、VUV、XAF、XOF、XPF;
  • 幂 3(千分位):BHD、IQD、JOD、KWD、OMR、TND;
  • 其余货币默认幂 2。

getSmallestUnit负责从标准单位换算到最小单位,其中对 3 位小数货币还会向上取整到最近的 10(对应部分货币的最小支付粒度约束);getAmountFromSmallestUnit则用于反向换算,Webhook 返回给订单系统的金额均经过该函数。对应的单元测试位于 src/utils/tests/get-smallest-unit.ts。

4.3 状态映射:PaymentIntent 状态 → Medusa 会话状态

getStatus(源码)是状态映射的核心,映射关系如下:

Stripe PaymentIntent 状态Medusa 会话状态说明
requires_payment_method(有 last_payment_error)ERROR有失败记录
requires_payment_method/requires_confirmationPENDING等待支付方式
processing(异步方式)PENDING_AUTHORIZATION2.17 引入的异步语义
processing(同步方式)PENDING处理中
requires_actionREQUIRES_MORE需要额外验证(如 3D Secure)
canceledCANCELED已取消
requires_captureAUTHORIZED已授权待捕获
succeededCAPTURED已捕获
其他PENDING兜底

4.4 错误处理与指数退避重试

handleStripeError(源码)按错误类型分派:

  • StripeCardError:intent 已创建但支付失败,返回该 intent 供支付会话引用,便于 Webhook 对账;
  • StripeConnectionError/StripeRateLimitError:结果不确定,返回retry: true
  • StripeAPIError:按 Stripe 官方建议视为不确定状态indeterminate_due_to: "stripe_api_error"),依赖 Webhook 而非直接判失败;
  • 其他错误:抛出异常,触发会话清理。

executeWithRetry(源码)默认最多重试 3 次,采用指数退避(baseDelay * 2^(attempt-1),并叠加 0.5~1.0 的随机抖动,避免重试风暴)。

五、Webhook 对账:以 session_id 为锚的事件驱动

getWebhookActionAndData(源码)是整个异步对账链路的核心,工作流程如下:

  1. 签名校验constructWebhookEvent读取请求头stripe-signature,调用stripe_.webhooks.constructEvent校验——这正是webhookSecret缺失会导致的故障点;
  2. 来源校验:检查 intent metadata 中的session_id。Medusa 创建的 intent 一定会携带该字段(见initiatePayment);没有session_id的 intent 视为其他集成(共享同一 Stripe 账户)创建,直接返回NOT_SUPPORTED,杜绝跨系统误操作;
  3. 事件分派:按event.type映射为 Medusa 的PaymentActions
Stripe 事件Medusa 动作
payment_intent.created/payment_intent.processingPENDING(异步方式为PENDING_AUTHORIZATION
payment_intent.canceledCANCELED
payment_intent.payment_failedFAILED
payment_intent.requires_actionREQUIRES_MORE
payment_intent.amount_capturable_updatedAUTHORIZED
payment_intent.partially_fundedREQUIRES_MORE
payment_intent.succeededSUCCESSFUL
其他NOT_SUPPORTED

每个动作都携带session_id和换算回标准单位的金额,供支付模块定位会话并推进订单状态。

六、模块使用方式与升级注意事项

6.1 安装与配置要点

  • 安装:在 Medusa 应用中通过npm install @medusajs/payment-stripe(对应 package.json 中主入口dist/index.js)安装后,在medusa-config的支付模块 providers 中注册stripe与所需支付方式,并为每个 provider 提供apiKey(必填)与webhookSecret(强烈建议);
  • 路由:在 Stripe Dashboard 配置 Webhook 端点,将上述 Stripe 事件(payment_intent.succeeded等)转发到 Medusa 的支付 Webhook 路由;
  • 异步方式:若使用 Klarna/Affirm 等异步支付方式,需配置asyncPaymentMethodTypes,并留意payment_intent.created事件返回PENDING_AUTHORIZATION的语义;
  • 金额:所有金额在 src/utils/get-smallest-unit.ts 的货币幂表约束下自动换算,无需业务侧手工处理。

6.2 升级路径与依赖对齐

  • 自 2.6.1 起(PR #11738),Medusa 移除了包版本的范围约束,@medusajs/payment-stripe@medusajs/framework严格同版本发布、同步升级;
  • 2.0.x → 2.20.x 期间,该包长期处于Patch Changes节奏,唯一一次Major Changes是 2.0.0 的 Medusa 2.0 重写,说明其接口在 2.x 生命周期内保持稳定,升级风险主要来自@medusajs/framework的同步要求;
  • 若你的订单流程依赖 Webhook 完成状态推进(3D Secure、异步捕获、OXXO 等),升级后务必确认webhookSecret已配置,否则将触发 2.16.0 引入的启动告警并导致支付流程停滞在pending

七、总结

从 CHANGELOG 的版本时间线可以看到,@medusajs/payment-stripe的演进路径清晰:2.0 完成框架级重写(StripeBase统一抽象),2.0.3 起持续补充本地化支付方式(PromptPay、OXXO),2.11~2.18 密集完善元数据合并、账户持有人安全、异步支付语义与 Payment Method Configurations 集成,每一步都通过 stripe-base.spec.ts 等测试锁定行为。对于要在 Medusa 上落地 Stripe 支付(尤其是多地区本地支付与异步支付场景)的开发者,本文梳理的配置项、状态映射表与 Webhook 事件对应关系,可以作为排障与二次开发的直接参考。

【免费下载链接】medusaThe world's most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusa

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 6:32:06

AI Agent实战入门:Python+LangGraph+CrewAI+AutoGen七日通关指南

1. 这不是“学AI”的路线图&#xff0c;而是你亲手造出第一个能干活的AI Agent的实操日志 我带过37个从零开始学AI Agent开发的学员&#xff0c;其中21个在6个月内独立交付了真实业务场景中的Agent系统——有给律所做合同条款比对的&#xff0c;有帮跨境电商做多平台库存同步的…

作者头像 李华
网站建设 2026/9/11 6:30:47

PairDrop实战:基于WebRTC的跨设备点对点文件传输方案解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 6:30:11

数据库全量迁移与一致性校验实战:从mydumper到增量同步

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 6:28:46

WorkBuddy开放平台个人开发者实战:从零构建Agent应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华