Skip to content

定价与会员体系调整 - 2026年4月

状态: 规划 / 影响分析 日期: 2026-04-08 来源: z-2-reqirements/20260405 会员和积分 价格调整 包含IOS第一版本.docx


目录


1. 概述

本次调整涉及两个平台的定价体系重构:

网页端: 提高月会员积分赠送 (Gold 4,000 -> 6,000, Diamond 24,000 -> 30,000), 新增年会员方案 (Gold $89.99/100K 积分一次性发放, Diamond $299.99/100万积分一次性发放), 更新最低积分价格的文案。

iOS端 (全新): 仅上线 Gold 会员三档 (周会员 $7.99/1,500积分, 月会员 $19.99/6,000积分, 年会员 $89.99/100,000积分一次性发放), 所有方案含3天免费试用, 不上线积分包购买, 不上线 Diamond 会员。

Google Play / Android: Android 将与 iOS 采用同一方向的移动端支付方案: Gold-only 订阅通过 Google Play Billing 售卖,并通过 RevenueCat 作为统一的移动订阅层。

核心风险点

  1. 年会员一次性发放 - 现系统在每次 invoice.payment_succeeded 事件时发放积分。年会员需一次性发放10万/100万积分,不能按月。
  2. 年会员取消后积分回收 - 需求文档提到"取消计划后积分回收",这是现系统没有的新功能。
  3. iOS 平台差异化 - iOS 产品目录与网页端不同,需要平台感知逻辑。
  4. 周会员 - 全新的计费周期,现系统不存在。
  5. iOS Apple IAP + RevenueCat 集成 - iOS 计费路径已经确定为 Apple IAP + RevenueCat。当前主要风险不再是“选哪条路”,而是 entitlement 同步、用户身份绑定、试用期行为和积分发放逻辑能否正确落地。
  6. Google Play Billing + RevenueCat 集成 - Android 已确认使用 Google Play Billing,并与 iOS 复用同一套 Gold-only 移动商品目录。当前主要风险是商品映射、entitlement 同步、续费/取消/退款处理和跨平台账号绑定。

2. 新旧价格对比

2a. 网页端订阅

方案当前月付 月付当前年付 年付
Gold$19.99, 4,000 SE/月$19.99, 6,000 SE/月$167.92/年 ($13.99/月), 4,000 SE/月$89.99/年, 100,000 SE 一次性
Diamond$69.99, 24,000 SE/月$69.99, 30,000 SE/月$587.92/年 ($48.99/月), 24,000 SE/月$299.99/年, 1,000,000 SE 一次性

关键变化:

  • 月会员积分赠送提升 (Gold +50%, Diamond +25%)
  • 年付价格大幅降低 (Gold: $167.92 -> $89.99 降46%; Diamond: $587.92 -> $299.99 降49%)
  • 年付积分改为一次性发放,不再按月
  • 年付现金折扣约为 Gold 62%、Diamond 64%; 如果 UI 仍保留 Diamond 的 "75% off" 标签,应把它视为营销文案,不要当作计算折扣值
  • 默认推荐方案: Gold 年会员
  • Diamond 年付显示 "75% off" 标签

2b. 网页端积分包 (不变)

积分包积分价格
Taster250$1.99
Mini600$3.99
Starter2,000$9.99
Best Value4,000$14.99

积分包价格和数量不做任何修改。

2c. iOS 订阅 (全新)

方案价格积分备注
Gold 周会员$7.991,500 SE新计费周期
Gold 月会员$19.996,000 SE与网页端一致
Gold 年会员$89.99100,000 SE 一次性与网页端一致
  • 所有方案含3天免费试用
  • 不上线积分包购买
  • 不上线 Diamond 会员
  • 默认推荐: Gold 年会员
  • 推送通知提醒用户每日领取积分

2d. Google Play / Android 计划

Android 已确认复用与 iOS 相同的第一版移动端商品目录:

  • Android 使用与 iOS 相同的移动端订阅目录: Gold 周付、Gold 月付、Gold 年付
  • Android 第一版同样不上积分包和 Diamond
  • Android 应用内购买走 Google Play Billing,并通过 RevenueCat 作为订阅层
  • 即使商业设计与 iOS 一致,试用和价格展示仍需在 Google Play Console 单独配置

2e. 灵能 (SE) 单价对比

