2025-10-28 14:13:13 +08:00

714 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 建行生活平台集成模块
*
* @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<Object>} - 返回支付结果
*/
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 Scheme5.3.1章节)
console.log('[建行收银台] iOS: 使用官方URL Scheme调起');
// 注意需要对支付串进行encodeURIComponent编码
const payUrl = 'ccbpayment://openCashier?params=' + encodeURIComponent(options.payment_string);
console.log('[建行收银台] 支付串原始长度:', options.payment_string.length);
console.log('[建行收银台] URL编码后总长度:', payUrl.length);
console.log('[建行收银台] URL前200字符:', payUrl.substring(0, 200));
// ⚠️ iOS URL Scheme 长度限制检查通常为2048字符
if (payUrl.length > 2048) {
console.error('[建行收银台] ❌ iOS URL长度超限:', payUrl.length, '> 2048');
reject({
code: -1,
msg: 'iOS URL长度超限请联系技术支持'
});
return;
}
try {
console.log('[建行收银台] 准备调起iOS收银台...');
window.location.href = payUrl;
console.log('[建行收银台] window.location.href已设置');
// iOS调起后认为成功实际支付结果通过异步通知获取
setTimeout(() => {
console.log('[建行收银台] ✅ iOS收银台已调起');
resolve({
code: 0,
msg: '已调起建行支付'
});
}, 500);
} catch (error) {
console.error('[建行收银台] ❌ iOS调起失败:', error);
reject({
code: -1,
msg: 'iOS调起失败: ' + error.message
});
}
} else {
// ⭐ Android: 使用官方文档的JSBridge方法5.3.1章节)
console.log('[建行收银台] Android: 检查CCBAndroid对象...');
console.log('[建行收银台] window.CCBAndroid存在:', !!window.CCBAndroid);
console.log('[建行收银台] CCBAndroid.callCashier存在:', !!(window.CCBAndroid && window.CCBAndroid.callCashier));
if (window.CCBAndroid && typeof window.CCBAndroid.callCashier === 'function') {
console.log('[建行收银台] 调用CCBAndroid.callCashier()...');
console.log('[建行收银台] 支付串长度:', options.payment_string.length);
// 调用官方文档指定的方法
window.CCBAndroid.callCashier(options.payment_string);
// Android调起后认为成功实际支付结果通过异步通知获取
setTimeout(() => {
console.log('[建行收银台] ✅ Android收银台已调起');
resolve({
code: 0,
msg: '已调起建行支付'
});
}, 500);
} else {
console.error('[建行收银台] ❌ CCBAndroid对象不存在或callCashier方法不存在');
console.error('[建行收银台] window对象包含的键:', Object.keys(window).filter(k => k.toLowerCase().includes('pay') || k.toLowerCase().includes('ccb') || k.toLowerCase().includes('android')));
// 降级尝试旧版本可能使用mbspay
if (window.mbspay && typeof window.mbspay.directpay === 'function') {
console.warn('[建行收银台] ⚠️ 降级使用mbspay.directpay()(旧版本)');
window.mbspay.directpay(options.payment_string);
setTimeout(() => {
console.log('[建行收银台] ✅ Android收银台已调起(降级方式)');
resolve({
code: 0,
msg: '已调起建行支付'
});
}, 500);
} else {
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;