news 2026/9/11 12:22:00

saas-starter 订阅暂停/恢复实战:从用户点击到 Webhook 同步的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
saas-starter 订阅暂停/恢复实战:从用户点击到 Webhook 同步的完整指南

saas-starter 订阅暂停/恢复实战:从用户点击到 Webhook 同步的完整指南

【免费下载链接】saas-starterGet started quickly with Next.js, Postgres, Stripe, and shadcn/ui.项目地址: https://gitcode.com/GitHub_Trending/sa/saas-starter

上个月,有客户说团队预算被砍,想先停三个月、下季度再续——这个场景正是 saas-starter 订阅暂停/恢复功能要解决的:让用户不取消订阅就能停掉计费,需要时再一键续上。项目基于 Next.js、Postgres(Drizzle ORM)、Stripe 和 shadcn/ui,本文直接拆给你看要改哪 4 个地方、怎么自测。

一张图看懂:订阅状态到底怎么流转

动代码之前,先把状态机定下来。"暂停"不是你在数据库里加的一个额外字段,而是 Stripe 官方认可的订阅状态,你的系统只做同步。

状态含义常见触发
active正常计费试用结束扣款成功,或暂停后恢复
paused停止扣费,工作区和数据保留用户在 Billing Portal 选择暂停
canceled订阅终止用户取消,或扣款永久失败

动手:3 步暂停你的订阅

站在用户视角,暂停就是三次点击:点仪表盘上的"管理订阅"进 Stripe Billing Portal → 选暂停模式 → 确认返回。

  1. 选模式immediate立即停止计费,本期不再扣款;at_period_end本期服务用到头,下期起暂停。
  2. 确认:Portal 落库变更,Stripe 同时把customer.subscription.paused事件推给后端 webhook。
  3. 回仪表盘:状态徽标从绿色"活跃"变琥珀色"已暂停",用户一眼明白——号没丢,只是停了扣费。

之所以能这么顺,是因为 Portal 是 Stripe 提供的现成界面,项目只在配置里打开对应能力,不用自己画暂停按钮。入口处的createCustomerPortalSession负责换一张带正确配置的 Portal 链接,这就是整个功能"看起来大、实际很小"的原因。

恢复订阅

恢复是暂停的反向操作:进同一个 Portal,点"恢复",确认卡片仍有效(卡过期的先换卡),完事。Stripe 推customer.subscription.resumed回来,库里状态变回active,徽标重新变绿。全程不需要重新注册或重建工作区。

幕后:改动到底发生在哪 4 个地方

把仓库翻一遍,真正要动的只有 4 处,全部可肉眼验证,且都不碰主结账链路。

1. teams 表的subscriptionStatus

lib/db/schema.ts 里 teams 表本来就留了三个 Stripe 同步列,不用加字段、不用跑迁移:

作用
stripeCustomerId从 Stripe Customer 反查本地团队
stripeSubscriptionId关联具体的 Stripe 订阅
subscriptionStatus同步 active/paused/canceled 等状态

2. Portal 配置里加 pause

lib/payments/stripe.ts 的 Portal 配置features里已有subscription_updatesubscription_cancel,补一项即可:

subscription_pause: { enabled: true, mode: 'immediate' // 或 at_period_end }

3. webhook 新增两个事件分支

app/api/stripe/webhook/route.ts 的 switch 现在只接updated/deleted,加上暂停/恢复:

case 'customer.subscription.paused': case 'customer.subscription.resumed': { const sub = event.data.object as Stripe.Subscription; const team = await getTeamByStripeCustomerId(sub.customer as string); await updateTeamSubscription(team.id, { subscriptionStatus: sub.status === 'paused' ? 'paused' : 'active' }); break; }

4. 前端状态徽标

仪表盘页面读team.subscriptionStatus做分支:paused渲染琥珀色 Badge"已暂停,将于 x 日恢复",active渲染绿色。想再严格些,就在 app/(dashboard)/layout.tsx/layout.tsx) 里对暂停态灰掉部分功能入口。

自测:5 步验证暂停/恢复真的生效

✅ 测试卡4242 4242 4242 4242,五步走完闭环:

  1. 本地跑起来,用测试卡过一次结账,确认teams表里subscriptionStatusactive
  2. 调暂停接口(或在 Portal 里点暂停):
# 暂停 curl -X POST https://api.stripe.com/v1/subscriptions/sub_xxx/pause \ -u sk_test_xxx: -d "pause[behavior]=immediate" # 恢复 curl -X POST https://api.stripe.com/v1/subscriptions/sub_xxx/resume -u sk_test_xxx:
  1. 查库,subscriptionStatus应变为paused
  2. 发恢复命令,状态回到active
  3. 看仪表盘:徽标从琥珀变绿。若没变,先查 webhook 签名校验有没有过。

避坑清单

下面四条,都是上线后会被真实扎到的地方。

  • webhook 重试:Stripe 投递失败会自动重发,处理逻辑必须幂等——同一状态写两次等价于写一次。
  • 邮件通知:暂停/恢复要通知到用户(Stripe Billing 自带邮件,或自己发),否则用户根本不知道扣费状态变了。
  • 权限控制:只有团队管理员能进账单管理入口,入口处做角色校验。
  • 暂停期功能限制:状态为paused时灰掉邀请成员、数据导出等功能,避免"停了计费却还在消耗资源"的尴尬。

【免费下载链接】saas-starterGet started quickly with Next.js, Postgres, Stripe, and shadcn/ui.项目地址: https://gitcode.com/GitHub_Trending/sa/saas-starter

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 12:17:05

Python迭代器深度解析:从for循环原理到生成器与itertools实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 12:17:04

OpenMontage 技能库实战:基于 Tailwind CSS v4 构建可扩展设计系统

OpenMontage 技能库实战:基于 Tailwind CSS v4 构建可扩展设计系统 【免费下载链接】OpenMontage Worlds first open-source, agentic video production system. 12 production pipelines, 100 tools, 700 agent skill and production-knowledge files. Turn your A…

作者头像 李华
网站建设 2026/9/11 12:11:03

项目管理深度解析(三十八)——项目建设团队怎么开展

摘要:本文围绕「项目建设团队怎么开展」这一主题,系统梳理了建设团队的关键动作与落地方法。文章从团队组建入手,介绍了人员获取与角色职责定义(RACI 矩阵)的方法;随后阐述了协作机制、能力建设、团队激励与…

作者头像 李华