产品价格SE单价 ($/SE)
Taster 积分包$1.99250$0.00796
Mini 积分包$3.99600$0.00665
Starter 积分包$9.992,000$0.00500
iOS Gold 周会员$7.991,500$0.00533
Best Value 积分包$14.994,000$0.00375
Gold 月会员$19.996,000$0.00333
Diamond 月会员$69.9930,000$0.00233
Gold 年会员$89.99100,000$0.00090
Diamond 年会员$299.991,000,000$0.00030

价格阶梯合理且自洽:承诺越高,单价越低。

新的最低 SE 单价是 Diamond 年会员 $0.0003/SE。作为对比,当前最低价 (显示在积分购买弹窗促销文案中) 是 Best Value 积分包 + Diamond 加成: $14.99 / 4,600 = ~$0.0033/SE。单价降低了 91%

备注: 需求文档中写的最低价是 $0.0002/SE,但 $299.99 / 1,000,000 = $0.0002999,四舍五入为 $0.0003。文档可能使用了约数。实际显示时应由 bestPerCredit.toFixed(4) 计算,结果为 "0.0003"

范围说明: 这里的单价对比只按主积分发放计算,不包含每日奖励、会员折扣和其他会员权益。


3. 影响分析

3a. 配置层

文件修改内容复杂度
config/stripe.ts更新网页端年付价格; 只有周会员走 Stripe 时才增加 GOLD_WEEKLY; 否则周会员应放在移动端计费配置中
config/stripe-credits.tsMONTHLY_GRANT_AMOUNTS Gold 4000->6000, Diamond 24000->30000
env.ts / 计费环境配置只有周会员走 Stripe 时才新增 STRIPE_PRICE_GOLD_WEEKLY; 否则应增加 Apple / Google / RevenueCat 商品标识

3b. 后端 / 业务逻辑

文件修改内容复杂度
lib/stripe-handlers.ts区分年付 vs 月付发放 (一次性 vs 循环); 实现年付取消积分回收
lib/auth.tsx保持 Stripe 订阅配置以网页端为主; iOS 订阅在 Better Auth Stripe 之外通过 Apple IAP + RevenueCat 处理
Prisma schema可能需要在 Subscription 模型增加 billingInterval 字段
移动端计费集成通过 RevenueCat 增加 Apple IAP 与 Google Play Billing 的 entitlement 同步路径

3c. UI 组件

文件修改内容复杂度
PricingSection.tsx月积分值 (4000->6000, 24000->30000); 年付价格展示; 一次性积分提示; 默认选中 Gold 年付
ComparePlansSection.tsx月度 SE 数值, 最大月度潜力重新计算
CreditPurchaseModal.tsxbestPerCredit 改为基于 Diamond 年付; 12种语言的促销文案更新
MembershipContent.tsxMONTHLY_GRANTS 常量 (4000->6000, 24000->30000)

3d. 移动商店相关

领域需要做什么复杂度
产品目录配置为 iOS + Google Play 建立移动商店配置,或建立共享移动目录并映射到不同商店商品 ID
免费试用配置App Store / Google Play 的试用期配置
移动端计费校验通过 RevenueCat webhook / entitlement 同步处理 Apple 和 Google 商品,并完成商店侧配置
推送通知iOS / Android App 功能 (每日领取提醒)不在当前范围内 (App 团队)
无积分包移动端 UI 应隐藏积分包购买入口低 (App 端)

3e. 文档

文件修改内容
docs/credit-system.md更新所有价格表, 新增年付方案章节
docs/credit-system.zh-CN.md同上,中文版

3f. 测试

文件修改内容
__tests__/stripe/credit-grants.test.ts更新 MONTHLY_GRANT_AMOUNTS 断言; 新增年付/周付测试
__tests__/stripe/webhook-handlers.test.ts更新差额积分计算; 新增年付场景
__tests__/stripe/integration-handlers.test.ts更新预期发放金额; 新增年付场景

4. 逐文件修改计划

4.1 config/stripe-credits.ts

typescript
// 修改前
export const MONTHLY_GRANT_AMOUNTS: Record<string, number> = {
  FREE: 0,
  GOLD: 4_000,
  DIAMOND: 24_000,
};

// 修改后
export const MONTHLY_GRANT_AMOUNTS: Record<string, number> = {
  FREE: 0,
  GOLD: 6_000,
  DIAMOND: 30_000,
};

// 新增: 年会员一次性积分发放量
export const YEARLY_GRANT_AMOUNTS: Record<string, number> = {
  FREE: 0,
  GOLD: 100_000,
  DIAMOND: 1_000_000,
};

4.2 config/stripe.ts

