fengketrade/public/ccblife-demo.html
2025-10-20 11:39:21 +08:00

863 lines
31 KiB
HTML
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.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>建行生活 H5 集成示例</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
background: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
font-size: 24px;
margin-bottom: 20px;
color: #333;
text-align: center;
}
.info-section {
background: #f8f9fa;
border-radius: 4px;
padding: 15px;
margin-bottom: 20px;
}
.info-item {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
font-size: 14px;
}
.info-item:last-child {
margin-bottom: 0;
}
.label {
color: #666;
}
.value {
color: #333;
font-weight: 500;
}
.btn {
display: block;
width: 100%;
padding: 12px 20px;
background: #0066cc;
color: #fff;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
margin-bottom: 15px;
transition: background 0.3s;
}
.btn:hover {
background: #0052a3;
}
.btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.btn-secondary {
background: #6c757d;
}
.btn-secondary:hover {
background: #545b62;
}
.btn-success {
background: #28a745;
}
.btn-success:hover {
background: #218838;
}
.result {
background: #f0f8ff;
border: 1px solid #b6d4fe;
border-radius: 4px;
padding: 15px;
margin-top: 20px;
white-space: pre-wrap;
font-family: monospace;
font-size: 12px;
max-height: 300px;
overflow-y: auto;
}
.status {
display: inline-block;
padding: 4px 8px;
border-radius: 3px;
font-size: 12px;
margin-left: 10px;
}
.status-success {
background: #d4edda;
color: #155724;
}
.status-error {
background: #f8d7da;
color: #721c24;
}
.status-warning {
background: #fff3cd;
color: #856404;
}
.loading {
display: none;
text-align: center;
padding: 20px;
}
.loading.active {
display: block;
}
.spinner {
border: 3px solid #f3f3f3;
border-top: 3px solid #0066cc;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 0 auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.divider {
height: 1px;
background: #e9ecef;
margin: 20px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>建行生活 H5 集成示例</h1>
<!-- 环境信息 -->
<div class="info-section">
<h3>环境信息</h3>
<div class="info-item">
<span class="label">当前环境:</span>
<span class="value" id="env-status">检测中...</span>
</div>
<div class="info-item">
<span class="label">设备类型:</span>
<span class="value" id="device-type">-</span>
</div>
<div class="info-item">
<span class="label">JSBridge 状态:</span>
<span class="value" id="bridge-status">未就绪</span>
</div>
<div class="info-item">
<span class="label">Bridge 对象:</span>
<span class="value" id="bridge-object">检测中...</span>
</div>
<div class="info-item">
<span class="label">登录状态:</span>
<span class="value" id="login-status">未登录</span>
</div>
<div class="info-item">
<span class="label">自动登录:</span>
<span class="value" id="auto-login-status">待检测</span>
</div>
<div class="info-item">
<span class="label">建行用户ID</span>
<span class="value" id="ccb-user-id">-</span>
</div>
<div class="info-item">
<span class="label">登录时间:</span>
<span class="value" id="login-time">-</span>
</div>
<div class="info-item">
<span class="label">建行参数:</span>
<span class="value" id="ccb-param"></span>
</div>
</div>
<div class="divider"></div>
<!-- 功能测试 -->
<h3 style="margin-bottom: 15px;">功能测试</h3>
<button class="btn" onclick="refreshEnv()">🔄 刷新环境检测</button>
<button class="btn" onclick="decryptCcbParam()">🔓 解密建行参数</button>
<button class="btn btn-success" onclick="testLogin()">🔐 测试登录入库(手动)</button>
<button class="btn btn-success" onclick="forceAutoLogin()">⚡ 强制自动登录(重新)</button>
<button class="btn" onclick="getUserInfo()">获取建行用户信息</button>
<button class="btn btn-secondary" onclick="doAutoLogin()">执行自动登录(JSBridge)</button>
<button class="btn btn-success" onclick="testPayment()">测试支付功能</button>
<div class="divider"></div>
<!-- 调试功能 -->
<h3 style="margin-bottom: 15px;">调试功能</h3>
<button class="btn btn-secondary" onclick="checkUrlParams()">检查 URL 参数</button>
<button class="btn btn-secondary" onclick="showUserAgent()">显示 User-Agent</button>
<button class="btn btn-secondary" onclick="checkBridgeObjects()">检查 Bridge 对象</button>
<button class="btn btn-secondary" onclick="clearCache()">清除缓存</button>
<button class="btn btn-secondary" onclick="toggleDebug()">切换调试模式</button>
<!-- 加载状态 -->
<div class="loading" id="loading">
<div class="spinner"></div>
<p style="margin-top: 10px;">处理中...</p>
</div>
<!-- 结果显示 -->
<div class="result" id="result" style="display: none;"></div>
</div>
<!-- 引入 JSBridge -->
<script src="/assets/js/ccblife-bridge.js"></script>
<script>
// 初始化配置
window.onload = function() {
// 初始化 CcbLifeBridge
CcbLifeBridge.init({
debug: true,
apiBaseUrl: '/addons/shopro'
});
// 更新环境状态
updateEnvStatus();
// 监听登录成功事件
window.addEventListener('ccb:login:success', function(e) {
console.log('登录成功事件:', e.detail);
updateLoginStatus(e.detail);
});
// 监听 Bridge 就绪
CcbLifeBridge.ready(function() {
document.getElementById('bridge-status').innerHTML =
'<span class="status status-success">已就绪</span>';
});
// 自动登录逻辑
autoLoginOnLoad();
};
// 页面加载时自动登录
function autoLoginOnLoad() {
console.log('[自动登录] 开始检测...');
// 更新状态显示
document.getElementById('auto-login-status').innerHTML =
'<span class="status status-warning">检测中...</span>';
// 获取 URL 参数
var params = CcbLifeBridge.getUrlParams();
// 检查是否有建行参数
if (params.ccbParamSJ) {
console.log('[自动登录] 检测到 ccbParamSJ 参数');
document.getElementById('auto-login-status').innerHTML =
'<span class="status status-warning">正在执行...</span>';
// 检查是否已经登录过(避免重复登录)
var token = localStorage.getItem('ccb_token');
var loginTimestamp = localStorage.getItem('ccb_login_timestamp');
var now = Date.now();
// 如果已登录且登录时间在30分钟内,跳过自动登录
if (token && loginTimestamp && (now - parseInt(loginTimestamp)) < 30 * 60 * 1000) {
console.log('[自动登录] 已存在有效登录,跳过');
document.getElementById('auto-login-status').innerHTML =
'<span class="status status-success">已跳过(使用缓存)</span>';
showResult('✅ 检测到已登录状态\n\n使用缓存的 Token\n\n如需重新登录,请点击"测试登录入库"按钮');
return;
}
// 显示自动登录提示
showResult('🔄 检测到建行参数,正在自动登录...\n\n1. 解密建行参数\n2. 验证用户信息\n3. 创建/更新用户\n4. 生成登录 Token');
showLoading(true);
// 延迟500ms执行,确保页面渲染完成
setTimeout(function() {
executeAutoLogin(params.ccbParamSJ);
}, 500);
} else {
console.log('[自动登录] 未检测到 ccbParamSJ 参数');
document.getElementById('auto-login-status').innerHTML =
'<span class="status status-error">未触发(无参数)</span>';
// 检查本地缓存
var cachedToken = localStorage.getItem('ccb_token');
var cachedUserInfo = localStorage.getItem('ccb_user_info');
if (cachedToken && cachedUserInfo) {
try {
var user = JSON.parse(cachedUserInfo);
console.log('[自动登录] 使用缓存的登录信息');
updateLoginStatus({ userInfo: user });
showResult('✅ 使用缓存的登录状态\n\n用户信息:\n' + JSON.stringify(user, null, 2));
} catch(e) {
console.error('[自动登录] 解析缓存失败', e);
}
} else {
console.log('[自动登录] 无缓存,显示未登录状态');
}
}
}
// 执行自动登录
function executeAutoLogin(ccbParamSJ) {
console.log('[自动登录] 开始调用登录接口');
// 调用后端登录接口
fetch('/addons/shopro/ccblife/login?ccbParamSJ=' + encodeURIComponent(ccbParamSJ), {
method: 'GET'
})
.then(response => response.json())
.then(data => {
showLoading(false);
console.log('[自动登录] 接口返回:', data);
if (data.code === 1) {
var loginData = data.data;
// 保存 token 和用户信息
localStorage.setItem('ccb_token', loginData.token);
localStorage.setItem('ccb_user_info', JSON.stringify(loginData.user_info));
localStorage.setItem('ccb_login_timestamp', Date.now().toString());
// 更新自动登录状态
document.getElementById('auto-login-status').innerHTML =
'<span class="status status-success">成功</span>';
// 更新登录时间
document.getElementById('login-time').textContent =
new Date().toLocaleString('zh-CN', { hour12: false });
// 更新页面显示
updateLoginStatus({ userInfo: loginData.user_info });
// 触发自定义事件
window.dispatchEvent(new CustomEvent('ccb:login:success', {
detail: loginData
}));
console.log('[自动登录] 登录成功!');
showResult('✅ 自动登录成功!\n\n' +
'用户昵称: ' + loginData.user_info.nickname + '\n' +
'手机号: ' + loginData.user_info.mobile + '\n' +
'建行用户ID: ' + loginData.user_info.ccb_user_id + '\n\n' +
'Token: ' + loginData.token.substr(0, 20) + '...\n\n' +
'是否新用户: ' + (loginData.user_info.is_new ? '是' : '否') + '\n' +
'登录时间: ' + new Date().toLocaleString('zh-CN', { hour12: false }) + '\n\n' +
'跳转URL: ' + loginData.redirect_url
);
// 如果有重定向URL且不是默认首页,可以自动跳转(可选)
// if (loginData.redirect_url && loginData.redirect_url !== '/pages/index/index') {
// setTimeout(function() {
// window.location.href = loginData.redirect_url;
// }, 2000);
// }
} else {
console.error('[自动登录] 登录失败:', data.msg);
// 更新自动登录状态
document.getElementById('auto-login-status').innerHTML =
'<span class="status status-error">失败</span>';
showResult('❌ 自动登录失败\n\n错误信息: ' + data.msg + '\n\n' +
(data.data ? '详细信息:\n' + JSON.stringify(data.data, null, 2) + '\n\n' : '') +
'请尝试手动点击"测试登录入库"按钮');
}
})
.catch(error => {
showLoading(false);
console.error('[自动登录] 请求异常:', error);
// 更新自动登录状态
document.getElementById('auto-login-status').innerHTML =
'<span class="status status-error">异常</span>';
showResult('❌ 自动登录请求失败\n\n' + error.message + '\n\n请检查网络连接或手动点击"测试登录入库"按钮');
});
}
// 更新环境状态
function updateEnvStatus() {
var isInApp = CcbLifeBridge.isInCcbApp();
var envEl = document.getElementById('env-status');
if (isInApp) {
envEl.innerHTML = '<span class="status status-success">建行生活 App 内</span>';
} else {
envEl.innerHTML = '<span class="status status-warning">普通浏览器</span>';
}
// 设备类型
var deviceType = CcbLifeBridge.isIOS() ? 'iOS' : 'Android';
document.getElementById('device-type').textContent = deviceType;
// 检查 Bridge 对象
checkBridgeObjectsStatus();
// 检查建行参数
var params = CcbLifeBridge.getUrlParams();
if (params.ccbParamSJ) {
document.getElementById('ccb-param').innerHTML =
'<span class="status status-warning">已加密(点击解密按钮)</span>';
} else {
document.getElementById('ccb-param').textContent = '无';
}
// 检查登录状态
var token = localStorage.getItem('ccb_token');
var userInfo = localStorage.getItem('ccb_user_info');
if (token && userInfo) {
try {
var user = JSON.parse(userInfo);
updateLoginStatus({ userInfo: user });
} catch(e) {
console.error('解析用户信息失败', e);
}
}
}
// 检查 Bridge 对象状态
function checkBridgeObjectsStatus() {
var bridgeObjects = [];
if (window.WebViewJavascriptBridge) {
bridgeObjects.push('WebViewJavascriptBridge');
}
if (window.mbspay) {
bridgeObjects.push('mbspay');
}
var bridgeEl = document.getElementById('bridge-object');
if (bridgeObjects.length > 0) {
bridgeEl.innerHTML = '<span class="status status-success">' +
bridgeObjects.join(', ') + '</span>';
} else {
bridgeEl.innerHTML = '<span class="status status-error">未检测到</span>';
}
}
// 刷新环境检测
function refreshEnv() {
updateEnvStatus();
// 重新检查 Bridge 就绪状态
if (CcbLifeBridge.isReady) {
document.getElementById('bridge-status').innerHTML =
'<span class="status status-success">已就绪</span>';
} else {
document.getElementById('bridge-status').innerHTML =
'<span class="status status-error">未就绪</span>';
// 等待 3 秒后重新检查
setTimeout(function() {
if (CcbLifeBridge.isReady) {
document.getElementById('bridge-status').innerHTML =
'<span class="status status-success">已就绪</span>';
}
}, 3000);
}
showResult('环境信息已刷新');
}
// 更新登录状态
function updateLoginStatus(data) {
document.getElementById('login-status').innerHTML =
'<span class="status status-success">已登录</span>';
if (data.userInfo && data.userInfo.ccb_user_id) {
document.getElementById('ccb-user-id').textContent = data.userInfo.ccb_user_id;
}
}
// 获取用户信息
function getUserInfo() {
showLoading(true);
CcbLifeBridge.getUserInfo(function(result) {
showLoading(false);
if (result.success) {
showResult('获取用户信息成功:\n' + JSON.stringify(result.data, null, 2));
} else {
showResult('获取用户信息失败:' + result.error);
}
});
}
// 执行自动登录
function doAutoLogin() {
if (!CcbLifeBridge.isInCcbApp()) {
showResult('错误:不在建行生活 App 内,无法执行自动登录');
return;
}
showLoading(true);
CcbLifeBridge.getUserInfo(function(result) {
if (result.success) {
CcbLifeBridge.doLogin(result.data);
showLoading(false);
showResult('正在登录...\n用户信息' + JSON.stringify(result.data, null, 2));
} else {
showLoading(false);
showResult('获取用户信息失败:' + result.error);
}
});
}
// 测试支付功能
function testPayment() {
// 检查是否已登录
var token = localStorage.getItem('ccb_token');
if (!token) {
showResult('错误:未登录\n\n请先执行"测试登录入库"获取 Token');
return;
}
// 检查是否在建行 App 内
if (!CcbLifeBridge.isInCcbApp()) {
showResult('警告:不在建行生活 App 内\n\n支付功能需要在建行 App 中才能调起');
}
// 输入订单ID
var orderId = prompt('请输入订单ID测试用', '1');
if (!orderId) {
return;
}
showLoading(true);
showResult('正在生成支付串...\n1. 验证订单\n2. 生成支付串\n3. 调起建行支付');
// 调用后端生成支付串
fetch('/addons/shopro/ccbpayment/createPayment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'token': token
},
body: JSON.stringify({
order_id: orderId
})
})
.then(response => response.json())
.then(data => {
showLoading(false);
if (data.code === 1) {
var paymentData = data.data;
showResult('✅ 支付串生成成功!\n\n' +
'订单编号:' + paymentData.order_sn + '\n' +
'支付流水号:' + paymentData.pay_flow_id + '\n' +
'支付金额:¥' + paymentData.amount + '\n' +
'MAC 签名:' + paymentData.mac.substr(0, 20) + '...\n\n' +
'支付串:\n' + paymentData.payment_string.substr(0, 100) + '...\n\n' +
'正在调起支付...'
);
// 调起建行支付
if (CcbLifeBridge.isInCcbApp()) {
CcbLifeBridge.payment({
payment_string: paymentData.payment_string
}, function(result) {
if (result.success) {
showResult('✅ 支付调起成功!\n\n请在建行 App 内完成支付\n\n' +
'支付完成后,页面会自动跳转');
// 轮询查询订单状态
pollPaymentStatus(orderId);
} else {
showResult('❌ 支付调起失败:' + result.error);
}
});
} else {
showResult('⚠️ 不在建行 App 内,无法调起支付\n\n' +
'但支付串已生成,可以手动测试:\n\n' +
'支付 URL\n' + paymentData.payment_url
);
}
} else {
showResult('❌ 生成支付串失败:' + data.msg);
}
})
.catch(error => {
showLoading(false);
showResult('❌ 请求失败:' + error.message);
});
}
// 轮询查询支付状态
function pollPaymentStatus(orderId) {
var token = localStorage.getItem('ccb_token');
var pollCount = 0;
var maxPoll = 60; // 最多轮询60次5分钟
var pollInterval = setInterval(function() {
pollCount++;
fetch('/addons/shopro/order/detail?id=' + orderId, {
method: 'GET',
headers: {
'token': token
}
})
.then(response => response.json())
.then(data => {
if (data.code === 1) {
var order = data.data;
if (order.status === 'paid' || order.status === 'completed') {
clearInterval(pollInterval);
showResult('✅ 支付成功!\n\n' +
'订单编号:' + order.order_sn + '\n' +
'订单状态:' + order.status + '\n' +
'支付时间:' + new Date(order.paid_time).toLocaleString()
);
} else if (pollCount >= maxPoll) {
clearInterval(pollInterval);
showResult('⏱ 查询超时\n\n请手动刷新订单状态');
}
}
})
.catch(error => {
console.error('轮询订单状态失败:', error);
});
}, 5000); // 每5秒查询一次
}
// 强制自动登录(忽略缓存)
function forceAutoLogin() {
var params = CcbLifeBridge.getUrlParams();
if (!params.ccbParamSJ) {
showResult('错误: URL 中没有 ccbParamSJ 参数\n\n请从建行生活 App 跳转到此页面,或在 URL 中添加 ccbParamSJ 参数');
return;
}
// 清除登录时间戳,强制重新登录
localStorage.removeItem('ccb_login_timestamp');
showResult('🔄 强制重新登录...\n\n忽略缓存,重新执行登录流程');
showLoading(true);
// 更新状态
document.getElementById('auto-login-status').innerHTML =
'<span class="status status-warning">强制执行...</span>';
// 延迟执行
setTimeout(function() {
executeAutoLogin(params.ccbParamSJ);
}, 500);
}
// 测试登录入库
function testLogin() {
var params = CcbLifeBridge.getUrlParams();
if (!params.ccbParamSJ) {
showResult('错误URL 中没有 ccbParamSJ 参数\n\n请从建行生活 App 跳转到此页面,或在 URL 中添加 ccbParamSJ 参数');
return;
}
showLoading(true);
showResult('正在登录...\n1. 解密建行参数\n2. 验证用户信息\n3. 创建/更新用户\n4. 生成登录 Token');
// 调用后端登录接口
fetch('/addons/shopro/ccblife/login?ccbParamSJ=' + encodeURIComponent(params.ccbParamSJ), {
method: 'GET'
})
.then(response => response.json())
.then(data => {
showLoading(false);
if (data.code === 1) {
var loginData = data.data;
// 保存 token 和用户信息
localStorage.setItem('ccb_token', loginData.token);
localStorage.setItem('ccb_user_info', JSON.stringify(loginData.user_info));
localStorage.setItem('ccb_login_timestamp', Date.now().toString());
// 更新页面显示
updateLoginStatus({ userInfo: loginData.user_info });
// 更新登录时间
document.getElementById('login-time').textContent =
new Date().toLocaleString('zh-CN', { hour12: false });
showResult('✅ 登录成功!\n\n' +
'用户信息:\n' + JSON.stringify(loginData.user_info, null, 2) + '\n\n' +
'Token' + loginData.token.substr(0, 20) + '...\n\n' +
'是否新用户:' + (loginData.user_info.is_new ? '是' : '否') + '\n\n' +
'跳转URL' + loginData.redirect_url
);
} else {
showResult('❌ 登录失败:' + data.msg);
}
})
.catch(error => {
showLoading(false);
showResult('❌ 请求失败:' + error.message);
});
}
// 解密建行参数
function decryptCcbParam() {
var params = CcbLifeBridge.getUrlParams();
if (!params.ccbParamSJ) {
showResult('错误URL 中没有 ccbParamSJ 参数');
return;
}
showLoading(true);
// 调用后端接口解密
fetch('/addons/shopro/ccblife/decryptParam', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
ccbParamSJ: params.ccbParamSJ
})
})
.then(response => response.json())
.then(data => {
showLoading(false);
if (data.code === 1) {
var decrypted = data.data;
showResult('解密成功:\n' + JSON.stringify(decrypted, null, 2));
// 更新显示
document.getElementById('ccb-param').innerHTML =
'<span class="status status-success">已解密</span>';
// 如果有用户ID显示出来
if (decrypted.userid) {
document.getElementById('ccb-user-id').textContent = decrypted.userid;
}
} else {
showResult('解密失败:' + data.msg);
}
})
.catch(error => {
showLoading(false);
showResult('请求失败:' + error.message);
});
}
// 检查 URL 参数
function checkUrlParams() {
var params = CcbLifeBridge.getUrlParams();
showResult('URL 参数:\n' + JSON.stringify(params, null, 2));
}
// 显示 User-Agent
function showUserAgent() {
var ua = navigator.userAgent;
showResult('User-Agent\n' + ua);
}
// 检查 Bridge 对象
function checkBridgeObjects() {
var info = {
WebViewJavascriptBridge: typeof window.WebViewJavascriptBridge !== 'undefined',
mbspay: typeof window.mbspay !== 'undefined',
bridgeReady: CcbLifeBridge.isReady,
isIOS: CcbLifeBridge.isIOS(),
isInCcbApp: CcbLifeBridge.isInCcbApp()
};
showResult('Bridge 对象检测:\n' + JSON.stringify(info, null, 2));
}
// 清除缓存
function clearCache() {
localStorage.removeItem('ccb_token');
localStorage.removeItem('ccb_user_info');
document.getElementById('login-status').innerHTML =
'<span class="status status-warning">未登录</span>';
document.getElementById('ccb-user-id').textContent = '-';
showResult('缓存已清除');
}
// 切换调试模式
function toggleDebug() {
CcbLifeBridge.config.debug = !CcbLifeBridge.config.debug;
showResult('调试模式:' + (CcbLifeBridge.config.debug ? '开启' : '关闭'));
}
// 显示加载状态
function showLoading(show) {
var loading = document.getElementById('loading');
if (show) {
loading.classList.add('active');
} else {
loading.classList.remove('active');
}
}
// 显示结果
function showResult(message) {
var resultEl = document.getElementById('result');
resultEl.style.display = 'block';
resultEl.textContent = message;
}
</script>
</body>
</html>