|
@@ -0,0 +1,414 @@
|
|
|
|
|
+# AI语音应用 - 订阅支付系统集成指南
|
|
|
|
|
+
|
|
|
|
|
+## 📋 功能概述
|
|
|
|
|
+
|
|
|
|
|
+已实现的订阅支付系统包含:
|
|
|
|
|
+- ✅ 4个套餐等级(免费、基础、专业、旗舰)
|
|
|
|
|
+- ✅ Token配额系统
|
|
|
|
|
+- ✅ 订阅API和前端页面
|
|
|
|
|
+- ✅ 支付订单管理
|
|
|
|
|
+- ✅ Token使用记录
|
|
|
|
|
+- ✅ 模拟支付(开发环境)
|
|
|
|
|
+- ⏳ 真实支付宝支付(需配置)
|
|
|
|
|
+- ⏳ 真实微信支付(需配置)
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 💰 套餐定价
|
|
|
|
|
+
|
|
|
|
|
+| 套餐 | 月付 | 年付 | Token配额 | 主要功能 |
|
|
|
|
|
+|------|------|------|----------|----------|
|
|
|
|
|
+| 免费版 | ¥0 | ¥0 | 10,000/月 | 每天3次,每次2000字 |
|
|
|
|
|
+| 基础版 | ¥9.9 | ¥99 | 50,000/月 | 全部音色,高清音质 |
|
|
|
|
|
+| 专业版 | ¥29.9 | ¥299 | 200,000/月 | 无损音质,API访问 |
|
|
|
|
|
+| 旗舰版 | ¥99 | ¥999 | 1,000,000/年 | 批量处理,团队管理 |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 🔧 支付宝支付集成
|
|
|
|
|
+
|
|
|
|
|
+### 1. 注册支付宝开放平台账号
|
|
|
|
|
+
|
|
|
|
|
+访问:[支付宝开放平台](https://open.alipay.com/)
|
|
|
|
|
+
|
|
|
|
|
+### 2. 创建应用
|
|
|
|
|
+
|
|
|
|
|
+1. 登录开放平台
|
|
|
|
|
+2. 点击「创建应用」
|
|
|
|
|
+3. 选择「自研应用」
|
|
|
|
|
+4. 填写应用信息
|
|
|
|
|
+5. 添加能力:
|
|
|
|
|
+ - 手机网站支付
|
|
|
|
|
+ - APP支付
|
|
|
|
|
+ - 当面付
|
|
|
|
|
+
|
|
|
|
|
+### 3. 配置密钥
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+# 1. 生成RSA2密钥对
|
|
|
|
|
+openssl genrsa -out app_private_key.pem 2048
|
|
|
|
|
+openssl rsa -in app_private_key.pem -pubout -out app_public_key.pem
|
|
|
|
|
+
|
|
|
|
|
+# 2. 在支付宝开放平台配置公钥
|
|
|
|
|
+# 将 app_public_key.pem 内容上传到支付宝
|
|
|
|
|
+
|
|
|
|
|
+# 3. 获取支付宝公钥
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4. 配置环境变量
|
|
|
|
|
+
|
|
|
|
|
+创建 `.env` 文件:
|
|
|
|
|
+
|
|
|
|
|
+```env
|
|
|
|
|
+# 支付宝配置
|
|
|
|
|
+ALIPAY_APP_ID=your_app_id
|
|
|
|
|
+ALIPAY_GATEWAY=https://openapi.alipay.com/gateway.do
|
|
|
|
|
+ALIPAY_PRIVATE_KEY=your_private_key
|
|
|
|
|
+ALIPAY_PUBLIC_KEY=alipay_public_key
|
|
|
|
|
+ALIPAY_NOTIFY_URL=https://your-domain.com/api/payment/alipay/callback
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 5. 安装SDK
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+npm install alipay-sdk
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 6. 更新支付服务
|
|
|
|
|
+
|
|
|
|
|
+编辑 `server/src/modules/payment/payment.service.ts`:
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+import Alipay from 'alipay-sdk'; // 添加
|
|
|
|
|
+
|
|
|
|
|
+const alipay = new Alipay({
|
|
|
|
|
+ appId: process.env.ALIPAY_APP_ID,
|
|
|
|
|
+ privateKey: process.env.ALIPAY_PRIVATE_KEY,
|
|
|
|
|
+ alipayPublicKey: process.env.ALIPAY_PUBLIC_KEY,
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+async function generateAlipayUrl(orderNo: string, amount: number, subject: string): Promise<string> {
|
|
|
|
|
+ const result = await alipay.exec('alipay.trade.page.pay', {
|
|
|
|
|
+ outTradeNo: orderNo,
|
|
|
|
|
+ productCode: 'FAST_INSTANT_TRADE_PAY',
|
|
|
|
|
+ totalAmount: amount.toString(),
|
|
|
|
|
+ subject: `AI语音应用-${subject}-订阅`,
|
|
|
|
|
+ body: `订阅${subject}套餐`,
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return result as string;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 💚 微信支付集成
|
|
|
|
|
+
|
|
|
|
|
+### 1. 注册微信商户平台账号
|
|
|
|
|
+
|
|
|
|
|
+访问:[微信商户平台](https://pay.weixin.qq.com/)
|
|
|
|
|
+
|
|
|
|
|
+### 2. 申请接入
|
|
|
|
|
+
|
|
|
|
|
+1. 完成商户资质认证
|
|
|
|
|
+2. 选择接入方式:Native支付 / JSAPI支付 / APP支付
|
|
|
|
|
+3. 配置支付密钥
|
|
|
|
|
+
|
|
|
|
|
+### 3. 获取API密钥
|
|
|
|
|
+
|
|
|
|
|
+1. 登录微信商户平台
|
|
|
|
|
+2. 进入「账户中心」→「API安全」
|
|
|
|
|
+3. 设置API密钥(32位)
|
|
|
|
|
+
|
|
|
|
|
+### 4. 安装SDK
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+npm install wechat-pay
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 5. 配置环境变量
|
|
|
|
|
+
|
|
|
|
|
+```env
|
|
|
|
|
+# 微信支付配置
|
|
|
|
|
+WECHAT_APP_ID=your_app_id
|
|
|
|
|
+WECHAT_MCH_ID=your_mch_id
|
|
|
|
|
+WECHAT_API_KEY=your_api_key
|
|
|
|
|
+WECHAT_NOTIFY_URL=https://your-domain.com/api/payment/wechat/callback
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 6. 更新支付服务
|
|
|
|
|
+
|
|
|
|
|
+编辑 `server/src/modules/payment/payment.service.ts`:
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+import { WechatPay } from 'wechat-pay';
|
|
|
|
|
+
|
|
|
|
|
+const wechatPay = new WechatPay({
|
|
|
|
|
+ mchId: process.env.WECHAT_MCH_ID,
|
|
|
|
|
+ privateKey: process.env.WECHAT_API_KEY,
|
|
|
|
|
+ appId: process.env.WECHAT_APP_ID,
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+async function generateWechatPay(orderNo: string, amount: number) {
|
|
|
|
|
+ const result = await wechatPay.unifiedOrder({
|
|
|
|
|
+ out_trade_no: orderNo,
|
|
|
|
|
+ body: 'AI语音应用-套餐订阅',
|
|
|
|
|
+ total_fee: Math.round(amount * 100), // 转换为分
|
|
|
|
|
+ trade_type: 'NATIVE',
|
|
|
|
|
+ notify_url: process.env.WECHAT_NOTIFY_URL,
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ qrcode: result.code_url,
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 🌐 支付回调处理
|
|
|
|
|
+
|
|
|
|
|
+### 支付宝回调
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+router.post('/alipay/callback', async (ctx: Context) => {
|
|
|
|
|
+ const alipaySignature = ctx.get('sign');
|
|
|
|
|
+ // 验证签名
|
|
|
|
|
+ const signVerified = alipay.checkSignature(ctx.request.body);
|
|
|
|
|
+
|
|
|
|
|
+ if (signVerified) {
|
|
|
|
|
+ const { out_trade_no, trade_status, trade_no } = ctx.request.body;
|
|
|
|
|
+
|
|
|
|
|
+ if (trade_status === 'TRADE_SUCCESS') {
|
|
|
|
|
+ await PaymentService.handlePaymentCallback(out_trade_no, trade_no, 'success');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ctx.body = 'success';
|
|
|
|
|
+});
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 微信支付回调
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+router.post('/wechat/callback', async (ctx: Context) => {
|
|
|
|
|
+ const xml = ctx.request.body;
|
|
|
|
|
+ // 解析XML并验证签名
|
|
|
|
|
+
|
|
|
|
|
+ if (result.return_code === 'SUCCESS' && result.result_code === 'SUCCESS') {
|
|
|
|
|
+ await PaymentService.handlePaymentCallback(
|
|
|
|
|
+ result.out_trade_no,
|
|
|
|
|
+ result.transaction_id,
|
|
|
|
|
+ 'success'
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ctx.body = '<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>';
|
|
|
|
|
+});
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 📱 前端支付流程
|
|
|
|
|
+
|
|
|
|
|
+### 1. 选择套餐
|
|
|
|
|
+```typescript
|
|
|
|
|
+// pages/member/index.vue
|
|
|
|
|
+const selectedPlan = ref<Plan>(null);
|
|
|
|
|
+
|
|
|
|
|
+function selectPlan(plan: Plan) {
|
|
|
|
|
+ selectedPlan.value = plan;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 2. 选择支付方式
|
|
|
|
|
+```typescript
|
|
|
|
|
+const paymentMethod = ref<'alipay' | 'wechat'>('alipay');
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 3. 创建订单
|
|
|
|
|
+```typescript
|
|
|
|
|
+async function createOrder() {
|
|
|
|
|
+ const result = await post('/payment/create', {
|
|
|
|
|
+ planId: selectedPlan.value.id,
|
|
|
|
|
+ paymentMethod: paymentMethod.value
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4. 跳转支付
|
|
|
|
|
+
|
|
|
|
|
+**支付宝:**
|
|
|
|
|
+```typescript
|
|
|
|
|
+if (result.paymentUrl) {
|
|
|
|
|
+ window.location.href = result.paymentUrl;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**微信支付:**
|
|
|
|
|
+```typescript
|
|
|
|
|
+if (result.qrcode) {
|
|
|
|
|
+ // 显示二维码
|
|
|
|
|
+ showQRCode(result.qrcode);
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 5. 轮询订单状态
|
|
|
|
|
+```typescript
|
|
|
|
|
+async function pollOrderStatus(orderNo: string) {
|
|
|
|
|
+ const interval = setInterval(async () => {
|
|
|
|
|
+ const order = await get(`/payment/orders/${orderNo}`);
|
|
|
|
|
+ if (order.status === 'paid') {
|
|
|
|
|
+ clearInterval(interval);
|
|
|
|
|
+ uni.showToast({ title: '支付成功', icon: 'success' });
|
|
|
|
|
+ // 更新用户状态
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 2000);
|
|
|
|
|
+
|
|
|
|
|
+ // 30秒后停止轮询
|
|
|
|
|
+ setTimeout(() => clearInterval(interval), 30000);
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 📊 数据库模型
|
|
|
|
|
+
|
|
|
|
|
+### SubscriptionPlan(套餐计划)
|
|
|
|
|
+```prisma
|
|
|
|
|
+model SubscriptionPlan {
|
|
|
|
|
+ id Int @id @default(autoincrement())
|
|
|
|
|
+ name String // 套餐名称
|
|
|
|
|
+ level Int // 套餐等级
|
|
|
|
|
+ priceMonthly Decimal // 月付价格
|
|
|
|
|
+ priceYearly Decimal // 年付价格
|
|
|
|
|
+ monthlyTokens Int // 月Token配额
|
|
|
|
|
+ yearlyTokens Int? // 年Token配额
|
|
|
|
|
+ dailyGenerations Int // 每日生成次数
|
|
|
|
|
+ perGenerationLimit Int // 单次生成限制
|
|
|
|
|
+ voiceOptions Int // 可用音色数
|
|
|
|
|
+ audioQuality String // 音质标准
|
|
|
|
|
+ apiAccess Boolean // API权限
|
|
|
|
|
+ batchProcessing Boolean // 批量处理
|
|
|
|
|
+ teamManagement Boolean // 团队管理
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Subscription(订阅记录)
|
|
|
|
|
+```prisma
|
|
|
|
|
+model Subscription {
|
|
|
|
|
+ id Int @id @default(autoincrement())
|
|
|
|
|
+ userId Int
|
|
|
|
|
+ planId Int
|
|
|
|
|
+ startDate DateTime
|
|
|
|
|
+ endDate DateTime
|
|
|
|
|
+ status String // active, expired, cancelled
|
|
|
|
|
+ autoRenew Boolean // 自动续费
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### TokenUsage(Token使用记录)
|
|
|
|
|
+```prisma
|
|
|
|
|
+model TokenUsage {
|
|
|
|
|
+ id Int @id @default(autoincrement())
|
|
|
|
|
+ userId Int
|
|
|
|
|
+ type String // text_to_speech, api_call
|
|
|
|
|
+ amount Int // 消耗数量
|
|
|
|
|
+ contentLength Int // 内容长度
|
|
|
|
|
+ description String?
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### TokenBalance(Token余额)
|
|
|
|
|
+```prisma
|
|
|
|
|
+model TokenBalance {
|
|
|
|
|
+ id Int @id @default(autoincrement())
|
|
|
|
|
+ userId Int @unique
|
|
|
|
|
+ totalTokens Int // 总配额
|
|
|
|
|
+ usedTokens Int // 已使用
|
|
|
|
|
+ resetDate DateTime? // 重置日期
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 🔒 Token消耗计算
|
|
|
|
|
+
|
|
|
|
|
+### 文本转语音
|
|
|
|
|
+```
|
|
|
|
|
+Token消耗 = 文本字数 × 1 Token/字
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 示例
|
|
|
|
|
+- 1000字文本 → 消耗 1000 Token
|
|
|
|
|
+- 5000字文本 → 消耗 5000 Token
|
|
|
|
|
+
|
|
|
|
|
+### 扣费时机
|
|
|
|
|
+1. 用户提交TTS生成请求
|
|
|
|
|
+2. 检查Token余额
|
|
|
|
|
+3. 余额充足 → 扣除Token → 开始生成
|
|
|
|
|
+4. 余额不足 → 返回错误提示
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 🧪 测试支付
|
|
|
|
|
+
|
|
|
|
|
+### 开发环境模拟支付
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+# 1. 创建订单
|
|
|
|
|
+curl -X POST http://localhost:3000/api/payment/create \
|
|
|
|
|
+ -H "Authorization: Bearer <token>" \
|
|
|
|
|
+ -H "Content-Type: application/json" \
|
|
|
|
|
+ -d '{"planId": 2, "paymentMethod": "mock"}'
|
|
|
|
|
+
|
|
|
|
|
+# 2. 模拟支付成功
|
|
|
|
|
+curl -X POST http://localhost:3000/api/payment/mock \
|
|
|
|
|
+ -H "Authorization: Bearer <token>" \
|
|
|
|
|
+ -H "Content-Type: application/json" \
|
|
|
|
|
+ -d '{"orderNo": "PAY20260412XXXXXX"}'
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 📝 注意事项
|
|
|
|
|
+
|
|
|
|
|
+### 1. 安全要点
|
|
|
|
|
+- ✅ 回调URL必须使用HTTPS
|
|
|
|
|
+- ✅ 验证支付回调签名
|
|
|
|
|
+- ✅ 防止订单重复处理
|
|
|
|
|
+- ✅ 使用环境变量存储密钥
|
|
|
|
|
+
|
|
|
|
|
+### 2. 费用结算
|
|
|
|
|
+- 支付宝:T+1 结算
|
|
|
|
|
+- 微信:T+1 结算
|
|
|
|
|
+
|
|
|
|
|
+### 3. 退款处理
|
|
|
|
|
+- 用户申请退款 → 后台审核 → 调用退款API
|
|
|
|
|
+- 退款后更新订阅状态和Token余额
|
|
|
|
|
+
|
|
|
|
|
+### 4. 自动续费
|
|
|
|
|
+- 可选功能,用户同意后开启
|
|
|
|
|
+- 订阅到期前7天自动扣款
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 📞 技术支持
|
|
|
|
|
+
|
|
|
|
|
+如遇问题,请检查:
|
|
|
|
|
+1. 环境变量配置是否正确
|
|
|
|
|
+2. 支付宝/微信商户平台配置
|
|
|
|
|
+3. 回调URL是否可访问
|
|
|
|
|
+4. 日志输出查看错误信息
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 🚀 下一步
|
|
|
|
|
+
|
|
|
|
|
+1. 配置真实的支付宝和微信支付
|
|
|
|
|
+2. 实现自动续费功能
|
|
|
|
|
+3. 添加退款功能
|
|
|
|
|
+4. 集成客服系统
|
|
|
|
|
+5. 添加数据分析报表
|