typescript
// 修改后 - 只有周会员走 Stripe 时才新增 GOLD_WEEKLY
export const STRIPE_PRICES = {
  GOLD_WEEKLY: process.env.STRIPE_PRICE_GOLD_WEEKLY ?? 'price_gold_weekly',
  GOLD_MONTHLY: process.env.STRIPE_PRICE_GOLD_MONTHLY ?? 'price_gold_monthly',
  GOLD_YEARLY: process.env.STRIPE_PRICE_GOLD_YEARLY ?? 'price_gold_yearly',
  DIAMOND_MONTHLY: process.env.STRIPE_PRICE_DIAMOND_MONTHLY ?? 'price_diamond_monthly',
  DIAMOND_YEARLY: process.env.STRIPE_PRICE_DIAMOND_YEARLY ?? 'price_diamond_yearly',
} as const;

// 修改后 - 更新年付价格
export const SUBSCRIPTION_PRICES = {
  GOLD: {
    weekly: 7.99,             // 仅移动端
    monthly: 19.99,
    yearlyPerMonth: 7.50,     // $89.99 / 12
    yearlyTotal: 89.99,
    discount: 62,             // (1 - 89.99 / (19.99*12)) * 100 ≈ 62%
  },
  DIAMOND: {
    monthly: 69.99,
    yearlyPerMonth: 25.00,    // $299.99 / 12
    yearlyTotal: 299.99,
    discount: 75,             // 需求文档要求显示 "75% off"
  },
} as const;

重要说明: 如果 Gold 周会员是移动商店专属商品,就不应放进 STRIPE_PRICES。这种情况下应保持 config/stripe.ts 只服务网页端,并单独维护 Apple / Google / RevenueCat 商品 ID 映射。

4.3 lib/stripe-handlers.ts - 核心改动

此文件需要最大幅度的修改。核心修改:

  1. 检测计费周期 (周/月/年) - 从 Stripe 订阅对象获取
  2. 月付方案: 继续按 MONTHLY_GRANT_AMOUNTS[tier] 每次发放
  3. 年付方案: 按 YEARLY_GRANT_AMOUNTS[tier] 一次性发放
  4. 年付取消: 回收剩余积分 (全新概念)
  5. 周付方案: 每次计费发放 1,500 积分

4.4 lib/auth.tsx - Better Auth Stripe 插件

Better Auth 的 Stripe 插件应优先只覆盖网页端 Stripe 方案。除非业务明确决定周会员也在网页端售卖,否则不应把 gold-weekly 直接建模为 Stripe 计划。

typescript
// 仅网页端 Stripe 方案
plans: [
  {
    name: 'gold',
    priceId: STRIPE_PRICES.GOLD_MONTHLY,
    annualDiscountPriceId: STRIPE_PRICES.GOLD_YEARLY,
  },
  {
    name: 'diamond',
    priceId: STRIPE_PRICES.DIAMOND_MONTHLY,
    annualDiscountPriceId: STRIPE_PRICES.DIAMOND_YEARLY,
  },
]

Gold 周会员已经确认走 iOS Apple IAP / RevenueCat,因此不应放在这里作为 Stripe 计划建模。

待确认: 3天免费试用是否也适用于网页端? 需求文档仅提到 iOS。

说明: Better Auth Stripe 的 freeTrial 验证只影响网页端 Stripe 计划。iOS 试用配置应放在 App Store Connect 和 RevenueCat 中。

4.5 PricingSection.tsx

diff
- monthlyCredits: 4_000,   // GOLD
+ monthlyCredits: 6_000,   // GOLD
- monthlyCredits: 24_000,  // DIAMOND
+ monthlyCredits: 30_000,  // DIAMOND

// 年付积分展示: 需显示 "100,000 SE Instantly!" 和 "1,000,000 SE Instantly!"
// 默认选中: Gold 年会员 (非当前默认)

4.6 ComparePlansSection.tsx

diff
// 月度 SE 赠送行
- gold: '4,000', diamond: '24,000'
+ gold: '6,000', diamond: '30,000'

// 最大月度潜力 (Gold: 6000 赠送 + 80*30 每日 = 8,400; Diamond: 30000 + 100*30 = 33,000)
- gold: '~6,400', diamond: '~27,000'
+ gold: '~8,400', diamond: '~33,000'

// 需新增年付价格行

4.7 CreditPurchaseModal.tsx

当前 bestPerCredit 计算仅基于积分包:

typescript
const bestPerCredit = Math.min(...CREDIT_PACKS.map((p) => p.priceUsd / (p.credits + calculateBonusCredits(p.credits, 'DIAMOND'))));

