ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Stripe 支付集成实战:从 Checkout 到订阅、Webhook 与退款的全模式实现指南

Stripe 支付集成实战:从 Checkout 到订阅、Webhook 与退款的全模式实现指南 Stripe 支付集成实战从 Checkout 到订阅、Webhook 与退款的全模式实现指南【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents导读本文是payment-processing插件中 Stripe 集成技能stripe-integration 技能的深度实战指南完整覆盖一次性支付、Embedded/Custom UI 结账、订阅创建、客户门户、Webhook 安全校验、客户管理与退款对账等全部核心模式。读完本文你将掌握一套可直接复制运行、兼顾 PCI 合规与幂等性的 Stripe 支付后端实现方案并理解每个 API 参数背后的设计意图。1. 技能定位与适用场景在 payment-processing 插件 中Stripe 集成以 Skill 的形式提供其入口文档为 SKILL.md而本文所讲的详细模式与完整可运行示例来自其参考文档 references/details.md。配套的 payment-integration 智能体 负责在具体项目中落地这些模式并强调安全第一、全部操作实现幂等、处理所有边界情况失败支付、争议、退款、先测试模式后生产迁移的工程原则。该技能适用于以下典型场景在 Web / 移动应用中实现支付处理搭建订阅计费系统周期性扣款、续费、取消处理一次性支付与 SCAStrong Customer Authentication欧盟强客户认证处理退款与争议Dispute管理客户的支付方式添加、设为默认、列出构建基于 Stripe Connect 的市场平台分账流程下文将围绕 references/details.md 的四大章节——支付实现模式5 种、Webhook 处理、客户管理、退款处理——逐一展开并结合仓库中 PCI 合规技能 与智能体安全要求做纵深补充。2. 核心概念三种支付流与订阅组件在动手写代码前先厘清 Stripe 提供的主要支付抽象这也是 SKILL.md 的核心概念层2.1 Checkout Sessions推荐路径适合大多数集成场景支持全部 UI 形态Stripe 托管结账页、嵌入式中结账表单、基于 Elements 的自定义 UI配合ui_modecustom可使用 Payment Element、Express Checkout Element内置行项目、折扣、税费、运费、地址收集、保存支付方式以及结账生命周期事件相比 Payment Intents集成与长期维护负担更低2.2 Payment Intents自定义控制最终金额含税、折扣、订阅、汇率换算需要你自己计算实现与长期维护成本更高需要 Stripe.js 才能满足 PCI 合规要求2.3 Setup Intents保存支付方式只收集支付方式而不扣款用于订阅和未来付款需要客户确认2.4 订阅的四个组件组件含义Product你销售的商品Price价格与计费周期如何计费、多久一次Subscription客户的周期性付款订阅Invoice每个计费周期自动生成的账单2.5 关键 Webhook 事件SKILL.md 列出的关键事件如下后文的 Webhook 章节会逐一处理其中一部分payment_intent.succeeded支付完成payment_intent.payment_failed支付失败customer.subscription.updated订阅变更customer.subscription.deleted订阅取消charge.refunded退款处理完成invoice.payment_succeeded订阅扣款成功3. 模式一一次性支付Hosted Checkout这是最省事的接入方式由 Stripe 托管结账页面后端只需创建一个 Checkout Session 并返回session.url供前端跳转。def create_checkout_session(amount, currencyusd): Create a one-time payment checkout session. try: session stripe.checkout.Session.create( line_items[{ price_data: { currency: currency, product_data: { name: Blue T-shirt, images: [https://example.com/product.jpg], }, unit_amount: amount, # Amount in cents }, quantity: 1, }], modepayment, success_urlhttps://yourdomain.com/success?session_id{CHECKOUT_SESSION_ID}, cancel_urlhttps://yourdomain.com/cancel, metadata{ order_id: order_123, user_id: user_456 } ) return session except stripe.error.StripeError as e: # Handle error print(fStripe error: {e.user_message}) raise关键参数说明modepayment声明这是一次性付款区别于modesubscription与modesetupunit_amount金额单位是分cents不是元——这是最常见的踩坑点SKILL.md 快速入门中unit_amount: 2000即注释为$20.00success_url中的{CHECKOUT_SESSION_ID}占位符会被 Stripe 自动替换为本次会话 ID用于回跳后核对订单metadata随会话透传业务字段如订单号、用户 ID后续在 Webhook 事件中可通过payment_intent.metadata取回异常处理统一捕获stripe.error.StripeError输出e.user_message便于向用户展示同时raise交给上层处理4. 模式二Elements Checkout Sessions自定义 UI如果不想跳出自己的页面可以使用ui_modecustom让结账完全嵌入应用内。后端仍用 Checkout Session但多返回一个client_secret给前端def create_checkout_session_for_elements(amount, currencyusd): Create a checkout session configured for Payment Element. session stripe.checkout.Session.create( modepayment, ui_modecustom, line_items[{ price_data: { currency: currency, product_data: {name: Blue T-shirt}, unit_amount: amount, }, quantity: 1, }], return_urlhttps://yourdomain.com/complete?session_id{CHECKOUT_SESSION_ID} ) return session.client_secret # Send to frontend与模式一的区别ui_modecustom不再让 Stripe 托管整个页面而是把支付 UI 控制权交给前端return_url取代success_url/cancel_urlCustom UI 模式下支付完成后由前端把用户带回return_url前端使用stripe.initCheckout 手动确认流程const stripe Stripe(pk_test_...); const appearance { theme: stripe }; const checkout stripe.initCheckout({ clientSecret, elementsOptions: { appearance }, }); const loadActionsResult await checkout.loadActions(); if (loadActionsResult.type success) { const { actions } loadActionsResult; const session actions.getSession(); const button document.getElementById(pay-button); const checkoutContainer document.getElementById(checkout-container); const emailInput document.getElementById(email); const emailErrors document.getElementById(email-errors); const errors document.getElementById(confirm-errors); // Display a formatted string representing the total amount checkoutContainer.append(Total: ${session.total.total.amount}); // Mount Payment Element const paymentElement checkout.createPaymentElement(); paymentElement.mount(#payment-element); // Store email for submission emailInput.addEventListener(blur, () { actions.updateEmail(emailInput.value).then((result) { if (result.error) emailErrors.textContent result.error.message; }); }); // Handle form submission button.addEventListener(click, () { actions.confirm().then((result) { if (result.type error) errors.textContent result.error.message; }); }); }要点解读appearance { theme: stripe }控制 Payment Element 外观主题可换flat或自定义variablesloadActions()返回后通过actions.getSession()拿到会话对象session.total.total.amount可直接展示应付总额actions.updateEmail(...)在邮箱失焦时异步校验并回写actions.confirm()在点击支付按钮时触发确认失败时以result.type error分支呈现错误信息安全边界前端只持有client_secret与公钥pk_test_...服务端密钥永远不出后端5. 模式三Elements Payment Intents备选方案原文档明确说明模式二Elements Checkout Sessions是 Stripe 推荐做法但如果需要完全自定义的结账 UI也可以用 Payment Intents 作为替代。二者的取舍呼应 SKILL.md 中的核心概念——Payment Intents 意味着你需要自己承担金额计算与更多维护工作。def create_payment_intent(amount, currencyusd, customer_idNone): Create a payment intent for bespoke checkout UI with Payment Element. intent stripe.PaymentIntent.create( amountamount, currencycurrency, customercustomer_id, automatic_payment_methods{ enabled: True, }, metadata{ integration_check: accept_a_payment } ) return intent.client_secret # Send to frontend前端挂载 Payment Element 并通过confirmPayment完成支付// Mount Payment Element and confirm via Payment Intents const stripe Stripe(pk_test_...); const appearance { theme: stripe }; const elements stripe.elements({ appearance, clientSecret }); const paymentElement elements.create(payment); paymentElement.mount(#payment-element); document.getElementById(pay-button).addEventListener(click, async () { const { error } await stripe.confirmPayment({ elements, confirmParams: { return_url: https://yourdomain.com/complete, }, }); if (error) { document.getElementById(errors).textContent error.message; } });automatic_payment_methods.enabledTrue让 Stripe 根据客户所在地区自动开放可用支付方式卡、Apple Pay、Google Pay 等无需手动枚举customercustomer_id将支付意图绑定到已有客户便于后续管理与退款metadata中的integration_check: accept_a_payment是 Stripe 官方文档示例常用的标记用于标识集成检查点6. 模式四订阅创建Subscription订阅模式下首次付款使用payment_behaviordefault_incomplete并expand出首张发票的 Payment Intent把client_secret交给前端完成首次扣款确认def create_subscription(customer_id, price_id): Create a subscription for a customer. try: subscription stripe.Subscription.create( customercustomer_id, items[{price: price_id}], payment_behaviordefault_incomplete, payment_settings{save_default_payment_method: on_subscription}, expand[latest_invoice.payment_intent], ) return { subscription_id: subscription.id, client_secret: subscription.latest_invoice.payment_intent.client_secret } except stripe.error.StripeError as e: print(fSubscription creation failed: {e}) raise参数设计意图payment_behaviordefault_incomplete订阅先以incomplete状态创建只有首期扣款成功后才转为active避免订阅已建但钱没收到的悬空状态payment_settings.save_default_payment_methodon_subscription把本次支付方式保存为订阅默认方式供后续周期自动扣款复用expand[latest_invoice.payment_intent]一次 API 调用直接带回最新发票及其 Payment Intent从而拿到client_secret完成 3D Secure 等首期确认返回结构同时给出subscription_id与client_secret前端确认后即可等待invoice.payment_succeeded/customer.subscription.updated等 Webhook 推进订阅状态配套的订阅快速入门来自 SKILL.md展示用modesubscriptionprice_data.recurring直接创建周期价格import stripe stripe.api_key sk_test_... # Create a checkout session session stripe.checkout.Session.create( line_items[{ price_data: { currency: usd, product_data: { name: Premium Subscription, }, unit_amount: 2000, # $20.00 recurring: { interval: month, }, }, quantity: 1, }], modesubscription, success_urlhttps://yourdomain.com/success?session_id{CHECKOUT_SESSION_ID}, cancel_urlhttps://yourdomain.com/cancel ) # Redirect user to session.url print(session.url)7. 模式五客户门户Customer Portal给客户一个自助管理订阅与账单的页面最省力的方式是用 Stripe Billing Portal——它托管了修改计划、更新支付方式、查看发票、取消订阅等全套能力def create_customer_portal_session(customer_id): Create a portal session for customers to manage subscriptions. session stripe.billing_portal.Session.create( customercustomer_id, return_urlhttps://yourdomain.com/account, ) return session.url # Redirect customer here只需customer_id与return_url两个参数Stripe 会生成门户地址把session.url302 重定向给客户即可无需自建订阅管理页面门户内客户对订阅的改动如取消同样会通过customer.subscription.updated/customer.subscription.deleted等 Webhook 通知后端同步8. Webhook 处理安全端点与事件分发支付是异步系统Webhook 是唯一的可靠事实来源。以下 Flask 端点展示了完整的签名校验与事件分发from flask import Flask, request import stripe app Flask(__name__) endpoint_secret whsec_... app.route(/webhook, methods[POST]) def webhook(): payload request.data sig_header request.headers.get(Stripe-Signature) try: event stripe.Webhook.construct_event( payload, sig_header, endpoint_secret ) except ValueError: # Invalid payload return Invalid payload, 400 except stripe.error.SignatureVerificationError: # Invalid signature return Invalid signature, 400 # Handle the event if event[type] payment_intent.succeeded: payment_intent event[data][object] handle_successful_payment(payment_intent) elif event[type] payment_intent.payment_failed: payment_intent event[data][object] handle_failed_payment(payment_intent) elif event[type] customer.subscription.deleted: subscription event[data][object] handle_subscription_canceled(subscription) return Success, 200 def handle_successful_payment(payment_intent): Process successful payment. customer_id payment_intent.get(customer) amount payment_intent[amount] metadata payment_intent.get(metadata, {}) # Update your database # Send confirmation email # Fulfill order print(fPayment succeeded: {payment_intent[id]}) def handle_failed_payment(payment_intent): Handle failed payment. error payment_intent.get(last_payment_error, {}) print(fPayment failed: {error.get(message)}) # Notify customer # Update order status def handle_subscription_canceled(subscription): Handle subscription cancellation. customer_id subscription[customer] # Update user access # Send cancellation email print(fSubscription canceled: {subscription[id]})安全与正确性要点必须使用原始请求体payload request.data直接取原始字节。如果先经过 JSON 解析再序列化签名校验必然失败——payment-integration 智能体 明确要求Raw Body Preservation签名验证前绝不修改 Webhook 请求体JSON 中间件会破坏签名校验签名校验stripe.Webhook.construct_event(payload, sig_header, endpoint_secret)同时校验时间戳与 HMAC 签名失败时区分ValueError载荷非法与SignatureVerificationError签名不合法均返回 400端点密钥whsec_...必须在 Stripe Dashboard 的 Webhook 端点配置中生成并与服务端环境变量一一对应测试环境与生产环境使用不同端点与密钥快速响应处理函数只做业务编排重活数据库写、外部 API应放到异步任务。智能体要求在 200ms 内返回 2xx因为超时会触发 Stripe 重试进而产生重复处理9. Webhook 最佳实践手工签名校验与幂等9.1 手工 HMAC 校验不依赖 SDK 时可以按 Stripe 的签名算法自行校验Stripe-Signature头内的时间戳与签名用 Webhook 密钥做 HMAC-SHA256import hashlib import hmac def verify_webhook_signature(payload, signature, secret): Manually verify webhook signature. expected_sig hmac.new( secret.encode(utf-8), payload, hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected_sig)注意hmac.compare_digest采用常数时间比较可防时序侧信道攻击真实生产环境还应校验签名时间戳的时效窗口防重放。9.2 幂等处理Stripe 不保证单次投递失败会重试因此处理器必须以事件 ID 为幂等键def handle_webhook_idempotently(event_id, handler): Ensure webhook is processed exactly once. # Check if event already processed if is_event_processed(event_id): return # Process event try: handler() mark_event_processed(event_id) except Exception as e: log_error(e) # Stripe will retry failed webhooks raisepayment-integration 智能体 记录的经典生产事故——乱序 Webhook 击垮无幂等性的 Lambda 函数——正是缺少该机制的代价。实现时通常把事件 ID 存进数据库唯一索引处理成功后再落库标记。10. 客户管理Customer Management在 Stripe 中Customer 是所有支付、订阅、发票的聚合根。以下三个函数覆盖客户生命周期的主要操作def create_customer(email, name, payment_method_idNone): Create a Stripe customer. customer stripe.Customer.create( emailemail, namename, payment_methodpayment_method_id, invoice_settings{ default_payment_method: payment_method_id } if payment_method_id else None, metadata{ user_id: 12345 } ) return customer def attach_payment_method(customer_id, payment_method_id): Attach a payment method to a customer. stripe.PaymentMethod.attach( payment_method_id, customercustomer_id ) # Set as default stripe.Customer.modify( customer_id, invoice_settings{ default_payment_method: payment_method_id } ) def list_customer_payment_methods(customer_id): List all payment methods for a customer. payment_methods stripe.PaymentMethod.list( customercustomer_id, typecard ) return payment_methods.data设计细节先 Token 后 Attachpayment_method_id通常来自前端 Stripe.js 收集的支付方式令牌服务器永远不接触原始卡号参见 PCI 合规技能 的Tokenization章节Attach 与设默认是两步PaymentMethod.attach把支付方式绑定到客户Customer.modify再把它设为发票默认扣款方式列表查询PaymentMethod.list(customer..., typecard)只取卡类支付方式返回data数组供前端渲染我的支付方式元数据贯通metadata{user_id: 12345}把 Stripe 客户与你业务系统的用户 ID 关联退款、争议、客服排查时都能回溯11. 退款与争议处理Refund Handling11.1 全额与部分退款stripe.Refund.create支持按支付意图退款金额省略时为全额退款def create_refund(payment_intent_id, amountNone, reasonNone): Create a refund. refund_params { payment_intent: payment_intent_id } if amount: refund_params[amount] amount # Partial refund if reason: refund_params[reason] reason # duplicate, fraudulent, requested_by_customer refund stripe.Refund.create(**refund_params) return refundamount以分为单位的退款额省略则全额退回reason合法取值包括duplicate重复扣款、fraudulent欺诈、requested_by_customer客户申请如实填写有助于 Stripe 的风险评估退款完成后会触发charge.refundedWebhook后端应据此更新订单状态11.2 争议举证客户向银行发起拒付Dispute时需要在期限内提交证据。stripe.Dispute.modify用于补充证据材料def handle_dispute(charge_id, evidence): Update dispute with evidence. stripe.Dispute.modify( charge_id, evidence{ customer_name: evidence.get(customer_name), customer_email_address: evidence.get(customer_email), shipping_documentation: evidence.get(shipping_proof), customer_communication: evidence.get(communication), } )争议对象与 Charge 关联而非 Payment Intent因此入参是charge_idevidence字段包括客户姓名、邮箱、物流单据、沟通记录等证据越完整胜诉率越高争议有严格的时间窗口建议在 Webhook 收到charge.dispute.created事件时立即启动内部举证流程12. 安全与 PCI 合规要点进阶支付集成的另一半工作是安全。payment-integration 智能体 与 pci-compliance 技能 给出了必须遵守的底线12.1 Webhook 安全五条军规签名校验始终用官方 SDK 校验 Webhook 签名绝不处理未经验证的 Webhook原始请求体保留验证前不得修改请求体JSON 中间件会破坏签名幂等处理器事件 ID 入库去重先查后处理快速响应200ms 内返回 2xx耗时操作异步化服务端复验以支付状态 API 回查为准绝不完全信任 Webhook 载荷或客户端响应12.2 PCI 合规红线绝不接触原始卡号使用 Stripe Elements / Stripe.js 等令牌化方案在供应商 iframe 内收集卡数据服务器只持有payment_method/token引用。参见 pci-compliance 技能的 Tokenization 章节禁止存储磁道数据、CVV、PIN 永远不落库PAN 如需存储必须加密参考该技能中 AES-256-GCM 的 EncryptedStorage 实现环境隔离测试密钥在生产环境必须失效杜绝测试卡在生产站可支付的典型事故日志脱敏日志中的卡号做掩码前 6 后 4中间打星CVV 等字段直接剔除12.3 已知失败模式来自智能体的实战清单支付处理器在流量高峰崩溃 → Webhook 队列积压、营收损失乱序 Webhook 击垮无幂等性的处理函数 → 生产故障未加密支付按钮被篡改价格 → 欺诈支付配置错误导致测试卡在正式环境被接受 → PCI 违规跳过 Webhook 签名校验 → 系统被恶意请求淹没13. 测试与验收测试卡矩阵SKILL.md 提供了完整的测试模式覆盖成功、拒付、3D Secure、余额不足等场景# Use test mode keys stripe.api_key sk_test_... # Test card numbers TEST_CARDS { success: 4242424242424242, declined: 4000000000000002, 3d_secure: 4000002500003155, insufficient_funds: 4000000000009995 } def test_payment_flow(): Test complete payment flow. # Create test customer customer stripe.Customer.create( emailtestexample.com ) # Create payment intent intent stripe.PaymentIntent.create( amount1000, automatic_payment_methods{ enabled: True }, currencyusd, customercustomer.id ) # Confirm with test card confirmed stripe.PaymentIntent.confirm( intent.id, payment_methodpm_card_visa # Test payment method ) assert confirmed.status succeeded测试要点全程使用sk_test_...测试密钥配合pm_card_visa等测试支付方式用4000000000000002验证拒付分支用4000002500003155验证 3D Secure 流程用4000000000009995验证余额不足与 dunning 流程上生产前至少覆盖成功支付、支付失败、订阅首期扣款、Webhook 重放幂等、部分退款、争议举证14. 延伸阅读stripe-integration 技能入口含快速开始与测试矩阵payment-processing 插件目录Stripe / PayPal / 计费自动化 / PCI 合规四个技能payment-integration 智能体安全要求、常见失败模式、PCI 清单pci-compliance 技能PCI DSS 12 项要求、令牌化、AES-256-GCM 加密billing-automation 技能账单周期、订阅状态机、dunning 与 proration结语Stripe 集成表面上是几个 API 调用实际考验的是对异步事件模型、幂等边界与合规红线的把握。本文给出的五种支付模式覆盖了从最快上线Hosted Checkout到完全自定义 UIElements的全部路径Webhook 章节的安全校验与幂等处理是生产可用的关键客户管理与退款对账则补齐了支付闭环的最后两环。以此为基础配合测试卡矩阵验证每一种分支即可构建一个稳健、合规、可长期维护的支付系统。【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表