用途
该技能面向 Web 与移动应用中的支付处理与订阅计费实现,主要覆盖 Stripe 集成(Checkout、Elements、PaymentIntents)、订阅生命周期管理、带幂等性的 webhook 处理、PCI 合规、按量与基于用量的计费、税费计算、退款处理,以及用于移动订阅的 RevenueCat。
适用场景包括:新增支付处理、构建订阅计费、处理 Stripe webhook、实现定价页面、管理支付失败,或集成移动应用内购买。
核心原则
- 绝不处理原始卡数据——使用 Stripe Elements 或 Checkout 让卡号完全不经服务器,从而保持 PCI SAQ-A(最简合规级别)而非 SAQ-D。
- Webhook 是可信来源——绝不信任客户端支付确认。客户端 PaymentIntent 成功不代表什么,直到 webhook 处理器确认
payment_intent.succeeded。系统应围绕 webhook 事件构建。 - 处处幂等——webhook 可能被多次投递,每个 webhook 处理器必须幂等,使用 Stripe 事件 ID 作为去重键。
- 优雅处理失败——支付失败原因众多(余额不足、卡过期、欺诈检测)。在订阅生命周期中内置重试流程、催款邮件和优雅降级。
- 使用 Stripe 测试模式测试——使用测试 API 密钥、测试卡号和测试时钟进行订阅生命周期测试,绝不使用真实支付方式测试。
主要能力与关键模式
模式 1:用于订阅的 Stripe Checkout
适用时机:希望由 Stripe 托管完整结账 UI,包括支付表单、优惠码和税费计算时。
服务端创建 Checkout Session 的示例(TypeScript / Next.js):先获取或创建 Stripe 客户,然后创建订阅模式的 checkout session,配置 success_url、cancel_url、订阅 metadata(userId)、14 天试用、automatic_tax 启用、允许优惠码、要求填写账单地址以计税、customer_update 地址与姓名自动更新,以及自动续费披露文案(custom_text.submit)与要求同意服务条款(consent_collection.terms_of_service)。客户端从定价卡向 /api/checkout 发送 priceId、userId、email,拿到 url 后跳转 Stripe Checkout。
理由:Stripe Checkout 负责 PCI 合规、3D Secure 认证、税费计算、优惠码和本地化。自建结账表单需数百小时工作并承担持续的 PCI 合规负担。除非有强需求要自建 UI,否则使用 Checkout。
模式 2:带幂等性的 Webhook 处理器
适用时机:每一个 Stripe 集成。Webhook 是获知支付状态的唯一可靠方式。
实现要点:以事件类型为键定义处理器,覆盖 checkout.session.completed(更新用户订阅 ID 与状态为 active)、customer.subscription.updated(同步订阅状态、套餐价格 ID、当前周期结束时间、是否期末取消)、customer.subscription.deleted(状态置 canceled、清除套餐)、invoice.payment_failed(状态置 past_due 并发送催款邮件,含应缴金额与下次重试时间)、invoice.paid(状态恢复 active)。
POST 处理器流程:(1) 用 stripe.webhooks.constructEvent 验证签名,失败返回 400;(2) 幂等性检查,查询事件 ID 是否已处理,已处理直接返回;(3) 调用对应事件处理器,成功后记录已处理事件;(4) 处理器报错时返回 500,触发 Stripe 自动重试。
理由:Stripe 至少投递一次 webhook,可能重复。存储已处理事件 ID 的幂等检查避免重复处理;签名验证防止伪造 webhook 攻击;返回 500 触发 Stripe 指数退避自动重试。
模式 3:自助服务客户门户
适用时机:让客户自行管理订阅(升级、降级、取消、更新支付方式),无需联系支持。
实现:创建 stripe.billingPortal.sessions,传入 customer 和 return_url。通过 stripe.billingPortal.configurations.create 一次性配置门户:subscription_update 允许价格与数量变更、按比例计费(create_prorations)、指定产品与价格;subscription_cancel 启用、模式为 at_period_end(不立即取消)、启用取消原因选项;payment_method_update、invoice_history 均启用。
理由:自助计费可减少 60-80% 支持工单。Stripe 客户门户是托管方案,处理套餐变更、按比例计费、带原因采集的取消、支付方式更新和发票历史,无需自建 UI。
模式 4:基于用量的计费
适用时机:定价基于消耗量(API 调用、存储、计算分钟、席位)而非固定订阅。
实现:用 stripe.subscriptionItems.createUsageRecord 上报用量,action 用 increment(累加)而非 set(替换)。后台任务按小时聚合:查询活跃的用量型订阅,获取用户小时用量,大于 0 时上报并记录用量报告。应用内通过 Redis 计数器跟踪用量(redis.incr 键并设置 7 天过期),检查是否接近套餐上限(达到 80% 发送警告邮件,达到上限抛出 UsageLimitExceededError)。
理由:按量计费使成本与价值对齐。按小时聚合减少对 Stripe 的 API 调用,同时保持数据足够新鲜用于开票。基于 Redis 的跟踪为限流提供亚毫秒级用量检查。
Stripe 测试卡参考
| 卡号 | 场景 |
|---|---|
4242 4242 4242 4242 |
支付成功 |
4000 0000 0000 3220 |
需要 3D Secure |
4000 0000 0000 9995 |
拒绝(余额不足) |
4000 0000 0000 0341 |
附加到客户失败 |
4000 0025 0000 3155 |
需要认证 |
使用任意未来到期日期和任意 3 位 CVC。
反模式
| 反模式 | 问题 | 更好做法 |
|---|---|---|
| 信任客户端支付确认 | 用户可伪造成功 | Webhook 处理器才是可信来源 |
| 服务器处理原始卡号 | PCI SAQ-D 合规(非常昂贵) | 使用 Stripe Elements 或 Checkout |
| webhook 处理器无幂等性 | 重复扣款、重复开通 | 按事件 ID 去重 |
| 立即取消订阅 | 用户在计费周期中失去访问 | 期末取消(cancel_at_period_end) |
| 在应用中硬编码价格 | 不改部署无法改价 | 价格 ID 存数据库或环境变量 |
| 支付失败无催款流程 | 静默收入流失(非自愿流失) | 自动重试 + 催款邮件 |
| 用真实支付方式测试 | 有真实扣款风险,无测试时钟 | 仅使用 Stripe 测试模式 |
检查清单
- [ ] Stripe API 密钥存于环境变量(绝不写入代码)
- [ ] 启用 webhook 签名验证
- [ ] webhook 处理器幂等(事件 ID 去重)
- [ ] 处理所有订阅状态变化(created、updated、canceled、past_due)
- [ ] 实现支付失败催款流程(邮件 + 重试)
- [ ] 配置客户门户用于自助计费
- [ ] 订阅按钮旁展示自动续费条款(价格、频率、试用结束、如何取消);采集明示同意;确认邮件重复这些条款
- [ ] 在线取消与注册一样简便(门户启用 subscription_cancel),无强制挽留电话(California ARL、FTC ROSCA)
- [ ] 所有开发与预发布使用测试模式
- [ ] 确认 PCI 合规级别(使用 Stripe Checkout/Elements 为 SAQ-A)
- [ ] 启用税费计算(Stripe Tax 或外部服务商)
- [ ] 为套餐变更配置按比例计费(升级/降级)
(资料原文检查清单以 "We" 截断,后续内容暂未提供。)