|
|
@@ -1077,11 +1077,19 @@
|
|
|
/**
|
|
|
* 微信 JSSDK(服务号「麦丽恩严U店」)— 仅用于 wx.config 签名,Secret 只放后端
|
|
|
* 后端 GET {API_BASE}/wx/getWxConfig 须用:AppID wx412792c77f47babd + 服务号 AppSecret
|
|
|
+ *
|
|
|
+ * 签名 url 官方要求(invalid signature 排查):
|
|
|
+ * 1. 前端用 location.href.split('#')[0] 动态取当前页完整 URL(含 ? 参数,不含 #)
|
|
|
+ * 2. encodeURIComponent 后作为 url 参数传给后端签名
|
|
|
+ * 3. wx.config 的 appId/timestamp/nonceStr/signature 须与后端生成签名时完全一致(nonceStr 驼峰)
|
|
|
+ * 4. appId 须与后端取 jsapi_ticket 的公众号一致
|
|
|
+ * 5. 分享进入时微信会附加 from、isappinstalled 等参数,必须参与签名,不可用静态写死 URL
|
|
|
*/
|
|
|
var WECHAT_MP_APP_ID = 'wx412792c77f47babd';
|
|
|
/** 微信开放标签 wx-open-launch-app — 移动应用「U店在哪」AppID(不是服务号 ID) */
|
|
|
var WECHAT_OPEN_APP_ID = 'wxf5f1efe3a9f5012e';
|
|
|
var WECHAT_GET_WX_CONFIG_PATH = '/wx/getWxConfig';
|
|
|
+ /** 仅 file:// 或无法读取 location.href 时的联调兜底,微信内真实环境不应走到此处 */
|
|
|
var H5_PAGE_BASE_FALLBACK = 'https://test.ailien.shop/h5/HBuilderProjects/';
|
|
|
var WX_GET_CONFIG_SIGN_URL = H5_PAGE_BASE_FALLBACK + 'shareIndex.html';
|
|
|
var WECHAT_JS_SAFE_HOSTS = ['uat.ailien.shop', 'test.ailien.shop'];
|
|
|
@@ -1221,31 +1229,36 @@
|
|
|
return isWxForceDebug() || isWxPcAutoDebugHost();
|
|
|
}
|
|
|
|
|
|
- function getWxSignUrlFull() {
|
|
|
+ /**
|
|
|
+ * 参与签名的页面 url(官方):当前页完整 URL,含协议与 ? 后 GET 参数,不含 # 及后面。
|
|
|
+ * 须每次由页面 JS 动态取得后传给后端,不可用固定地址代替(分享链会带微信附加参数)。
|
|
|
+ */
|
|
|
+ function getWxSignPageUrlForApi() {
|
|
|
var forced = String(q('wxSignUrl') || '').trim();
|
|
|
if (forced) return forced.split('#')[0];
|
|
|
- if (location.origin && /^https?:/i.test(location.origin)) {
|
|
|
- return (
|
|
|
- location.origin +
|
|
|
- (location.pathname || '/h5/HBuilderProjects/shareIndex.html') +
|
|
|
- (location.search || '')
|
|
|
- );
|
|
|
- }
|
|
|
- var href = String(location.href || '').split('#')[0];
|
|
|
- if (href && href.indexOf('file:') !== 0) return href;
|
|
|
+ var pageUrl = String(location.href || '').split('#')[0];
|
|
|
+ if (pageUrl && /^https?:\/\//i.test(pageUrl)) return pageUrl;
|
|
|
return WX_GET_CONFIG_SIGN_URL;
|
|
|
}
|
|
|
|
|
|
+ function getWxSignUrlFull() {
|
|
|
+ return getWxSignPageUrlForApi();
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 仅 pathname,无 query;invalid signature 时若后端按「无参数」签名可 ?wxSignBaseOnly=1 重试 */
|
|
|
function getWxSignUrlBase() {
|
|
|
- if (location.origin && /^https?:/i.test(location.origin)) {
|
|
|
- return location.origin + (location.pathname || '/h5/HBuilderProjects/shareIndex.html');
|
|
|
+ var pageUrl = getWxSignPageUrlForApi();
|
|
|
+ try {
|
|
|
+ var u = new URL(pageUrl);
|
|
|
+ return u.origin + u.pathname;
|
|
|
+ } catch (eU) {
|
|
|
+ return WX_GET_CONFIG_SIGN_URL;
|
|
|
}
|
|
|
- return WX_GET_CONFIG_SIGN_URL;
|
|
|
}
|
|
|
|
|
|
function getWxConfigSignUrl() {
|
|
|
if (String(q('wxSignBaseOnly') || '') === '1') return getWxSignUrlBase();
|
|
|
- return getWxSignUrlFull();
|
|
|
+ return getWxSignPageUrlForApi();
|
|
|
}
|
|
|
|
|
|
function syncBrowserUrlForWxSign(signUrl) {
|
|
|
@@ -1258,11 +1271,14 @@
|
|
|
}
|
|
|
|
|
|
function buildWxGetConfigRequestUrl(signPageUrl) {
|
|
|
+ var urlForSign = String(signPageUrl || getWxSignPageUrlForApi())
|
|
|
+ .split('#')[0]
|
|
|
+ .trim();
|
|
|
return (
|
|
|
API_BASE.replace(/\/$/, '') +
|
|
|
WECHAT_GET_WX_CONFIG_PATH +
|
|
|
'?url=' +
|
|
|
- encodeURIComponent(signPageUrl || getWxConfigSignUrl())
|
|
|
+ encodeURIComponent(urlForSign)
|
|
|
);
|
|
|
}
|
|
|
|
|
|
@@ -1287,15 +1303,20 @@
|
|
|
if (!d || typeof d !== 'object') return null;
|
|
|
var appId = resolveWxConfigAppIdFromSignData(d);
|
|
|
var timestamp = d.timestamp != null ? d.timestamp : d.timeStamp;
|
|
|
- var nonceStr = d.nonceStr || d.noncestr || d.nonce;
|
|
|
+ /* 官方字段名为 nonceStr(S 大写),与 wx.config 保持一致 */
|
|
|
+ var nonceStr =
|
|
|
+ d.nonceStr != null && String(d.nonceStr) !== ''
|
|
|
+ ? d.nonceStr
|
|
|
+ : d.noncestr != null && String(d.noncestr) !== ''
|
|
|
+ ? d.noncestr
|
|
|
+ : d.nonce;
|
|
|
var signature = d.signature || d.sign;
|
|
|
- if (!appId || timestamp == null || !nonceStr || !signature) return null;
|
|
|
- var signedUrl =
|
|
|
- d.signUrl != null && String(d.signUrl).trim() !== ''
|
|
|
- ? String(d.signUrl).trim().split('#')[0]
|
|
|
- : signUrlUsed != null && String(signUrlUsed).trim() !== ''
|
|
|
- ? String(signUrlUsed).trim().split('#')[0]
|
|
|
- : '';
|
|
|
+ if (!appId || timestamp == null || nonceStr == null || nonceStr === '' || !signature) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ var signedUrl = String(signUrlUsed || '')
|
|
|
+ .split('#')[0]
|
|
|
+ .trim();
|
|
|
return {
|
|
|
appId: String(appId),
|
|
|
timestamp: Number(timestamp),
|
|
|
@@ -1322,10 +1343,10 @@
|
|
|
if (/invalid signature/i.test(errMsg)) {
|
|
|
return (
|
|
|
tip +
|
|
|
- ':签名无效。地址栏:' +
|
|
|
- String(location.href || '').split('#')[0] +
|
|
|
- ';签名 url:' +
|
|
|
- (signPageUrl || getWxSignUrlFull())
|
|
|
+ ':签名无效。请核对:①后端用与前端相同的 url 签名;②url=' +
|
|
|
+ (signPageUrl || getWxSignPageUrlForApi()) +
|
|
|
+ ';③nonceStr/timestamp 与接口返回一致;④appId=' +
|
|
|
+ WECHAT_MP_APP_ID
|
|
|
);
|
|
|
}
|
|
|
if (/require\s*subscribe/i.test(errMsg)) {
|
|
|
@@ -1353,8 +1374,11 @@
|
|
|
}
|
|
|
|
|
|
function fetchWeChatJssdkSign(signPageUrl) {
|
|
|
- signPageUrl = signPageUrl || getWxConfigSignUrl();
|
|
|
+ signPageUrl = String(signPageUrl || getWxConfigSignUrl())
|
|
|
+ .split('#')[0]
|
|
|
+ .trim();
|
|
|
var url = buildWxGetConfigRequestUrl(signPageUrl);
|
|
|
+ console.log('[wx] 签名 url(传给后端)=', signPageUrl);
|
|
|
console.log('[wx] GET getWxConfig →', url);
|
|
|
return fetch(url, {
|
|
|
method: 'GET',
|
|
|
@@ -1403,40 +1427,31 @@
|
|
|
}
|
|
|
|
|
|
function applyWxConfig(sign, signPageUrl) {
|
|
|
- var urlForWx =
|
|
|
- sign.signUrl && String(sign.signUrl).trim() !== ''
|
|
|
- ? String(sign.signUrl).trim()
|
|
|
- : signPageUrl;
|
|
|
+ var urlForWx = String(signPageUrl || sign.signUrl || getWxSignPageUrlForApi())
|
|
|
+ .split('#')[0]
|
|
|
+ .trim();
|
|
|
syncBrowserUrlForWxSign(urlForWx);
|
|
|
+ var wxConfigParams = {
|
|
|
+ debug: isWxDebugOn(),
|
|
|
+ appId: String(sign.appId),
|
|
|
+ timestamp: sign.timestamp,
|
|
|
+ nonceStr: String(sign.nonceStr),
|
|
|
+ signature: String(sign.signature),
|
|
|
+ jsApiList: [],
|
|
|
+ openTagList: ['wx-open-launch-app']
|
|
|
+ };
|
|
|
return new Promise(function (resolve) {
|
|
|
- try {
|
|
|
- alert(
|
|
|
- 'wx.config 参数\n' +
|
|
|
- JSON.stringify(
|
|
|
- {
|
|
|
- debug: false,
|
|
|
- appId: sign.appId,
|
|
|
- timestamp: sign.timestamp,
|
|
|
- nonceStr: sign.nonceStr,
|
|
|
- signature: sign.signature,
|
|
|
- jsApiList: [],
|
|
|
- openTagList: ['wx-open-launch-app'],
|
|
|
- signUrl: urlForWx
|
|
|
- },
|
|
|
- null,
|
|
|
- 2
|
|
|
- )
|
|
|
- );
|
|
|
- } catch (alertErr) {}
|
|
|
- wx.config({
|
|
|
- debug:false,
|
|
|
- appId: sign.appId,
|
|
|
- timestamp: sign.timestamp,
|
|
|
- nonceStr: sign.nonceStr,
|
|
|
- signature: sign.signature,
|
|
|
- jsApiList: [],
|
|
|
- openTagList: ['wx-open-launch-app']
|
|
|
- });
|
|
|
+ if (isWxDebugOn()) {
|
|
|
+ try {
|
|
|
+ window.alert(
|
|
|
+ '签名 url(应与地址栏一致,不含#):\n' +
|
|
|
+ urlForWx +
|
|
|
+ '\n\nwx.config:\n' +
|
|
|
+ JSON.stringify(wxConfigParams, null, 2)
|
|
|
+ );
|
|
|
+ } catch (alertErr) {}
|
|
|
+ }
|
|
|
+ wx.config(wxConfigParams);
|
|
|
wx.ready(function () {
|
|
|
weChatJssdkConfigured = true;
|
|
|
document.body.classList.add('wx-jssdk-ready');
|
|
|
@@ -1537,6 +1552,11 @@
|
|
|
wxConfigSignRetriedBaseUrl = false;
|
|
|
wxInitLastError = '';
|
|
|
var signPageUrl = getWxConfigSignUrl();
|
|
|
+ if (isWxDebugOn()) {
|
|
|
+ try {
|
|
|
+ window.alert('签名 url(getWxConfig):\n' + signPageUrl);
|
|
|
+ } catch (eDbg) {}
|
|
|
+ }
|
|
|
|
|
|
bindWeChatLaunchTagEvents();
|
|
|
wxJssdkInitPromise = fetchWeChatJssdkSign(signPageUrl)
|