Menu

版本 4.x 更新记录

提示

  • 请查看 package.json 文件中的 version 字段获取版本号
  • 3.x 的更新记录,请参考 版本 3.x 更新记录

4.0.0

4.0.0 是一次重写级别的升级:支付、积分、团队三个模块被完全重做,定价数据从数据库搬进代码,数据库迁移文件重建基线。从 3.x 升级不是 pnpm install 就能完成的,请先读下面的「破坏性变更」。

破坏性变更(从 3.x 升级必读)

  • 定价不再存数据库:删除 pricing_planspricing_plan_groups 两张表,删除管理后台的定价管理页面(/dashboard/admin/prices 全部文件)。定价的唯一事实来源变成 config/pricing.ts
  • 积分表重建:删除 usagecredit_logs,新增 credit_balancesorganization_credit_balancescredit_transactions
  • 新增组织相关表organizationmemberinvitation(Better Auth organization 插件)

定价:从数据库迁到代码

  • 新增:config/pricing.ts 作为定价唯一事实来源。一个套餐 = 一个对象,里面同时写清楚 provider、provider 侧的价格 ID({ test, live } 两套环境)、赠送积分、以及 en/zh/ja 三语文案,不再散落在 i18n 文件里
  • 新增:config/credits.ts 存放非付费的积分发放规则,目前是注册赠送积分 signupBonusCredits(默认 30,设为 0 即关闭)
  • 新增:套餐按 provider 字段声明由谁收款,三家 provider 各自填对应字段:stripePriceId / creemProductId / paypalPlanId
  • 新增:scripts/bootstrap-stripe-prices.tspnpm stripe:bootstrap),按配置文件在 Stripe 侧批量创建价格
  • 优化:定价组件重写为 PricingSection / PricingCard / CheckoutButton 三件套,删除原来的 PricingAll / PricingByGroup / PricingByPaymentType / PricingCardDisplay / PricingCTA
  • 约定:planId 永不复用、售出过的套餐永不删除(历史订单和积分流水靠它反查),下架用 active: false

