7.3 KiB
7.3 KiB
VIP / Credits Frontend API
本文档是前端接入会员、积分、权益和历史解锁的推荐接口说明。新版接入原则:
- 前端权益状态只认
GET /api/user/entitlements。 - 游客和登录非会员权益一致,只按
isVip区分会员/非会员。 - 支付完成后只刷新一次
GET /api/user/entitlements。 dolBalance是旧字段名,等价于creditBalance,新代码优先使用creditBalance。
推荐调用链
打开充值/VIP 弹窗
-> GET /api/payment/plans
-> POST /api/payment/create-order
-> 打开 data.payParams.url / checkout_url / payment_url
-> GET /api/payment/order-status 轮询,或等待 WebSocket payment_success
-> GET /api/user/entitlements 刷新权益
不要再按订单类型分别调用 /api/payment/vip-status 和 /api/user/credits 刷新状态。
GET /api/user/entitlements
用途:当前用户 VIP、积分和权益快照。支持游客 token 和登录 token。
认证:需要 Authorization: Bearer <token>,游客 token 也可以。
响应示例:
{
"success": true,
"data": {
"userId": "user-1",
"isGuest": false,
"isVip": false,
"vipExpiresAt": null,
"creditBalance": 120,
"dolBalance": 120,
"policy": {
"membershipState": "non_vip",
"nonVipEntitlementsShared": true,
"guestSameAsLoggedInNonVip": true,
"refreshAfterPayment": "GET /api/user/entitlements"
},
"costs": {
"normal_message": 2,
"private_message": 10,
"voice_message": 20,
"photo": 40,
"voice_call_minute": 50,
"private_album_10": 300,
"private_album_20": 600
},
"quotas": {
"normalChatFreeDaily": 30,
"privateUnlockFreeDaily": 1,
"voiceMessageFreeDaily": 0,
"photoFreeDaily": 0
},
"historyUnlock": {
"enabled": true,
"order": "oldest_first",
"chargeMode": "highest_cost_per_locked_message",
"insufficientBalanceBehavior": "no_deduction",
"costs": {
"private_message": 10,
"voice_message": 20,
"photo": 40
}
}
}
}
前端使用:
- App 启动、登录成功、游客 token 创建成功后调用。
- 支付成功、充值成功、历史解锁成功后调用。
isGuest只用于 UI 身份展示,不用于权益判断。
GET /api/payment/plans
用途:获取当前国家对应的会员/积分套餐。
认证:不需要登录。后端通过 CF-IPCountry 判断国家;登录后创建订单会再次按用户国家校验价格。
响应重点字段:
{
"plan_id": "vip_monthly",
"plan_name": "月度会员",
"order_type": "vip_monthly",
"amount_cents": 1999,
"original_amount_cents": 1999,
"daily_price_cents": 66,
"currency": "USD",
"pricing_tier": "T1",
"vip_days": 30,
"dol_amount": null
}
字段说明:
amount_cents: 实付金额,传给支付服务。original_amount_cents: 划线价;无划线价时为null,字段一定存在。currency: 支付币种。plan_id: 创建订单时传回后端。
POST /api/payment/create-order
用途:创建支付订单。
认证:需要登录 token。
请求:
{
"planId": "vip_monthly",
"payChannel": "stripe",
"autoRenew": true
}
响应:
{
"success": true,
"data": {
"orderId": "ord-1",
"payParams": {
"url": "https://pay.example/ord-1"
}
}
}
前端使用:
planId必须来自/api/payment/plans。payParams原样交给支付 SDK 或打开其中的支付 URL。- 后端创建订单时使用 plans 里的
amount_cents/currency,不会再固定覆盖成 CNY。
GET /api/payment/order-status
用途:轮询支付订单状态。
认证:需要登录 token,只能查自己的订单。
请求:
GET /api/payment/order-status?order_id=ord-1
响应:
{
"success": true,
"data": {
"orderId": "ord-1",
"status": "paid",
"orderType": "vip",
"planId": "vip_monthly"
}
}
状态:
pending: 支付处理中,继续轮询。paid: 支付成功,停止轮询并调用/api/user/entitlements。failed: 支付失败,停止轮询并提示用户。
WebSocket payment_success / payment_failed
用途:支付服务回调后,后端主动推送支付结果。
成功事件示例:
{
"type": "payment_success",
"orderId": "ord-1",
"payType": "vip",
"planName": "月度会员",
"vipExpiresAt": "2026-07-26T00:00:00+00:00"
}
积分充值成功示例:
{
"type": "payment_success",
"orderId": "ord-2",
"payType": "dol",
"planName": "1000 Credits",
"dolAmount": 1000,
"dolBalance": 1120
}
前端收到 payment_success 后:
停止 order-status 轮询
-> GET /api/user/entitlements
-> 用 creditBalance/isVip 更新页面
POST /api/chat/unlock-history
用途:一键解锁历史锁定消息,按创建时间从旧到新处理。
认证:需要登录 token。
请求:无 body。
余额不足响应:
{
"success": true,
"data": {
"unlocked": false,
"reason": "insufficient_balance",
"totalLocked": 2,
"unlockedCount": 0,
"privateCount": 1,
"imageCount": 1,
"voiceCount": 0,
"requiredCredits": 50,
"currentCredits": 20,
"remainingCredits": 20,
"shortfallCredits": 30,
"costsByMessage": {
"private-1": 10,
"photo-1": 40
}
}
}
余额充足响应:
{
"success": true,
"data": {
"unlocked": true,
"reason": "ok",
"requiredCredits": 60,
"currentCredits": 100,
"remainingCredits": 40,
"shortfallCredits": 0,
"messageIds": ["voice-1", "photo-1"]
}
}
计费规则:
- 私密消息:10 credits。
- 语音消息:20 credits。
- 照片:40 credits。
- 同一条消息命中多个类型时,按最高成本计一次。
- 余额不足时不扣费、不解锁,只返回差额。
- 已解锁过的消息会跳过。
兼容接口
以下接口保留给旧前端,但新前端不推荐作为权益状态来源:
GET /api/payment/vip-status: 只返回isVip/vipExpiresAt。GET /api/user/credits: 只返回dolBalance。GET /api/user/stats: 用户统计、亲密度、记忆等,不再用于支付后刷新权益。
测试证明
本地测试命令:
.\.venv\Scripts\python.exe -m pytest -q tests/test_vip_credit_entitlements.py
.\.venv\Scripts\python.exe -m pytest -q tests/test_private_unlock.py tests/test_elio_image_paywall.py
已通过结果:
tests/test_vip_credit_entitlements.py: 13 passed
tests/test_private_unlock.py tests/test_elio_image_paywall.py: 11 passed
覆盖证明:
/api/user/entitlements: 已测游客、登录非会员、VIP;游客和登录非会员的policy/costs/quotas一致。/api/payment/plans: 已测 T1/T2 国家;所有计划都有original_amount_cents。/api/payment/create-order: 已测国家价格计划会传入创建订单;支付服务收到的金额和币种来自 plan。/api/payment/order-status: 已测pending/paid/failed和禁止查询他人订单。- WebSocket
payment_success: 已测 VIP 和积分发放事件 payload。 /api/chat/unlock-history: 已测无锁定消息、余额不足不扣费、余额充足扣费、已解锁跳过。- 兼容接口:已测
/api/payment/vip-status和/api/user/credits仍可用。