mirror of
https://gitee.com/liuxioabin/fengketrade.git
synced 2026-04-17 21:03:17 +08:00
440 lines
8.6 KiB
JavaScript
440 lines
8.6 KiB
JavaScript
|
|
/**
|
|||
|
|
* 建行生活平台集成模块
|
|||
|
|
*
|
|||
|
|
* @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() {
|
|||
|
|
// 检测环境
|
|||
|
|
this.detectEnvironment();
|
|||
|
|
|
|||
|
|
// 如果在建行App内,初始化JSBridge
|
|||
|
|
if (this.isInCcbApp) {
|
|||
|
|
this.setupBridge();
|
|||
|
|
// 自动登录
|
|||
|
|
this.autoLogin();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('[CcbLife] 初始化完成, 是否在建行App内:', this.isInCcbApp);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检测运行环境
|
|||
|
|
*/
|
|||
|
|
detectEnvironment() {
|
|||
|
|
// #ifdef H5
|
|||
|
|
const ua = navigator.userAgent.toLowerCase();
|
|||
|
|
|
|||
|
|
// 检查User-Agent
|
|||
|
|
if (ua.indexOf('ccblife') > -1 || ua.indexOf('ccb') > -1) {
|
|||
|
|
this.isInCcbApp = true;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查URL参数
|
|||
|
|
const urlParams = this.getUrlParams();
|
|||
|
|
if (urlParams.ccbParamSJ || urlParams.from === 'ccblife') {
|
|||
|
|
this.isInCcbApp = true;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查JSBridge
|
|||
|
|
if (window.WebViewJavascriptBridge || window.mbspay) {
|
|||
|
|
this.isInCcbApp = true;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
// #endif
|
|||
|
|
|
|||
|
|
// #ifdef APP-PLUS
|
|||
|
|
// 在APP中也可能通过插件方式集成建行SDK
|
|||
|
|
// TODO: 检测建行SDK插件
|
|||
|
|
// #endif
|
|||
|
|
|
|||
|
|
// #ifdef MP-WEIXIN
|
|||
|
|
// 小程序环境不支持建行生活
|
|||
|
|
this.isInCcbApp = false;
|
|||
|
|
// #endif
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 设置JSBridge
|
|||
|
|
*/
|
|||
|
|
setupBridge() {
|
|||
|
|
// #ifdef H5
|
|||
|
|
const self = this;
|
|||
|
|
|
|||
|
|
// iOS WebViewJavascriptBridge
|
|||
|
|
if (window.WebViewJavascriptBridge) {
|
|||
|
|
self.bridge = window.WebViewJavascriptBridge;
|
|||
|
|
self.onBridgeReady();
|
|||
|
|
} else {
|
|||
|
|
document.addEventListener('WebViewJavascriptBridgeReady', function() {
|
|||
|
|
self.bridge = window.WebViewJavascriptBridge;
|
|||
|
|
self.onBridgeReady();
|
|||
|
|
}, false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Android 直接通过window对象
|
|||
|
|
if (window.mbspay && !self.bridge) {
|
|||
|
|
self.bridge = window.mbspay;
|
|||
|
|
self.onBridgeReady();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置超时检查
|
|||
|
|
setTimeout(() => {
|
|||
|
|
if (!self.isReady) {
|
|||
|
|
console.warn('[CcbLife] JSBridge 未就绪');
|
|||
|
|
}
|
|||
|
|
}, 3000);
|
|||
|
|
// #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;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取建行用户信息
|
|||
|
|
*/
|
|||
|
|
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: '获取用户信息失败'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 自动登录
|
|||
|
|
*/
|
|||
|
|
async autoLogin() {
|
|||
|
|
try {
|
|||
|
|
// 检查是否已登录
|
|||
|
|
const token = uni.getStorageSync('token');
|
|||
|
|
if (token) {
|
|||
|
|
console.log('[CcbLife] 用户已登录');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取用户信息
|
|||
|
|
const userResult = await this.getUserInfo();
|
|||
|
|
if (userResult.code !== 0) {
|
|||
|
|
throw new Error(userResult.msg);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 调用后端登录接口
|
|||
|
|
const loginResult = await ccbApi.autoLogin(userResult.data);
|
|||
|
|
|
|||
|
|
if (loginResult.code === 1) {
|
|||
|
|
// 保存Token和用户信息
|
|||
|
|
uni.setStorageSync('token', loginResult.data.token);
|
|||
|
|
uni.setStorageSync('userInfo', loginResult.data.userInfo);
|
|||
|
|
|
|||
|
|
// 更新Shopro用户状态
|
|||
|
|
sheep.$store('user').getInfo();
|
|||
|
|
|
|||
|
|
console.log('[CcbLife] 自动登录成功');
|
|||
|
|
|
|||
|
|
// 触发登录成功事件
|
|||
|
|
uni.$emit('ccb:login:success', loginResult.data);
|
|||
|
|
} else {
|
|||
|
|
throw new Error(loginResult.msg);
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('[CcbLife] 自动登录失败:', error);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 调起建行支付
|
|||
|
|
*/
|
|||
|
|
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
|
|||
|
|
// 区分iOS和Android
|
|||
|
|
if (this.isIOS()) {
|
|||
|
|
// iOS使用URL Scheme
|
|||
|
|
this.paymentForIOS(options, resolve, reject);
|
|||
|
|
} else {
|
|||
|
|
// Android使用JSBridge
|
|||
|
|
this.paymentForAndroid(options, resolve, reject);
|
|||
|
|
}
|
|||
|
|
// #endif
|
|||
|
|
|
|||
|
|
// #ifdef APP-PLUS
|
|||
|
|
// APP中调用原生插件
|
|||
|
|
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支付
|
|||
|
|
*/
|
|||
|
|
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
|
|||
|
|
// TODO: 调用建行SDK插件
|
|||
|
|
uni.showToast({
|
|||
|
|
title: 'APP支付暂未实现',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
reject({
|
|||
|
|
code: -1,
|
|||
|
|
msg: 'APP支付暂未实现'
|
|||
|
|
});
|
|||
|
|
// #endif
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 调用原生方法
|
|||
|
|
*/
|
|||
|
|
callNative(method, params, callback) {
|
|||
|
|
// #ifdef H5
|
|||
|
|
try {
|
|||
|
|
if (this.isIOS() && this.bridge && this.bridge.callHandler) {
|
|||
|
|
// iOS WebViewJavascriptBridge
|
|||
|
|
this.bridge.callHandler(method, params, callback);
|
|||
|
|
} else if (window.mbspay && window.mbspay[method]) {
|
|||
|
|
// Android直接调用
|
|||
|
|
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
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 判断是否iOS
|
|||
|
|
*/
|
|||
|
|
isIOS() {
|
|||
|
|
// #ifdef H5
|
|||
|
|
return /iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|||
|
|
// #endif
|
|||
|
|
// #ifndef H5
|
|||
|
|
return uni.getSystemInfoSync().platform === 'ios';
|
|||
|
|
// #endif
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 处理URL跳转登录
|
|||
|
|
*/
|
|||
|
|
async handleUrlLogin() {
|
|||
|
|
const params = this.getUrlParams();
|
|||
|
|
|
|||
|
|
// 如果有ccbParamSJ参数,说明是从建行跳转过来的
|
|||
|
|
if (params.ccbParamSJ) {
|
|||
|
|
try {
|
|||
|
|
// 调用后端解密并登录
|
|||
|
|
const result = await ccbApi.login(params);
|
|||
|
|
|
|||
|
|
if (result.code === 1) {
|
|||
|
|
// 保存Token和用户信息
|
|||
|
|
uni.setStorageSync('token', result.data.token);
|
|||
|
|
uni.setStorageSync('userInfo', result.data.user_info);
|
|||
|
|
|
|||
|
|
// 更新用户状态
|
|||
|
|
sheep.$store('user').getInfo();
|
|||
|
|
|
|||
|
|
// 跳转到指定页面
|
|||
|
|
const redirectUrl = result.data.redirect_url || '/pages/index/index';
|
|||
|
|
uni.reLaunch({
|
|||
|
|
url: redirectUrl
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: result.msg || '登录失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('[CcbLife] URL登录失败:', error);
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '登录失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 导出平台对象
|
|||
|
|
export default CcbLifePlatform;
|