mirror of
https://gitee.com/liuxioabin/fengketrade.git
synced 2026-04-17 12:57:32 +08:00
437 lines
13 KiB
JavaScript
437 lines
13 KiB
JavaScript
/**
|
|
* 建行生活 JSBridge 集成库
|
|
*
|
|
* 功能说明:
|
|
* 1. 检测是否在建行生活 App 内运行
|
|
* 2. 获取建行用户信息
|
|
* 3. 调起建行支付
|
|
* 4. 处理 URL 参数解密
|
|
*
|
|
* @author Billy
|
|
* @date 2025-01-17
|
|
*/
|
|
|
|
(function(window) {
|
|
'use strict';
|
|
|
|
// 建行生活 JSBridge 对象
|
|
var CcbLifeBridge = {
|
|
// 配置信息
|
|
config: {
|
|
// API 基础地址
|
|
apiBaseUrl: '/addons/shopro',
|
|
// 调试模式
|
|
debug: false,
|
|
// 超时时间(毫秒)
|
|
timeout: 10000,
|
|
// 重试次数
|
|
retryTimes: 3
|
|
},
|
|
|
|
// 初始化状态
|
|
isReady: false,
|
|
readyCallbacks: [],
|
|
|
|
/**
|
|
* 初始化
|
|
* @param {Object} options 配置选项
|
|
*/
|
|
init: function(options) {
|
|
// 合并配置
|
|
if (options) {
|
|
Object.assign(this.config, options);
|
|
}
|
|
|
|
// 监听 JSBridge 就绪事件
|
|
this.setupBridge();
|
|
|
|
// 自动登录(如果在建行 App 内)
|
|
if (this.isInCcbApp()) {
|
|
this.autoLogin();
|
|
}
|
|
|
|
this.log('CcbLifeBridge 初始化完成');
|
|
},
|
|
|
|
/**
|
|
* 设置 JSBridge
|
|
*/
|
|
setupBridge: function() {
|
|
var 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(function() {
|
|
if (!self.isReady) {
|
|
self.log('JSBridge 未就绪,可能不在建行 App 内');
|
|
}
|
|
}, 3000);
|
|
},
|
|
|
|
/**
|
|
* Bridge 就绪回调
|
|
*/
|
|
onBridgeReady: function() {
|
|
this.isReady = true;
|
|
this.log('JSBridge 已就绪');
|
|
|
|
// 执行所有等待的回调
|
|
this.readyCallbacks.forEach(function(callback) {
|
|
callback();
|
|
});
|
|
this.readyCallbacks = [];
|
|
},
|
|
|
|
/**
|
|
* 等待 Bridge 就绪
|
|
* @param {Function} callback 回调函数
|
|
*/
|
|
ready: function(callback) {
|
|
if (this.isReady) {
|
|
callback();
|
|
} else {
|
|
this.readyCallbacks.push(callback);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 检测是否在建行生活 App 内
|
|
* @returns {Boolean}
|
|
*/
|
|
isInCcbApp: function() {
|
|
var ua = navigator.userAgent.toLowerCase();
|
|
|
|
// 检查 User-Agent
|
|
if (ua.indexOf('ccblife') > -1 || ua.indexOf('ccb') > -1) {
|
|
return true;
|
|
}
|
|
|
|
// 检查 URL 参数
|
|
var urlParams = this.getUrlParams();
|
|
if (urlParams.ccbParamSJ || urlParams.from === 'ccblife') {
|
|
return true;
|
|
}
|
|
|
|
// 检查是否有 JSBridge
|
|
if (window.WebViewJavascriptBridge || window.mbspay) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* 获取 URL 参数
|
|
* @returns {Object}
|
|
*/
|
|
getUrlParams: function() {
|
|
var params = {};
|
|
var search = window.location.search.substring(1);
|
|
|
|
if (search) {
|
|
var pairs = search.split('&');
|
|
pairs.forEach(function(pair) {
|
|
var parts = pair.split('=');
|
|
if (parts.length === 2) {
|
|
params[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
|
|
}
|
|
});
|
|
}
|
|
|
|
return params;
|
|
},
|
|
|
|
/**
|
|
* 获取建行用户信息
|
|
* @param {Function} callback 回调函数
|
|
*/
|
|
getUserInfo: function(callback) {
|
|
var self = this;
|
|
|
|
if (!this.isInCcbApp()) {
|
|
callback({
|
|
success: false,
|
|
error: '不在建行生活 App 内'
|
|
});
|
|
return;
|
|
}
|
|
|
|
this.ready(function() {
|
|
self.callNative('getUserInfo', {}, function(result) {
|
|
if (result && result.userid) {
|
|
callback({
|
|
success: true,
|
|
data: {
|
|
ccb_user_id: result.userid,
|
|
mobile: result.mobile || '',
|
|
nickname: result.nickname || '',
|
|
avatar: result.avatar || ''
|
|
}
|
|
});
|
|
} else {
|
|
callback({
|
|
success: false,
|
|
error: '获取用户信息失败'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 自动登录
|
|
*/
|
|
autoLogin: function() {
|
|
var self = this;
|
|
|
|
// 检查是否已登录
|
|
if (localStorage.getItem('ccb_token')) {
|
|
this.log('用户已登录');
|
|
return;
|
|
}
|
|
|
|
// 获取用户信息并登录
|
|
this.getUserInfo(function(result) {
|
|
if (result.success) {
|
|
self.doLogin(result.data);
|
|
} else {
|
|
self.log('获取用户信息失败:' + result.error);
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 执行登录
|
|
* @param {Object} userInfo 用户信息
|
|
*/
|
|
doLogin: function(userInfo) {
|
|
var self = this;
|
|
|
|
// 调用后端登录接口
|
|
this.ajax({
|
|
url: this.config.apiBaseUrl + '/ccblife/autoLogin',
|
|
method: 'POST',
|
|
data: userInfo,
|
|
success: function(response) {
|
|
if (response.code === 1) {
|
|
// 保存 Token
|
|
localStorage.setItem('ccb_token', response.data.token);
|
|
localStorage.setItem('ccb_user_info', JSON.stringify(response.data.userInfo));
|
|
|
|
self.log('自动登录成功');
|
|
|
|
// 触发登录成功事件
|
|
self.triggerEvent('ccb:login:success', response.data);
|
|
} else {
|
|
self.log('登录失败:' + response.msg);
|
|
}
|
|
},
|
|
error: function(error) {
|
|
self.log('登录请求失败:' + error);
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 调起建行支付
|
|
* @param {Object} options 支付参数
|
|
* @param {Function} callback 回调函数
|
|
*/
|
|
payment: function(options, callback) {
|
|
var self = this;
|
|
|
|
if (!this.isInCcbApp()) {
|
|
callback({
|
|
success: false,
|
|
error: '不在建行生活 App 内'
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 必需参数检查
|
|
if (!options.payment_string) {
|
|
callback({
|
|
success: false,
|
|
error: '缺少支付串参数'
|
|
});
|
|
return;
|
|
}
|
|
|
|
this.ready(function() {
|
|
// 区分 iOS 和 Android
|
|
if (self.isIOS()) {
|
|
// iOS 使用 URL Scheme
|
|
self.paymentForIOS(options, callback);
|
|
} else {
|
|
// Android 使用 JSBridge
|
|
self.paymentForAndroid(options, callback);
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* iOS 支付
|
|
*/
|
|
paymentForIOS: function(options, callback) {
|
|
var paymentUrl = 'comccbpay://pay?' + options.payment_string;
|
|
|
|
// 尝试打开支付页面
|
|
window.location.href = paymentUrl;
|
|
|
|
// 设置回调检查
|
|
setTimeout(function() {
|
|
callback({
|
|
success: true,
|
|
message: '已调起支付,请在建行 App 内完成支付'
|
|
});
|
|
}, 1000);
|
|
},
|
|
|
|
/**
|
|
* Android 支付
|
|
*/
|
|
paymentForAndroid: function(options, callback) {
|
|
var self = this;
|
|
|
|
// 调用原生支付方法
|
|
this.callNative('payment', {
|
|
payment_string: options.payment_string
|
|
}, function(result) {
|
|
if (result && result.success) {
|
|
callback({
|
|
success: true,
|
|
data: result
|
|
});
|
|
} else {
|
|
callback({
|
|
success: false,
|
|
error: result ? result.error : '支付失败'
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 调用原生方法
|
|
* @param {String} method 方法名
|
|
* @param {Object} params 参数
|
|
* @param {Function} callback 回调函数
|
|
*/
|
|
callNative: function(method, params, callback) {
|
|
var self = this;
|
|
|
|
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 直接调用
|
|
var result = window.mbspay[method](JSON.stringify(params));
|
|
if (callback) {
|
|
callback(typeof result === 'string' ? JSON.parse(result) : result);
|
|
}
|
|
} else {
|
|
self.log('原生方法不存在:' + method);
|
|
if (callback) {
|
|
callback({
|
|
success: false,
|
|
error: '原生方法不存在'
|
|
});
|
|
}
|
|
}
|
|
} catch (e) {
|
|
self.log('调用原生方法失败:' + e.message);
|
|
if (callback) {
|
|
callback({
|
|
success: false,
|
|
error: e.message
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 判断是否 iOS
|
|
*/
|
|
isIOS: function() {
|
|
return /iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
},
|
|
|
|
/**
|
|
* 触发事件
|
|
*/
|
|
triggerEvent: function(eventName, data) {
|
|
var event = new CustomEvent(eventName, {
|
|
detail: data
|
|
});
|
|
window.dispatchEvent(event);
|
|
},
|
|
|
|
/**
|
|
* AJAX 请求
|
|
*/
|
|
ajax: function(options) {
|
|
var xhr = new XMLHttpRequest();
|
|
var self = this;
|
|
|
|
xhr.open(options.method || 'GET', options.url, true);
|
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
|
|
|
// 添加 Token
|
|
var token = localStorage.getItem('ccb_token');
|
|
if (token) {
|
|
xhr.setRequestHeader('token', token);
|
|
}
|
|
|
|
xhr.onload = function() {
|
|
if (xhr.status >= 200 && xhr.status < 300) {
|
|
var response = JSON.parse(xhr.responseText);
|
|
if (options.success) {
|
|
options.success(response);
|
|
}
|
|
} else {
|
|
if (options.error) {
|
|
options.error('请求失败:' + xhr.status);
|
|
}
|
|
}
|
|
};
|
|
|
|
xhr.onerror = function() {
|
|
if (options.error) {
|
|
options.error('网络错误');
|
|
}
|
|
};
|
|
|
|
xhr.send(options.data ? JSON.stringify(options.data) : null);
|
|
},
|
|
|
|
/**
|
|
* 日志输出
|
|
*/
|
|
log: function(message) {
|
|
if (this.config.debug) {
|
|
console.log('[CcbLifeBridge] ' + message);
|
|
}
|
|
}
|
|
};
|
|
|
|
// 暴露到全局
|
|
window.CcbLifeBridge = CcbLifeBridge;
|
|
|
|
})(window); |