新定价下最便宜的 SE 是 Diamond 年付 ($299.99 / 1,000,000 = $0.0003)。促销文案应改为引用此价格。

修改方案: 硬编码或计算 Diamond 年付单价并用于促销文案。促销文案在12种语言中内联书写 (约468-534行),每种都需更新。

4.8 MembershipContent.tsx

diff
- const MONTHLY_GRANTS: Record<string, number> = { FREE: 0, GOLD: 4_000, DIAMOND: 24_000 };
+ const MONTHLY_GRANTS: Record<string, number> = { FREE: 0, GOLD: 6_000, DIAMOND: 30_000 };

建议: 此处是 config/stripe-credits.tsMONTHLY_GRANT_AMOUNTS 的重复定义。实施时应改为从配置文件导入。


5. 架构难点与解决方案

难点 1: 年会员一次性发放 vs 月会员循环发放

问题: 当前 grantSubscriptionCredits() 函数对每次 invoice.payment_succeeded 都使用 MONTHLY_GRANT_AMOUNTS[tier]。年付订阅 Stripe 每年只触发一次 invoice,但发放金额会是错误的 (会发6,000而不是100,000)。

方案: 在 grantSubscriptionCredits() 中新增 billingInterval 参数,根据计费周期选择不同的发放金额表:

  • 'year' -> YEARLY_GRANT_AMOUNTS[tier]
  • 'week' -> WEEKLY_GRANT_AMOUNTS[tier]
  • 'month' -> MONTHLY_GRANT_AMOUNTS[tier]

计费周期从 Stripe 订阅对象的 items.data[0].price.recurring.interval 字段获取。

难点 2: 年会员取消后积分回收

问题: 需求提到"取消计划后积分回收",当前系统没有此功能。

方案:

  • 年付订阅被立即取消 (非到期取消) 时:
    1. 计算原始发放金额
    2. 从用户余额中扣除 (最低为0,不允许负数)
    3. 创建 SUBSCRIPTION_RECLAIM 类型积分流水记录

待确认: "取消"是指立即取消 (退款场景) 还是到期取消? Stripe 支持两种模式。

难点 3: 周会员 (新计费周期)

方案:

  • 如果周会员走 Stripe,在 Stripe 后台创建 GOLD_WEEKLY 价格 (recurring, interval=week)
  • 如果周会员仅限 iOS,则应在 Apple IAP / RevenueCat 层建模,不要放进 Stripe
  • 新增 WEEKLY_GRANT_AMOUNTS 配置
  • 只有 Stripe 路径才在 PLAN_TO_TIER 中处理 'gold-weekly' -> 'GOLD'; Apple 路径则走商品映射表

难点 4: iOS 平台差异化

推荐方案: 客户端控制 UI。iOS App 只渲染 Gold 方案,不显示 Diamond 和积分包。后端无需区分平台,所有有效订阅都正常处理。

如果用户通过 iOS 订阅 Gold 后在网页端访问,其 tier 仍为 GOLD,网页端正常显示会员状态。

难点 5: 3天免费试用

方案:

  • 如果商品走 Stripe,在 Stripe 价格或订阅创建时配置 trial_period_days: 3
  • 如果商品走 Apple IAP,在 App Store Connect 或 RevenueCat 中配置试用

待确认: 免费试用期间是否发放积分?

难点 6: 升级差额积分

问题: Gold 年付用户 (已获得10万积分) 升级到 Diamond 年付,是否发放90万差额? 金额巨大。

场景分析:

差额备注
Gold 月付 (6K/月)Diamond 月付 (30K/月)24,000与现有逻辑一致
Gold 周付 (1.5K/周)Gold 月付 (6K/月)0同等级,无差额。正确。
Gold 年付 (10万一次)Diamond 年付 (100万一次)90万?金额巨大,需决策
Gold 月付 (6K/月)Gold 年付 (10万一次)???同等级但计费周期不同

待确认: 年付升级差额发放规则。

难点 7: iOS Apple IAP + RevenueCat 落地

问题: Apple 要求所有 iOS 应用内的数字商品购买必须使用 Apple 内购 (IAP) 系统。MysticX 积分属于数字商品。因此 iOS 订阅不能直接走 Stripe,必须使用 StoreKit / Apple IAP。

