ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Spree 官方 Stripe 支付网关 spree_stripe 完全指南:支付会话、Webhook 自动注册与升级迁移

Spree 官方 Stripe 支付网关 spree_stripe 完全指南:支付会话、Webhook 自动注册与升级迁移 Spree 官方 Stripe 支付网关 spree_stripe 完全指南支付会话、Webhook 自动注册与升级迁移【免费下载链接】spreeOpen Source eCommerce Platform for B2B, Marketplace, and Enterprise. REST API, TypeScript SDK, and production-ready Next.js storefront. Self-host it. Own your stack. No vendor lock-in. Zero platform fees.项目地址: https://gitcode.com/GitHub_Trending/sp/spree本指南围绕当前仓库 spree/providers/stripe/README.md 展开系统讲解 Spree Commerce 官方 Stripe 支付网关spree_stripe的安装、配置、支付会话Payment Session架构、Webhook 自动注册机制、从 1.x 的升级路径以及离线测试方式。读完本文你将掌握如何在 Spree 6.x 中开箱启用 Stripe 全渠道收款卡片、Klarna、Apple Pay 等理解网关在后台如何自动完成 Webhook 端点注册与签名密钥管理并能独立完成旧版本数据的迁移验证。一、spree_stripe 是什么spree_stripe是 Spree Commerce 的官方 Stripe 支付网关位于仓库 spree/providers/stripe基于 Spree 的支付会话 APIPayment Session API构建。与传统的网关在服务端直接扣款模式不同支付意图Payment Intent的创建与确认发生在会话Session流程中而捕获capture、退款refund与取消cancel则由 Spree core 的支付生命周期驱动。从 网关主类 可以看到SpreeStripe::Gateway继承自::Spree::Gateway并混入了四个能力模块class Gateway ::Spree::Gateway include SpreeStripe::Gateway::PaymentSessions include SpreeStripe::Gateway::PaymentSetupSessions include SpreeStripe::Gateway::Webhooks include SpreeStripe::Gateway::Connect end这四个模块分别负责支付会话、支付方式保存会话Setup Session、Webhook 校验与端点注册、以及面向 Marketplace 的 Connect 账户能力。1.1 支持的支付方式根据 README 与 支付来源模型目录网关支持卡片支付card快捷结账Apple Pay、Google Pay先买后付 / 本地支付Klarna、Affirm、Afterpay、Alipay、iDEAL、Link直接借记与银行转账SEPA Direct Debit、Przelewy24、银行转账bank transfer客户可通过 Setup Session 保存支付方式供后续使用off-session 扣款每个支付来源对应一个Spree::PaymentSource子类例如 sepa_debit.rb 声明其支持的动作仅为credit退款而 klarna.rb 同样以credit为唯一动作——这些声明决定了管理后台里对某笔支付可以执行哪些操作。1.2 依赖与运行环境spree_stripe.gemspec 给出了硬性约束Ruby 版本要求 3.2依赖spree_core版本不低于当前 Spree 版本Stripe Ruby SDK 版本区间 10.1, 19开发依赖vcr用于离线录制回放测试网关启动时engine.rb 在after_initialize回调中完成三件注册Rails.application.config.spree.payment_methods SpreeStripe::Gateway Spree.subscribers SpreeStripe::CustomerUpdatedSubscriber Spree.payout_providers SpreeStripe::PayoutProvider即网关自动注册为可用支付方式、订阅客户更新事件、并注册为卖家结算payout提供方——这也是 README 中没有任何东西需要生成generate也没有迁移需要运行的源码依据。安装生成器 install_generator.rb 本身就是一个 no-op仅打印提示信息。二、安装按照 README在应用的Gemfile中加入一行gem spree_stripe然后执行bundle install安装即完成。网关通过 engine 自行注册见上文无需运行rails g spree_stripe:install该生成器是 no-op保留仅为了兼容旧调用习惯复制任何迁移文件网关不拥有数据表网关的配置全部存放在支付方法Payment Method的 preferences 中这是它零迁移的关键设计。若要调整后台队列SpreeStripe.queue默认复用Spree.queues.default可通过SpreeStripe.queue ...覆盖见 lib/spree_stripe.rb。三、配置3.1 后台创建支付方法在管理后台创建一个 Stripe 支付方法填入两个密钥Preference说明类型publishable_keyStripe 可发布密钥pk_ 开头用于客户端 SDKpassword 类型保存时隐藏secret_keyStripe 密钥sk_ 开头服务端所有 API 调用均使用password 类型保存时隐藏见 gateway.rb 第 11-15 行两个密钥都有presence校验且非测试环境下保存时会执行validate_secret_key——它会调用Stripe::Refund.list({ limit: 0 })来验证密钥有效性并针对三类错误给出本地化提示见 en.yml误填了可发布密钥You have provided your publishable key instead of your secret key密钥无效Secret key is invalidStripe 服务不可用Something went wrong with Stripe. Try again later.其余全部自动化Webhook 端点自动注册支付方法保存时网关异步向 Stripe 注册 Webhook 端点并把 Stripe 返回的签名密钥signing secret存回支付方法偏好快捷结账域名注册门店域名会被登记到 Stripe使得 Apple Pay / Google Pay 能在结账页自动出现。3.2 Webhook 自动注册的实现细节Webhook 端点注册由 webhooks.rb 与 create_gateway_webhooks.rb 共同完成支付方法每次 create/update 提交后after_commit触发CreateWebhookEndpointJob见 create_webhook_endpoint_job.rb由于 6.0 起一个支付方法只属于一个 store每个网关只有一个 Webhook 端点因此签名密钥直接以内部 preference 存储webhook_signing_secretpassword 类型与webhook_endpoint_idstring 类型均标记为internal: true——操作员不需要、也不应该手工填写手工覆盖反而会破坏签名校验注册端点时只订阅有处理逻辑的事件避免订阅了却无人消费的幽灵事件。支付端点订阅三个事件payment_intent.succeeded→ 映射为capturedpayment_intent.amount_capturable_updated→ 映射为authorizedpayment_intent.payment_failed→ 映射为failed幂等设计注册前会以limit: 100拉取现有端点列表按 URL enabled_events 精确匹配若已有端点且签名密钥已存储直接复用绝不重复创建Stripe 不视 URL 为唯一重复创建会导致每个事件投递两次、其中一半用我们未持有的密钥签名密钥只在端点创建时由 Stripe 一次性返回因此当存在我们未持有密钥的旧端点时服务会先删除旧端点再重建。Webhook 签名校验verify_webhook_signature会依次尝试所有可用签名密钥首选支付方法上存储的密钥仅在开发环境额外接受ENV[STRIPE_SIGNING_SECRET]——这正是 README 中该环境变量的用途Stripe CLI 转发的事件使用 CLI 自己的密钥签名而非端点的密钥。源码注释明确警告接受该变量不能用于非开发环境否则一个泄露的值会变成所有网关的伪造密钥。校验失败统一抛出Spree::PaymentMethod::WebhookSignatureError签名合法后网关把 Stripe 事件翻译成 core 的 Webhook 控制器与Spree::Payments::HandleWebhook可消费的规范化结构后续的幂等、加锁、支付创建与订单完成全部交由 core 处理。3.3 本地开发Stripe CLI在本地联调时用 Stripe CLI 转发事件并设置export STRIPE_SIGNING_SECRETstripe-cli-webhook-signing-secret该变量仅在Rails.env.development?时被纳入校验候选见 webhooks.rb 第 91-95 行。3.4 支付会话与保存支付方式支付会话Spree::PaymentSessions::Stripe见 stripe.rb一次支付会话就是一个 Stripe Payment Intent——创建会话即创建 IntentIntent 的 id 存为会话的 external idSpree 侧不落任何 intent 记录。会话上存储client_secret、ephemeral_key_secret、stripe_payment_method_id供门店前端 SDK 完成 3DS 等确认流程保存支付方式会话Spree::PaymentSetupSessions::Stripe见 payment_setup_sessions.rb通过 Stripe SetupIntent EphemeralKey 在不扣款的前提下把卡或 SEPA 等保存到客户名下供未来 off-session 扣款使用客户同步网关通过fetch_or_create_customer在 Spree 客户与 Stripe Customer 之间建立GatewayCustomer映射并用 customer_presenter.rb 构造客户负载订阅 customer_updated_subscriber.rb 在 Spree 客户信息变更时同步更新 Stripe。3.5 风险校验码映射Stripe 返回的地址/CVC 校验状态pass/fail/unchecked会被转换为 Spree core 风控分析读取的 AVS/CVV 响应码见 gateway.rb 的 AVS_CODES / CVV_CODES 与 risk_codes_for存储在支付来源的 metadata 中。四、从 spree_stripe 1.x 升级这是 README 着墨最多、也最容易踩坑的部分。4.1 背景签名密钥从独立表迁移到支付方法偏好1.x 时代Webhook 签名密钥存放在独立的spree_stripe_webhook_keys表并通过关联表与支付方法多对多连接——因为当时一个支付方法可能被多个 store 共享。6.0 起一个支付方法只属于一个 store每个网关只有一个端点于是签名密钥简化为网关偏好preference。4.2 执行迁移升级后执行一次bundle exec rake spree:upgrade:migrate_stripe_webhook_keys该任务也会作为rake spree:upgrade的一部分自动运行。迁移实现位于 migrate_webhook_keys.rake关键行为幂等已有签名密钥的网关直接跳过旧表保留不删作为回滚路径兼容加密旧密钥列若使用 deterministic Active Record encryption 写入迁移会用临时模型以相同方式声明加密读取从未配置加密密钥的安装则以明文读取多密钥取最新关联历史上一支付方法可能关联多个密钥迁移取最近关联的那一个按created_at DESC, id DESC因为这才是 Stripe 当前签名使用的端点失败不静默单网关迁移失败会被记录并打印任务以非零退出码abort提示解决后重跑若旧表不存在则打印提示并跳过。4.3 迁移完成前的行为README 明确警告迁移执行之前携带 6.0 之前 Webhook 端点的网关会拒绝所有传入 Webhook。原因在 webhooks.rb 的注释 中写得很清楚——旧的签名密钥在独立表里网关偏好中没有签名密钥校验必然失败。4.4 1.x 遗留功能的迁移前提以下路径只在 1.x 系列中存在升级前必须先完成支付会话迁移使用 Rails storefrontRails 视图结账的应用遗留的stripe_eventWebhook 路径Stripe Tax 功能这些功能在 6.0 系列中不再提供需要先迁移到新的支付会话流程。五、面向 Marketplace 的 Connect 能力虽然 README 未展开但仓库中Connect模块是网关的另一半能力值得补充spree_stripe同时是一个卖家结算提供方SpreeStripe::PayoutProvider。独立 Webhook 端点Marketplace 需要第二个 Webhook 端点connect.rb订阅account.updated、payout.paid、payout.failed。Stripe 按事件来源区分订阅把卖家事件并进支付端点不会拓宽它而会切换它导致支付 Webhook 停止送达Express 账户与托管 onboardingcreate_connect_account_link为卖家创建 Stripe Express 账户并生成一次性 onboarding 链接创建时使用基于参数哈希的幂等键spree-seller-id-sha256前32位既防重复点击开户又不至于把参数修正后的重试请求错误地复用缓存失败结算状态驱动account.updated决定卖家何时可被付款payouts_enabled并通过SellerTransfers::ExecutePendingJob触发待执行结算payout.*事件按 Stripe 自身的 payout id 精确匹配结算记录避免重投递时把已完成的结算错标为下一笔。六、测试离线 VCR 套件README 给出的测试方式bundle exec rake test_app # 首次运行生成测试应用 bundle exec rspec测试套件默认完全离线基于录制的 VCR cassettes 回放见 spec/support/vcr.rbc.default_cassette_options { record: ENV[RECORD_VCR] ? :new_episodes : :none }默认record: :none缺少 cassette 时直接失败而不是悄悄用环境中的任意凭据去真实录制保证 CI 与本地环境的可重复性录制新交互设置RECORD_VCR1并配置真实的 Stripe 测试凭据STRIPE_PUBLISHABLE_KEY、STRIPE_SECRET_KEY会被过滤为占位符cassette 路径由示例名决定重命名带:vcr标签的示例会改变 cassette 路径从而重新录制而不是复用旧文件——这是 README 特别提示注意点的源码依据敏感数据过滤请求头中的X-Stripe-Client-User-Agent携带账户指纹会被过滤响应体中的whsec_*Webhook 签名密钥也会被替换为STRIPE_WEBHOOK_SIGNING_SECRET占位符。仓库 spec/vcr 目录下已收录 26 个 cassette覆盖支付意图创建/捕获/取消/退款、Webhook 端点创建、客户创建与更新、Connect 账户与卖家结算等关键路径对应断言可参考 gateway_spec.rb如#webhook_url断言端点形如#{store.url_or_custom_domain}/api/v3/webhooks/payments/#{gateway.prefixed_id}与 connect_spec.rb。七、重要参数速查参数 / 偏好位置说明publishable_key/secret_key支付方法偏好后台填写必填保存时自动校验有效性webhook_signing_secret支付方法内部偏好由 Webhook 注册流程自动写入勿手工覆盖webhook_endpoint_id支付方法内部偏好Stripe 端点 id注册后回写STRIPE_SIGNING_SECRET环境变量仅开发环境供 Stripe CLI 转发校验STRIPE_CONNECT_SIGNING_SECRET环境变量仅开发环境Connect 端点同用途见 connect.rb 第 285-289 行STRIPE_LOG_LEVEL环境变量Stripe SDK 日志级别默认info见 initializers/stripe.rbStripe API 版本initializers/stripe.rb固定为2023-10-16并调用set_app_info标识请求来源八、总结spree_stripe将 Stripe 的接入成本压缩到了极致bundle install后后台填入两个密钥即可收款Webhook 端点注册、签名密钥回写、Apple Pay/Google Pay 域名登记全部自动完成支付会话架构让确认发生在客户端服务端生命周期authorize/capture/refund/cancel/void由 Spree core 统一驱动面向 1.x 用户的spree:upgrade:migrate_stripe_webhook_keys任务则提供了幂等、可回滚的平滑升级路径。离线 VCR 测试套件保证了在任何环境下都可以无网络依赖地验证网关行为。【免费下载链接】spreeOpen Source eCommerce Platform for B2B, Marketplace, and Enterprise. REST API, TypeScript SDK, and production-ready Next.js storefront. Self-host it. Own your stack. No vendor lock-in. Zero platform fees.项目地址: https://gitcode.com/GitHub_Trending/sp/spree创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进