757 lines
20 KiB
Markdown
757 lines
20 KiB
Markdown
# New API 支付系统集成计划(简化版)
|
||
|
||
## 概述
|
||
|
||
将 New API 作为 qwen-client 和 qwen-agent 的支付后端。qwen-agent 只做代理转发,不维护配额和交易数据。
|
||
|
||
**创建日期**: 2026-02-25
|
||
**更新日期**: 2026-02-25
|
||
**状态**: 待审核
|
||
**复杂度**: 中
|
||
|
||
---
|
||
|
||
## 核心设计
|
||
|
||
### 设计原则
|
||
|
||
1. **New API 是数据源**: 所有配额、交易、订单数据由 New API 维护
|
||
2. **qwen-agent 是代理层**: 只转发请求,不存储支付相关数据
|
||
3. **Session 同步登录**: 用户登录 qwen-agent 时,同步登录 New API 并存储 session
|
||
4. **Cookie 转发**: 代理请求时转发用户的 session cookie 实现 New API 鉴权
|
||
|
||
### 架构图
|
||
|
||
```
|
||
┌─────────────┐ ┌─────────────────┐ ┌─────────────┐
|
||
│ qwen-client │ ───► │ qwen-agent │ ───► │ New API │
|
||
│ (前端) │ │ (代理 + Session)│ │ (支付后端) │
|
||
└─────────────┘ └─────────────────┘ └─────────────┘
|
||
│
|
||
▼
|
||
存储用户对应的
|
||
New API Session Cookie
|
||
```
|
||
|
||
### 支付方式支持
|
||
|
||
| 支付方式 | New API 接口 | 说明 |
|
||
|---------|-------------|------|
|
||
| 易支付 | `/api/user/pay` | 通用支付方式(微信/支付宝等) |
|
||
| Stripe | `/api/user/stripe/pay` | 国际信用卡支付 |
|
||
| Creem | `/api/user/creem/pay` | 新支付方式,支持套餐 |
|
||
|
||
---
|
||
|
||
## 数据库设计
|
||
|
||
### 只需新增一个字段
|
||
|
||
```sql
|
||
-- 在 agent_user 表添加 New API Session
|
||
ALTER TABLE agent_user ADD COLUMN new_api_session VARCHAR(500);
|
||
ALTER TABLE agent_user ADD COLUMN new_api_user_id INTEGER;
|
||
```
|
||
|
||
**不需要新建表**,所有配额、交易、订单数据都在 New API 中。
|
||
|
||
---
|
||
|
||
## 核心流程
|
||
|
||
### 1. 用户登录同步
|
||
|
||
```
|
||
用户登录 qwen-client
|
||
│
|
||
▼
|
||
qwen-agent 验证本地用户
|
||
│
|
||
▼
|
||
qwen-agent 用相同凭证调用 New API /api/user/login
|
||
│
|
||
├─ 成功 → 存储 new_api_session 到 agent_user 表
|
||
│
|
||
└─ 失败 → 如果是用户不存在,先在 New API 注册再登录
|
||
│
|
||
▼
|
||
返回 qwen-agent 的 token 给前端
|
||
```
|
||
|
||
### 2. 支付/充值流程
|
||
|
||
```
|
||
用户点击充值
|
||
│
|
||
▼
|
||
qwen-agent 读取用户的 new_api_session
|
||
│
|
||
▼
|
||
代理请求到 New API (带 session cookie)
|
||
│
|
||
▼
|
||
返回支付链接给前端
|
||
│
|
||
▼
|
||
用户完成支付 (New API 直接处理)
|
||
│
|
||
▼
|
||
前端跳转到 New API 的用户中心查看结果
|
||
```
|
||
|
||
### 3. 配额检查
|
||
|
||
```
|
||
用户发起聊天请求
|
||
│
|
||
▼
|
||
qwen-agent 调用 New API /api/user/self 获取实时配额
|
||
│
|
||
├─ 配额不足 → 返回 402 错误 + New API 充值链接
|
||
│
|
||
└─ 配额充足 → 处理请求
|
||
│
|
||
▼
|
||
(可选) 调用 New API 扣减配额
|
||
```
|
||
|
||
---
|
||
|
||
## API 设计
|
||
|
||
### qwen-agent 新增接口
|
||
|
||
| 端点 | 方法 | 描述 | 代理的 New API 接口 |
|
||
|------|------|------|---------------------|
|
||
| `/api/v1/payment/info` | GET | 获取充值配置(含套餐) | `/api/user/topup/info` |
|
||
| `/api/v1/payment/plans` | GET | 获取订阅套餐列表 | `/api/subscription/plans` |
|
||
| `/api/v1/payment/create` | POST | 创建支付订单(易支付) | `/api/user/pay` |
|
||
| `/api/v1/payment/stripe` | POST | 创建 Stripe 支付 | `/api/user/stripe/pay` |
|
||
| `/api/v1/payment/creem` | POST | 创建 Creem 支付(套餐) | `/api/user/creem/pay` |
|
||
| `/api/v1/payment/amount` | POST | 计算实际支付金额 | `/api/user/amount` |
|
||
| `/api/v1/quota` | GET | 查询用户配额 | `/api/user/self` |
|
||
| `/api/v1/topup/history` | GET | 充值记录 | `/api/user/topup/self` |
|
||
|
||
所有接口都是代理转发,使用存储的 new_api_session。
|
||
|
||
---
|
||
|
||
## New API 接口详情
|
||
|
||
### 1. 获取充值配置 `/api/user/topup/info` (GET)
|
||
|
||
**鉴权**: 需要登录(Cookie)
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "",
|
||
"data": {
|
||
"enable_online_topup": true,
|
||
"enable_stripe_topup": true,
|
||
"enable_creem_topup": true,
|
||
"creem_products": [
|
||
{
|
||
"productId": "prod_xxx",
|
||
"name": "基础套餐",
|
||
"price": 9.99,
|
||
"currency": "USD",
|
||
"quota": 500000
|
||
}
|
||
],
|
||
"pay_methods": [
|
||
{"name": "微信支付", "type": "wxpay", "color": "#07C160", "min_topup": "1"},
|
||
{"name": "支付宝", "type": "alipay", "color": "#1677FF", "min_topup": "1"}
|
||
],
|
||
"min_topup": 1,
|
||
"stripe_min_topup": 1,
|
||
"amount_options": [100, 500, 1000, 5000],
|
||
"discount": {
|
||
"100": 1.0,
|
||
"500": 0.95,
|
||
"1000": 0.9,
|
||
"5000": 0.85
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**字段说明**:
|
||
- `enable_online_topup`: 是否启用易支付
|
||
- `enable_stripe_topup`: 是否启用 Stripe 支付
|
||
- `enable_creem_topup`: 是否启用 Creem 支付
|
||
- `creem_products`: Creem 套餐列表(**这是套餐列表的数据来源**)
|
||
- `pay_methods`: 支持的支付方式列表
|
||
- `min_topup`: 最小充值额度
|
||
- `amount_options`: 预设充值额度选项
|
||
- `discount`: 折扣配置(额度 -> 折扣率)
|
||
|
||
### 2. 获取订阅套餐列表 `/api/subscription/plans` (GET)
|
||
|
||
**鉴权**: 需要登录(Cookie)
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"plan": {
|
||
"id": 1,
|
||
"title": "月度订阅",
|
||
"subtitle": "适合轻度用户",
|
||
"price_amount": 9.99,
|
||
"currency": "USD",
|
||
"duration_unit": "month",
|
||
"duration_value": 1,
|
||
"total_amount": 500000,
|
||
"enabled": true,
|
||
"sort_order": 100,
|
||
"stripe_price_id": "price_xxx",
|
||
"creem_product_id": "prod_xxx",
|
||
"max_purchase_per_user": 0,
|
||
"upgrade_group": "",
|
||
"quota_reset_period": "monthly"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**字段说明**:
|
||
- `title`: 套餐名称
|
||
- `price_amount`: 价格
|
||
- `duration_unit`: 周期单位(month/year/custom)
|
||
- `duration_value`: 周期数值
|
||
- `total_amount`: 包含的配额
|
||
- `stripe_price_id`: Stripe 价格 ID
|
||
- `creem_product_id`: Creem 产品 ID
|
||
|
||
### 3. 创建易支付订单 `/api/user/pay` (POST)
|
||
|
||
**请求**:
|
||
```json
|
||
{
|
||
"amount": 1000,
|
||
"payment_method": "wxpay"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"message": "success",
|
||
"data": {...},
|
||
"url": "https://pay.example.com/submit.php?..."
|
||
}
|
||
```
|
||
|
||
### 4. 创建 Creem 支付 `/api/user/creem/pay` (POST)
|
||
|
||
**请求**:
|
||
```json
|
||
{
|
||
"product_id": "prod_xxx",
|
||
"payment_method": "creem"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"message": "success",
|
||
"data": {
|
||
"checkout_url": "https://checkout.creem.io/...",
|
||
"order_id": "ref_xxx"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5. 获取用户信息(含配额)`/api/user/self` (GET)
|
||
|
||
**鉴权**: 需要登录(Cookie)
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"id": 1,
|
||
"username": "user",
|
||
"email": "user@example.com",
|
||
"quota": 500000,
|
||
"used_quota": 10000,
|
||
"group": "default"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 6. 用户登录 `/api/user/login` (POST)
|
||
|
||
**请求**:
|
||
```json
|
||
{
|
||
"username": "user",
|
||
"password": "password"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
- 返回 Set-Cookie header 包含 session
|
||
- 用户信息 JSON
|
||
|
||
---
|
||
|
||
## 实施步骤
|
||
|
||
### Step 1: 添加环境变量配置
|
||
|
||
在 `utils/settings.py` 添加 New API 配置:
|
||
|
||
```python
|
||
from pydantic_settings import BaseSettings
|
||
|
||
class Settings(BaseSettings):
|
||
# ... 现有配置 ...
|
||
|
||
# New API 配置
|
||
NEW_API_BASE_URL: str = "https://new-api.example.com"
|
||
NEW_API_TIMEOUT: int = 30
|
||
|
||
class Config:
|
||
env_file = ".env"
|
||
```
|
||
|
||
### Step 2: 创建 New API 代理工具类
|
||
|
||
创建 `utils/new_api_proxy.py`:
|
||
|
||
```python
|
||
import httpx
|
||
from typing import Optional, Any
|
||
from utils.settings import Settings
|
||
|
||
settings = Settings()
|
||
|
||
class NewAPIProxy:
|
||
"""New API 代理工具类"""
|
||
|
||
def __init__(self):
|
||
self.base_url = settings.NEW_API_BASE_URL
|
||
self.timeout = settings.NEW_API_TIMEOUT
|
||
|
||
async def login(self, username: str, password: str) -> Optional[str]:
|
||
"""登录 New API 并返回 session cookie"""
|
||
async with httpx.AsyncClient() as client:
|
||
resp = await client.post(
|
||
f"{self.base_url}/api/user/login",
|
||
json={"username": username, "password": password},
|
||
timeout=self.timeout
|
||
)
|
||
if resp.status_code == 200 and resp.json().get("success"):
|
||
# 提取 session cookie
|
||
cookies = resp.cookies.get("session")
|
||
return cookies
|
||
return None
|
||
|
||
async def register(self, username: str, password: str, email: str) -> bool:
|
||
"""在 New API 注册用户"""
|
||
async with httpx.AsyncClient() as client:
|
||
resp = await client.post(
|
||
f"{self.base_url}/api/user/register",
|
||
json={"username": username, "password": password, "email": email},
|
||
timeout=self.timeout
|
||
)
|
||
return resp.status_code == 200
|
||
|
||
async def proxy_get(
|
||
self,
|
||
endpoint: str,
|
||
session_cookie: str
|
||
) -> dict:
|
||
"""代理 GET 请求到 New API"""
|
||
async with httpx.AsyncClient() as client:
|
||
resp = await client.get(
|
||
f"{self.base_url}{endpoint}",
|
||
cookies={"session": session_cookie},
|
||
timeout=self.timeout
|
||
)
|
||
return resp.json()
|
||
|
||
async def proxy_post(
|
||
self,
|
||
endpoint: str,
|
||
data: dict,
|
||
session_cookie: str
|
||
) -> dict:
|
||
"""代理 POST 请求到 New API"""
|
||
async with httpx.AsyncClient() as client:
|
||
resp = await client.post(
|
||
f"{self.base_url}{endpoint}",
|
||
json=data,
|
||
cookies={"session": session_cookie},
|
||
timeout=self.timeout
|
||
)
|
||
return resp.json()
|
||
|
||
async def get_user_info(self, session_cookie: str) -> Optional[dict]:
|
||
"""获取用户信息(含配额)"""
|
||
try:
|
||
result = await self.proxy_get("/api/user/self", session_cookie)
|
||
if result.get("success"):
|
||
return result.get("data")
|
||
except:
|
||
pass
|
||
return None
|
||
|
||
async def check_quota(self, session_cookie: str) -> tuple[bool, int]:
|
||
"""检查用户配额"""
|
||
user_info = await self.get_user_info(session_cookie)
|
||
if user_info:
|
||
quota = user_info.get("quota", 0)
|
||
return quota > 0, quota
|
||
# 降级策略:无法获取配额时允许使用
|
||
return True, -1
|
||
|
||
# 全局实例
|
||
new_api_proxy = NewAPIProxy()
|
||
```
|
||
|
||
### Step 3: 修改登录逻辑
|
||
|
||
修改 `routes/bot_manager.py` 中的登录函数:
|
||
|
||
```python
|
||
from utils.new_api_proxy import new_api_proxy
|
||
|
||
async def login_user(username: str, password: str):
|
||
# 1. 验证本地用户
|
||
user = await verify_local_user(username, password)
|
||
|
||
# 2. 同步登录 New API
|
||
new_api_session = await new_api_proxy.login(username, password)
|
||
|
||
# 3. 如果登录失败,尝试注册
|
||
if not new_api_session:
|
||
await new_api_proxy.register(username, password, user.email)
|
||
new_api_session = await new_api_proxy.login(username, password)
|
||
|
||
# 4. 存储 session
|
||
if new_api_session:
|
||
await db.execute(
|
||
"UPDATE agent_user SET new_api_session = $1 WHERE id = $2",
|
||
new_api_session, user.id
|
||
)
|
||
|
||
# 5. 返回本地 token
|
||
return generate_local_token(user)
|
||
```
|
||
|
||
### Step 4: 创建支付代理路由
|
||
|
||
创建 `routes/payment.py`:
|
||
|
||
```python
|
||
from fastapi import APIRouter, Depends, HTTPException
|
||
from pydantic import BaseModel
|
||
from typing import Optional
|
||
from utils.new_api_proxy import new_api_proxy
|
||
from utils.auth import get_current_user
|
||
|
||
router = APIRouter(prefix="/api/v1", tags=["payment"])
|
||
|
||
class CreatePaymentRequest(BaseModel):
|
||
amount: int
|
||
payment_method: str
|
||
|
||
class CreemPaymentRequest(BaseModel):
|
||
product_id: str
|
||
payment_method: str = "creem"
|
||
|
||
class AmountRequest(BaseModel):
|
||
amount: int
|
||
|
||
@router.get("/payment/info")
|
||
async def get_payment_info(user = Depends(get_current_user)):
|
||
"""获取充值配置(含套餐列表)"""
|
||
if not user.new_api_session:
|
||
raise HTTPException(status_code=401, detail="未同步 New API session")
|
||
return await new_api_proxy.proxy_get("/api/user/topup/info", user.new_api_session)
|
||
|
||
@router.get("/payment/plans")
|
||
async def get_subscription_plans(user = Depends(get_current_user)):
|
||
"""获取订阅套餐列表"""
|
||
if not user.new_api_session:
|
||
raise HTTPException(status_code=401, detail="未同步 New API session")
|
||
return await new_api_proxy.proxy_get("/api/subscription/plans", user.new_api_session)
|
||
|
||
@router.post("/payment/create")
|
||
async def create_payment(req: CreatePaymentRequest, user = Depends(get_current_user)):
|
||
"""创建支付订单(易支付)"""
|
||
if not user.new_api_session:
|
||
raise HTTPException(status_code=401, detail="未同步 New API session")
|
||
return await new_api_proxy.proxy_post(
|
||
"/api/user/pay",
|
||
{"amount": req.amount, "payment_method": req.payment_method},
|
||
user.new_api_session
|
||
)
|
||
|
||
@router.post("/payment/stripe")
|
||
async def create_stripe_payment(req: AmountRequest, user = Depends(get_current_user)):
|
||
"""创建 Stripe 支付"""
|
||
if not user.new_api_session:
|
||
raise HTTPException(status_code=401, detail="未同步 New API session")
|
||
return await new_api_proxy.proxy_post(
|
||
"/api/user/stripe/pay",
|
||
{"amount": req.amount},
|
||
user.new_api_session
|
||
)
|
||
|
||
@router.post("/payment/creem")
|
||
async def create_creem_payment(req: CreemPaymentRequest, user = Depends(get_current_user)):
|
||
"""创建 Creem 支付(套餐)"""
|
||
if not user.new_api_session:
|
||
raise HTTPException(status_code=401, detail="未同步 New API session")
|
||
return await new_api_proxy.proxy_post(
|
||
"/api/user/creem/pay",
|
||
{"product_id": req.product_id, "payment_method": req.payment_method},
|
||
user.new_api_session
|
||
)
|
||
|
||
@router.post("/payment/amount")
|
||
async def get_payment_amount(req: AmountRequest, user = Depends(get_current_user)):
|
||
"""计算实际支付金额"""
|
||
if not user.new_api_session:
|
||
raise HTTPException(status_code=401, detail="未同步 New API session")
|
||
return await new_api_proxy.proxy_post(
|
||
"/api/user/amount",
|
||
{"amount": req.amount},
|
||
user.new_api_session
|
||
)
|
||
|
||
@router.get("/quota")
|
||
async def get_quota(user = Depends(get_current_user)):
|
||
"""获取用户配额"""
|
||
if not user.new_api_session:
|
||
raise HTTPException(status_code=401, detail="未同步 New API session")
|
||
user_info = await new_api_proxy.get_user_info(user.new_api_session)
|
||
if user_info:
|
||
return {
|
||
"quota": user_info.get("quota", 0),
|
||
"used_quota": user_info.get("used_quota", 0)
|
||
}
|
||
raise HTTPException(status_code=500, detail="获取配额失败")
|
||
|
||
@router.get("/topup/history")
|
||
async def get_topup_history(user = Depends(get_current_user)):
|
||
"""获取充值记录"""
|
||
if not user.new_api_session:
|
||
raise HTTPException(status_code=401, detail="未同步 New API session")
|
||
return await new_api_proxy.proxy_get("/api/user/topup/self", user.new_api_session)
|
||
```
|
||
|
||
### Step 5: 在 Chat 路由添加配额检查
|
||
|
||
在 `routes/chat.py` 添加配额检查中间件:
|
||
|
||
```python
|
||
from utils.new_api_proxy import new_api_proxy
|
||
|
||
async def check_quota_before_chat(user) -> tuple[bool, int]:
|
||
"""聊天前检查配额"""
|
||
if not user.new_api_session:
|
||
# 没有 session,允许通过(可能是非支付用户)
|
||
return True, -1
|
||
|
||
try:
|
||
has_quota, quota = await new_api_proxy.check_quota(user.new_api_session)
|
||
return has_quota, quota
|
||
except Exception as e:
|
||
# 降级策略:New API 不可用时允许使用
|
||
logger.warning(f"配额检查失败: {e}")
|
||
return True, -1
|
||
|
||
# 在 chat 处理函数中调用
|
||
async def handle_chat(request, user):
|
||
has_quota, quota = await check_quota_before_chat(user)
|
||
if not has_quota:
|
||
raise HTTPException(
|
||
status_code=402,
|
||
detail={
|
||
"error": "quota_exceeded",
|
||
"message": "配额不足,请充值",
|
||
"topup_url": f"{settings.NEW_API_BASE_URL}/console/topup"
|
||
}
|
||
)
|
||
# ... 继续处理聊天请求 ...
|
||
```
|
||
|
||
### Step 6: 注册路由
|
||
|
||
在 `fastapi_app.py` 中注册新路由:
|
||
|
||
```python
|
||
from routes.payment import router as payment_router
|
||
|
||
app.include_router(payment_router)
|
||
```
|
||
|
||
### Step 7: 数据库迁移
|
||
|
||
```sql
|
||
-- 在 agent_user 表添加 New API Session
|
||
ALTER TABLE agent_user ADD COLUMN new_api_session VARCHAR(500);
|
||
ALTER TABLE agent_user ADD COLUMN new_api_user_id INTEGER;
|
||
```
|
||
|
||
---
|
||
|
||
## 环境变量
|
||
|
||
```bash
|
||
# .env
|
||
NEW_API_BASE_URL=https://new-api.example.com
|
||
NEW_API_TIMEOUT=30
|
||
```
|
||
|
||
---
|
||
|
||
## 前端集成指南
|
||
|
||
### 1. 充值页面流程
|
||
|
||
```typescript
|
||
// 1. 获取充值配置
|
||
const paymentInfo = await fetch('/api/v1/payment/info');
|
||
|
||
// 2. 根据返回的支付方式展示选项
|
||
const { pay_methods, creem_products, amount_options, discount } = paymentInfo.data;
|
||
|
||
// 3. 用户选择套餐或输入金额
|
||
// - 如果选择 Creem 套餐:调用 /api/v1/payment/creem
|
||
// - 如果选择自定义金额:调用 /api/v1/payment/create
|
||
|
||
// 4. 获取支付链接并跳转
|
||
const result = await fetch('/api/v1/payment/creem', {
|
||
method: 'POST',
|
||
body: JSON.stringify({ product_id: 'prod_xxx' })
|
||
});
|
||
|
||
// 5. 跳转到支付页面
|
||
window.location.href = result.data.checkout_url;
|
||
```
|
||
|
||
### 2. 显示配额余额
|
||
|
||
```typescript
|
||
// 获取用户配额
|
||
const quota = await fetch('/api/v1/quota');
|
||
|
||
// 显示格式化后的配额
|
||
// quota.quota - 当前可用配额
|
||
// quota.used_quota - 已使用配额
|
||
```
|
||
|
||
### 3. 配额不足处理
|
||
|
||
```typescript
|
||
// 当聊天返回 402 错误时
|
||
if (error.status === 402) {
|
||
const { topup_url } = error.detail;
|
||
// 显示充值提示
|
||
// 提供"去充值"按钮,跳转到 topup_url
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 用户体验流程
|
||
|
||
### 充值流程(易支付)
|
||
|
||
```
|
||
1. 用户点击"充值"按钮
|
||
2. 前端调用 GET /api/v1/payment/info 获取配置
|
||
3. 展示支付方式和预设金额选项
|
||
4. 用户选择金额和支付方式
|
||
5. 前端调用 POST /api/v1/payment/create
|
||
6. 获取支付链接,跳转到支付页面
|
||
7. 用户完成支付
|
||
8. 支付成功后跳转回前端
|
||
```
|
||
|
||
### 充值流程(Creem 套餐)
|
||
|
||
```
|
||
1. 用户点击"购买套餐"
|
||
2. 前端调用 GET /api/v1/payment/info 获取 creem_products
|
||
3. 展示套餐列表(名称、价格、配额)
|
||
4. 用户选择套餐
|
||
5. 前端调用 POST /api/v1/payment/creem
|
||
6. 获取 checkout_url,跳转到 Creem 支付页面
|
||
7. 用户完成支付
|
||
8. 自动跳转回前端
|
||
```
|
||
|
||
### 配额检查
|
||
|
||
```
|
||
1. 用户发送聊天消息
|
||
2. qwen-agent 检查配额
|
||
3. 如果配额充足:正常处理
|
||
4. 如果配额不足:返回 402 + 充值链接
|
||
5. 前端显示"配额不足,请充值"提示
|
||
```
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
简化方案的核心是 **不重复存储数据**,让 New API 作为唯一的支付数据源。qwen-agent 只负责:
|
||
|
||
1. 用户登录时同步 New API session
|
||
2. 代理转发支付相关请求
|
||
3. 实时查询配额进行检查
|
||
|
||
### 套餐列表获取方式
|
||
|
||
套餐列表有两个来源:
|
||
|
||
1. **Creem 产品列表**(推荐)
|
||
- 接口:`/api/user/topup/info` → `creem_products` 字段
|
||
- 适用于一次性购买配额包
|
||
|
||
2. **订阅套餐列表**
|
||
- 接口:`/api/subscription/plans`
|
||
- 适用于周期性订阅
|
||
|
||
### 与原方案对比
|
||
|
||
| 项目 | 原方案 | 简化方案 |
|
||
|------|--------|----------|
|
||
| 新建数据库表 | 4 个 | 0 个 |
|
||
| 新增字段 | 多个 | 2 个 |
|
||
| 本地配额管理 | 需要 | 不需要 |
|
||
| Webhook 处理 | 需要 | 不需要 |
|
||
| 交易记录 | 本地存储 | New API 管理 |
|
||
| 实施周期 | 2-3 周 | 3-5 天 |
|
||
|
||
### 文件变更清单
|
||
|
||
| 文件 | 操作 | 说明 |
|
||
|------|------|------|
|
||
| `utils/settings.py` | 修改 | 添加 New API 环境变量 |
|
||
| `utils/new_api_proxy.py` | 新建 | New API 代理工具类 |
|
||
| `routes/payment.py` | 新建 | 支付代理路由 |
|
||
| `routes/bot_manager.py` | 修改 | 登录逻辑添加 session 同步 |
|
||
| `routes/chat.py` | 修改 | 添加配额检查 |
|
||
| `fastapi_app.py` | 修改 | 注册新路由 |
|
||
| 数据库迁移 | 执行 | 添加 new_api_session 字段 |
|
||
|
||
### 依赖添加
|
||
|
||
```bash
|
||
poetry add httpx
|
||
```
|