支付履约:统一的 lib/billing

  • 重构:新增 lib/billing/,把三家 provider 的 webhook 事件收敛到一套与 provider 无关的核心履约逻辑。原来的 lib/payments/app/api/*/webhook/handlers.tsapp/api/payment/verify-success/* 全部删除
    • types.ts:统一的订阅状态 CanonicalSubscriptionStatus 与各类履约入参,金额一律整数分
    • fulfillment.ts:provider 无关的核心 —— 一次性购买、订阅付费、订阅升级、订阅同步、订阅终止、退款
    • stripe.ts / creem.ts / paypal.ts:每家一个适配器,只负责把 webhook 负载翻译成核心入参,SDK 类型不外泄
    • cancel.tscancelProviderSubscription 统一取消实现,兼容三家「已取消」的不同报错
    • duplicate.ts:重复订阅结算 —— 取消后开的那笔、退款、Redis 去重后告警;退款失败会抛出让 provider 重试
    • reconcile.ts:定时对账,补救丢失的 webhook(续费重放 + PayPal 挂起捕获的推进)
    • notify.ts:webhook 触发的通知(发放失败告警管理员、续费失败通知用户、欺诈预警),全部 Redis 去重
    • customer.tsstripeCustomerId 的唯一读写口,遇到脏 ID 自愈
  • 新增:订阅升级(跨套餐变更)走增量履约,按差额补发积分,并发订阅升级通知
  • 新增:PayPal eCheck 等挂起付款先落 status='pending' 订单行,由定时任务推进为成功或失败
  • 新增:管理后台订单页支持退款操作(RefundDialog
  • 优化:幂等性做成结构性的 —— orders(provider, provider_order_id) 唯一索引 + onConflictDoNothing + 累计退款额,不再靠防御式判断
  • 优化:退款改写原订单行,而不是新增一行
  • 优化:webhook 路由保持轻薄 —— 可重试错误返回 5xx,PermanentFulfillmentError 先告警再 ack
  • 优化:Creem SDK 从 0.4 升级到 1.6,移除 @creem_io/nextjslib/creem/client.ts 与类型定义大幅精简

积分系统重写

  • 重构:新增 lib/credits/,余额只能通过 ledger.ts 变更,credit_transactions 为只追加日志(delta + 快照 + 序号)
  • 新增:双桶模型 —— 订阅积分(每次发放时重置,订阅结束清零)与购买积分(永不过期),消费优先扣订阅桶
  • 新增:年付套餐的月度定投 —— 首月立即发放,剩余 11 次按月滴灌,定时任务与惰性结算共用同一入口,行锁 + 复检保证并发只发一次
  • 新增:app/api/cron/credits 定时任务入口(POST + Authorization: Bearer $CRON_SECRET),负责积分定投结算、逾期续费对账、PayPal 挂起捕获对账
  • 新增:注册赠送积分,事务内判重,重放无害
  • 新增:app/[locale]/(protected)/dashboard/credit-usage-example/ 积分消费示例
  • 优化:余额永不为负,回收类操作在 0 处截断

团队 / 组织

  • 新增:基于 Better Auth organization 插件的团队能力,组织即计费主体,共享一个积分池(organization_credit_balances
  • 新增:/dashboard/team 团队管理页 —— 订阅、共享积分池、成员与席位、积分流水四张卡片;未建团队时走创建/选择引导
  • 新增:components/shared/WorkspaceSwitcher.tsx 个人/团队工作区切换
  • 新增:邮件邀请加入团队,emails/organization-invitation.tsx 模板 + /accept-invitation/[id] 接受页
  • 新增:席位上限 = 团队套餐的 seats,无订阅时为 1(仅容纳 owner)。四个 Better Auth 钩子做前置校验,另有咨询锁 + 超额回收兜底并发
  • 新增:组织删除守卫 —— 存在有效订阅或任何资金流水时拒绝删除,外键 RESTRICT 作为数据库层兜底
  • 约定:团队工作区内不售卖积分包;资金相关行在用户删除时 SET NULL,在组织删除时 RESTRICT

管理后台

  • 新增:优惠券控制台 /dashboard/admin/coupons。Stripe 是唯一存储,本地不建表不迁移 —— 一个优惠券 = Stripe Coupon(折扣、适用范围、计费时长)+ Stripe Promotion Code(对客码、次数上限、有效期、限制条件)。码创建后不可改,所以 UI 只提供启用/停用与「复制一份再改」
  • 新增:积分管理页 /dashboard/admin/credits,全局积分流水审计 + 按邮箱批量发放(去重,单次上限 200,未匹配邮箱会回报)
  • 新增:用户详情页 /dashboard/admin/users/[userId],单用户 360 度视图:资料、积分余额、订阅、累计统计与历史,支持手工调整积分
  • 新增:注册邮箱黑名单 —— 管理后台维护前缀/后缀/完整地址三类规则,存 Redis,注册钩子同时比对原始小写形式与归一化形式,防 Gmail 点号与 + 后缀绕过。读取失败静默降级为空规则,不阻断正常注册
  • 新增:删除用户账号时先清理该用户的 R2 资源(lib/storage/account-purge.ts),清理失败则中止删除
  • 重做:Overview 概览页。单一时间窗(?range=7d|30d|90d,UTC 自然日)驱动整页,五个区块各自 Suspense 并行流式加载,任一区块失败不拖垮整页
    • KPI 行:净收入、MRR、新增注册、积分消耗
    • 趋势图:单序列柱状图 + 指标切换(切换不重新请求)
    • 待处理:逾期订阅、挂起订单、期末取消、未读反馈、失败订单、非本位币订单,每行直达对应管理页
    • 分布卡 + 最近 8 笔订单
    • 口径:金额为整数分,净收入 = 总额 − 退款,挂起/失败不计收入,本位币取定价配置中第一个启用套餐的币种,注册数排除匿名账号,积分消耗 = 消费 − 消费退回,MRR = active + past_due 按月度费率折算
  • 优化:数据表容器与各管理页布局统一,移动端响应式改善

用户端

  • 新增:/dashboard/billing 账单页 —— 两个积分桶、当前套餐、购买记录与积分流水(两张表独立分页)。团队工作区下顶部横幅引导去 /dashboard/team
  • 重做:components/layout/SidebarUserCard.tsx 替换原来的 SidebarUserNavCurrentUserBenefitsDisplay,侧边栏直接展示积分余额
  • 新增:--credit 主题色(金币金),浅色/深色主题下角色对调

其他

  • 新增:/api/health 健康检查端点,并行探测各依赖服务并折叠成单一 HTTP 状态(200 / 503),未配置的依赖记为 skipped 而非失败。故意公开且故意不返回细节:不需要密钥,也不回传错误信息、版本号或主机名
  • 新增:components/shared/DomTranslationGuard.tsx,防御性处理浏览器整页翻译导致的 React DOM 崩溃
  • 优化:字体体系整理,移除 @fontsource/science-gothic,Header、Footer、侧边栏等组件统一字体用法
  • 优化:全仓库 CLAUDE.md 与代码注释改写为英文

技术栈升级

  • Next.js 16.1.116.3.3
  • React 19.2.319.2.8
  • creem 0.4.01.6.0(移除 @creem_io/nextjs
  • cloudflare 6.4.07.1.0
  • Drizzle ORM 0.44(PostgreSQL,public schema)
  • Better Auth 1.4(启用 organization 插件)