当前架构假设所有订阅都走 Stripe 回调。iOS 订阅的流程完全不同:

  1. 用户通过 Apple IAP (StoreKit 2) 订阅
  2. Apple 处理支付并抽成 30%
  3. iOS App 将交易/收据发送到后端
  4. 后端通过 Apple App Store Server API 验证收据
  5. 后端授予会员等级和积分

这与 Stripe 回调流程在架构上有本质区别。

已确认方案:

  • iOS 订阅走 Apple IAP
  • RevenueCat 作为订阅抽象层和 entitlement 同步层
  • 后端优先接 RevenueCat webhook / entitlement 事件,而不是先自建 StoreKit 收据校验链路

实施要求:

  1. 在 App Store Connect 中创建 iOS 商品
  2. 在 RevenueCat 中镜像这些商品,并映射到内部 tier / billing interval
  3. 设计稳定的 appUserId 策略,保证 RevenueCat 用户能可靠映射到 MysticX 用户
  4. 处理 RevenueCat webhook 事件,覆盖激活、续费、取消、退款、过期
  5. entitlement 校验通过后,复用同一套内部积分发放引擎
  6. 正确处理 restore purchases,避免用户换设备或重新登录后丢失权益

推荐的整体架构:

  • 网页端: 继续使用 Stripe + Better Auth 处理 Gold 月付、Gold 年付、Diamond 月付、Diamond 年付
  • 移动商店: 使用 RevenueCat 统一处理 Apple IAP 和 Google Play Billing 的 Gold 周付、Gold 月付、Gold 年付
  • 后端: 统一收口到一个 entitlement 模型,至少记录 providerproductIdbillingIntervaltier 和发放策略

难点 8: Google Play Billing vs 网页端 Stripe

问题: Android 应用内的数字订阅必须走 Google Play Billing,不能在应用内直接走 Stripe。这与 iOS 一样,会形成移动商店计费和网页 Stripe 计费并存的架构。

已确认方案:

  • iOS 和 Android 共用一套移动端 entitlement 模型
  • 使用 RevenueCat,让 Apple 和 Google 的购买事件走同一条归一化链路
  • 保持 iOS 与 Android 商品目录一致
  • 单独验证 Google Play 的试用、续费、退款、取消和 entitlement 失效流程

6. 移动商店计划

6.1 按计费路径拆分的后端计划

已确认的 iOS 路径: Apple IAP + RevenueCat

  1. 网页端订阅继续使用 Stripe + Better Auth
  2. iOS 商品在 App Store Connect 创建,Android 商品在 Google Play Console 创建,不在 Stripe 中创建
  3. 将 Apple / Google / RevenueCat 商品映射到内部 tier 和 billing interval
  4. 增加服务端 entitlement 同步 / webhook 处理
  5. 在 entitlement 校验通过后复用同一套积分发放引擎
  6. 把 RevenueCat customer identity 与 MysticX 用户身份稳定绑定,确保恢复购买归属正确

6.2 仅移动 App 需要做的 (不在当前范围)

  1. 推送通知 (每日领取提醒)
  2. UI: 仅显示 Gold 三档,不显示 Diamond / 积分包
  3. 支付墙设计: 年会员加 "Founder's Pass" 标签
  4. 积分到账动效: 10万积分到账时的华丽效果
  5. App Store / Play Store 商品与支付墙配置

6.3 App Store 定价与 Apple IAP 分析

Apple 要求 iOS 应用内的数字商品必须使用内购 (IAP) 系统。因此 iOS 订阅不会走 Stripe,而是走 Apple 支付系统,并通过 RevenueCat 做订阅层封装。架构影响详见难点 7

Apple 抽成 30% (小型企业计划为 15%)。实际收入:

方案价格Apple 抽成 (30%)实际收入Stripe 同价实际收入
Gold 周会员$7.99$2.40$5.59-
Gold 月会员$19.99$6.00$13.99~$19.11
Gold 年会员$89.99$27.00$62.99~$87.08

每引导一个用户从网页端付款而非 iOS,年会员可多赚约 $24。

6.4 Google Play Billing 分析

Google Play Store 要求 Android 应用内数字订阅走 Google Play Billing。与 iOS 类似,但实现细节不同:

  • 商品和试用在 Google Play Console 中配置
  • 校验走 Google Play purchase token / Developer API,或者由 RevenueCat 抽象处理
  • 生命周期事件可以直接接 Google Play,也可以通过 RevenueCat + RTDN 归一
  • 财务在做利润测算前,应先在 Play Console 当前账号计划里确认真实费率,不要直接套用 Apple 的费率

6.5 移动端与网页端会员互通

