@yorkjs/wechat
Version:
process wechat auth/pay/share tool
275 lines (264 loc) • 10 kB
JavaScript
/**
* wechat.js v2.0.0
* (c) 2021-2022 shushu2013
* Released under the MIT License.
*/
import * as Url from '@yorkjs/url';
const STORAGE_PREFIX = '@@wechat@@';
const AUTH_PAGE_UNLOAD_TIMESTAMP = 'auth_page_unload_timestamp';
let globalConfig;
function init$1(config) {
globalConfig = config;
// 记录发起授权时,页面离开时的时间戳(微信授权,可能会弹出授权提示框)
// 用作微信授权后,重定向回来判断时间是否过期
config.onPageLeave(setAuthPageUnloadTimestamp);
}
function getGlobalConfig() {
return globalConfig;
}
function getStorage(key) {
return globalConfig.storage.get(`${STORAGE_PREFIX}_${key}`);
}
function setStorage(key, value) {
globalConfig.storage.set(`${STORAGE_PREFIX}_${key}`, value);
}
function removeStorage(key) {
globalConfig.storage.remove(`${STORAGE_PREFIX}_${key}`);
}
const userAgent = navigator.userAgent;
/iphone|ipad/i.test(userAgent);
const isAndroid = /android/i.test(userAgent);
let isAuthing = false;
function setAuthPageUnloadTimestamp() {
if (isAuthing) {
setStorage(AUTH_PAGE_UNLOAD_TIMESTAMP, getGlobalConfig().getTimestamp());
}
}
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Before_Develop/Official_Accounts/official_account_website_authorization.html
// 应用授权作用域
// snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),
// snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)
// const SCOPES = ['snsapi_base', 'snsapi_userinfo']
function auth(state, url, scope, appId, componentAppId) {
// 注意: 微信参数顺序和大小写有要求
const uri = encodeURIComponent(url);
let queryStr = `appid=${appId}&redirect_uri=${uri}&response_type=code&scope=${scope}&state=${state}`;
if (componentAppId) {
queryStr += `&component_appid=${componentAppId}`;
}
isAuthing = true;
location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?${queryStr}#wechat_redirect`;
}
function endAuth$1(biz) {
isAuthing = false;
removeStorage(AUTH_PAGE_UNLOAD_TIMESTAMP);
}
function normalizeShareUrl$1(url, callback) {
return normalizeUrl$1(url, function (urlObj) {
if (callback) {
callback(urlObj);
}
// Bugfix: https://zhuanlan.zhihu.com/p/45068949
// 安卓系统,把 /# 变成 /?#,防止微信分享时,把链接 # 后边的字符串都截掉了,导致分享出去的链接不对
const hasSearch = urlObj.search && urlObj.search !== '?';
if (isAndroid && !hasSearch && urlObj.hash) {
urlObj.hash = `?${urlObj.hash}`;
}
});
}
function normalizeUrl$1(url, callback) {
// 去除微信授权相关 state 和 code
const urlObj = Url.parseUrl(url);
if (!urlObj) {
return url;
}
if (urlObj.search) {
const queryObj = Url.parseQuery(urlObj.search.slice(1));
if (queryObj.state && queryObj.code) {
delete queryObj.state;
delete queryObj.code;
const searchStr = Url.stringifyQuery(queryObj);
urlObj.search = searchStr ? `?${searchStr}` : '';
}
}
if (callback) {
callback(urlObj);
}
return Url.stringifyUrl(urlObj);
}
// 弹出授权页面
function startAuth$1(biz, url, appId, componentAppId) {
const state = encodeURIComponent(biz);
const scope = 'snsapi_userinfo';
auth(state, url, scope, appId, componentAppId);
}
// 静默授权,不弹出授权页面
function startSilentAuth$1(biz, url, appId, componentAppId) {
const state = encodeURIComponent(biz);
const scope = 'snsapi_base';
auth(state, url, scope, appId, componentAppId);
}
const stateMap = {};
function isValidTimestamp(expireTimestamp) {
const nowTimestamp = getGlobalConfig().getTimestamp();
const authPageUnloadTimestamp = getStorage(AUTH_PAGE_UNLOAD_TIMESTAMP);
if (authPageUnloadTimestamp) {
return nowTimestamp - authPageUnloadTimestamp < expireTimestamp;
}
return false;
}
function checkQueryState(query, checkRule) {
const { expireSeconds, once } = checkRule;
const { state } = query;
if (state) {
// 1 时间是否过期
if (expireSeconds && !isValidTimestamp(expireSeconds * 1000)) {
return false;
}
// 2 是否读取过了
if (once && stateMap[state]) {
return false;
}
return true;
}
return false;
}
// 解析 query
function parseAuthQuery(url) {
const query = {};
const urlObj = Url.parseUrl(url);
if (!urlObj.search) {
return query;
}
const queryObj = Url.parseQuery(urlObj.search.slice(1));
if (queryObj.state && queryObj.code) {
query.code = queryObj.code;
query.state = queryObj.state;
}
return query;
}
// 读取 query
function getAuthQuery$1(url, checkRule) {
const query = parseAuthQuery(url);
const { state, code } = query;
if (state && code) {
// 1. 不需要校验
if (!checkRule) {
return query;
}
const { once } = checkRule;
// 2. 校验 query 自身参数
if (!checkQueryState(query, checkRule)) {
return {};
}
// 3. 在当前页面生命周期生效,只读一次,记录 stateMap
if (once) {
stateMap[state] = true;
}
}
return query;
}
function share$1(wx, signature, jsApiList, shareInfo, debug = false) {
return new Promise(function (resolve, reject) {
// config 信息验证后会执行 ready 方法,所有接口调用都必须在 config 接口获得结果之后,
// config 是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,
// 则须把相关接口放在 ready 函数中调用来确保正确执行
wx.config({
debug,
appId: signature.appId,
timestamp: signature.timestamp,
nonceStr: signature.nonceStr,
signature: signature.signature,
jsApiList
});
wx.ready(function () {
wx.checkJsApi({
jsApiList,
success(res) {
const result = res.checkResult;
if (result.updateAppMessageShareData) {
wx.updateAppMessageShareData({
title: shareInfo.title,
desc: shareInfo.content,
link: shareInfo.url,
imgUrl: shareInfo.image
});
}
if (result.updateTimelineShareData) {
wx.updateTimelineShareData({
title: shareInfo.title,
link: shareInfo.url,
imgUrl: shareInfo.image,
});
}
if (result.onMenuShareWeibo) {
wx.onMenuShareWeibo({
title: shareInfo.title,
desc: shareInfo.content,
link: shareInfo.url,
imgUrl: shareInfo.image
});
}
// 旧版,用来适配Android
if (result.onMenuShareAppMessage) {
// 即将废弃
wx.onMenuShareAppMessage({
title: shareInfo.title,
desc: shareInfo.content,
link: shareInfo.url,
imgUrl: shareInfo.image
});
}
if (result.onMenuShareTimeline) {
// 即将废弃
wx.onMenuShareTimeline({
title: shareInfo.title,
link: shareInfo.url,
imgUrl: shareInfo.image,
});
}
}
});
});
wx.error(function (res) {
// config 信息验证失败会执行 error 函数,如签名过期导致验证失败,
// 具体错误信息可以打开 config 的 debug 模式查看,
// 也可以在返回的 res 参数中查看,对于 SPA 可以在这里更新签名
reject(res);
});
resolve();
});
}
function pay$1(params) {
return new Promise(function (resolve, reject) {
if (window.WeixinJSBridge) {
window.WeixinJSBridge.invoke('getBrandWCPayRequest', params, function (res) {
const { err_msg } = res;
if (err_msg == 'get_brand_wcpay_request:ok') {
resolve(res);
}
else if (err_msg !== 'get_brand_wcpay_request:cancel') {
reject(err_msg || '支付失败');
}
});
}
else {
reject('缺少微信环境支持');
}
});
}
const init = init$1;
const startAuth = startAuth$1;
const startSilentAuth = startSilentAuth$1;
const endAuth = endAuth$1;
const getAuthQuery = getAuthQuery$1;
const share = share$1;
const pay = pay$1;
const normalizeUrl = normalizeUrl$1;
const normalizeShareUrl = normalizeShareUrl$1;
/**
* 版本
*/
const version = "2.0.0";
export { endAuth, getAuthQuery, init, normalizeShareUrl, normalizeUrl, pay, share, startAuth, startSilentAuth, version };
//# sourceMappingURL=wechat.esm.js.map