chore: H5 支付中转页输出完整微信失败诊断

This commit is contained in:
XingQue committed 2026-08-12 19:53:18 +08:00
1 parent f6cd0bc747
commit a92d71ff62
1 file changed
+188 -34
+188 -34
View File
@@ -3,43 +3,138 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>微信支付</title>
<title>微信支付诊断</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f7f7f7; }
.wrap { max-width: 420px; margin: 0 auto; padding: 56px 24px; text-align: center; }
.logo { width: 64px; height: 64px; margin: 0 auto 20px; border-radius: 14px; background: #07c160; color: #fff; font-size: 34px; line-height: 64px; font-weight: 700; }
.title { font-size: 20px; font-weight: 600; color: #111; margin-bottom: 10px; }
.sub { font-size: 14px; color: #888; line-height: 1.6; margin-bottom: 36px; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #111; color: #eee; }
.wrap { max-width: 640px; margin: 0 auto; padding: 24px 16px 48px; }
.title { font-size: 18px; font-weight: 700; margin-bottom: 8px; color: #fff; }
.sub { font-size: 14px; color: #aaa; line-height: 1.5; margin-bottom: 16px; word-break: break-all; }
.btn {
display: block; width: 100%; padding: 16px; font-size: 17px; font-weight: 600;
border: none; border-radius: 10px; background: #07c160; color: #fff;
display: block; width: 100%; padding: 14px; font-size: 16px; font-weight: 600;
border: none; border-radius: 10px; background: #07c160; color: #fff; margin-bottom: 10px;
}
.btn[disabled] { opacity: 0.6; }
.btn.gray { background: #333; }
.btn[disabled] { opacity: 0.55; }
pre {
margin-top: 12px; padding: 12px; background: #000; color: #0f0;
font-size: 11px; line-height: 1.45; white-space: pre-wrap; word-break: break-all;
border-radius: 8px; max-height: 70vh; overflow: auto;
border: 1px solid #333;
}
.ok { color: #07c160; }
.err { color: #ff5a5a; }
</style>
</head>
<body>
<div class="wrap">
<div class="logo">微</div>
<div class="title">微信支付</div>
<div class="sub" id="tip">正在调起支付...</div>
<button class="btn" type="button" id="payBtn">立即支付</button>
<div class="title">微信支付(诊断模式)</div>
<div class="sub" id="tip">初始化…</div>
<button class="btn" type="button" id="payBtn">立即支付 / 重试</button>
<button class="btn gray" type="button" id="copyBtn">复制下方全部诊断到剪贴板</button>
<pre id="log">loading…</pre>
</div>
<script>
(function () {
var STORAGE_KEY = 'wx_h5_jsapi_pay';
var tip = document.getElementById('tip');
var btn = document.getElementById('payBtn');
var copyBtn = document.getElementById('copyBtn');
var logEl = document.getElementById('log');
var lines = [];
var payload = null;
var pay = null;
var returnUrl = '';
function now() {
try { return new Date().toISOString(); } catch (e) { return String(Date.now()); }
}
function push() {
var args = Array.prototype.slice.call(arguments);
var s = '[' + now() + '] ' + args.map(function (x) {
if (x == null) return String(x);
if (typeof x === 'string') return x;
try { return JSON.stringify(x, null, 2); } catch (e) { return String(x); }
}).join(' ');
lines.push(s);
logEl.textContent = lines.join('\n\n');
try { console.log.apply(console, args); } catch (e) {}
}
function setTip(t, isErr) {
tip.innerHTML = t;
tip.className = 'sub ' + (isErr ? 'err' : '');
}
function safeGetStorage() {
try {
return sessionStorage.getItem(STORAGE_KEY);
} catch (e) {
push('sessionStorage.getItem 异常', String(e && e.message || e));
return null;
}
}
function dumpEnv(tag) {
var href = '';
var hash = '';
var origin = '';
var path = '';
var beforeHash = '';
try {
href = String(location.href || '');
hash = String(location.hash || '');
origin = String(location.origin || '');
path = String(location.pathname || '');
beforeHash = href.split('#')[0];
} catch (e) {
push('读 location 失败', String(e));
}
push('==== ' + tag + ' ====');
push('location.href', href);
push('微信校验用的 URL(# 前面)', beforeHash);
push('location.origin', origin);
push('location.pathname', path);
push('location.hash', hash);
push('document.URL', String(document.URL || ''));
push('document.referrer', String(document.referrer || '(空)'));
push('userAgent', String(navigator.userAgent || ''));
push('WeixinJSBridge', typeof WeixinJSBridge === 'undefined' ? 'undefined' : 'ready');
push('应匹配的授权目录前缀', 'https://www.abas.asia/hqhd/dingdan/');
push('beforeHash 是否以该前缀开头', beforeHash.indexOf('https://www.abas.asia/hqhd/dingdan/') === 0);
push('beforeHash 是否以 xqh5 开头', beforeHash.indexOf('https://www.abas.asia/xqh5/') === 0);
}
try {
payload = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || 'null');
var raw = safeGetStorage();
push('sessionStorage 原始长度', raw ? raw.length : 0);
push('sessionStorage 原始内容', raw || '(空,支付参数没带过来)');
payload = raw ? JSON.parse(raw) : null;
} catch (e) {
payload = null;
push('解析 sessionStorage JSON 失败', String(e && e.message || e));
}
var pay = payload && payload.pay ? payload.pay : null;
var returnUrl = (payload && payload.returnUrl) || '';
function setTip(t) { tip.innerText = t; }
pay = payload && payload.pay ? payload.pay : null;
returnUrl = (payload && payload.returnUrl) || '';
dumpEnv('页面加载');
push('payload.orderId', payload && payload.orderId);
push('payload.returnUrl', returnUrl || '(空)');
push('payload.ts', payload && payload.ts);
if (pay) {
push('pay.appId', pay.appId || pay.appid || '(空)');
push('pay.timeStamp', pay.timeStamp);
push('pay.nonceStr', pay.nonceStr);
push('pay.package', pay.package);
push('pay.signType', pay.signType || '(默认MD5)');
push('pay.paySign 前8位', String(pay.paySign || '').slice(0, 8));
push('pay.paySign 长度', String(pay.paySign || '').length);
push('完整 pay 对象', pay);
} else {
push('pay 对象', '(没有,无法调起支付)');
}
function goBack() {
try { sessionStorage.removeItem(STORAGE_KEY); } catch (e) {}
@@ -51,48 +146,107 @@
}
function invokePay() {
dumpEnv('点击/自动调起前');
if (!pay || !pay.timeStamp || !pay.nonceStr || !pay.package || !pay.paySign) {
setTip('支付参数丢失,请返回重新下单');
setTip('支付参数丢失,请返回重新下单(见下方日志)', true);
push('中止:pay 参数不完整', pay);
return;
}
if (typeof WeixinJSBridge === 'undefined') {
setTip('请在微信内打开');
setTip('WeixinJSBridge 不存在,请用微信内置浏览器打开', true);
push('中止:无 WeixinJSBridge');
return;
}
var appId = pay.appId || pay.appid || '';
if (!appId) {
setTip('缺少 appId,请返回重试');
setTip('缺少 appId', true);
push('中止:无 appId');
return;
}
btn.disabled = true;
WeixinJSBridge.invoke('getBrandWCPayRequest', {
var req = {
appId: appId,
timeStamp: String(pay.timeStamp),
nonceStr: pay.nonceStr,
package: pay.package,
signType: pay.signType || 'MD5',
paySign: pay.paySign
}, function (res) {
btn.disabled = false;
var msg = (res && res.err_msg) || '';
if (msg === 'get_brand_wcpay_request:ok') {
setTip('支付成功,正在返回...');
setTimeout(goBack, 400);
} else if (msg.indexOf('cancel') >= 0) {
setTip('已取消,可再次点击支付');
} else {
setTip(msg || '支付未完成,请重试');
}
};
push('即将 WeixinJSBridge.invoke getBrandWCPayRequest', {
appId: req.appId,
timeStamp: req.timeStamp,
nonceStr: req.nonceStr,
package: req.package,
signType: req.signType,
paySign_len: String(req.paySign || '').length
});
setTip('正在调起微信支付…');
btn.disabled = true;
try {
WeixinJSBridge.invoke('getBrandWCPayRequest', req, function (res) {
btn.disabled = false;
dumpEnv('微信回调时');
push('微信回调 res 完整对象', res);
push('微信回调 JSON', (function () {
try { return JSON.stringify(res); } catch (e) { return String(res); }
})());
var msg = (res && res.err_msg) || '';
var errDesc = (res && (res.err_desc || res.errMsg || res.err_msg)) || '';
push('err_msg', msg);
push('err_desc/其它', errDesc);
// 把所有可枚举字段打出来
try {
var keys = [];
for (var k in res) {
if (Object.prototype.hasOwnProperty.call(res, k)) {
keys.push(k + '=' + res[k]);
}
}
push('res 所有字段', keys.join(' | '));
} catch (e) {}
if (msg === 'get_brand_wcpay_request:ok') {
setTip('<span class="ok">支付成功,正在返回…</span>');
setTimeout(goBack, 500);
} else if (msg.indexOf('cancel') >= 0) {
setTip('已取消,可点按钮重试');
} else {
setTip('<span class="err">支付失败:' + (msg || '未知') + '</span><br/>把下方绿色日志整段复制发给技术', true);
push('!!!! 支付失败,请把本页全部绿色日志复制发送 !!!!');
push('若含「URL未注册」,对照:微信校验 URL 必须落在商户 JSAPI 授权目录前缀下');
push('当前校验 URL', String(location.href || '').split('#')[0]);
push('调起用的 appId', appId);
}
});
} catch (e) {
btn.disabled = false;
push('invoke 抛异常', String(e && e.message || e), e);
setTip('调起异常:' + String(e && e.message || e), true);
}
}
copyBtn.onclick = function () {
var text = logEl.textContent || '';
function ok() { setTip('已复制,去粘贴发给技术'); }
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(ok).catch(function () {
prompt('请手动全选复制:', text);
});
} else {
prompt('请手动全选复制:', text);
}
};
btn.onclick = invokePay;
if (!pay) {
setTip('支付参数丢失,请返回点单页重新下单');
setTip('支付参数丢失:请从星阙点单页重新点「确认支付」再跳进来', true);
push('提示:同域 sessionStorage 丢失常见于开了新窗口/清理缓存;请勿复制链接单独打开本页');
return;
}
setTip('参数已就绪,自动调起支付…');
if (typeof WeixinJSBridge === 'undefined') {
document.addEventListener('WeixinJSBridgeReady', invokePay, false);
push('等待 WeixinJSBridgeReady');
} else {
setTimeout(invokePay, 80);
}