已实现的订阅支付系统包含:
| 套餐 | 月付 | 年付 | Token配额 | 主要功能 |
|---|---|---|---|---|
| 免费版 | ¥0 | ¥0 | 10,000/月 | 每天3次,每次2000字 |
| 基础版 | ¥9.9 | ¥99 | 50,000/月 | 全部音色,高清音质 |
| 专业版 | ¥29.9 | ¥299 | 200,000/月 | 无损音质,API访问 |
| 旗舰版 | ¥99 | ¥999 | 1,000,000/年 | 批量处理,团队管理 |
访问:支付宝开放平台
# 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. 获取支付宝公钥
创建 .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
npm install alipay-sdk
编辑 server/src/modules/payment/payment.service.ts:
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;
}
访问:微信商户平台
npm install wechat-pay
# 微信支付配置
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
编辑 server/src/modules/payment/payment.service.ts:
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,
};
}
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';
});
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>';
});
// pages/member/index.vue
const selectedPlan = ref<Plan>(null);
function selectPlan(plan: Plan) {
selectedPlan.value = plan;
}
const paymentMethod = ref<'alipay' | 'wechat'>('alipay');
async function createOrder() {
const result = await post('/payment/create', {
planId: selectedPlan.value.id,
paymentMethod: paymentMethod.value
});
return result;
}
支付宝:
if (result.paymentUrl) {
window.location.href = result.paymentUrl;
}
微信支付:
if (result.qrcode) {
// 显示二维码
showQRCode(result.qrcode);
}
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);
}
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 // 团队管理
}
model Subscription {
id Int @id @default(autoincrement())
userId Int
planId Int
startDate DateTime
endDate DateTime
status String // active, expired, cancelled
autoRenew Boolean // 自动续费
}
model TokenUsage {
id Int @id @default(autoincrement())
userId Int
type String // text_to_speech, api_call
amount Int // 消耗数量
contentLength Int // 内容长度
description String?
}
model TokenBalance {
id Int @id @default(autoincrement())
userId Int @unique
totalTokens Int // 总配额
usedTokens Int // 已使用
resetDate DateTime? // 重置日期
}
Token消耗 = 文本字数 × 1 Token/字
# 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"}'
如遇问题,请检查: