UNPKG

t-comm

Version:

专业、稳定、纯粹的工具库

294 lines (290 loc) 10.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); /** * 微信小程序/小游戏 ⇄ 腾讯 QQ 小程序 登录互通能力(核心实现) * * 文档参考: * - https://doc.weixin.qq.com/doc/w3_AQQARgb8AOsUIV9oM3SQTG0ERPg9D * - https://doc.weixin.qq.com/doc/w3_AEIAjwYaACMCNak2HLbrYSemshvHF * - https://git.woa.com/qzone-web-v2/mini-program * * 设计原则:零外部业务耦合 * - 不依赖 @tencent/pmd-network、@tencent/pmd-tools、oops 等业务包 * - 接口域名、post 方法、QQ 互联 AppId 均由调用方注入 * - 仅依赖 wx.* 全局 API(小程序/小游戏内置) */ // ============ 默认常量 ============ /** 腾讯 QQ 小程序的 appId(默认值,可被参数覆盖) */ var DEFAULT_QQ_MP_APP_ID = 'wx26da53d900421226'; /** QQ 登录页路径(默认值,可被参数覆盖) */ var DEFAULT_QQ_LOGIN_PATH = 'pagesLogin/pages/login/login'; /** QueryUserInfo 接口路径(默认值,可被参数覆盖) */ var DEFAULT_QUERY_USER_INFO_PATH = 'pmdtrpc.commcgi.user.user/QueryUserInfo'; // ============ 内部工具 ============ /** * 安全获取 wx 全局对象 */ function getWX() { // eslint-disable-next-line @typescript-eslint/no-explicit-any return typeof wx !== 'undefined' ? wx : globalThis.wx; } /** * 默认 storage:基于 wx.setStorageSync / wx.getStorageSync */ function getDefaultStorage() { var w = getWX(); return { set: function set(key, value) { var _a; (_a = w === null || w === void 0 ? void 0 : w.setStorageSync) === null || _a === void 0 ? void 0 : _a.call(w, key, value); }, get: function get(key) { var _a; var v = (_a = w === null || w === void 0 ? void 0 : w.getStorageSync) === null || _a === void 0 ? void 0 : _a.call(w, key); return v === '' || v == null ? undefined : v; }, remove: function remove(key) { var _a; (_a = w === null || w === void 0 ? void 0 : w.removeStorageSync) === null || _a === void 0 ? void 0 : _a.call(w, key); } }; } /** * 简易 query 拼接(小程序环境不支持 URLSearchParams) */ function buildQuery(params) { return Object.keys(params).filter(function (k) { return params[k] !== undefined && params[k] !== null && params[k] !== ''; }).map(function (k) { return "".concat(encodeURIComponent(k), "=").concat(encodeURIComponent("".concat(params[k]))); }).join('&'); } /** * 解析 host:支持字符串或函数 */ function resolveHost(host) { var h = typeof host === 'function' ? host() : host; // 去除末尾斜杠 return h.replace(/\/+$/, ''); } // ============ 核心 API ============ /** * 从 App.onShow 的 options 中提取 QQ 登录票据 * * @param options App.onShow 回调参数 * @param qqMpAppId 腾讯 QQ 小程序 appId(默认 wx26da53d900421226) * @returns QQ 登录票据信息,未登录或失败时返回 undefined */ function getQQTicketInfo(options) { var qqMpAppId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_QQ_MP_APP_ID; var _a; var referrerInfo = options === null || options === void 0 ? void 0 : options.referrerInfo; if (!referrerInfo || referrerInfo.appId !== qqMpAppId) { return; } var extraData = referrerInfo.extraData; if (!((_a = extraData === null || extraData === void 0 ? void 0 : extraData.tickets) === null || _a === void 0 ? void 0 : _a.length)) { return; } var retcode = extraData.retcode, tickets = extraData.tickets; var ticket = tickets[0]; // 票据获取失败 if (retcode !== 0 || !ticket) { return; } var name = ticket.name, qqTicket = ticket.ticket, qqAppid = ticket.appid, qqOpenid = ticket.openid, qqRefreshToken = ticket.refresh_token, qqExpiresIn = ticket.expires_in, qqIsNew = ticket.is_new; var qqAccessToken = name === 'access_token' ? qqTicket : ''; return { qqOpenid: qqOpenid, qqAccessToken: qqAccessToken, qqRefreshToken: qqRefreshToken, qqAppid: qqAppid, qqExpiresIn: qqExpiresIn, qqIsNew: qqIsNew }; } /** * 处理 QQ 登录返回:提取票据并写入 storage * * 通常在 App.onShow 中调用: * ```ts * onShow(options => { * const ticket = handleQQLoginOnShow(options); * if (ticket) { * // 用 ticket 调 queryQQLoginUserInfo * } * }); * ``` * * @param options App.onShow 回调参数 * @param config 配置(QQ 小程序 appId / 自定义 storage / storage key) * @returns 提取到的票据信息,若非 QQ 登录回调或失败则返回 undefined */ function handleQQLoginOnShow(options, config) { var _ref = config || {}, _ref$qqMpAppId = _ref.qqMpAppId, qqMpAppId = _ref$qqMpAppId === void 0 ? DEFAULT_QQ_MP_APP_ID : _ref$qqMpAppId, storage = _ref.storage, storageKey = _ref.storageKey; var ticketInfo = getQQTicketInfo(options, qqMpAppId); console.info('[qq-mp] get qq ticket info:', ticketInfo); if (!ticketInfo) { return; } try { var store = storage || getDefaultStorage(); store.set(storageKey, ticketInfo); } catch (e) { console.error('[qq-mp] save qq ticket failed:', e); } return ticketInfo; } /** * 从 storage 读取已保存的 QQ 登录票据 */ function readQQTicketInfo(config) { var _ref2 = config || {}, storage = _ref2.storage, storageKey = _ref2.storageKey; try { var store = storage || getDefaultStorage(); return store.get(storageKey); } catch (e) { console.error('[qq-mp] read qq ticket failed:', e); return; } } /** * 清除已保存的 QQ 登录票据 */ function clearQQTicketInfo(config) { var _a; var _ref3 = config || {}, storage = _ref3.storage, storageKey = _ref3.storageKey; try { var store = storage || getDefaultStorage(); (_a = store.remove) === null || _a === void 0 ? void 0 : _a.call(store, storageKey); } catch (e) { console.error('[qq-mp] clear qq ticket failed:', e); } } /** * 跳转腾讯 QQ 小程序进行 QQ 登录 * * 调用后 QQ 小程序登录完成,会通过 wx.navigateBackMiniProgram 返回, * 在本小程序的 App.onShow 中通过 referrerInfo.extraData 拿到票据。 * * @param options 必填 qqAppId(QQ 互联 AppId) */ function launchQQMP(options) { var qqAppId = options.qqAppId, _options$qqMpAppId = options.qqMpAppId, qqMpAppId = _options$qqMpAppId === void 0 ? DEFAULT_QQ_MP_APP_ID : _options$qqMpAppId, _options$loginPath = options.loginPath, loginPath = _options$loginPath === void 0 ? DEFAULT_QQ_LOGIN_PATH : _options$loginPath, _options$forcePwd = options.forcePwd, forcePwd = _options$forcePwd === void 0 ? false : _options$forcePwd, _options$retcodes = options.retcodes, retcodes = _options$retcodes === void 0 ? [] : _options$retcodes, envVersion = options.envVersion; return new Promise(function (resolve, reject) { var w = getWX(); if (!(w === null || w === void 0 ? void 0 : w.navigateToMiniProgram)) { reject(new Error('[qq-mp] wx.navigateToMiniProgram not available')); return; } var params = { appId: qqMpAppId, path: loginPath, extraData: { // 是否强制输入密码(关闭快速登录) force_pwd: forcePwd, // 透传给 QQ 小程序的状态码列表 retcodes: retcodes, // 票据列表(目前仅支持 access_token) tickets: [{ // 票据类型,目前仅支持 access_token,不区分大小写 name: 'access_token', // 需要登录的小程序的 QQ 互联 appid appid: "".concat(qqAppId) }], // 用于统计耗时 clickTime: Date.now() }, success: function success(res) { return resolve(res); }, fail: function fail(err) { return reject(err); } }; if (envVersion) { params.envVersion = envVersion; } w.navigateToMiniProgram(params); }); } /** * 使用 QQ 票据请求后台 QueryUserInfo 接口换取登录态 * * 接口域名、post 方法均由调用方注入,组件不耦合任何业务包。 * * @param options 必填 host / post / ticketInfo * @returns 后台返回的用户信息;票据无效或失败时返回 undefined */ function queryQQLoginUserInfo(options) { var host = options.host, post = options.post, ticketInfo = options.ticketInfo, _options$apiPath = options.apiPath, apiPath = _options$apiPath === void 0 ? DEFAULT_QUERY_USER_INFO_PATH : _options$apiPath, fallbackQQAppId = options.fallbackQQAppId, extraQuery = options.extraQuery, extraReqData = options.extraReqData; if (!(ticketInfo === null || ticketInfo === void 0 ? void 0 : ticketInfo.qqOpenid) || !ticketInfo.qqAccessToken) { console.warn('[qq-mp] queryQQLoginUserInfo skipped, no ticket'); return Promise.resolve(undefined); } if (!post) { console.error('[qq-mp] queryQQLoginUserInfo missing `post` function'); return Promise.resolve(undefined); } var queryParams = Object.assign({ openid: "".concat(ticketInfo.qqOpenid), appid: "".concat(ticketInfo.qqAppid || fallbackQQAppId || ''), access_token: "".concat(ticketInfo.qqAccessToken), acctype: 'qq' }, extraQuery || {}); var query = buildQuery(queryParams); var baseHost = resolveHost(host); var path = apiPath.replace(/^\/+/, ''); var url = "".concat(baseHost, "/").concat(path).concat(query ? "?".concat(query) : ''); console.info('[qq-mp] queryQQLoginUserInfo url:', url); return post({ url: url, reqData: extraReqData || {} }).then(function (res) { console.info('[qq-mp] queryQQLoginUserInfo res:', res); return res; })["catch"](function (err) { console.error('[qq-mp] queryQQLoginUserInfo failed:', err); return undefined; }); } exports.DEFAULT_QQ_LOGIN_PATH = DEFAULT_QQ_LOGIN_PATH; exports.DEFAULT_QQ_MP_APP_ID = DEFAULT_QQ_MP_APP_ID; exports.DEFAULT_QUERY_USER_INFO_PATH = DEFAULT_QUERY_USER_INFO_PATH; exports.clearQQTicketInfo = clearQQTicketInfo; exports.getQQTicketInfo = getQQTicketInfo; exports.handleQQLoginOnShow = handleQQLoginOnShow; exports.launchQQMP = launchQQMP; exports.queryQQLoginUserInfo = queryQQLoginUserInfo; exports.readQQTicketInfo = readQQTicketInfo;