ai-clone/frontend-ai/utils/payment.js
2026-03-05 14:29:21 +08:00

1031 lines
27 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 支付服务工具
* 统一管理支付相关功能
*/
import { API_BASE } from '@/config/api.js';
/**
* 模拟支付模式配置
* 设置为 true 时,所有支付都会使用模拟数据,不调用真实支付接口
*/
export const MOCK_PAYMENT_MODE = false;
export function mapOfficialVoiceDisplay(list) {
const regionPrefixMap = {
'湾区大叔': '台湾',
'台湾小何': '台湾',
'双节棍小哥': '台湾',
'广州德哥': '广州',
'浩宇小哥': '大陆',
'湾湾小何': '台湾',
'北京小爷': '北京',
'京腔侃爷/Harmony': '北京',
'林北浩儿': '台湾',
'粤语小灿': '广东',
'泉州子轩': '福建',
'豫州子轩': '河南',
'呆萌川妹': '四川',
'广西远舟': '广西',
'妹坨洁儿': '湖南'
};
return (list || []).map(v => {
if (!v) return v;
const vt = v.voice_type || 'CLONE';
if (vt !== 'OFFICIAL') return v;
const rawName = v.voice_name ? String(v.voice_name) : '';
const prefix = rawName && regionPrefixMap[rawName] ? regionPrefixMap[rawName] : '';
if (!prefix || !rawName) return v;
if (rawName.indexOf(prefix + '-') === 0) return v;
return { ...v, voice_name: `${prefix}-${rawName}` };
});
}
/**
* 服务类型映射(后端类型 -> 前端配置)
*/
const SERVICE_TYPE_MAPPING = {
'CREATE_VOICE': 'voice_clone',
'PHOTO_REVIVAL': 'photo_revival',
'VOLCENGINE_VIDEO': 'volcengine_video',
'SYNTHESIZE': 'tts_synthesis',
'VIDEO_CALL': 'video_call',
'AI_CALL': 'conversation'
};
/**
* 服务类型配置(默认值,会被后端数据覆盖)
*/
export const SERVICE_TYPES = {
VOICE_CLONE: {
type: 'voice_clone',
backendType: 'CREATE_VOICE',
name: '声音克隆',
desc: '克隆您的声音让AI学会您的音色',
price: 0.00,
freeTrialCount: 0
},
PHOTO_REVIVAL: {
type: 'photo_revival',
backendType: 'PHOTO_REVIVAL',
name: '照片复活',
desc: '让照片中的人开口说话',
price: 0.00,
freeTrialCount: 0
},
VOLCENGINE_VIDEO: {
type: 'volcengine_video',
backendType: 'VOLCENGINE_VIDEO',
name: '火山视频复活',
desc: '火山视频复活',
price: 0.00,
freeTrialCount: 0
},
TTS_SYNTHESIS: {
type: 'tts_synthesis',
backendType: 'SYNTHESIZE',
name: '语音合成',
desc: '使用已有音色合成语音',
price: 0.00,
freeTrialCount: 0
},
VIDEO_CALL: {
type: 'video_call',
backendType: 'VIDEO_CALL',
name: 'AI视频通话',
desc: 'AI视频通话',
price: 0.00,
freeTrialCount: 0
},
CONVERSATION: {
type: 'conversation',
backendType: 'AI_CALL',
name: '实时对话',
desc: '与AI进行实时语音对话',
price: 0.00,
freeTrialCount: 0
}
};
// 缓存价格数据
let priceCache = null;
let priceCacheTime = 0;
const CACHE_DURATION = 5 * 60 * 1000; // 5分钟缓存
/**
* 从后端获取服务价格列表
* @returns {Promise<Array>}
*/
export async function fetchServicePrices() {
// 检查缓存
const now = Date.now();
if (priceCache && (now - priceCacheTime) < CACHE_DURATION) {
console.log('[Payment] 使用缓存价格数据');
return priceCache;
}
console.log('[Payment] 开始获取服务价格API地址:', `${API_BASE}/api/pay/prices`);
return new Promise((resolve, reject) => {
uni.request({
url: `${API_BASE}/api/pay/prices`,
method: 'GET',
success: (res) => {
console.log('[Payment] 价格API响应:', res);
if (res.statusCode === 200 && res.data) {
priceCache = res.data;
priceCacheTime = now;
console.log('[Payment] 后端返回价格数据:', res.data);
// 更新SERVICE_TYPES中的价格
let updateCount = 0;
res.data.forEach(priceItem => {
console.log('[Payment] 处理价格项:', priceItem);
const frontendType = SERVICE_TYPE_MAPPING[priceItem.serviceType];
console.log('[Payment] 映射类型:', priceItem.serviceType, '->', frontendType);
if (frontendType) {
const serviceKey = Object.keys(SERVICE_TYPES).find(
key => SERVICE_TYPES[key].type === frontendType
);
if (serviceKey && SERVICE_TYPES[serviceKey]) {
console.log('[Payment] 更新服务:', serviceKey, '价格:', priceItem.price, '免费次数:', priceItem.freeTrialCount);
SERVICE_TYPES[serviceKey].price = priceItem.price;
SERVICE_TYPES[serviceKey].freeTrialCount = priceItem.freeTrialCount || 0;
SERVICE_TYPES[serviceKey].name = priceItem.serviceName;
SERVICE_TYPES[serviceKey].desc = priceItem.description || SERVICE_TYPES[serviceKey].desc;
updateCount++;
}
} else {
console.warn('[Payment] 未找到映射的前端类型:', priceItem.serviceType);
}
});
console.log('[Payment] 价格更新完成,共更新', updateCount, '个服务');
console.log('[Payment] 最终SERVICE_TYPES:', JSON.stringify(SERVICE_TYPES, null, 2));
resolve(res.data);
} else {
console.error('[Payment] 获取价格失败,状态码:', res.statusCode);
if (priceCache) {
resolve(priceCache);
return;
}
reject(new Error('获取价格失败'));
}
},
fail: (err) => {
console.error('[Payment] 获取价格请求失败:', err);
if (priceCache) {
resolve(priceCache);
return;
}
reject(err);
}
});
});
}
/**
* 生成订单号
*/
function generateOrderNo() {
const timestamp = Date.now();
const random = Math.floor(Math.random() * 10000).toString().padStart(4, '0');
return `ORD${timestamp}${random}`;
}
/**
* 获取用户信息
*/
function getUserInfo() {
return {
userId: uni.getStorageSync('userId') || '',
token: uni.getStorageSync('token') || ''
};
}
async function fetchUsagePreview(frontendServiceType) {
try {
const { userId, token } = getUserInfo();
if (!userId) return null;
const service = Object.values(SERVICE_TYPES).find(s => s.type === frontendServiceType);
const backendServiceType = service ? service.backendType : frontendServiceType;
return await new Promise((resolve) => {
uni.request({
url: `${API_BASE}/api/pay/usage-preview?serviceType=${backendServiceType}`,
method: 'GET',
header: {
'X-User-Id': userId,
'Authorization': token ? `Bearer ${token}` : ''
},
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.success) {
resolve(res.data);
return;
}
resolve(null);
},
fail: () => resolve(null)
});
});
} catch (e) {
return null;
}
}
/**
* 后端资格校验:免费次数/补发次数/已支付 都视为可用。
* @param {string} frontendServiceType - 前端 service.type
* @returns {Promise<boolean>} - true 表示无需支付可直接使用
*/
async function checkBackendEligibility(frontendServiceType) {
try {
const { userId, token } = getUserInfo();
if (!userId) return false;
const service = Object.values(SERVICE_TYPES).find(s => s.type === frontendServiceType);
const backendServiceType = service ? service.backendType : frontendServiceType;
return await new Promise((resolve) => {
uni.request({
url: `${API_BASE}/api/pay/check?userId=${userId}&serviceType=${backendServiceType}`,
method: 'GET',
header: {
'X-User-Id': userId,
'Authorization': token ? `Bearer ${token}` : ''
},
success: (res) => {
// 按次付费场景:只有“明确剩余次数>0”才视为可用。
// 兼容后端可能返回的字段:
// - paid: boolean
// - remainingTotal / remaining / remainingCount / remainingTimes
// - freeTrialRemaining / freeTrialLeft
if (res.statusCode === 200 && res.data && typeof res.data.paid === 'boolean') {
const data = res.data || {};
console.log('[Payment] /api/pay/check 响应:', backendServiceType, data);
if (data.paid === false) {
resolve(false);
return;
}
const remainingTotal = (typeof data.remainingTotal === 'number') ? data.remainingTotal
: (typeof data.remaining === 'number') ? data.remaining
: (typeof data.remainingCount === 'number') ? data.remainingCount
: (typeof data.remainingTimes === 'number') ? data.remainingTimes
: null;
const freeTrialRemaining = (typeof data.freeTrialRemaining === 'number') ? data.freeTrialRemaining
: (typeof data.freeTrialLeft === 'number') ? data.freeTrialLeft
: null;
// 有任何一种剩余次数>0则允许跳过支付
if ((typeof remainingTotal === 'number' && remainingTotal > 0) || (typeof freeTrialRemaining === 'number' && freeTrialRemaining > 0)) {
resolve(true);
return;
}
// paid=true 但没有明确剩余次数(或<=0视为需要付费
resolve(false);
return;
}
resolve(false);
},
fail: () => resolve(false)
});
});
} catch (e) {
console.error('[Payment] 后端资格校验异常:', e);
return false;
}
}
/**
* 创建订单
* @param {string} serviceType - 服务类型
* @param {object} extraData - 额外数据
* @returns {Promise}
*/
export async function createOrder(serviceType, extraData = {}) {
try {
// 先获取最新价格
await fetchServicePrices();
// 获取服务配置
const service = Object.values(SERVICE_TYPES).find(s => s.type === serviceType);
if (!service) {
throw new Error('无效的服务类型');
}
const orderNo = generateOrderNo();
console.log('[Payment] 创建订单:', {
orderNo,
serviceType,
serviceName: service.name,
amount: service.price
});
return {
orderNo,
serviceType,
serviceName: service.name,
amount: service.price,
service,
...extraData
};
} catch (error) {
console.error('[Payment] 创建订单失败:', error);
throw error;
}
}
/**
* 获取支付渠道
* @returns {string} 'wechat' | 'alipay'
*/
function getPaymentChannel() {
// #ifdef MP-WEIXIN
return 'wechat';
// #endif
// #ifdef MP-ALIPAY
return 'alipay';
// #endif
// #ifdef APP-PLUS
// App环境可以根据用户选择或默认使用微信
// 这里默认使用微信,也可以让用户选择
return uni.getStorageSync('preferred_payment') || 'wechat';
// #endif
// 默认返回微信
return 'wechat';
}
/**
* 创建后端支付订单
* @param {string} serviceType - 服务类型(前端类型)
* @param {number} userId - 用户ID
* @returns {Promise}
*/
export async function createBackendOrder(serviceType, userId) {
// 模拟支付模式:直接返回模拟订单数据
if (MOCK_PAYMENT_MODE) {
console.log('[Payment] 模拟支付模式:创建模拟订单');
const service = Object.values(SERVICE_TYPES).find(s => s.type === serviceType);
const mockOrderNo = 'MOCK_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
return new Promise((resolve) => {
setTimeout(() => {
resolve({
orderNo: mockOrderNo,
userId: userId,
serviceType: service ? service.backendType : serviceType,
serviceName: service ? service.name : '未知服务',
amount: service ? service.price : 0,
status: 'PENDING',
paymentChannel: 'mock',
qrCodeUrl: 'MOCK_QR_CODE_' + mockOrderNo,
createdAt: new Date().toISOString()
});
}, 500); // 模拟网络延迟
});
}
// 真实支付模式
const channel = getPaymentChannel();
// 将前端类型转换为后端类型
const service = Object.values(SERVICE_TYPES).find(s => s.type === serviceType);
const backendServiceType = service ? service.backendType : serviceType;
console.log('[Payment] 服务类型转换:', serviceType, '->', backendServiceType);
// 获取用户标识用于小程序支付
let bizId = null;
if (channel === 'wechat') {
// #ifdef MP-WEIXIN
const openid = uni.getStorageSync('wx_openid');
console.log('[Payment] wx_openid:', openid);
if (openid) {
bizId = 'wx_openid_' + openid;
} else {
console.error('[Payment] 缺少wx_openid无法创建微信小程序支付订单');
return Promise.reject(new Error('缺少openid请先退出并重新微信登录'));
}
// #endif
// #ifdef APP-PLUS
// App微信支付不需要openid
bizId = 'wx_app';
// #endif
} else if (channel === 'alipay') {
const alipayUserId = uni.getStorageSync('alipay_user_id');
if (alipayUserId) {
bizId = 'alipay_user_' + alipayUserId;
}
}
console.log('[Payment] 下单参数:', { userId, serviceType: backendServiceType, paymentChannel: channel, bizId });
return new Promise((resolve, reject) => {
uni.request({
url: `${API_BASE}/api/pay/orders`,
method: 'POST',
header: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${uni.getStorageSync('token') || ''}`
},
data: {
userId: userId,
serviceType: backendServiceType,
paymentChannel: channel,
bizId: bizId
},
success: (res) => {
if (res.statusCode === 200 && res.data) {
console.log('[Payment] 后端订单创建成功:', res.data);
resolve(res.data);
} else {
console.error('[Payment] 后端订单创建失败:', {
statusCode: res.statusCode,
data: res.data,
header: res.header
});
if (res.data && res.data.message) {
console.error('[Payment] 后端错误信息:', res.data.message);
}
reject(new Error(res.data?.message || '创建订单失败'));
}
},
fail: (err) => {
console.error('[Payment] 创建订单请求失败:', err);
reject(err);
}
});
});
}
/**
* 执行微信支付支持小程序和App
* @param {object} orderData - 订单数据
* @returns {Promise}
*/
function executeWechatPayment(orderData) {
return new Promise((resolve, reject) => {
// 模拟支付模式:直接返回成功
if (MOCK_PAYMENT_MODE) {
console.log('[Payment] 模拟支付模式:模拟微信支付成功');
setTimeout(() => {
resolve({
success: true,
orderNo: orderData.orderNo,
payTime: new Date().toISOString(),
message: '支付成功(模拟)'
});
}, 1000); // 模拟1秒支付延迟
return;
}
// #ifdef MP-WEIXIN
// 微信小程序支付
uni.requestPayment({
timeStamp: orderData.timeStamp || String(Date.now()),
nonceStr: orderData.nonceStr || '',
package: orderData.package || '',
signType: orderData.signType || 'RSA',
paySign: orderData.paySign || '',
success: (res) => {
console.log('[Payment] 微信支付成功:', res);
uni.request({
url: `${API_BASE}/api/pay/orders/${orderData.orderNo}/sync/wechat`,
method: 'POST',
header: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${uni.getStorageSync('token') || ''}`
},
success: (syncRes) => {
if (syncRes.statusCode === 200) {
console.log('[Payment] 订单同步成功:', syncRes.data);
resolve({
success: true,
orderNo: orderData.orderNo,
payTime: new Date().toISOString(),
message: '支付成功'
});
} else {
console.error('[Payment] 订单同步失败:', syncRes);
reject(new Error(syncRes.data?.message || '支付成功但订单同步失败'));
}
},
fail: (err) => {
console.error('[Payment] 订单同步请求失败:', err);
reject(new Error('支付成功但订单同步请求失败'));
}
});
},
fail: (err) => {
console.error('[Payment] 微信支付失败:', err);
if (err.errMsg && err.errMsg.includes('cancel')) {
reject(new Error('用户取消支付'));
} else {
reject(new Error(err.errMsg || '支付失败'));
}
}
});
// #endif
// #ifdef APP-PLUS
uni.requestPayment({
provider: 'wxpay',
orderInfo: orderData.orderInfo || '',
success: (res) => {
console.log('[Payment] App微信支付成功:', res);
resolve({
success: true,
orderNo: orderData.orderNo,
payTime: new Date().toISOString(),
message: '支付成功'
});
},
fail: (err) => {
console.error('[Payment] App微信支付失败:', err);
if (err.errMsg && err.errMsg.includes('cancel')) {
reject(new Error('用户取消支付'));
} else {
reject(new Error(err.errMsg || '支付失败'));
}
}
});
// #endif
// #if !defined(MP-WEIXIN) && !defined(APP-PLUS)
reject(new Error('当前环境不支持微信支付'));
// #endif
});
}
/**
* 执行支付宝支付支持小程序和App
* @param {object} orderData - 订单数据
* @returns {Promise}
*/
function executeAlipayPayment(orderData) {
return new Promise((resolve, reject) => {
// 模拟支付模式:直接返回成功
if (MOCK_PAYMENT_MODE) {
console.log('[Payment] 模拟支付模式:模拟支付宝支付成功');
setTimeout(() => {
resolve({
success: true,
orderNo: orderData.orderNo,
payTime: new Date().toISOString(),
message: '支付成功(模拟)'
});
}, 1000); // 模拟1秒支付延迟
return;
}
// #ifdef MP-ALIPAY
// 支付宝小程序支付
my.tradePay({
tradeNO: orderData.tradeNo || '',
success: (res) => {
console.log('[Payment] 支付宝支付成功:', res);
resolve({
success: true,
orderNo: orderData.orderNo,
payTime: new Date().toISOString(),
message: '支付成功'
});
},
fail: (err) => {
console.error('[Payment] 支付宝支付失败:', err);
if (err.resultCode === '6001') {
reject(new Error('用户取消支付'));
} else {
reject(new Error(err.memo || '支付失败'));
}
}
});
// #endif
// #ifdef APP-PLUS
// App环境暂时使用模拟支付开发测试用
console.log('[Payment] App环境 - 使用模拟支付');
setTimeout(() => {
console.log('[Payment] 模拟支付宝支付成功');
resolve({
success: true,
orderNo: orderData.orderNo,
payTime: new Date().toISOString(),
message: '支付成功(模拟)'
});
}, 1000); // 模拟1秒支付延迟
// 真实支付代码(暂时注释)
// uni.requestPayment({
// provider: 'alipay',
// orderInfo: orderData.orderInfo || orderData.tradeNo || '',
// success: (res) => {
// console.log('[Payment] App支付宝支付成功:', res);
// resolve({
// success: true,
// orderNo: orderData.orderNo,
// payTime: new Date().toISOString(),
// message: '支付成功'
// });
// },
// fail: (err) => {
// console.error('[Payment] App支付宝支付失败:', err);
// if (err.errMsg && err.errMsg.includes('cancel')) {
// reject(new Error('用户取消支付'));
// } else {
// reject(new Error(err.errMsg || '支付失败'));
// }
// }
// });
// #endif
// #if !defined(MP-ALIPAY) && !defined(APP-PLUS)
reject(new Error('当前环境不支持支付宝支付'));
// #endif
});
}
/**
* 执行支付(真实版本)
* @param {string} serviceType - 服务类型
* @param {number} userId - 用户ID
* @returns {Promise}
*/
export async function executePayment(serviceType, userId) {
// 优先校验后端是否已具备使用资格(免费次数/补发/已支付)
try {
const eligible = await checkBackendEligibility(serviceType);
if (eligible) {
console.log('[Payment] 后端校验通过,跳过支付:', serviceType);
return {
success: true,
message: '已具备使用资格,无需支付',
skipPayment: true
};
}
} catch (e) {
console.error('[Payment] 后端资格校验失败(忽略继续走支付):', e);
}
// 先获取服务价格
await fetchServicePrices();
const service = Object.values(SERVICE_TYPES).find(s => s.type === serviceType);
// 如果价格为0直接返回成功跳过支付
if (service && service.price === 0) {
console.log('[Payment] 服务价格为0跳过支付流程');
const orderNo = generateOrderNo();
const result = {
success: true,
orderNo: orderNo,
payTime: new Date().toISOString(),
message: '免费服务,无需支付',
free: true
};
uni.showToast({
title: '免费使用',
icon: 'success',
duration: 1500
});
console.log('[Payment] 免费服务,直接返回成功:', result);
return result;
}
// #ifdef APP-PLUS
// APP环境使用真实支付流程
try {
uni.showLoading({
title: '创建订单中...',
mask: true
});
const orderData = await createBackendOrder(serviceType, userId);
uni.showLoading({
title: '正在调起支付...',
mask: true
});
const channel = getPaymentChannel();
let paymentResult;
if (channel === 'wechat') {
paymentResult = await executeWechatPayment(orderData);
} else if (channel === 'alipay') {
paymentResult = await executeAlipayPayment(orderData);
} else {
throw new Error('不支持的支付渠道');
}
uni.hideLoading();
return paymentResult;
} catch (error) {
uni.hideLoading();
throw error;
}
// #endif
// #ifndef APP-PLUS
// 小程序环境:使用真实支付流程
try {
uni.showLoading({
title: '创建订单中...',
mask: true
});
// 1. 创建后端订单
const orderData = await createBackendOrder(serviceType, userId);
uni.showLoading({
title: '正在调起支付...',
mask: true
});
// 2. 根据渠道执行支付
const channel = getPaymentChannel();
let paymentResult;
if (channel === 'wechat') {
paymentResult = await executeWechatPayment(orderData);
} else if (channel === 'alipay') {
paymentResult = await executeAlipayPayment(orderData);
} else {
throw new Error('不支持的支付渠道');
}
uni.hideLoading();
return paymentResult;
} catch (error) {
uni.hideLoading();
throw error;
}
// #endif
}
/**
* 检查订单支付状态(真实版本)
* @param {string} orderNo - 订单号
* @returns {Promise}
*/
export function checkPaymentStatus(orderNo) {
return new Promise((resolve, reject) => {
uni.request({
url: `${API_BASE}/api/pay/orders/${orderNo}`,
method: 'GET',
header: {
'Authorization': `Bearer ${uni.getStorageSync('token') || ''}`
},
success: (res) => {
if (res.statusCode === 200 && res.data) {
console.log('[Payment] 查询支付状态成功:', res.data);
resolve({
success: true,
orderNo: res.data.orderNo,
status: res.data.status,
payTime: res.data.paidAt
});
} else {
reject(new Error('查询订单状态失败'));
}
},
fail: (err) => {
console.error('[Payment] 查询订单状态失败:', err);
reject(err);
}
});
});
}
/**
* 完整的支付流程
* @param {string} serviceType - 服务类型
* @param {object} options - 配置选项
* @returns {Promise}
*/
export async function processPayment(serviceType, options = {}) {
const {
extraData = {},
onOrderCreated = null,
onPaymentSuccess = null,
onPaymentFailed = null
} = options;
try {
// 1. 创建订单
console.log('[Payment] 创建订单:', serviceType);
const orderData = await createOrder(serviceType, extraData);
if (onOrderCreated) {
onOrderCreated(orderData);
}
// 2. 执行支付
console.log('[Payment] 执行支付:', serviceType);
const userId = getUserInfo().userId;
const paymentResult = await executePayment(serviceType, userId);
// 3. 支付成功
console.log('[Payment] 支付成功:', paymentResult);
if (onPaymentSuccess) {
onPaymentSuccess(paymentResult);
}
return {
success: true,
orderNo: orderData.orderNo,
...paymentResult
};
} catch (error) {
console.error('[Payment] 支付流程失败:', error);
if (onPaymentFailed) {
onPaymentFailed(error);
}
throw error;
}
}
/**
* 显示支付弹窗并处理支付
* 这是一个高级封装配合PaymentModal组件使用
* @param {object} vm - Vue实例
* @param {string} serviceType - 服务类型
* @param {function} onSuccess - 支付成功回调
* @param {function} onFailed - 支付失败回调
*/
export async function showPaymentModal(vm, serviceType, onSuccess, onFailed) {
// 先获取最新价格
try {
await fetchServicePrices();
} catch (e) {
// ignore
}
// 优先校验后端是否已具备使用资格(免费次数/补发/已支付),避免弹支付窗
try {
const eligible = await checkBackendEligibility(serviceType);
if (eligible) {
console.log('[Payment] 后端校验通过,跳过支付弹窗:', serviceType);
try {
const preview = await fetchUsagePreview(serviceType);
if (preview && preview.consumeType === 'FREE_TRIAL') {
uni.showToast({
title: '本次使用免费次数',
icon: 'none',
duration: 1500
});
}
} catch (e) {
// ignore
}
if (onSuccess) {
onSuccess({ success: true, skipPayment: true, message: '已具备使用资格,无需支付' });
}
return;
}
} catch (e) {
console.error('[Payment] 后端资格校验失败(忽略继续弹窗):', e);
}
// 获取服务配置
const service = Object.values(SERVICE_TYPES).find(s => s.type === serviceType);
if (!service) {
const error = new Error('无效的服务类型');
console.error('[Payment] 显示支付弹窗失败:', error);
uni.showToast({
title: '无效的服务类型',
icon: 'none'
});
if (onFailed) {
onFailed(error);
}
return;
}
// 如果价格为0直接执行支付跳过弹窗
if (service.price === 0) {
console.log('[Payment] 服务价格为0跳过支付弹窗直接执行');
try {
const userId = getUserInfo().userId;
const result = await executePayment(serviceType, userId);
// 调用成功回调
if (onSuccess) {
onSuccess(result);
}
} catch (error) {
console.error('[Payment] 免费服务执行失败:', error);
if (onFailed) {
onFailed(error);
}
}
return;
}
// 价格不为0显示支付弹窗
createOrder(serviceType)
.then(orderData => {
// 设置弹窗数据
vm.paymentModalData = {
show: true,
serviceType: service.type,
serviceName: service.name,
serviceDesc: service.desc,
price: service.price,
orderNo: orderData.orderNo
};
// 保存回调
vm._paymentOnSuccess = onSuccess;
vm._paymentOnFailed = onFailed;
})
.catch(error => {
console.error('[Payment] 创建订单失败:', error);
uni.showToast({
title: error.message || '创建订单失败',
icon: 'none'
});
if (onFailed) {
onFailed(error);
}
});
}
/**
* 处理支付确认在PaymentModal的confirm事件中调用
* @param {object} vm - Vue实例
* @param {object} paymentData - 支付数据
*/
export async function handlePaymentConfirm(vm, paymentData) {
try {
// 执行支付
const userId = getUserInfo().userId;
const result = await executePayment(paymentData.serviceType, userId);
// 通知PaymentModal支付成功
if (vm.$refs.paymentModal) {
vm.$refs.paymentModal.paymentSuccess();
}
// 关闭弹窗
vm.paymentModalData.show = false;
// 显示成功提示
uni.showToast({
title: '支付成功',
icon: 'success'
});
// 调用成功回调
if (vm._paymentOnSuccess) {
vm._paymentOnSuccess(result);
}
if (vm._paymentResolve) {
vm._paymentResolve(result);
}
} catch (error) {
console.error('[Payment] 支付失败:', error);
// 通知PaymentModal支付失败
if (vm.$refs.paymentModal) {
vm.$refs.paymentModal.paymentFailed(error);
}
// 显示错误提示
uni.showToast({
title: error.message || '支付失败',
icon: 'none'
});
// 调用失败回调
if (vm._paymentOnFailed) {
vm._paymentOnFailed(error);
}
if (vm._paymentReject) {
vm._paymentReject(error);
}
}
}
export default {
SERVICE_TYPES,
createOrder,
executePayment,
checkPaymentStatus,
processPayment,
showPaymentModal,
handlePaymentConfirm
};