定价与会员体系调整 - 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 作为统一的移动订阅层。
核心风险点
- 年会员一次性发放 - 现系统在每次
invoice.payment_succeeded事件时发放积分。年会员需一次性发放10万/100万积分,不能按月。 - 年会员取消后积分回收 - 需求文档提到"取消计划后积分回收",这是现系统没有的新功能。
- iOS 平台差异化 - iOS 产品目录与网页端不同,需要平台感知逻辑。
- 周会员 - 全新的计费周期,现系统不存在。
- iOS Apple IAP + RevenueCat 集成 - iOS 计费路径已经确定为 Apple IAP + RevenueCat。当前主要风险不再是“选哪条路”,而是 entitlement 同步、用户身份绑定、试用期行为和积分发放逻辑能否正确落地。
- 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. 网页端积分包 (不变)
| 积分包 | 积分 | 价格 |
|---|---|---|
| Taster | 250 | $1.99 |
| Mini | 600 | $3.99 |
| Starter | 2,000 | $9.99 |
| Best Value | 4,000 | $14.99 |
积分包价格和数量不做任何修改。
2c. iOS 订阅 (全新)
| 方案 | 价格 | 积分 | 备注 |
|---|---|---|---|
| Gold 周会员 | $7.99 | 1,500 SE | 新计费周期 |
| Gold 月会员 | $19.99 | 6,000 SE | 与网页端一致 |
| Gold 年会员 | $89.99 | 100,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.99 | 250 | $0.00796 |
| Mini 积分包 | $3.99 | 600 | $0.00665 |
| Starter 积分包 | $9.99 | 2,000 | $0.00500 |
| iOS Gold 周会员 | $7.99 | 1,500 | $0.00533 |
| Best Value 积分包 | $14.99 | 4,000 | $0.00375 |
| Gold 月会员 | $19.99 | 6,000 | $0.00333 |
| Diamond 月会员 | $69.99 | 30,000 | $0.00233 |
| Gold 年会员 | $89.99 | 100,000 | $0.00090 |
| Diamond 年会员 | $299.99 | 1,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.ts | MONTHLY_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.tsx | bestPerCredit 改为基于 Diamond 年付; 12种语言的促销文案更新 | 中 |
MembershipContent.tsx | MONTHLY_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
// 修改前
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
// 修改后 - 只有周会员走 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 - 核心改动
此文件需要最大幅度的修改。核心修改:
- 检测计费周期 (周/月/年) - 从 Stripe 订阅对象获取
- 月付方案: 继续按
MONTHLY_GRANT_AMOUNTS[tier]每次发放 - 年付方案: 按
YEARLY_GRANT_AMOUNTS[tier]一次性发放 - 年付取消: 回收剩余积分 (全新概念)
- 周付方案: 每次计费发放 1,500 积分
4.4 lib/auth.tsx - Better Auth Stripe 插件
Better Auth 的 Stripe 插件应优先只覆盖网页端 Stripe 方案。除非业务明确决定周会员也在网页端售卖,否则不应把 gold-weekly 直接建模为 Stripe 计划。
// 仅网页端 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
- 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
// 月度 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 计算仅基于积分包:
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
- 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.ts中MONTHLY_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: 年会员取消后积分回收
问题: 需求提到"取消计划后积分回收",当前系统没有此功能。
方案:
- 年付订阅被立即取消 (非到期取消) 时:
- 计算原始发放金额
- 从用户余额中扣除 (最低为0,不允许负数)
- 创建
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 订阅的流程完全不同:
- 用户通过 Apple IAP (StoreKit 2) 订阅
- Apple 处理支付并抽成 30%
- iOS App 将交易/收据发送到后端
- 后端通过 Apple App Store Server API 验证收据
- 后端授予会员等级和积分
这与 Stripe 回调流程在架构上有本质区别。
已确认方案:
- iOS 订阅走 Apple IAP
- RevenueCat 作为订阅抽象层和 entitlement 同步层
- 后端优先接 RevenueCat webhook / entitlement 事件,而不是先自建 StoreKit 收据校验链路
实施要求:
- 在 App Store Connect 中创建 iOS 商品
- 在 RevenueCat 中镜像这些商品,并映射到内部 tier / billing interval
- 设计稳定的
appUserId策略,保证 RevenueCat 用户能可靠映射到 MysticX 用户 - 处理 RevenueCat webhook 事件,覆盖激活、续费、取消、退款、过期
- entitlement 校验通过后,复用同一套内部积分发放引擎
- 正确处理 restore purchases,避免用户换设备或重新登录后丢失权益
推荐的整体架构:
- 网页端: 继续使用 Stripe + Better Auth 处理 Gold 月付、Gold 年付、Diamond 月付、Diamond 年付
- 移动商店: 使用 RevenueCat 统一处理 Apple IAP 和 Google Play Billing 的 Gold 周付、Gold 月付、Gold 年付
- 后端: 统一收口到一个 entitlement 模型,至少记录
provider、productId、billingInterval、tier和发放策略
难点 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
- 网页端订阅继续使用 Stripe + Better Auth
- iOS 商品在 App Store Connect 创建,Android 商品在 Google Play Console 创建,不在 Stripe 中创建
- 将 Apple / Google / RevenueCat 商品映射到内部 tier 和 billing interval
- 增加服务端 entitlement 同步 / webhook 处理
- 在 entitlement 校验通过后复用同一套积分发放引擎
- 把 RevenueCat customer identity 与 MysticX 用户身份稳定绑定,确保恢复购买归属正确
6.2 仅移动 App 需要做的 (不在当前范围)
- 推送通知 (每日领取提醒)
- UI: 仅显示 Gold 三档,不显示 Diamond / 积分包
- 支付墙设计: 年会员加 "Founder's Pass" 标签
- 积分到账动效: 10万积分到账时的华丽效果
- 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 新增测试场景
- 年付订阅创建 -> 一次性发放正确金额
- 年付订阅续费 -> 根据当前生效的商品 / 价格映射,确认年付积分再次正确发放
- 周付发票 -> 正确发放周积分
- 年付取消积分回收 -> 正确扣除
- 年付取消但余额不足 -> 扣至0
- Gold 月付升级到 Gold 年付
- Gold 年付升级到 Diamond 年付 -> 差额计算
- iOS 免费试用开始 -> 是否发放积分
- iOS 试用转正 -> 积分发放
- Google Play 首次购买 -> entitlement 和积分发放正确
- 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. 待老板确认的问题
关键问题 (实施前需要答案)
年会员取消积分回收 - 文档提到"取消计划后积分回收":
- (a) 用户点"取消"后订阅到期结束 -> 不回收 (已付全年费用)?
- (b) 退款场景 (纠纷/管理员操作) -> 回收积分?
- (c) 年中立即取消 -> 按比例回收积分?
- 如果用户已花掉大部分积分,余额不够回收怎么办? 是否允许负数,还是扣至0?
免费试用期积分发放 - 3天免费试用期间是否发放积分 (1,500/6,000/100,000)?
- 如果试用期发放积分,用户取消试用后是否回收?
年付升级差额 - Gold 年付用户 (已获10万积分) 升级到 Diamond 年付 ($299.99),是否发放90万差额积分? 金额很大。
现有年会员处理 - 是否创建新的 Stripe 价格 ID (保留现有用户旧价格)? 还是更新现有价格 (影响所有续费)?
周会员是否上网页端? - 文档提到周会员仅限 iOS。网页端会员展示页面要不要也加"周、月、年"切换?
Diamond 年付 "75% off" 计算 - $299.99 vs $69.99*12 = $839.88,实际折扣约 64%。文档要求显示 "75% off"。是按文档写75%,还是显示数学上准确的 ~64%?
年会员权益范围 - 年付一次性发放积分之外,是否仍然完整享有该 tier 对应的每日积分、折扣和高级功能? 当前文档默认答案是是。
RevenueCat 身份绑定策略 - RevenueCat
appUserId是否应与我们用户 ID 一一绑定? 匿名使用、登录切换和 restore purchases 合并规则怎么定?
次要问题 (可后续决定)
年付展示格式 - 定价切换器继续用"月付/年付"还是改其他 UI? 文档说"先保持现有的"。
"Get 100,000 Energy Instantly!" 文案 - 这段文字显示在定价卡上还是结算页面上,还是两者都显示?
推送通知文案 - 文档建议: "Allen, your daily 80 energy is ready to align with the stars." 是否用真实用户名? 是否根据等级调整积分数 (80/100)? (移动 App 团队问题)
年付积分到账动效 - "星空炸裂效果"只做移动端还是网页端也要做?
"Founder's Pass" 标签 - 仅限移动端支付墙,还是网页端定价页也要显示?
会员升级通知 - 向现有会员发送邮件通知、站内通知,还是两者都发?
附录 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 (网页端)
- 新建价格: Gold 年会员 - $89.99/年, 循环 (替代旧 $167.92/年)
- 新建价格: Diamond 年会员 - $299.99/年, 循环 (替代旧 $587.92/年)
- 可选: Gold 周会员 - 仅在周会员也走 Stripe 时才创建
- 更新环境变量: 将新的 Stripe price ID 添加到
.env.production,.env.development
App Store Connect (iOS)
- 创建 Gold 周付 / 月付 / 年付订阅
- 配置 3 天试用或 introductory offer
- 将商品接入 RevenueCat
- 配置 RevenueCat webhook / entitlement 映射和 customer identity 策略
Google Play Console (Android)
- 创建 Gold 周付 / 月付 / 年付订阅
- 配置试用 / introductory offer
- 将商品接入 RevenueCat
- 配置内部测试与测试账号