如果用户在 iOS 或 Android 订阅后又访问网页端:

  • tier 仍应识别为 GOLD
  • 网页端需要正确展示其会员状态
  • 网页端不应展示 iOS 专属的周会员管理入口
  • 如果用户想升级 Diamond,应引导到网页端订阅

如果用户先在网页端订阅了 Diamond,之后再登录 iOS 或 Android App:

  • App 仍应识别其 DIAMOND 权益,并解锁 Diamond 对应能力
  • App 应展示 Diamond 为当前生效方案,标记为在 App Store / Play Store 以外管理
  • App 不应提供 App Store 或 Google Play 的 Diamond 购买入口,因为第一版移动商品目录里没有 Diamond
  • App 不应把这类用户当成未订阅用户去展示 Gold 免费试用或 Gold 购买卡片

Apple 反导流警告 (App Store Review Guideline 3.1.3(b)): Apple 规则允许 App 展示用户在其他地方购买的内容,但明确禁止 App 通过任何形式(外链、按钮、文字提示)引导用户在 IAP 以外的地方完成购买。具体来说:

  • 不能提供任何指向网页端会员页的外链或按钮。哪怕是"前往网站管理"这类按钮,只要目标页面含有定价或购买 UI,Apple 就会将其认定为反导流行为并拒审。
  • 只能展示纯信息性文字标签,例如: "您的 Diamond 会员已生效 (于 App Store 以外管理)",不附带任何可点击链接。
  • 如果确实需要跳转链接,目标页面必须是仅展示订阅状态的专属页面,且对来自 iOS 的用户完全隐藏定价和购买 UI。

Android / Google Play: Google Play 对外链限制比 Apple 宽松,指向订阅管理页而非新购流程的外链通常被允许。但第一版建议 Android 也先采用只读标签方案; App 上线稳定后,再考虑为 Android 单独增加"前往网站管理"入口。

要把这个流程做好,后端不能只存 tier。还应保留 provider (stripe / apple / revenuecat)、商品标识和计费周期,这样管理页面才能正确展示状态和可升级路径。

推荐规则: 先认权益,再看售卖目录。移动端可以只卖 Gold,但仍然要以只读方式承认并展示网页端带来的 Diamond 权益。移动端绝对不能出现任何可能被 Apple 解读为引导用户绕过 IAP 进行购买决策的 UI 元素。


7. 迁移与现有用户考虑

7.1 现有月会员

Gold 月会员: 下个计费周期起自动获得 6,000 SE (原 4,000)。免费升级,无需额外操作。

Diamond 月会员: 下个计费周期起自动获得 30,000 SE (原 24,000)。免费升级。

建议: 向现有会员发送通知 "好消息!您的月度积分已提升!"

7.2 现有年会员

现有年会员使用旧价格 ($167.92/$587.92)。Stripe 年付订阅每年只在续费时触发一次 invoice.payment_succeeded

关键边界情况: 部署新代码后,发放逻辑将根据 billingInterval 决定金额。当现有年会员续费时:

  • Stripe 收取价格 ($167.92 / $587.92),因为价格 ID 未变
  • 但新代码会发放的年付积分 (10万 / 100万)
  • 这意味着现有年会员付了更多钱,但获得与新用户 ($89.99) 相同的积分

这对现有用户实际上是有利的 (积分大幅增加),但可能不是预期行为。

建议: 创建新的 Stripe 价格。对现有年会员有三种处理方式:

  • (A) 保持旧价格和旧发放逻辑 (需在代码中保留旧的发放路径)
  • (B) 续费时自动迁移到新价格 (需更新 Stripe 订阅)
  • (C) 保持旧价格但按新规则发放积分 (对用户有利,实现简单)

7.3 通知

需求提到要发送"会员权益升级通知"。建议对所有活跃会员发送一次性通知。


8. 测试计划

8.1 需要更新的单元测试

测试文件修改内容
__tests__/stripe/credit-grants.test.ts更新 MONTHLY_GRANT_AMOUNTS 断言; 新增年付/周付测试
__tests__/stripe/webhook-handlers.test.ts更新差额计算; 新增年付场景
__tests__/stripe/integration-handlers.test.ts更新预期金额 (4000->6000, 24000->30000); 新增年付场景

