fengketrade/public/ccblife-demo.html
2025-10-20 10:03:55 +08:00

675 lines
23 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">建行用户ID</span>
<span class="value" id="ccb-user-id">-</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" onclick="getUserInfo()">获取建行用户信息</button>
<button class="btn btn-secondary" onclick="doAutoLogin()">执行自动登录</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>';
});
};
// 更新环境状态
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 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));
// 更新页面显示
updateLoginStatus({ userInfo: loginData.user_info });
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>