|
@@ -228,6 +228,23 @@ onMounted(async () => {
|
|
|
paymentMethod.value = 'wechat';
|
|
paymentMethod.value = 'wechat';
|
|
|
// 处理 OAuth 回调
|
|
// 处理 OAuth 回调
|
|
|
await handleWechatOAuth();
|
|
await handleWechatOAuth();
|
|
|
|
|
+
|
|
|
|
|
+ // 🔧 如果刚从 OAuth 回来 + openid 已缓存,自动触发支付
|
|
|
|
|
+ try {
|
|
|
|
|
+ const justDidOAuth = sessionStorage.getItem('wx_oauth_just_done');
|
|
|
|
|
+ if (justDidOAuth === '1') {
|
|
|
|
|
+ sessionStorage.removeItem('wx_oauth_just_done');
|
|
|
|
|
+ const cachedOpenid = localStorage.getItem('wx_openid');
|
|
|
|
|
+ if (cachedOpenid) {
|
|
|
|
|
+ console.log('[WeChat Pay] 检测到 OAuth 刚完成,自动触发支付');
|
|
|
|
|
+ // 延迟执行,确保页面已完全渲染
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ openid.value = cachedOpenid;
|
|
|
|
|
+ handleWechatJsapiPay();
|
|
|
|
|
+ }, 500);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (_) {}
|
|
|
// 微信环境不预创建订单(JSAPI 支付时另外创建)
|
|
// 微信环境不预创建订单(JSAPI 支付时另外创建)
|
|
|
} else {
|
|
} else {
|
|
|
// 非微信环境:进入页面时直接创建订单
|
|
// 非微信环境:进入页面时直接创建订单
|
|
@@ -296,15 +313,23 @@ async function checkWechatOrderStatus(orderNo: string): Promise<string | null> {
|
|
|
// ==================== 微信 JSAPI 支付(公众号内) ====================
|
|
// ==================== 微信 JSAPI 支付(公众号内) ====================
|
|
|
// 使用 WeixinJSBridge.invoke 直接调起(官方 V3 推荐方式,无需 JSSDK 配置)
|
|
// 使用 WeixinJSBridge.invoke 直接调起(官方 V3 推荐方式,无需 JSSDK 配置)
|
|
|
// 参考: https://pay.weixin.qq.com/doc/v3/merchant/4012791870
|
|
// 参考: https://pay.weixin.qq.com/doc/v3/merchant/4012791870
|
|
|
|
|
+// 常见失败原因检查清单:
|
|
|
|
|
+// 1. timeStamp 必须是字符串(不是数字) - 后端已修复
|
|
|
|
|
+// 2. 微信商户平台需配置「支付授权目录」
|
|
|
|
|
+// 3. 公众号后台需配置「网页授权域名」
|
|
|
|
|
+// 4. 使用正确的公众号 appId(非小程序 appId)
|
|
|
|
|
+let pendingPayResult: any = null; // 用于 WeixinJSBridge 异步注入场景
|
|
|
|
|
+
|
|
|
async function handleWechatJsapiPay() {
|
|
async function handleWechatJsapiPay() {
|
|
|
// 如果没有 openid,需要先授权
|
|
// 如果没有 openid,需要先授权
|
|
|
if (!openid.value) {
|
|
if (!openid.value) {
|
|
|
|
|
+ console.log('[WeChat Pay] 无 openid,跳转 OAuth 授权');
|
|
|
await redirectToWechatOAuth();
|
|
await redirectToWechatOAuth();
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
paying.value = true;
|
|
paying.value = true;
|
|
|
- uni.showLoading({ title: '拉起支付...' });
|
|
|
|
|
|
|
+ uni.showLoading({ title: '创建订单...' });
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
// 创建 JSAPI 支付订单
|
|
// 创建 JSAPI 支付订单
|
|
@@ -324,135 +349,245 @@ async function handleWechatJsapiPay() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 注意:post 函数返回的是 response.data,即 { code, message, data: {...} }
|
|
// 注意:post 函数返回的是 response.data,即 { code, message, data: {...} }
|
|
|
- const responseData = result.data || result; // 兼容直接返回 data 的情况
|
|
|
|
|
|
|
+ const responseData = result.data || result;
|
|
|
orderInfo.value.orderNo = responseData.orderNo;
|
|
orderInfo.value.orderNo = responseData.orderNo;
|
|
|
|
|
+
|
|
|
|
|
+ // 🔧 保存待调用结果,用于 WeixinJSBridge 异步注入场景
|
|
|
|
|
+ pendingPayResult = responseData;
|
|
|
|
|
+
|
|
|
|
|
+ console.log('[WeChat Pay] 订单创建成功,准备调起支付');
|
|
|
|
|
+ console.log('[WeChat Pay] payParams 详情:', JSON.stringify({
|
|
|
|
|
+ appId: responseData.payParams?.appId,
|
|
|
|
|
+ timeStamp: `${responseData.payParams?.timeStamp} (type:${typeof responseData.payParams?.timeStamp})`,
|
|
|
|
|
+ nonceStr: responseData.payParams?.nonceStr,
|
|
|
|
|
+ package: responseData.payParams?.package,
|
|
|
|
|
+ signType: responseData.payParams?.signType,
|
|
|
|
|
+ paySign_length: responseData.payParams?.paySign?.length,
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
uni.hideLoading();
|
|
uni.hideLoading();
|
|
|
|
|
+ uni.showLoading({ title: '拉起支付...' });
|
|
|
|
|
|
|
|
- // 使用 WeixinJSBridge.invoke 调起支付(微信官方推荐方式)
|
|
|
|
|
- const currentResult = responseData; // 保存引用,避免闭包问题
|
|
|
|
|
-
|
|
|
|
|
- console.log('[WeChat Pay] 准备调起 WeixinJSBridge, payParams:', JSON.stringify(currentResult.payParams));
|
|
|
|
|
-
|
|
|
|
|
- // 检查 WeixinJSBridge 是否存在
|
|
|
|
|
- if (typeof (window as any).WeixinJSBridge === 'undefined') {
|
|
|
|
|
- console.error('[WeChat Pay] WeixinJSBridge 不存在,等待注入...');
|
|
|
|
|
-
|
|
|
|
|
- // 如果 WeixinJSBridge 还未注入,监听注入事件
|
|
|
|
|
- if (document.addEventListener) {
|
|
|
|
|
- document.addEventListener('WeixinJSBridgeReady', () => {
|
|
|
|
|
- console.log('[WeChat Pay] WeixinJSBridge 已注入,开始调起支付');
|
|
|
|
|
- invokeWeixinJSBridge(currentResult);
|
|
|
|
|
- }, false);
|
|
|
|
|
- } else if ((document as any).attachEvent) {
|
|
|
|
|
- (document as any).attachEvent('WeixinJSBridgeReady', () => {
|
|
|
|
|
- console.log('[WeChat Pay] WeixinJSBridge 已注入,开始调起支付');
|
|
|
|
|
- invokeWeixinJSBridge(currentResult);
|
|
|
|
|
- });
|
|
|
|
|
- (document as any).attachEvent('onWeixinJSBridgeReady', () => {
|
|
|
|
|
- console.log('[WeChat Pay] WeixinJSBridge 已注入,开始调起支付');
|
|
|
|
|
- invokeWeixinJSBridge(currentResult);
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 调用 WeixinJSBridge(带自动重试)
|
|
|
|
|
+ tryInvokeWeixinPay(responseData);
|
|
|
|
|
|
|
|
- // WeixinJSBridge 已存在,直接调起
|
|
|
|
|
- invokeWeixinJSBridge(currentResult);
|
|
|
|
|
} catch (error: any) {
|
|
} catch (error: any) {
|
|
|
paying.value = false;
|
|
paying.value = false;
|
|
|
uni.hideLoading();
|
|
uni.hideLoading();
|
|
|
console.error('[WeChat Pay] 创建JSAPI支付失败:', error);
|
|
console.error('[WeChat Pay] 创建JSAPI支付失败:', error);
|
|
|
- if (error.message && error.message.includes('openid')) {
|
|
|
|
|
|
|
+ const errMsg = error?.message || String(error);
|
|
|
|
|
+
|
|
|
|
|
+ if (errMsg.includes('openid')) {
|
|
|
localStorage.removeItem('wx_openid');
|
|
localStorage.removeItem('wx_openid');
|
|
|
openid.value = '';
|
|
openid.value = '';
|
|
|
await redirectToWechatOAuth();
|
|
await redirectToWechatOAuth();
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- uni.showToast({ title: error.message || '支付失败', icon: 'none' });
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 更友好的错误提示
|
|
|
|
|
+ let tip = '支付失败';
|
|
|
|
|
+ if (errMsg.includes('appid')) tip = '微信AppID配置错误';
|
|
|
|
|
+ else if (errMsg.includes('mchid')) tip = '商户号配置错误';
|
|
|
|
|
+ else if (errMsg.includes('PARAM_ERROR')) tip = '支付参数错误,请检查配置';
|
|
|
|
|
+ else if (errMsg.includes('SIGN_ERROR')) tip = '签名验证失败,请检查证书';
|
|
|
|
|
+ else if (errMsg.includes('NOT_ENOUGH')) tip = '商户余额不足';
|
|
|
|
|
+ else tip = errMsg.substring(0, 50);
|
|
|
|
|
+
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '支付失败',
|
|
|
|
|
+ content: `错误:${tip}\n\n请截图发给客服排查`,
|
|
|
|
|
+ showCancel: false,
|
|
|
|
|
+ confirmText: '知道了'
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 尝试调起微信支付(带 WeixinJSBridge 检测和重试)
|
|
|
|
|
+function tryInvokeWeixinPay(payResult: any, retryCount: number = 0) {
|
|
|
|
|
+ const MAX_RETRY = 30; // 最多等 3 秒(100ms × 30)
|
|
|
|
|
+ const wjsBridge = (window as any).WeixinJSBridge;
|
|
|
|
|
+
|
|
|
|
|
+ if (wjsBridge && typeof wjsBridge.invoke === 'function') {
|
|
|
|
|
+ // ✅ WeixinJSBridge 已就绪,直接调起
|
|
|
|
|
+ console.log('[WeChat Pay] WeixinJSBridge 就绪,调起支付');
|
|
|
|
|
+ invokeWeixinJSBridge(payResult);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // WeixinJSBridge 未就绪,等待注入
|
|
|
|
|
+ if (retryCount === 0) {
|
|
|
|
|
+ console.log('[WeChat Pay] WeixinJSBridge 未就绪,开始等待注入...');
|
|
|
|
|
+
|
|
|
|
|
+ // 方式 1:监听官方注入事件
|
|
|
|
|
+ if (document.addEventListener) {
|
|
|
|
|
+ document.addEventListener('WeixinJSBridgeReady', () => {
|
|
|
|
|
+ console.log('[WeChat Pay] 收到 WeixinJSBridgeReady 事件');
|
|
|
|
|
+ invokeWeixinJSBridge(pendingPayResult || payResult);
|
|
|
|
|
+ }, { once: true });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 方式 2:兼容旧版 IE
|
|
|
|
|
+ if ((document as any).attachEvent) {
|
|
|
|
|
+ (document as any).attachEvent('onWeixinJSBridgeReady', () => {
|
|
|
|
|
+ console.log('[WeChat Pay] 收到 onWeixinJSBridgeReady 事件');
|
|
|
|
|
+ invokeWeixinJSBridge(pendingPayResult || payResult);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 方式 3:轮询检测(兜底机制,解决事件丢失问题)
|
|
|
|
|
+ if (retryCount < MAX_RETRY) {
|
|
|
|
|
+ setTimeout(() => tryInvokeWeixinPay(payResult, retryCount + 1), 100);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 超时:WeixinJSBridge 始终未注入
|
|
|
|
|
+ paying.value = false;
|
|
|
|
|
+ uni.hideLoading();
|
|
|
|
|
+ console.error('[WeChat Pay] ❌ WeixinJSBridge 注入超时(等待3秒)');
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '环境异常',
|
|
|
|
|
+ content: '请在微信内打开此页面进行支付\n\n提示:\n1. 确认在微信浏览器中访问\n2. 确认域名已在公众号配置',
|
|
|
|
|
+ showCancel: false,
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 调用 WeixinJSBridge.invoke
|
|
// 调用 WeixinJSBridge.invoke
|
|
|
function invokeWeixinJSBridge(payResult: any) {
|
|
function invokeWeixinJSBridge(payResult: any) {
|
|
|
- const payParams = payResult.payParams;
|
|
|
|
|
-
|
|
|
|
|
|
|
+ const payParams = payResult?.payParams;
|
|
|
|
|
+
|
|
|
if (!payParams || !payParams.package) {
|
|
if (!payParams || !payParams.package) {
|
|
|
- console.error('[WeChat Pay] payParams 异常:', payParams);
|
|
|
|
|
- uni.showToast({ title: '支付参数异常', icon: 'none' });
|
|
|
|
|
|
|
+ console.error('[WeChat Pay] ❌ payParams 异常:', JSON.stringify(payResult));
|
|
|
paying.value = false;
|
|
paying.value = false;
|
|
|
|
|
+ uni.hideLoading();
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '参数异常',
|
|
|
|
|
+ content: '支付参数不完整,请重试',
|
|
|
|
|
+ showCancel: false,
|
|
|
|
|
+ });
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- console.log('[WeChat Pay] 调用 WeixinJSBridge.invoke, params:', JSON.stringify(payParams));
|
|
|
|
|
|
|
+ // 🔧 关键校验:确保所有字段都是字符串类型
|
|
|
|
|
+ const wxp = {
|
|
|
|
|
+ appId: String(payParams.appId || ''),
|
|
|
|
|
+ timeStamp: String(payParams.timeStamp || ''), // ⚠️ 必须是字符串!
|
|
|
|
|
+ nonceStr: String(payParams.nonceStr || ''),
|
|
|
|
|
+ package: String(payParams.package || ''), // prepay_id=wx...
|
|
|
|
|
+ signType: String(payParams.signType || 'RSA'),
|
|
|
|
|
+ paySign: String(payParams.paySign || ''),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 校验必填字段
|
|
|
|
|
+ if (!wxp.appId || !wxp.timeStamp || !wxp.nonceStr || !wxp.package || !wxp.paySign) {
|
|
|
|
|
+ console.error('[WeChat Pay] ❌ 支付参数不完整:', JSON.stringify(wxp));
|
|
|
|
|
+ paying.value = false;
|
|
|
|
|
+ uni.hideLoading();
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '参数不完整',
|
|
|
|
|
+ content: `缺少字段:${[
|
|
|
|
|
+ !wxp.appId && 'appId',
|
|
|
|
|
+ !wxp.timeStamp && 'timeStamp',
|
|
|
|
|
+ !wxp.nonceStr && 'nonceStr',
|
|
|
|
|
+ !wxp.package && 'package',
|
|
|
|
|
+ !wxp.paySign && 'paySign',
|
|
|
|
|
+ ].filter(Boolean).join(', ')}`,
|
|
|
|
|
+ showCancel: false,
|
|
|
|
|
+ });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.log('[WeChat Pay] 🚀 调用 WeixinJSBridge.invoke');
|
|
|
|
|
+ console.log('[WeChat Pay] 参数:', JSON.stringify({
|
|
|
|
|
+ ...wxp,
|
|
|
|
|
+ paySign: wxp.paySign.substring(0, 30) + '...',
|
|
|
|
|
+ package_length: wxp.package.length,
|
|
|
|
|
+ }));
|
|
|
|
|
|
|
|
(window as any).WeixinJSBridge.invoke(
|
|
(window as any).WeixinJSBridge.invoke(
|
|
|
'getBrandWCPayRequest',
|
|
'getBrandWCPayRequest',
|
|
|
- {
|
|
|
|
|
- appId: payParams.appId,
|
|
|
|
|
- timeStamp: payParams.timeStamp,
|
|
|
|
|
- nonceStr: payParams.nonceStr,
|
|
|
|
|
- package: payParams.package,
|
|
|
|
|
- signType: payParams.signType || 'RSA',
|
|
|
|
|
- paySign: payParams.paySign,
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ wxp,
|
|
|
async (res: any) => {
|
|
async (res: any) => {
|
|
|
- console.log('[WeChat Pay] WeixinJSBridge 回调:', JSON.stringify(res));
|
|
|
|
|
|
|
+ uni.hideLoading();
|
|
|
paying.value = false;
|
|
paying.value = false;
|
|
|
|
|
+ console.log('[WeChat Pay] 回调结果:', JSON.stringify(res));
|
|
|
|
|
|
|
|
- if (res.err_msg === 'get_brand_wcpay_request:ok') {
|
|
|
|
|
- // 支付成功(前端返回,需调用查询API确认)
|
|
|
|
|
- console.log('[WeChat Pay] 前端返回支付成功,查询订单状态...');
|
|
|
|
|
- const orderStatus = await checkWechatOrderStatus(payResult.orderNo);
|
|
|
|
|
-
|
|
|
|
|
- if (orderStatus === 'SUCCESS') {
|
|
|
|
|
- uni.showModal({
|
|
|
|
|
- title: '支付成功',
|
|
|
|
|
- content: productType.value === 'token-pack' ? '积分包购买成功!' : '恭喜您订阅成功!',
|
|
|
|
|
- showCancel: false,
|
|
|
|
|
- success: () => uni.switchTab({ url: '/pages/mine/index' })
|
|
|
|
|
- });
|
|
|
|
|
- } else {
|
|
|
|
|
- // 前端返回成功但订单未成功(延迟回调)
|
|
|
|
|
- uni.showToast({ title: '订单确认中,请稍后查看', icon: 'none' });
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // 支付失败或取消
|
|
|
|
|
- console.log('[WeChat Pay] 支付失败或取消:', res.err_msg);
|
|
|
|
|
-
|
|
|
|
|
- // 官方要求:必须调用查询订单API确认最终状态
|
|
|
|
|
- const orderStatus = await checkWechatOrderStatus(payResult.orderNo);
|
|
|
|
|
-
|
|
|
|
|
- if (orderStatus === 'SUCCESS') {
|
|
|
|
|
- // 桥回调失败但订单已支付(延迟回调)
|
|
|
|
|
- uni.showModal({
|
|
|
|
|
- title: '支付成功',
|
|
|
|
|
- content: productType.value === 'token-pack' ? '积分包购买成功!' : '恭喜您订阅成功!',
|
|
|
|
|
- showCancel: false,
|
|
|
|
|
- success: () => uni.switchTab({ url: '/pages/mine/index' })
|
|
|
|
|
- });
|
|
|
|
|
- } else {
|
|
|
|
|
- // 用户取消或真正失败
|
|
|
|
|
- const isCancel = res.err_msg === 'get_brand_wcpay_request:cancel';
|
|
|
|
|
- uni.showModal({
|
|
|
|
|
- title: isCancel ? '支付取消' : '支付失败',
|
|
|
|
|
- content: isCancel ? '您已取消支付' : `支付失败 (${res.err_msg || '未知错误'})`,
|
|
|
|
|
- showCancel: !isCancel,
|
|
|
|
|
- confirmText: '重新支付',
|
|
|
|
|
- cancelText: '取消',
|
|
|
|
|
- success: (modalRes: any) => {
|
|
|
|
|
- if (modalRes.confirm) {
|
|
|
|
|
- // 重新调起支付
|
|
|
|
|
- paying.value = false;
|
|
|
|
|
- handleWechatJsapiPay();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 处理回调
|
|
|
|
|
+ await handleWechatPayCallback(res, payResult.orderNo);
|
|
|
}
|
|
}
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 统一处理微信支付回调结果
|
|
|
|
|
+async function handleWechatPayCallback(res: any, orderNo: string) {
|
|
|
|
|
+ // err_msg 可能的值:
|
|
|
|
|
+ // get_brand_wcpay_request:ok - 支付成功
|
|
|
|
|
+ // get_brand_wcpay_request:cancel - 用户取消
|
|
|
|
|
+ // get_brand_wcpay_request:fail - 支付失败
|
|
|
|
|
+ const errMsg = res?.err_msg || res?.errMsg || '';
|
|
|
|
|
+
|
|
|
|
|
+ if (errMsg.indexOf(':ok') !== -1) {
|
|
|
|
|
+ // 前端返回成功,查询服务端确认
|
|
|
|
|
+ console.log('[WeChat Pay] ✅ 前端返回成功,查询服务端确认...');
|
|
|
|
|
+ const orderStatus = await checkWechatOrderStatus(orderNo);
|
|
|
|
|
+
|
|
|
|
|
+ if (orderStatus === 'SUCCESS') {
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '支付成功',
|
|
|
|
|
+ content: productType.value === 'token-pack' ? '积分包购买成功!' : '恭喜您订阅成功!',
|
|
|
|
|
+ showCancel: false,
|
|
|
|
|
+ success: () => uni.switchTab({ url: '/pages/mine/index' })
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 可能回调还没到服务端
|
|
|
|
|
+ uni.showToast({ title: '订单确认中,请稍后查看', icon: 'none', duration: 3000 });
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 支付未成功
|
|
|
|
|
+ console.log('[WeChat Pay] ❌ 支付未成功:', errMsg);
|
|
|
|
|
+
|
|
|
|
|
+ // 官方建议:无论如何都查询一次
|
|
|
|
|
+ const orderStatus = await checkWechatOrderStatus(orderNo);
|
|
|
|
|
+ if (orderStatus === 'SUCCESS') {
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '支付成功',
|
|
|
|
|
+ content: productType.value === 'token-pack' ? '积分包购买成功!' : '恭喜您订阅成功!',
|
|
|
|
|
+ showCancel: false,
|
|
|
|
|
+ success: () => uni.switchTab({ url: '/pages/mine/index' })
|
|
|
|
|
+ });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 真正失败 - 显示具体错误和解决建议
|
|
|
|
|
+ const isCancel = errMsg.indexOf(':cancel') !== -1;
|
|
|
|
|
+ if (isCancel) {
|
|
|
|
|
+ uni.showToast({ title: '已取消支付', icon: 'none' });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 🔍 构造详细错误诊断
|
|
|
|
|
+ let diagnosis = '';
|
|
|
|
|
+ if (errMsg.includes('no_balance')) diagnosis = '\n原因:商户余额不足';
|
|
|
|
|
+ else if (errMsg.includes('bank_error')) diagnosis = '\n原因:银行卡异常';
|
|
|
|
|
+ else if (errMsg.includes('not_sufficient_funds')) diagnosis = '\n原因:余额不足';
|
|
|
|
|
+ else if (errMsg.includes('param_error') || errMsg.includes('sign')) diagnosis = '\n原因:支付参数错误(请检查配置)';
|
|
|
|
|
+ else diagnosis = `\n错误码:${errMsg}`;
|
|
|
|
|
+
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '支付失败',
|
|
|
|
|
+ content: `支付未完成${diagnosis}\n\n如持续失败,请截图联系客服`,
|
|
|
|
|
+ showCancel: true,
|
|
|
|
|
+ confirmText: '重新支付',
|
|
|
|
|
+ cancelText: '关闭',
|
|
|
|
|
+ success: (modalRes: any) => {
|
|
|
|
|
+ if (modalRes.confirm) {
|
|
|
|
|
+ handleWechatJsapiPay();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
async function handlePay() {
|
|
async function handlePay() {
|
|
|
// 防重处理:2秒内禁止重复点击(官方要求按钮防抖)
|
|
// 防重处理:2秒内禁止重复点击(官方要求按钮防抖)
|
|
|
const now = Date.now();
|
|
const now = Date.now();
|