/** * 建行生活平台集成模块 * * @author Billy * @date 2025-01-17 */ import sheep from '@/sheep'; import ccbApi from './api'; // 建行生活配置 const config = { // 是否开启调试 debug: true, // API基础路径 apiBaseUrl: '/addons/shopro', // 超时时间 timeout: 10000, }; // 建行生活平台对象 const CcbLifePlatform = { // 平台标识 platform: 'ccblife', // 平台名称 name: '建行生活', // 是否在建行生活环境中 isInCcbApp: false, // JSBridge 对象(已禁用) // bridge: null, // 就绪状态(已禁用) // isReady: false, // 就绪回调队列(已禁用) // readyCallbacks: [], /** * 初始化 */ init() { console.log('[CcbLife] ========== 建行生活平台初始化 =========='); // 检测环境 this.detectEnvironment(); // ⚠️ JSBridge 已禁用,目前不需要 // if (this.isInCcbApp) { // this.setupBridge(); // } console.log('[CcbLife] ✅ 初始化完成'); console.log('[CcbLife] 是否在建行App内:', this.isInCcbApp); console.log('[CcbLife] 平台标识:', this.platform); console.log('[CcbLife] ==========================================='); }, /** * 检测运行环境 */ detectEnvironment() { console.log('[CcbLife] 开始检测运行环境...'); // #ifdef H5 const ua = navigator.userAgent.toLowerCase(); console.log('[CcbLife] User-Agent:', ua); // 检查User-Agent if (ua.indexOf('ccblife') > -1 || ua.indexOf('ccb') > -1) { console.log('[CcbLife] ✅ 通过UA检测到建行App'); this.isInCcbApp = true; return; } // 检查URL参数 const urlParams = this.getUrlParams(); console.log('[CcbLife] URL参数:', urlParams); if (urlParams.ccbParamSJ || urlParams.from === 'ccblife') { console.log('[CcbLife] ✅ 通过URL参数检测到建行App'); console.log('[CcbLife] ccbParamSJ:', urlParams.ccbParamSJ ? '存在' : '不存在'); console.log('[CcbLife] from:', urlParams.from); this.isInCcbApp = true; return; } console.log('[CcbLife] ❌ 未检测到建行App环境'); // ⚠️ 已移除 JSBridge 检查(目前不需要) // if (window.WebViewJavascriptBridge || window.mbspay) { // this.isInCcbApp = true; // return; // } // #endif // #ifdef APP-PLUS // 在APP中也可能通过插件方式集成建行SDK // TODO: 检测建行SDK插件 console.log('[CcbLife] App环境,检测建行SDK插件...'); // #endif // #ifdef MP-WEIXIN // 小程序环境不支持建行生活 console.log('[CcbLife] 小程序环境,不支持建行生活'); this.isInCcbApp = false; // #endif }, /** * 设置JSBridge(已禁用 - 目前不需要) */ // setupBridge() { // // #ifdef H5 // const self = this; // let bridgeCheckCount = 0; // const MAX_BRIDGE_CHECK = 20; // const BRIDGE_CHECK_INTERVAL = 500; // // // iOS WebViewJavascriptBridge // if (window.WebViewJavascriptBridge) { // self.bridge = window.WebViewJavascriptBridge; // self.onBridgeReady(); // return; // } // // // 监听iOS Bridge就绪事件 // document.addEventListener('WebViewJavascriptBridgeReady', function() { // self.bridge = window.WebViewJavascriptBridge; // self.onBridgeReady(); // }, false); // // // Android 直接通过window对象 // if (window.mbspay) { // self.bridge = window.mbspay; // self.onBridgeReady(); // return; // } // // // 轮询检查Bridge是否已加载 // const bridgeCheckInterval = setInterval(() => { // bridgeCheckCount++; // // if (window.WebViewJavascriptBridge && !self.isReady) { // clearInterval(bridgeCheckInterval); // self.bridge = window.WebViewJavascriptBridge; // self.onBridgeReady(); // console.log(`[CcbLife] iOS Bridge 已就绪(检查${bridgeCheckCount}次)`); // return; // } // // if (window.mbspay && !self.isReady) { // clearInterval(bridgeCheckInterval); // self.bridge = window.mbspay; // self.onBridgeReady(); // console.log(`[CcbLife] Android Bridge 已就绪(检查${bridgeCheckCount}次)`); // return; // } // // if (bridgeCheckCount >= MAX_BRIDGE_CHECK) { // clearInterval(bridgeCheckInterval); // console.error('[CcbLife] ⚠️ JSBridge 初始化超时'); // } // }, BRIDGE_CHECK_INTERVAL); // // #endif // }, /** * Bridge就绪回调(已禁用) */ // onBridgeReady() { // this.isReady = true; // console.log('[CcbLife] JSBridge 已就绪'); // this.readyCallbacks.forEach(callback => callback()); // this.readyCallbacks = []; // }, /** * 等待Bridge就绪(已禁用) */ // ready(callback) { // if (this.isReady) { // callback(); // } else { // this.readyCallbacks.push(callback); // } // }, /** * 获取URL参数 */ getUrlParams() { const params = {}; // #ifdef H5 const search = window.location.search.substring(1); if (search) { const pairs = search.split('&'); pairs.forEach(pair => { const parts = pair.split('='); if (parts.length === 2) { params[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]); } }); } // #endif return params; }, /** * 获取建行用户信息(已禁用 - 依赖JSBridge) */ // async getUserInfo() { // return new Promise((resolve, reject) => { // if (!this.isInCcbApp) { // reject({ // code: -1, // msg: '不在建行生活App内' // }); // return; // } // // this.ready(() => { // this.callNative('getUserInfo', {}, (result) => { // if (result && result.userid) { // resolve({ // code: 0, // data: { // ccb_user_id: result.userid, // mobile: result.mobile || '', // nickname: result.nickname || '', // avatar: result.avatar || '' // } // }); // } else { // reject({ // code: -1, // msg: '获取用户信息失败' // }); // } // }); // }); // }); // }, /** * 自动登录(已禁用 - 依赖JSBridge的getUserInfo) * 目前改用App.vue中的checkCCBLogin统一处理URL参数登录 * @param {Number} retryCount - 重试次数 */ // async autoLogin(retryCount = 0) { // const MAX_RETRY = 2; // const RETRY_DELAY = 2000; // // try { // const token = uni.getStorageSync('token'); // if (token) { // console.log('[CcbLife] 用户已登录,跳过自动登录'); // try { // await sheep.$store('user').getInfo(); // console.log('[CcbLife] Token有效'); // return { success: true, cached: true }; // } catch (e) { // console.warn('[CcbLife] Token已失效,清除并重新登录'); // uni.removeStorageSync('token'); // uni.removeStorageSync('userInfo'); // } // } // // console.log('[CcbLife] 开始自动登录...'); // // // 获取用户信息 // const userResult = await this.getUserInfo(); // if (userResult.code !== 0) { // throw new Error(userResult.msg || '获取建行用户信息失败'); // } // // console.log('[CcbLife] 建行用户信息获取成功:', userResult.data); // // // 调用后端登录接口 // const loginResult = await ccbApi.autoLogin(userResult.data); // // if (loginResult.code === 1) { // sheep.$store('user').setToken(loginResult.data.token); // uni.setStorageSync('userInfo', loginResult.data.user_info || loginResult.data.userInfo); // console.log('[CcbLife] ✅ 自动登录成功'); // uni.$emit('ccb:login:success', loginResult.data); // uni.showToast({ // title: '欢迎回来', // icon: 'success', // duration: 1500 // }); // return { success: true, data: loginResult.data }; // } else { // throw new Error(loginResult.msg || '登录接口返回失败'); // } // } catch (error) { // console.error(`[CcbLife] ❌ 自动登录失败(第${retryCount + 1}次):`, error.message || error); // // if (retryCount < MAX_RETRY) { // console.log(`[CcbLife] ${RETRY_DELAY / 1000}秒后进行第${retryCount + 2}次尝试...`); // return new Promise((resolve) => { // setTimeout(() => { // resolve(this.autoLogin(retryCount + 1)); // }, RETRY_DELAY); // }); // } // // console.error('[CcbLife] 自动登录失败,已达最大重试次数'); // uni.showModal({ // title: '登录提示', // content: '自动登录失败,请点击登录按钮手动登录', // showCancel: false, // confirmText: '知道了' // }); // return { success: false, error: error.message || '未知错误' }; // } // }, /** * 调起建行支付(已禁用 - 依赖JSBridge) */ // async payment(options) { // return new Promise((resolve, reject) => { // if (!this.isInCcbApp) { // reject({ // code: -1, // msg: '不在建行生活App内' // }); // return; // } // // if (!options.payment_string) { // reject({ // code: -1, // msg: '缺少支付串参数' // }); // return; // } // // this.ready(() => { // // #ifdef H5 // if (this.isIOS()) { // this.paymentForIOS(options, resolve, reject); // } else { // this.paymentForAndroid(options, resolve, reject); // } // // #endif // // // #ifdef APP-PLUS // this.paymentForApp(options, resolve, reject); // // #endif // }); // }); // }, /** * iOS支付(已禁用) */ // paymentForIOS(options, resolve, reject) { // // #ifdef H5 // const paymentUrl = 'comccbpay://pay?' + options.payment_string; // window.location.href = paymentUrl; // setTimeout(() => { // resolve({ // code: 0, // msg: '已调起支付,请在建行App内完成支付' // }); // }, 1000); // // #endif // }, /** * Android支付(已禁用 - 依赖JSBridge) */ // paymentForAndroid(options, resolve, reject) { // this.callNative('payment', { // payment_string: options.payment_string // }, (result) => { // if (result && result.success) { // resolve({ // code: 0, // data: result // }); // } else { // reject({ // code: -1, // msg: result ? result.error : '支付失败' // }); // } // }); // }, /** * APP支付(已禁用) */ // paymentForApp(options, resolve, reject) { // // #ifdef APP-PLUS // uni.showToast({ // title: 'APP支付暂未实现', // icon: 'none' // }); // reject({ // code: -1, // msg: 'APP支付暂未实现' // }); // // #endif // }, /** * 调用原生方法(已禁用 - JSBridge相关) */ // callNative(method, params, callback) { // // #ifdef H5 // try { // if (this.isIOS() && this.bridge && this.bridge.callHandler) { // this.bridge.callHandler(method, params, callback); // } else if (window.mbspay && window.mbspay[method]) { // const result = window.mbspay[method](JSON.stringify(params)); // if (callback) { // callback(typeof result === 'string' ? JSON.parse(result) : result); // } // } else { // console.warn('[CcbLife] 原生方法不存在:', method); // if (callback) { // callback({ // success: false, // error: '原生方法不存在' // }); // } // } // } catch (e) { // console.error('[CcbLife] 调用原生方法失败:', e); // if (callback) { // callback({ // success: false, // error: e.message // }); // } // } // // #endif // }, /** * 调起建行收银台 * 根据建行App服务方接入文档,使用支付串调起收银台 * * @param {Object} options - 支付参数 * @param {String} options.payment_string - 支付串 * @param {Number} options.order_id - 订单ID * @param {String} options.order_sn - 订单号 * @returns {Promise} - 返回支付结果 */ async payment(options) { console.log('[建行收银台] ========== 调起建行收银台 =========='); console.log('[建行收银台] 参数:', { order_id: options.order_id, order_sn: options.order_sn, payment_string_length: options.payment_string?.length }); return new Promise((resolve, reject) => { if (!this.isInCcbApp) { console.error('[建行收银台] ❌ 不在建行生活App内'); reject({ code: -1, msg: '不在建行生活App内' }); return; } if (!options.payment_string) { console.error('[建行收银台] ❌ 缺少支付串参数'); reject({ code: -1, msg: '缺少支付串参数' }); return; } console.log('[建行收银台] 支付串前100字符:', options.payment_string.substring(0, 100)); try { // #ifdef H5 const isIOS = this.isIOS(); console.log('[建行收银台] 设备类型:', isIOS ? 'iOS' : 'Android'); if (isIOS) { // iOS: 使用 URL Scheme 调起支付 console.log('[建行收银台] iOS: 使用URL Scheme调起'); const payUrl = 'mbspay://direct?' + options.payment_string; console.log('[建行收银台] 跳转URL长度:', payUrl.length); window.location.href = payUrl; // iOS调起后认为成功(实际支付结果通过异步通知获取) setTimeout(() => { console.log('[建行收银台] ✅ iOS收银台已调起'); resolve({ code: 0, msg: '已调起建行支付' }); }, 500); } else { // Android: 调用 JSBridge 方法 console.log('[建行收银台] Android: 检查mbspay对象...'); console.log('[建行收银台] window.mbspay存在:', !!window.mbspay); console.log('[建行收银台] mbspay.directpay存在:', !!(window.mbspay && window.mbspay.directpay)); if (window.mbspay && typeof window.mbspay.directpay === 'function') { console.log('[建行收银台] 调用mbspay.directpay()...'); window.mbspay.directpay(options.payment_string); // Android调起后认为成功(实际支付结果通过异步通知获取) setTimeout(() => { console.log('[建行收银台] ✅ Android收银台已调起'); resolve({ code: 0, msg: '已调起建行支付' }); }, 500); } else { console.error('[建行收银台] ❌ mbspay对象不存在或directpay方法不存在'); console.error('[建行收银台] window对象键:', Object.keys(window).filter(k => k.toLowerCase().includes('pay') || k.toLowerCase().includes('ccb'))); reject({ code: -1, msg: '建行支付环境异常,请更新建行生活App' }); } } // #endif // #ifndef H5 console.error('[建行收银台] ❌ 非H5环境'); reject({ code: -1, msg: '仅支持H5环境' }); // #endif } catch (error) { console.error('[建行收银台] ❌ 调起失败:', error); console.error('[建行收银台] 错误类型:', error.name); console.error('[建行收银台] 错误信息:', error.message); console.error('[建行收银台] 错误堆栈:', error.stack); reject({ code: -1, msg: error.message || '调起支付失败' }); } console.log('[建行收银台] ==========================================='); }); }, /** * 判断是否iOS */ isIOS() { // #ifdef H5 return /iPhone|iPad|iPod/i.test(navigator.userAgent); // #endif // #ifndef H5 return uni.getSystemInfoSync().platform === 'ios'; // #endif }, /** * 处理URL跳转登录 * 建行App通过URL携带加密参数跳转到H5时调用 * * ⚠️ 注意:此方法已禁用,改用App.vue中的checkCCBLogin统一处理 * 原因:App.vue实现了更完善的逻辑(ccbParamSJ比较、用户切换、防重复调用) */ async handleUrlLogin() { console.log('[CcbLife] handleUrlLogin被调用,但已禁用(改用App.vue统一处理)'); return; // 🔒 禁用此方法,避免与App.vue重复调用 const params = this.getUrlParams(); // 如果有ccbParamSJ参数,说明是从建行跳转过来的 if (params.ccbParamSJ) { console.log('[CcbLife] 检测到URL登录参数,开始处理...'); // 显示加载提示 uni.showLoading({ title: '登录中...', mask: true }); try { // 检查是否已经登录过(避免重复登录) const existingToken = uni.getStorageSync('token'); const loginTimestamp = uni.getStorageSync('ccb_url_login_timestamp'); const now = Date.now(); // 如果5分钟内已经用同样的参数登录过,跳过 if (existingToken && loginTimestamp && (now - loginTimestamp) < 5 * 60 * 1000) { console.log('[CcbLife] 检测到最近已登录,跳过URL登录'); uni.hideLoading(); // 直接跳转 const redirectUrl = params.redirect_url || '/pages/index/index'; uni.reLaunch({ url: redirectUrl }); return; } // 调用后端解密并登录 const result = await ccbApi.login(params); uni.hideLoading(); if (result.code === 1) { // 🔑 使用setToken方法更新登录状态(关键!) sheep.$store('user').setToken(result.data.token); // 保存用户信息和登录时间戳 uni.setStorageSync('userInfo', result.data.user_info); uni.setStorageSync('ccb_url_login_timestamp', Date.now()); console.log('[CcbLife] ✅ URL登录成功'); // 触发登录成功事件 uni.$emit('ccb:login:success', result.data); // 显示欢迎提示 uni.showToast({ title: `欢迎${result.data.user_info?.nickname || ''}`, icon: 'success', duration: 1500 }); // 延迟跳转,让用户看到提示 setTimeout(() => { const redirectUrl = result.data.redirect_url || '/pages/index/index'; uni.reLaunch({ url: redirectUrl }); }, 1500); } else { throw new Error(result.msg || '登录失败'); } } catch (error) { console.error('[CcbLife] ❌ URL登录失败:', error); uni.hideLoading(); // 显示详细错误 uni.showModal({ title: 'URL登录失败', content: error.message || error.msg || '参数解密失败,请重新进入', showCancel: true, cancelText: '返回', confirmText: '重试', success: (res) => { if (res.confirm) { // 重试 this.handleUrlLogin(); } else { // 返回建行或首页 uni.reLaunch({ url: '/pages/index/index' }); } } }); } } } }; // 导出平台对象 export default CcbLifePlatform;