8.2 新增测试场景

  1. 年付订阅创建 -> 一次性发放正确金额
  2. 年付订阅续费 -> 根据当前生效的商品 / 价格映射,确认年付积分再次正确发放
  3. 周付发票 -> 正确发放周积分
  4. 年付取消积分回收 -> 正确扣除
  5. 年付取消但余额不足 -> 扣至0
  6. Gold 月付升级到 Gold 年付
  7. Gold 年付升级到 Diamond 年付 -> 差额计算
  8. iOS 免费试用开始 -> 是否发放积分
  9. iOS 试用转正 -> 积分发放
  10. Google Play 首次购买 -> entitlement 和积分发放正确
  11. Google Play 续费 / 取消 / 退款 -> entitlement 和积分状态保持正确

8.3 手动测试清单

  • [ ] 网页端: Gold 月付显示 $19.99, 6,000 SE
  • [ ] 网页端: Diamond 月付显示 $69.99, 30,000 SE
  • [ ] 网页端: Gold 年付显示 $89.99, 100,000 SE (一次性)
  • [ ] 网页端: Diamond 年付显示 $299.99, 1,000,000 SE (一次性)
  • [ ] 网页端: Diamond 年付显示 "75% off"
  • [ ] 网页端: 默认选中 Gold 年会员
  • [ ] 网页端: 积分购买弹窗促销文案已更新
  • [ ] 网页端: 会员对比表数值正确
  • [ ] 网页端: 订阅 Gold 月付发放 6,000 SE
  • [ ] 网页端: 订阅 Gold 年付一次性发放 100,000 SE
  • [ ] 网页端: 订阅 Diamond 年付一次性发放 1,000,000 SE
  • [ ] 网页端: 取消年付回收积分 (如实现)
  • [ ] iOS: 仅显示 Gold 三档
  • [ ] iOS: 无积分包购买入口
  • [ ] iOS: 3天免费试用正常工作
  • [ ] iOS: 周会员每次计费发放 1,500 SE
  • [ ] Android / Google Play: 如产品确认一致,展示同一套移动端商品目录
  • [ ] Android / Google Play: 试用、续费、取消和 entitlement 同步正常

9. 实施清单

阶段 1: 配置与后端 (优先)

  • [ ] 在 Stripe 后台创建网页端新的年付价格 (Gold Yearly $89.99, Diamond Yearly $299.99)
  • [ ] 只有周会员也走 Stripe 时,才创建 GOLD_WEEKLY
  • [ ] 只有周会员走 Stripe 时,才增加 STRIPE_PRICE_GOLD_WEEKLY; 否则增加 Apple / Google / RevenueCat 商品标识配置
  • [ ] 更新 config/stripe-credits.ts - 月付积分 (6000/30000), 新增年付/周付积分
  • [ ] 更新 config/stripe.ts - 价格, 新增周付, 更新年付金额
  • [ ] 更新 lib/stripe-handlers.ts - 计费周期感知发放, 年付一次性逻辑
  • [ ] 更新 lib/auth.tsx - 保持 Stripe 计划以网页端为主,除非周会员也走 Stripe
  • [ ] 更新 tier / product 映射以支持不同 provider 的商品
  • [ ] 增加 iOS RevenueCat entitlement 同步 / webhook 集成
  • [ ] 定义 RevenueCat appUserId 映射和 restore purchases 行为
  • [ ] 如有 schema 变更运行 npx prisma generate

阶段 2: UI 更新

  • [ ] 更新 PricingSection.tsx - 积分值, 年付展示, 默认选中
  • [ ] 更新 ComparePlansSection.tsx - 月度 SE 值, 最大潜力
  • [ ] 更新 CreditPurchaseModal.tsx - 促销文案 (12种语言), bestPerCredit 计算
  • [ ] 更新 MembershipContent.tsx - MONTHLY_GRANTS 值

阶段 3: 测试

  • [ ] 更新 __tests__/stripe/credit-grants.test.ts
  • [ ] 更新 __tests__/stripe/webhook-handlers.test.ts
  • [ ] 更新 __tests__/stripe/integration-handlers.test.ts
  • [ ] 新增年付/周付/试用测试场景

阶段 4: 文档

  • [ ] 更新 docs/credit-system.md
  • [ ] 更新 docs/credit-system.zh-CN.md

阶段 5: iOS 支持

  • [ ] 完成 iOS RevenueCat 产品、entitlement 与 webhook 配置
  • [ ] 定义 iOS RevenueCat appUserId / 账号绑定策略
  • [ ] 验证 restore purchases 在重装 / 切账号场景下的行为
  • [ ] 与 iOS App 团队协调产品目录
  • [ ] 在 App Store Connect 设置内购 (Gold 周付/月付/年付)
  • [ ] 在 App Store Connect 配置免费试用

