/* global wx, getCurrentPages */ function parsePayParams(rawValue) { try { const params = JSON.parse(decodeURIComponent(String(rawValue || ''))); if (!params || typeof params !== 'object') { return null; } return params; } catch (error) { console.error('[wechat-pay] parse params failed', error); return null; } } function isVirtualPaymentParams(payParams) { return ( typeof payParams.mode === 'string' && typeof payParams.signData === 'string' && typeof payParams.paySig === 'string' && typeof payParams.signature === 'string' ); } function safeCompareVersion(left, right) { const leftParts = String(left || '') .split('.') .map((part) => Number(part) || 0); const rightParts = String(right || '') .split('.') .map((part) => Number(part) || 0); const length = Math.max(leftParts.length, rightParts.length); for (let index = 0; index < length; index += 1) { const leftValue = leftParts[index] || 0; const rightValue = rightParts[index] || 0; if (leftValue > rightValue) { return 1; } if (leftValue < rightValue) { return -1; } } return 0; } function canUseVirtualPayment() { if (typeof wx === 'undefined') { return false; } if (typeof wx.canIUse === 'function' && wx.canIUse('requestVirtualPayment')) { return true; } const version = typeof wx.getSystemInfoSync === 'function' ? wx.getSystemInfoSync()?.SDKVersion || '' : ''; return safeCompareVersion(version, '2.19.2') >= 0; } function resolvePayStatus(error) { const errMsg = error && error.errMsg ? error.errMsg : ''; const errCode = Number(error && error.errCode); return errCode === -2 || /cancel/i.test(errMsg) ? 'cancel' : 'fail'; } function normalizePayError(error) { if (!error) { return ''; } if (typeof error === 'string') { return error; } try { return JSON.stringify({ errCode: error.errCode, errMsg: error.errMsg, }); } catch (_error) { return String(error.errMsg || error); } } function requestOrdinaryPayment(payParams) { return new Promise((resolve) => { wx.requestPayment({ timeStamp: String(payParams.timeStamp || ''), nonceStr: String(payParams.nonceStr || ''), package: String(payParams.package || ''), signType: payParams.signType || 'RSA', paySign: String(payParams.paySign || ''), success() { resolve({ status: 'success', errorMessage: '' }); }, fail(error) { resolve({ status: resolvePayStatus(error), errorMessage: normalizePayError(error), }); }, }); }); } function requestVirtualPayment(payParams) { return new Promise((resolve) => { if (!canUseVirtualPayment() || typeof wx.requestVirtualPayment !== 'function') { console.error('[wechat-pay] requestVirtualPayment unavailable', { canUseVirtualPayment: canUseVirtualPayment(), hasRequestVirtualPayment: typeof wx.requestVirtualPayment === 'function', }); resolve({ status: 'fail', errorMessage: '当前微信基础库不支持 requestVirtualPayment', }); return; } wx.requestVirtualPayment({ mode: String(payParams.mode || ''), signData: String(payParams.signData || ''), paySig: String(payParams.paySig || ''), signature: String(payParams.signature || ''), success() { resolve({ status: 'success', errorMessage: '' }); }, fail(error) { console.error('[wechat-pay] requestVirtualPayment failed', error); resolve({ status: resolvePayStatus(error), errorMessage: normalizePayError(error), }); }, }); }); } function requestWechatPayment(payParams) { if (isVirtualPaymentParams(payParams)) { return requestVirtualPayment(payParams); } return requestOrdinaryPayment(payParams); } const PAY_RESULT_STORAGE_KEY = 'genarrative:wechat-pay-result'; function appendPayResult(url, result) { const hashIndex = String(url || '').indexOf('#'); const baseUrl = hashIndex >= 0 ? String(url).slice(0, hashIndex) : String(url || ''); const rawHash = hashIndex >= 0 ? String(url).slice(hashIndex + 1) : ''; const nextHash = rawHash .split('&') .filter((part) => part && !part.startsWith('wx_pay_result=')) .concat(`wx_pay_result=${encodeURIComponent(result)}`) .join('&'); return `${baseUrl}#${nextHash}`; } function buildPayResultValue(requestId, orderId, payResult) { const segments = [requestId, payResult.status, orderId || '']; if (payResult.errorMessage) { segments.push(encodeURIComponent(payResult.errorMessage)); } return segments.join(':'); } function notifyPreviousWebView(requestId, orderId, payResult) { const result = buildPayResultValue(requestId, orderId, payResult); wx.setStorageSync(PAY_RESULT_STORAGE_KEY, result); const pages = getCurrentPages(); const previousPage = pages.length >= 2 ? pages[pages.length - 2] : null; if (previousPage && typeof previousPage.setData === 'function') { previousPage.setData({ webViewUrl: appendPayResult(previousPage.data.webViewUrl, result), }); } } function createWechatPayPage(pageContext) { return { data: { title: '正在拉起支付', errorMessage: '', }, async onLoad(query) { const requestId = String(query.requestId || ''); const orderId = String(query.orderId || ''); const payParams = parsePayParams(query.payParams); if (!requestId || !payParams) { const page = pageContext ?? this; page.setData({ title: '支付失败', errorMessage: '缺少支付参数。', }); return; } const payResult = await requestWechatPayment(payParams); notifyPreviousWebView(requestId, orderId, payResult); wx.navigateBack(); }, handleBack() { wx.navigateBack(); }, }; } module.exports = { canUseVirtualPayment, PAY_RESULT_STORAGE_KEY, appendPayResult, buildPayResultValue, createWechatPayPage, normalizePayError, parsePayParams, safeCompareVersion, requestWechatPayment, requestVirtualPayment, };