186 lines
4.5 KiB
Markdown
186 lines
4.5 KiB
Markdown
# 定价页面改进说明
|
||
|
||
## 概述
|
||
|
||
定价页面现在实现了智能的套餐过滤和按钮显示逻辑,确保只显示有效的可订阅套餐,并提供清晰的用户体验。
|
||
|
||
## 主要改进
|
||
|
||
### 1. 智能套餐过滤
|
||
|
||
定价页面现在只显示以下套餐:
|
||
|
||
1. **免费套餐** - 总是显示
|
||
2. **用户当前套餐** - 总是显示(即使没有 stripePriceId)
|
||
3. **有效的付费套餐** - 必须有 `stripePriceId` 才显示
|
||
|
||
#### 过滤逻辑
|
||
```typescript
|
||
const filteredPlans = plans.filter((plan: SubscriptionPlan) => {
|
||
// 免费套餐总是显示
|
||
if (isPlanFree(plan)) return true
|
||
|
||
// 用户当前套餐总是显示
|
||
if (userData && isCurrentPlan(plan.id)) return true
|
||
|
||
// 其他套餐必须有 stripePriceId 才能显示(可订阅)
|
||
return plan.stripePriceId && plan.stripePriceId.trim() !== ''
|
||
})
|
||
```
|
||
|
||
### 2. 智能按钮显示
|
||
|
||
按钮显示遵循以下逻辑:
|
||
|
||
#### 免费套餐
|
||
- **当前套餐**: 显示 "当前方案" 按钮(禁用)
|
||
- **非当前套餐**: 不显示任何按钮
|
||
|
||
#### 付费套餐
|
||
- **当前套餐**: 显示 "当前方案" 按钮(禁用)
|
||
- **可订阅套餐**: 显示 "立即订阅" 按钮
|
||
- **无价格ID套餐**: 不显示按钮(理论上不会出现,因为已过滤)
|
||
|
||
#### 按钮逻辑代码
|
||
```typescript
|
||
{(() => {
|
||
// 免费套餐逻辑
|
||
if (isFree) {
|
||
if (isCurrent) {
|
||
return <Button variant="outline" disabled>{t('currentPlan')}</Button>
|
||
}
|
||
return null // 免费套餐且非当前套餐,不显示按钮
|
||
}
|
||
|
||
// 付费套餐逻辑
|
||
if (isCurrent) {
|
||
return <Button variant="outline" disabled>{t('currentPlan')}</Button>
|
||
}
|
||
|
||
// 可订阅的付费套餐
|
||
if (plan.stripePriceId && plan.stripePriceId.trim() !== '') {
|
||
return (
|
||
<SubscribeButton priceId={plan.stripePriceId} planName={plan.displayName}>
|
||
{t('subscribeNow')}
|
||
</SubscribeButton>
|
||
)
|
||
}
|
||
|
||
return null
|
||
})()}
|
||
```
|
||
|
||
### 3. 多语言支持
|
||
|
||
添加了新的翻译键:
|
||
|
||
#### 英文 (en.json)
|
||
```json
|
||
{
|
||
"pricing": {
|
||
"subscribeNow": "Subscribe Now",
|
||
"currentPlan": "Current Plan"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 中文 (zh.json)
|
||
```json
|
||
{
|
||
"pricing": {
|
||
"subscribeNow": "立即订阅",
|
||
"currentPlan": "当前方案"
|
||
}
|
||
}
|
||
```
|
||
|
||
## 用户体验场景
|
||
|
||
### 1. 匿名用户
|
||
- 看到:免费套餐(无按钮)+ 有价格ID的付费套餐(立即订阅按钮)
|
||
- 不看到:没有价格ID的付费套餐
|
||
|
||
### 2. 免费用户
|
||
- 看到:免费套餐(当前方案按钮)+ 有价格ID的付费套餐(立即订阅按钮)
|
||
- 不看到:没有价格ID的付费套餐
|
||
|
||
### 3. Pro 用户
|
||
- 看到:免费套餐(无按钮)+ 当前Pro套餐(当前方案按钮)+ 其他有价格ID的套餐(立即订阅按钮)
|
||
- 不看到:没有价格ID的付费套餐
|
||
|
||
## 技术实现
|
||
|
||
### 1. 动态数据获取
|
||
```typescript
|
||
useEffect(() => {
|
||
fetchPlans()
|
||
}, [userData]) // 依赖 userData,确保用户数据加载后再过滤套餐
|
||
```
|
||
|
||
### 2. 套餐判定工具
|
||
使用统一的工具函数:
|
||
- `isPlanFree(plan)` - 判断是否为免费套餐
|
||
- `isPlanPro(plan)` - 判断是否为Pro套餐
|
||
- `isCurrentPlan(planId)` - 判断是否为用户当前套餐
|
||
|
||
### 3. 类型安全
|
||
```typescript
|
||
const filteredPlans = (data.plans || []).filter((plan: SubscriptionPlan) => {
|
||
// 过滤逻辑
|
||
})
|
||
```
|
||
|
||
## 配置要求
|
||
|
||
### 1. 套餐配置
|
||
确保付费套餐有有效的 `stripePriceId`:
|
||
|
||
```sql
|
||
-- 检查套餐配置
|
||
SELECT id, name, "displayName", price, "stripePriceId", "isActive"
|
||
FROM subscription_plans
|
||
WHERE "isActive" = true;
|
||
|
||
-- 设置价格ID
|
||
UPDATE subscription_plans
|
||
SET "stripePriceId" = 'price_your_stripe_price_id'
|
||
WHERE name = 'pro';
|
||
```
|
||
|
||
### 2. 验证工具
|
||
使用测试脚本验证配置:
|
||
|
||
```bash
|
||
npx tsx scripts/test-pricing-page-filtering.ts
|
||
```
|
||
|
||
## 优势
|
||
|
||
1. **用户友好**: 只显示用户可以实际订阅的套餐
|
||
2. **防止错误**: 避免显示无法订阅的套餐
|
||
3. **清晰导航**: 明确的按钮状态和操作
|
||
4. **灵活配置**: 支持动态套餐管理
|
||
5. **多语言**: 完整的国际化支持
|
||
|
||
## 故障排除
|
||
|
||
### 问题:套餐不显示
|
||
**原因**: 付费套餐没有设置 `stripePriceId`
|
||
**解决**: 在数据库中设置正确的 Stripe 价格 ID
|
||
|
||
### 问题:按钮不显示
|
||
**原因**: 套餐被过滤掉或逻辑判断问题
|
||
**解决**: 检查套餐配置和用户状态
|
||
|
||
### 问题:翻译缺失
|
||
**原因**: 缺少 `subscribeNow` 翻译键
|
||
**解决**: 在对应语言文件中添加翻译
|
||
|
||
## 测试建议
|
||
|
||
1. 测试不同用户状态下的页面显示
|
||
2. 验证按钮功能和状态
|
||
3. 检查多语言显示
|
||
4. 确认套餐过滤逻辑
|
||
5. 测试订阅流程
|