/** * 微信支付工具类 * 封装微信支付相关功能 */ const api = require('./api') const config = require('../config/index') /** * 发起微信支付 * @param {string} orderId - 订单ID * @param {string} orderType - 订单类型: recharge/vip/companion/gift * @returns {Promise} */ const requestPayment = async (orderId, orderType = 'recharge') => { try { console.log('[Payment] ========== 开始支付流程 ==========') console.log('[Payment] 订单ID:', orderId) console.log('[Payment] 订单类型:', orderType) // 1. 调用后端API获取支付参数 wx.showLoading({ title: '正在调起支付...', mask: true }) const paymentParams = await api.payment.prepay({ orderId, orderType }) console.log('[Payment] 获取支付参数成功') console.log('[Payment] 完整响应:', JSON.stringify(paymentParams)) // 后端返回格式: { success: true, data: { timeStamp, nonceStr, package, signType, paySign, ... } } if (!paymentParams.success || !paymentParams.data) { throw new Error(paymentParams.error || paymentParams.message || '获取支付参数失败') } // 只提取微信支付需要的5个参数,忽略其他字段(如 total_fee, orderId 等) const { timeStamp, nonceStr, package: packageValue, signType, paySign, // 以下字段仅用于日志,不传递给 wx.requestPayment total_fee, orderId: responseOrderId, orderNo } = paymentParams.data console.log('[Payment] 支付参数解析:') console.log(' - timeStamp:', timeStamp) console.log(' - nonceStr:', nonceStr) console.log(' - package:', packageValue) console.log(' - signType:', signType) console.log(' - paySign:', paySign ? paySign.substring(0, 20) + '...' : 'null') console.log(' - total_fee (仅日志):', total_fee) console.log(' - orderId (仅日志):', responseOrderId) console.log(' - orderNo (仅日志):', orderNo) // 验证必要参数 if (!timeStamp || !nonceStr || !packageValue || !paySign) { console.error('[Payment] 支付参数不完整') throw new Error('支付参数不完整') } // 2. 调用微信支付API wx.hideLoading() console.log('[Payment] 调用wx.requestPayment') console.log('[Payment] ⚠️ 注意:只传递5个必需参数,不传递 total_fee') // 构造支付参数对象,确保只包含5个必需字段 const paymentRequest = { timeStamp: String(timeStamp), nonceStr: String(nonceStr), package: String(packageValue), signType: signType || 'MD5', paySign: String(paySign) } console.log('[Payment] 最终支付参数:', JSON.stringify(paymentRequest)) return new Promise((resolve, reject) => { wx.requestPayment({ ...paymentRequest, success: (res) => { console.log('[Payment] ✓ 支付成功:', res) resolve({ success: true, message: '支付成功' }) }, fail: (err) => { console.error('[Payment] ✗ 支付失败:', err) console.error('[Payment] 错误详情:', JSON.stringify(err)) // 用户取消支付 if (err.errMsg === 'requestPayment:fail cancel') { reject({ code: 'USER_CANCEL', message: '您已取消支付' }) } // 支付失败 else { reject({ code: 'PAYMENT_FAIL', message: err.errMsg || '支付失败,请稍后重试', detail: err }) } } }) }) } catch (error) { wx.hideLoading() console.error('[Payment] ✗ 支付流程错误:', error) throw error } } /** * 查询订单支付状态 * @param {string} orderId - 订单ID * @param {number} confirm - 是否主动确认 (1/0) * @returns {Promise} */ const queryOrderStatus = async (orderId, confirm = 0) => { try { console.log('[Payment] 查询订单状态:', orderId, 'confirm:', confirm) const res = await api.payment.queryOrder(orderId, { confirm }) console.log('[Payment] 订单状态:', res.data?.status) return res } catch (error) { console.error('[Payment] 查询订单状态失败:', error) throw error } } /** * 轮询查询订单状态 * @param {string} orderId - 订单ID * @param {number} maxRetries - 最大重试次数 * @param {number} interval - 轮询间隔(ms) * @returns {Promise} */ const pollOrderStatus = (orderId, maxRetries = 30, interval = 2000) => { return new Promise((resolve, reject) => { let retries = 0 console.log('[Payment] 开始轮询订单状态') console.log('[Payment] 最大重试次数:', maxRetries) console.log('[Payment] 轮询间隔:', interval, 'ms') const poll = async () => { try { retries++ console.log(`[Payment] 第 ${retries}/${maxRetries} 次查询`) // 前3次查询带 confirm=1 参数,促使后端主动向微信查询状态 const confirm = retries <= 3 ? 1 : 0 const res = await queryOrderStatus(orderId, confirm) if (res.success && res.data) { const status = res.data.status console.log(`[Payment] 订单状态: ${status}`) // 支付成功 - 后端状态: completed if (status === 'completed') { console.log('[Payment] ✓ 订单支付成功并已完成') resolve({ success: true, data: res.data, status: 'completed' }) return } // 已支付但未完成 - 后端状态: paid if (status === 'paid') { console.log('[Payment] ✓ 订单已支付,等待完成') // 继续轮询,等待变为 completed } // 支付失败 - 后端状态: cancelled if (status === 'cancelled') { console.log('[Payment] ✗ 订单已取消') reject({ code: 'ORDER_CANCELLED', message: '订单已取消' }) return } // 继续轮询 if (retries < maxRetries) { console.log('[Payment] 订单状态为', status, ',继续轮询...') setTimeout(poll, interval) } else { console.log('[Payment] ✗ 查询超时') reject({ code: 'TIMEOUT', message: '支付结果查询超时,请稍后在订单列表中查看', data: res.data }) } } else { reject({ code: 'QUERY_FAIL', message: res.error || res.message || '查询订单失败' }) } } catch (error) { console.error('[Payment] 轮询查询失败:', error) // 如果是网络错误,继续重试 if (retries < maxRetries) { console.log('[Payment] 查询出错,继续重试...') setTimeout(poll, interval) } else { reject(error) } } } // 开始第一次查询 poll() }) } /** * 完整支付流程(创建订单 + 支付 + 查询状态) * @param {object} orderData - 订单数据 * @param {string} orderType - 订单类型 * @returns {Promise} */ const completePayment = async (orderData, orderType) => { try { console.log('[Payment] ========== 完整支付流程开始 ==========') console.log('[Payment] 测试模式:', config.TEST_MODE ? '开启' : '关闭') // 1. 创建订单 wx.showLoading({ title: '创建订单中...', mask: true }) let orderRes switch (orderType) { case 'recharge': orderRes = await api.payment.createRechargeOrder(orderData) break case 'vip': orderRes = await api.payment.createVipOrder(orderData) break case 'companion': orderRes = await api.companion.createOrder(orderData) break default: throw new Error('不支持的订单类型') } if (!orderRes.success || !orderRes.data) { throw new Error(orderRes.message || '创建订单失败') } const orderId = orderRes.data.orderId console.log('[Payment] ✓ 订单创建成功:', orderId) wx.hideLoading() // 测试模式:跳过微信支付,直接模拟支付成功 if (config.TEST_MODE) { console.log('[Payment] ⚠️ 测试模式:跳过微信支付,直接模拟支付成功') wx.showLoading({ title: '模拟支付中...', mask: true }) // 延迟1秒模拟支付过程 await new Promise(resolve => setTimeout(resolve, 1000)) // 调用后端测试支付接口,直接标记订单为已支付 try { const testPayRes = await api.payment.testPay({ orderId }) console.log('[Payment] ✓ 测试支付成功:', testPayRes) } catch (error) { console.error('[Payment] ✗ 测试支付失败:', error) // 即使测试支付接口失败,也继续流程(可能后端没有此接口) } wx.hideLoading() // 显示成功提示 wx.showToast({ title: '支付成功(测试)', icon: 'success', duration: 2000 }) console.log('[Payment] ========== 支付流程完成(测试模式)==========') return { success: true, orderId, testMode: true, message: '测试模式支付成功' } } // 正式模式:走真实微信支付流程 // 2. 发起支付 await requestPayment(orderId, orderType) // 3. 查询订单状态 wx.showLoading({ title: '支付成功,处理中...', mask: true }) const statusRes = await pollOrderStatus(orderId) wx.hideLoading() console.log('[Payment] ========== 支付流程完成 ==========') return { success: true, orderId, orderData: statusRes.data } } catch (error) { wx.hideLoading() console.error('[Payment] ========== 支付流程失败 ==========') throw error } } module.exports = { requestPayment, queryOrderStatus, pollOrderStatus, completePayment }