From 537647ec740cf88374da08f622d4c8dc0724fe7f Mon Sep 17 00:00:00 2001
From: Billy <641833868@qq.com>
Date: Fri, 17 Oct 2025 17:34:36 +0800
Subject: [PATCH] =?UTF-8?q?=E5=89=8D=E7=AB=AF=E4=BF=AE=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
frontend/pages/ccblife/index.vue | 427 +++++++++++++++++
frontend/sheep/platform/index.js | 18 +-
frontend/sheep/platform/pay.js | 84 +++-
.../sheep/platform/provider/ccblife/api.js | 78 ++++
.../sheep/platform/provider/ccblife/index.js | 440 ++++++++++++++++++
5 files changed, 1044 insertions(+), 3 deletions(-)
create mode 100644 frontend/pages/ccblife/index.vue
create mode 100644 frontend/sheep/platform/provider/ccblife/api.js
create mode 100644 frontend/sheep/platform/provider/ccblife/index.js
diff --git a/frontend/pages/ccblife/index.vue b/frontend/pages/ccblife/index.vue
new file mode 100644
index 0000000..c0d9b6b
--- /dev/null
+++ b/frontend/pages/ccblife/index.vue
@@ -0,0 +1,427 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ userInfo.nickname || '建行用户' }}
+ 建行ID: {{ userInfo.ccb_user_id }}
+
+
+ 建行专享
+
+
+
+
+
+
+
+ 建行专享优惠
+ 查看更多 >
+
+
+
+
+
+
+ {{ item.title }}
+ {{ item.desc }}
+
+ {{ item.tag }}
+
+
+
+
+
+
+
+
+
+ 建行用户专享商品
+
+
+
+
+
+
+ {{ item.title }}
+
+ ¥
+ {{ item.price }}
+ ¥{{ item.originalPrice }}
+
+
+ 建行专享
+
+
+
+
+
+
+
+
+ — 建行生活,让生活更美好 —
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/sheep/platform/index.js b/frontend/sheep/platform/index.js
index 36a54fc..2c0d0c8 100644
--- a/frontend/sheep/platform/index.js
+++ b/frontend/sheep/platform/index.js
@@ -15,6 +15,7 @@ import { isWxBrowser } from '@/sheep/helper/utils';
// #endif
import wechat from './provider/wechat/index.js';
import apple from './provider/apple';
+import ccblife from './provider/ccblife/index.js';
import share from './share';
import Pay from './pay';
@@ -28,7 +29,14 @@ let platform = '';
let isWechatInstalled = true;
// #ifdef H5
-if (isWxBrowser()) {
+// 先检测建行生活环境
+ccblife.detectEnvironment();
+
+if (ccblife.isInCcbApp) {
+ name = 'CcbLife';
+ provider = 'ccb';
+ platform = 'ccblife';
+} else if (isWxBrowser()) {
name = 'WechatOfficialAccount';
provider = 'wechat';
platform = 'officialAccount';
@@ -65,13 +73,19 @@ const load = () => {
if (provider === 'wechat') {
wechat.load();
}
+ if (provider === 'ccb') {
+ ccblife.init();
+ // 处理URL跳转登录
+ ccblife.handleUrlLogin();
+ }
};
-// 使用厂商独占sdk name = 'wechat' | 'alipay' | 'apple'
+// 使用厂商独占sdk name = 'wechat' | 'alipay' | 'apple' | 'ccb'
const useProvider = (_provider = '') => {
if (_provider === '') _provider = provider;
if (_provider === 'wechat') return wechat;
if (_provider === 'apple') return apple;
+ if (_provider === 'ccb') return ccblife;
};
// 支付服务转发
diff --git a/frontend/sheep/platform/pay.js b/frontend/sheep/platform/pay.js
index f1fb545..00ceb1d 100644
--- a/frontend/sheep/platform/pay.js
+++ b/frontend/sheep/platform/pay.js
@@ -5,6 +5,8 @@ import $wxsdk from '@/sheep/libs/sdk-h5-weixin';
import {
getRootUrl
} from '@/sheep/helper';
+import CcbLifePlatform from './provider/ccblife/index';
+import ccbApi from './provider/ccblife/api';
/**
* 支付
@@ -31,6 +33,9 @@ export default class SheepPay {
alipay: () => {
this.redirectPay(); // 现在公众号可以直接跳转支付宝页面
},
+ ccb: () => {
+ this.ccbPay(); // 建行支付
+ },
money: () => {
this.moneyPay();
},
@@ -45,6 +50,9 @@ export default class SheepPay {
alipay: () => {
this.copyPayLink();
},
+ ccb: () => {
+ sheep.$helper.toast('小程序暂不支持建行支付');
+ },
money: () => {
this.moneyPay();
},
@@ -59,6 +67,9 @@ export default class SheepPay {
alipay: () => {
this.alipay();
},
+ ccb: () => {
+ this.ccbPay(); // 建行支付
+ },
money: () => {
this.moneyPay();
},
@@ -68,11 +79,19 @@ export default class SheepPay {
},
H5: {
wechat: () => {
- this.wechatWapPay();
+ // 如果在建行App内,使用建行支付
+ if (CcbLifePlatform.isInCcbApp) {
+ this.ccbPay();
+ } else {
+ this.wechatWapPay();
+ }
},
alipay: () => {
this.redirectPay();
},
+ ccb: () => {
+ this.ccbPay(); // 建行支付
+ },
money: () => {
this.moneyPay();
},
@@ -268,6 +287,69 @@ export default class SheepPay {
}
}
+ // 建行生活支付
+ async ccbPay() {
+ let that = this;
+
+ // 检查是否在建行App内
+ if (!CcbLifePlatform.isInCcbApp) {
+ sheep.$helper.toast('请在建行生活App内使用建行支付');
+ return;
+ }
+
+ // 获取订单ID(从订单号查询)
+ const orderInfo = await sheep.$api.order.detail(this.orderSN);
+ if (!orderInfo || orderInfo.code !== 1) {
+ sheep.$helper.toast('获取订单信息失败');
+ return;
+ }
+
+ // 调用后端生成支付串
+ const paymentResult = await ccbApi.createPayment(orderInfo.data.id);
+
+ if (paymentResult.code !== 1) {
+ sheep.$helper.toast(paymentResult.msg || '生成支付串失败');
+ return;
+ }
+
+ // 调起建行支付
+ try {
+ const result = await CcbLifePlatform.payment({
+ payment_string: paymentResult.data.payment_string,
+ order_id: orderInfo.data.id,
+ order_sn: this.orderSN
+ });
+
+ if (result.code === 0) {
+ // 支付成功,通知后端
+ const callbackResult = await ccbApi.paymentCallback({
+ order_id: orderInfo.data.id,
+ trans_id: result.data?.trans_id || '',
+ pay_time: new Date().getTime()
+ });
+
+ if (callbackResult.code === 1) {
+ that.payResult('success');
+ } else {
+ sheep.$helper.toast('支付确认失败,请联系客服');
+ that.payResult('fail');
+ }
+ } else {
+ // 支付失败或取消
+ if (result.msg && result.msg.includes('取消')) {
+ sheep.$helper.toast('支付已取消');
+ } else {
+ sheep.$helper.toast(result.msg || '支付失败');
+ that.payResult('fail');
+ }
+ }
+ } catch (error) {
+ console.error('[建行支付] 错误:', error);
+ sheep.$helper.toast('支付失败');
+ that.payResult('fail');
+ }
+ }
+
// 支付结果跳转,success:成功,fail:失败
payResult(resultType) {
sheep.$router.redirect('/pages/pay/result', {
diff --git a/frontend/sheep/platform/provider/ccblife/api.js b/frontend/sheep/platform/provider/ccblife/api.js
new file mode 100644
index 0000000..d89c170
--- /dev/null
+++ b/frontend/sheep/platform/provider/ccblife/api.js
@@ -0,0 +1,78 @@
+/**
+ * 建行生活 API 接口
+ *
+ * @author Billy
+ * @date 2025-01-17
+ */
+
+import request from '@/sheep/request';
+
+const ccbApi = {
+ /**
+ * URL跳转登录
+ * 建行App会携带加密参数跳转到此地址
+ */
+ login: (params) => {
+ return request({
+ url: '/ccblife/login',
+ method: 'GET',
+ params: params,
+ custom: {
+ showLoading: true,
+ auth: false
+ }
+ });
+ },
+
+ /**
+ * 自动登录(JSBridge方式)
+ * H5在建行App内打开时,通过JSBridge获取用户信息后调用
+ */
+ autoLogin: (data) => {
+ return request({
+ url: '/ccblife/autoLogin',
+ method: 'POST',
+ data: data,
+ custom: {
+ showLoading: true,
+ auth: false
+ }
+ });
+ },
+
+ /**
+ * 生成支付串
+ * 用于调起建行收银台
+ */
+ createPayment: (orderId) => {
+ return request({
+ url: '/ccbpayment/createPayment',
+ method: 'POST',
+ data: {
+ order_id: orderId
+ },
+ custom: {
+ showLoading: true,
+ auth: true
+ }
+ });
+ },
+
+ /**
+ * 支付回调
+ * 支付完成后通知后端
+ */
+ paymentCallback: (data) => {
+ return request({
+ url: '/ccbpayment/callback',
+ method: 'POST',
+ data: data,
+ custom: {
+ showLoading: false,
+ auth: true
+ }
+ });
+ }
+};
+
+export default ccbApi;
\ No newline at end of file
diff --git a/frontend/sheep/platform/provider/ccblife/index.js b/frontend/sheep/platform/provider/ccblife/index.js
new file mode 100644
index 0000000..397db03
--- /dev/null
+++ b/frontend/sheep/platform/provider/ccblife/index.js
@@ -0,0 +1,440 @@
+/**
+ * 建行生活平台集成模块
+ *
+ * @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;
\ No newline at end of file