ai-clone/frontend-ai/utils/payment.js

1031 lines
27 KiB
JavaScript
Raw Normal View History

2026-03-05 14:29:21 +08:00
/**
* 支付服务工具
* 统一管理支付相关功能
*/
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
};