阶段 6: Google Play 支持

  • [ ] 在 Google Play Console 设置订阅 (Gold 周付/月付/年付)
  • [ ] 在 Google Play Console 配置试用 / introductory offer
  • [ ] 完成 Google Play 商品和 RevenueCat entitlement 映射
  • [ ] 配置内部测试 / 测试账号并验证跨平台 entitlement 同步

10. 待老板确认的问题

关键问题 (实施前需要答案)

  1. 年会员取消积分回收 - 文档提到"取消计划后积分回收":

    • (a) 用户点"取消"后订阅到期结束 -> 不回收 (已付全年费用)?
    • (b) 退款场景 (纠纷/管理员操作) -> 回收积分?
    • (c) 年中立即取消 -> 按比例回收积分?
    • 如果用户已花掉大部分积分,余额不够回收怎么办? 是否允许负数,还是扣至0?
  2. 免费试用期积分发放 - 3天免费试用期间是否发放积分 (1,500/6,000/100,000)?

    • 如果试用期发放积分,用户取消试用后是否回收?
  3. 年付升级差额 - Gold 年付用户 (已获10万积分) 升级到 Diamond 年付 ($299.99),是否发放90万差额积分? 金额很大。

  4. 现有年会员处理 - 是否创建新的 Stripe 价格 ID (保留现有用户旧价格)? 还是更新现有价格 (影响所有续费)?

  5. 周会员是否上网页端? - 文档提到周会员仅限 iOS。网页端会员展示页面要不要也加"周、月、年"切换?

  6. Diamond 年付 "75% off" 计算 - $299.99 vs $69.99*12 = $839.88,实际折扣约 64%。文档要求显示 "75% off"。是按文档写75%,还是显示数学上准确的 ~64%?

  7. 年会员权益范围 - 年付一次性发放积分之外,是否仍然完整享有该 tier 对应的每日积分、折扣和高级功能? 当前文档默认答案是是。

  8. RevenueCat 身份绑定策略 - RevenueCat appUserId 是否应与我们用户 ID 一一绑定? 匿名使用、登录切换和 restore purchases 合并规则怎么定?

次要问题 (可后续决定)

  1. 年付展示格式 - 定价切换器继续用"月付/年付"还是改其他 UI? 文档说"先保持现有的"。

  2. "Get 100,000 Energy Instantly!" 文案 - 这段文字显示在定价卡上还是结算页面上,还是两者都显示?

  3. 推送通知文案 - 文档建议: "Allen, your daily 80 energy is ready to align with the stars." 是否用真实用户名? 是否根据等级调整积分数 (80/100)? (移动 App 团队问题)

  4. 年付积分到账动效 - "星空炸裂效果"只做移动端还是网页端也要做?

  5. "Founder's Pass" 标签 - 仅限移动端支付墙,还是网页端定价页也要显示?

  6. 会员升级通知 - 向现有会员发送邮件通知、站内通知,还是两者都发?


附录 A: 不需要修改的部分

以下内容保持不变:

  • 积分包价格和数量 (Taster/Mini/Starter/Best Value)
  • 每日积分 (Free: 50, Gold: 80, Diamond: 100)
  • 注册奖励 (300 SE)
  • 会员积分包加成 (Gold +10%, Diamond +15%)
  • 积分消耗 (解读: 200, 后续: 50/100/200/400 等)
  • 邀请奖励
  • 牌面皮肤/读者折扣 (Gold 40%, Diamond 60%)

附录 B: 计费平台配置任务

代码部署前需要在对应计费平台完成:

Stripe (网页端)

  1. 新建价格: Gold 年会员 - $89.99/年, 循环 (替代旧 $167.92/年)
  2. 新建价格: Diamond 年会员 - $299.99/年, 循环 (替代旧 $587.92/年)
  3. 可选: Gold 周会员 - 仅在周会员也走 Stripe 时才创建
  4. 更新环境变量: 将新的 Stripe price ID 添加到 .env.production, .env.development

App Store Connect (iOS)

  1. 创建 Gold 周付 / 月付 / 年付订阅
  2. 配置 3 天试用或 introductory offer
  3. 将商品接入 RevenueCat
  4. 配置 RevenueCat webhook / entitlement 映射和 customer identity 策略

Google Play Console (Android)

  1. 创建 Gold 周付 / 月付 / 年付订阅
  2. 配置试用 / introductory offer
  3. 将商品接入 RevenueCat
  4. 配置内部测试与测试账号