用途:在基础 webhook 已就绪后,处理支付与订阅状态同步的边缘情况,包括乱序事件、重试、状态对账,以及构建准确反映各子状态的用户状态展示。前提是幂等与基础事件处理已存在(参见 subscription-fullstack)。
准备问题:是否有 webhookEvent 去重表;用户可见状态页位于账户设置、账单页还是应用头部横幅;Stripe 在标记未付款前重试几次(Dashboard 可配,默认约 4 周内 4 次);催款邮件由 Stripe Smart Retries、自有系统还是两者发送。
核心模式:1)乱序投递:Stripe 不保证事件顺序,应按 stripeCustomerId 而非 stripeSubId 查找并写入,因为后者在 checkout.session.completed 之前可能为空。2)时间戳保护更新:给订阅模型加 lastWebhookAt,用 updateMany 配合 lastWebhookAt 小于事件时间的条件,避免过期事件覆盖较新状态。3)对账任务:通过 cron 或队列定期运行,查找超过 24 小时未收到 webhook 且处于非终态(active、trialing、past_due)的订阅,调用 Stripe 直接同步,逐条隔离错误;也能捕获用户在 Stripe Dashboard 直接取消的情况。4)状态展示:定义状态到文案、severity 与操作的映射,注意 cancelAtPeriodEnd 是 active 订阅上的标记,须先于 status 判断。5)结账乐观更新:成功页直接取 Stripe session,可按 payment_status 立即展示成功或待处理,再轮询自有 DB 直到同步。6)按需对账:提供管理员强制同步端点与限流的用户自助刷新端点(如每分钟 3 次),避免用户刷爆 Stripe 限流。
输出物:subscription 模型新增 lastWebhookAt;lib/reconcile.ts 夜间任务;api/billing/refresh.ts 限流自助与管理员端点;lib/subscriptionDisplay.ts 状态映射;含乐观取 session 与轮询的 BillingSuccessPage;BillingStatusRefresh 按钮。
限制:资料针对 Stripe 生态,未提供其他支付平台支持;未说明具体框架与部署方式;价格、收费与效果暂未提供。