Stripe Payment 服务
Webhook 与同步
Stripe webhook、支付记录和 topup 到账之间的同步关系。
支付服务最关键的同步链路是:
Stripe 事件
-> webhook 记录
-> 更新支付记录
-> 完成 topup
-> 用户余额到账为什么 webhook 要单独记录
因为 Stripe 是支付事实的上游来源。
把 webhook 单独记录下来有几个直接好处:
- 后续可以排查
- 支付记录状态可以重建
- 必要时可以重放或补偿同步
也就是说,webhook 记录层不是多余步骤,而是支付同步的事实底座。
启用示例
base.use(stripePaymentService({
balance: {
readTopup: async (topup_id) => await balance.readTopup(topup_id),
finishTopup: async (topup_id, extra) => await balance.finishTopup(topup_id, extra),
},
secret_key: process.env.STRIPE_SECRET_KEY,
webhook_secret: process.env.STRIPE_WEBHOOK_SECRET,
}));配置好以后,Stripe 应该把事件发到:
POST /v1/payment.stripe/webhook如果你没有显式配置跳转地址,服务会优先基于 DOWNCITY_CITY_BASE_URL,再回退到当前请求 origin,并自动提供两张可直接访问的默认结果页:
GET /v1/payment.stripe/redirect/successGET /v1/payment.stripe/redirect/cancel
当前阶段真正同步什么
这层不是发放产品权益,也不是复制 Stripe 全量对象。
当前阶段真正做的是:
- 找到对应的 Stripe 支付记录
- 找到它关联的 topup
- 在支付成功时调用
balance.finishTopup() - 把支付记录标记成
paid/expired/failed
事件范围
一次性充值链路只需要很小的 Stripe 事件面:
checkout.session.completed:必须打通的成功路径,用它完成 topupcheckout.session.expired:把支付记录标记为过期payment_intent.payment_failed:把支付记录标记为失败
其他 Stripe 事件可以先忽略,等产品明确需要新的支付流再接。
幂等规则
webhook 处理必须在两层保证幂等:
- 事件级幂等:重复的 Stripe
event_id不能重复应用 - 到账级幂等:重复成功事件不能让同一笔 topup 重复到账
实践里,先检查已保存的 payment 状态,再让 balance.finishTopup() 成为唯一的钱包到账入口。
常见场景
场景一:支付成功了,但余额还没到账
最先该检查的是:
- webhook 到 City 了吗
- webhook 事件有没有被记录
- 支付记录有没有更新
- topup 有没有被 finish
场景二:需要人工对账或修复
如果 Stripe 和 City 当前状态不一致,webhook 记录层和支付记录层会是非常重要的依据。
常用 API / 入口
POST /v1/payment.stripe/checkout/createPOST /v1/payment.stripe/webhookGET /v1/payment.stripe/payments