From 1b8d4538ae554c8d4875452db1ffadcd516f38ff Mon Sep 17 00:00:00 2001 From: Billy <641833868@qq.com> Date: Mon, 20 Oct 2025 15:51:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/shopro/controller/Ccbpayment.php | 23 +- .../shopro/library/ccblife/CcbEncryption.php | 1 - .../library/ccblife/CcbPaymentService.php | 12 +- doc/ccblife-frontend-testing-guide.md | 500 ------------ doc/ccblife-implementation-guide.md | 252 ------ doc/ccblife-test-summary.md | 321 -------- doc/ccblife-uniapp-integration.md | 363 --------- doc/ccblife_bugfix_summary.md | 539 ------------- doc/ccblife_test_guide.md | 744 ------------------ doc/frontend-deployment-guide.md | 579 -------------- 10 files changed, 17 insertions(+), 3317 deletions(-) delete mode 100644 doc/ccblife-frontend-testing-guide.md delete mode 100644 doc/ccblife-implementation-guide.md delete mode 100644 doc/ccblife-test-summary.md delete mode 100644 doc/ccblife-uniapp-integration.md delete mode 100644 doc/ccblife_bugfix_summary.md delete mode 100644 doc/ccblife_test_guide.md delete mode 100644 doc/frontend-deployment-guide.md diff --git a/addons/shopro/controller/Ccbpayment.php b/addons/shopro/controller/Ccbpayment.php index 6fc7bf0..cc47a1a 100644 --- a/addons/shopro/controller/Ccbpayment.php +++ b/addons/shopro/controller/Ccbpayment.php @@ -88,29 +88,18 @@ class Ccbpayment extends Common } // 4. 生成支付串 + // ⚠️ 注意: generatePaymentString()内部已经完成了以下操作: + // - 更新订单的ccb_pay_flow_id字段 + // - 记录支付日志到ccb_payment_log表 + // 控制器不应该重复操作,否则会导致数据重复写入! $result = $this->paymentService->generatePaymentString($orderId); if (!$result['status']) { $this->error('支付串生成失败: ' . $result['message']); } - // 5. 保存支付流水号到订单 - $order->ccb_pay_flow_id = $result['data']['pay_flow_id']; // ✅ 使用真实的支付流水号 - $order->save(); - - // 6. 记录支付日志 - $this->savePaymentLog($order, $result['data']['payment_string'], $result['data']['pay_flow_id']); - - // 7. 返回支付串 - $this->success('支付串生成成功', [ - 'payment_string' => $result['data']['payment_string'], - 'payment_url' => $result['data']['payment_url'], - 'mac' => $result['data']['mac'], - 'order_id' => $order->id, - 'order_sn' => $order->order_sn, - 'pay_flow_id' => $result['data']['pay_flow_id'], // ✅ 返回真实的支付流水号 - 'amount' => $result['data']['amount'], - ]); + // 5. 直接返回支付串(不再重复保存数据库!) + $this->success('支付串生成成功', $result['data']); } catch (Exception $e) { Log::error('[建行支付] 生成支付串失败 order_id:' . ($orderId ?? 0) . ' error:' . $e->getMessage()); diff --git a/addons/shopro/library/ccblife/CcbEncryption.php b/addons/shopro/library/ccblife/CcbEncryption.php index d970510..30dcee5 100644 --- a/addons/shopro/library/ccblife/CcbEncryption.php +++ b/addons/shopro/library/ccblife/CcbEncryption.php @@ -70,7 +70,6 @@ class CcbEncryption { $this->privateKey = $this->config['private_key'] ?? ''; $this->publicKey = $this->config['public_key'] ?? ''; - $this->platformPublicKey = $this->config['platform_public_key'] ?? ''; if (empty($this->privateKey)) { throw new Exception('服务方私钥未配置'); diff --git a/addons/shopro/library/ccblife/CcbPaymentService.php b/addons/shopro/library/ccblife/CcbPaymentService.php index a8b1b15..80b6066 100644 --- a/addons/shopro/library/ccblife/CcbPaymentService.php +++ b/addons/shopro/library/ccblife/CcbPaymentService.php @@ -96,6 +96,9 @@ class CcbPaymentService */ public function generatePaymentString($orderId) { + // ⚠️ 开启事务保护,确保数据一致性 + Db::startTrans(); + try { // 获取订单信息 $order = Order::find($orderId); @@ -176,6 +179,9 @@ class CcbPaymentService 'pay_flow_id' => $payFlowId ]); + // ✅ 提交事务 + Db::commit(); + return [ 'status' => true, 'message' => '支付串生成成功', @@ -185,11 +191,15 @@ class CcbPaymentService 'payment_url' => $paymentUrl, 'order_sn' => $order['order_sn'], 'pay_flow_id' => $payFlowId, - 'amount' => number_format($order['pay_fee'], 2, '.', '') + 'amount' => number_format($order['pay_fee'], 2, '.', ''), + 'order_id' => $orderId // 补充order_id字段 ] ]; } catch (\Exception $e) { + // ❌ 回滚事务 + Db::rollback(); + Log::error('建行支付串生成失败: ' . $e->getMessage()); return [ 'status' => false, diff --git a/doc/ccblife-frontend-testing-guide.md b/doc/ccblife-frontend-testing-guide.md deleted file mode 100644 index 4d4e213..0000000 --- a/doc/ccblife-frontend-testing-guide.md +++ /dev/null @@ -1,500 +0,0 @@ -# 建行生活前端测试指南 - -## 测试环境准备 - -### 1. 前端项目结构确认 - -确认以下文件已正确创建: - -``` -frontend/ -├── sheep/platform/ -│ ├── provider/ccblife/ -│ │ ├── index.js ✅ 已创建 -│ │ └── api.js ✅ 已创建 -│ ├── index.js ✅ 已修改(添加建行支持) -│ └── pay.js ✅ 已修改(添加建行支付) -├── pages/ccblife/ -│ └── index.vue ✅ 已创建 -├── static/ -│ └── ccb-test.html ✅ 已创建(测试页面) -└── pages.json ✅ 已修改(添加路由配置) -``` - -### 2. 检查文件完整性 - -```bash -cd /Users/billy/Code/fengketrade.com/frontend - -# 检查建行模块文件 -ls -l sheep/platform/provider/ccblife/ - -# 检查建行页面 -ls -l pages/ccblife/ - -# 检查测试页面 -ls -l static/ccb-test.html -``` - -## 测试方法 - -### 方法一:使用 HBuilderX(推荐) - -这是 uni-app 项目的标准开发方式。 - -#### 步骤 1: 打开项目 - -1. 启动 HBuilderX -2. 文件 → 打开目录 → 选择 `frontend` 目录 -3. 等待项目加载完成 - -#### 步骤 2: 运行到浏览器 - -1. 点击工具栏的"运行"按钮 -2. 选择"运行到浏览器" → "Chrome"(或其他浏览器) -3. 等待编译完成,会自动打开浏览器 - -#### 步骤 3: 访问测试页面 - -在浏览器中访问: -``` -http://localhost:8080/static/ccb-test.html -``` - -或访问建行专属页面: -``` -http://localhost:8080/#/pages/ccblife/index -``` - -#### 步骤 4: 模拟建行环境 - -在 URL 后添加参数模拟建行环境: -``` -http://localhost:8080/#/pages/index/index?from=ccblife&ccbParamSJ=test123 -``` - -### 方法二:使用 Vite 开发服务器 - -如果项目配置了 Vite,可以使用命令行运行。 - -#### 步骤 1: 安装依赖 - -```bash -cd frontend -npm install -``` - -#### 步骤 2: 启动开发服务器 - -```bash -# 如果有 dev:h5 脚本 -npm run dev:h5 - -# 或者直接使用 vite -npx vite -``` - -#### 步骤 3: 访问测试页面 - -默认地址:`http://localhost:3000`(端口以实际为准) - -### 方法三:使用测试服务器 - -将前端代码部署到测试服务器。 - -#### 步骤 1: 访问后端静态测试页面 - -``` -http://fengketrade.test/ccblife-demo.html -``` - -这个页面已经在后端 public 目录创建,可以直接访问。 - -#### 步骤 2: 访问测试接口 - -``` -http://fengketrade.test/addons/shopro/ccbtest -``` - -## 测试项目清单 - -### 1. 模块加载测试 - -#### 测试目标 -验证建行模块是否正确导入和初始化。 - -#### 测试步骤 -1. 打开浏览器开发者工具(F12) -2. 访问测试页面 -3. 在 Console 中输入: - -```javascript -// 检查 sheep 对象 -console.log(window.$shop || window.sheep); - -// 检查平台对象 -console.log(sheep.$platform); - -// 检查平台名称 -console.log(sheep.$platform.name); - -// 检查提供商 -console.log(sheep.$platform.provider); -``` - -#### 预期结果 -- 在非建行环境:`name` 应该是 `H5` 或 `WechatOfficialAccount` -- 在建行环境:`name` 应该是 `CcbLife`,`provider` 应该是 `ccb` - -### 2. 环境检测测试 - -#### 测试目标 -验证系统能否正确识别建行生活 App 环境。 - -#### 测试步骤 - -**情况 1:普通浏览器** -``` -访问: http://localhost:3000/ -预期: 识别为 H5 或 WechatOfficialAccount 环境 -``` - -**情况 2:模拟建行环境(URL 参数)** -``` -访问: http://localhost:3000/?from=ccblife -预期: 识别为 CcbLife 环境 -``` - -**情况 3:模拟建行参数** -``` -访问: http://localhost:3000/?ccbParamSJ=test123 -预期: 识别为 CcbLife 环境 -``` - -#### 验证方法 - -在 Console 中执行: -```javascript -// 方法1:通过平台对象 -console.log(sheep.$platform.provider === 'ccb' ? '建行环境' : '非建行环境'); - -// 方法2:直接导入模块(在实际项目中) -// import CcbLifePlatform from '@/sheep/platform/provider/ccblife/index'; -// console.log(CcbLifePlatform.isInCcbApp); -``` - -### 3. 页面路由测试 - -#### 测试目标 -验证建行专属页面是否可以正常访问。 - -#### 测试步骤 - -1. **访问首页** -``` -http://localhost:3000/#/pages/index/index -``` - -2. **访问建行专属页面** -``` -http://localhost:3000/#/pages/ccblife/index -``` - -3. **使用路由跳转** - -在任意页面的 Console 中执行: -```javascript -uni.navigateTo({ - url: '/pages/ccblife/index' -}); -``` - -#### 预期结果 -- 页面能正常加载 -- 显示"建行生活"标题 -- 导航栏背景色为建行红(#F51C13) - -### 4. API 接口测试 - -#### 测试目标 -验证前端能否正确调用后端建行接口。 - -#### 测试步骤 - -在 Console 中执行: - -```javascript -// 测试1:URL跳转登录接口 -fetch('/addons/shopro/ccblife/login?ccbParamSJ=test') - .then(res => res.json()) - .then(data => console.log('登录接口:', data)) - .catch(err => console.error('登录接口错误:', err)); - -// 测试2:环境检查 -fetch('/addons/shopro/ccbtest/checkConfig') - .then(res => res.json()) - .then(data => console.log('配置检查:', data)) - .catch(err => console.error('配置检查错误:', err)); - -// 测试3:数据库检查 -fetch('/addons/shopro/ccbtest/checkDatabase') - .then(res => res.json()) - .then(data => console.log('数据库检查:', data)) - .catch(err => console.error('数据库检查错误:', err)); -``` - -#### 预期结果 -- 接口返回 JSON 格式数据 -- 不应该出现 404 或 500 错误 - -### 5. 支付功能测试 - -#### 测试目标 -验证建行支付流程是否正常。 - -#### 前置条件 -- 已登录用户 -- 有可支付的订单 - -#### 测试步骤 - -1. **创建测试订单**(可选) -``` -访问: http://fengketrade.test/#/pages/goods/index?id=1 -添加商品到购物车 → 去结算 → 提交订单 -``` - -2. **选择建行支付** - -在支付页面,如果在建行环境中,应该看到"建行支付"选项。 - -3. **模拟支付调用** - -在 Console 中执行: -```javascript -// 注意:这需要有实际的订单 -sheep.$platform.pay('ccb', 'goods', 'ORDER202501170001'); -``` - -#### 预期结果 -- 非建行环境:提示"请在建行生活App内使用建行支付" -- 建行环境(模拟):尝试调用 JSBridge - -### 6. 日志和错误测试 - -#### 测试目标 -验证错误处理和日志记录是否正常。 - -#### 测试步骤 - -1. **查看 Console 日志** - -启动应用后,在 Console 中应该看到: -``` -[CcbLife] 初始化完成, 是否在建行App内: false -``` - -2. **触发错误** - -在非建行环境调用建行功能: -```javascript -// 应该输出友好的错误提示 -sheep.$platform.useProvider('ccb').getUserInfo() - .catch(err => console.log('预期的错误:', err)); -``` - -#### 预期结果 -- 有清晰的日志输出 -- 错误信息友好可读 - -## 使用静态测试页面 - -访问 `http://fengketrade.test/static/ccb-test.html`(或前端开发服务器对应地址) - -### 测试页面功能 - -1. **环境信息显示** - - 自动检测当前平台 - - 显示是否在建行App内 - - 显示 User-Agent 和 URL 参数 - -2. **模块加载测试** - - 点击"测试模块导入"按钮 - - 检查建行模块文件是否存在 - -3. **环境检测测试** - - 点击"测试环境检测"按钮 - - 查看多种检测方式的结果 - -4. **URL参数测试** - - 点击"测试URL参数解析"按钮 - - 验证参数解析功能 - -### 模拟建行环境 - -在测试页面 URL 后添加参数: -``` -?from=ccblife&ccbParamSJ=testdata123 -``` - -完整示例: -``` -http://localhost:3000/static/ccb-test.html?from=ccblife&ccbParamSJ=testdata123 -``` - -## 常见问题排查 - -### 问题1:页面打不开 - -**症状**:访问建行页面显示 404 - -**排查步骤**: -1. 检查 `pages.json` 是否正确配置 -```bash -grep -A 10 "ccblife" pages.json -``` - -2. 检查页面文件是否存在 -```bash -ls -l pages/ccblife/index.vue -``` - -3. 重新编译项目(在 HBuilderX 中点击"停止" → "运行") - -### 问题2:模块导入失败 - -**症状**:Console 显示 "Cannot find module" 错误 - -**排查步骤**: -1. 检查文件路径 -```bash -ls -l sheep/platform/provider/ccblife/ -``` - -2. 检查文件内容是否有语法错误 -```bash -node --check sheep/platform/provider/ccblife/index.js -``` - -3. 清除缓存重新编译 - -### 问题3:平台识别错误 - -**症状**:在建行环境中仍识别为 H5 - -**排查步骤**: -1. 检查 URL 参数 -```javascript -console.log(window.location.search); -``` - -2. 检查 User-Agent -```javascript -console.log(navigator.userAgent); -``` - -3. 手动测试检测函数 -```javascript -const urlParams = new URLSearchParams(window.location.search); -console.log('from参数:', urlParams.get('from')); -console.log('ccbParamSJ参数:', urlParams.get('ccbParamSJ')); -``` - -### 问题4:API 请求失败 - -**症状**:接口返回 404 或跨域错误 - -**排查步骤**: -1. 检查后端服务是否运行 -```bash -curl http://fengketrade.test/addons/shopro/ccbtest -``` - -2. 检查 `.env` 配置 -```bash -cat .env | grep SHOPRO_BASE_URL -``` - -3. 检查跨域配置(如果前后端分离) - -## 真机测试 - -### iOS 设备测试 - -1. **使用 Safari 调试** - - Mac 上打开 Safari → 开发 → [设备名] → [页面] - - 可以查看 Console 和调试 - -2. **HBuilderX 真机运行** - - 连接 iOS 设备 - - 运行 → 运行到手机或模拟器 → iOS - -### Android 设备测试 - -1. **使用 Chrome 调试** - - 电脑 Chrome 访问 `chrome://inspect` - - 查看设备上的页面 - -2. **HBuilderX 真机运行** - - 连接 Android 设备(开启 USB 调试) - - 运行 → 运行到手机或模拟器 → Android - -## 性能测试 - -### 加载时间 - -在 Console 中查看: -```javascript -// 查看性能数据 -console.log(performance.timing); - -// 计算加载时间 -const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart; -console.log('页面加载时间:', loadTime + 'ms'); -``` - -### 内存使用 - -使用 Chrome DevTools: -1. Performance → 录制 -2. 操作页面 -3. 停止录制 -4. 查看内存使用情况 - -## 测试报告模板 - -```markdown -## 建行生活前端测试报告 - -**测试日期**: 2025-01-17 -**测试环境**: [开发环境/测试环境] -**测试人员**: [姓名] - -### 测试结果 - -| 测试项 | 状态 | 备注 | -|-------|------|------| -| 模块加载 | ✅ 通过 | 所有模块正常加载 | -| 环境检测 | ✅ 通过 | 正确识别建行环境 | -| 页面路由 | ✅ 通过 | 页面可以正常访问 | -| API接口 | ✅ 通过 | 接口返回正常 | -| 支付功能 | ⏳ 待测试 | 需要建行真机环境 | - -### 发现的问题 - -1. [问题描述] - - **严重程度**: 高/中/低 - - **复现步骤**: ... - - **预期行为**: ... - - **实际行为**: ... - -### 建议 - -1. [建议内容] -``` - ---- - -*文档版本:1.0.0* -*最后更新:2025-01-17* -*作者:Billy* \ No newline at end of file diff --git a/doc/ccblife-implementation-guide.md b/doc/ccblife-implementation-guide.md deleted file mode 100644 index 4a21e05..0000000 --- a/doc/ccblife-implementation-guide.md +++ /dev/null @@ -1,252 +0,0 @@ -# 建行生活 H5 商城对接实施指南 - -## 项目概述 - -本文档描述了 Shopro 商城系统与建行生活 App 的完整对接方案实现。所有代码已根据您的实际数据库结构进行调整,确保与现有系统完美兼容。 - -## 已实现功能清单 - -### 1. 核心加密模块 -- ✅ **RSA 加密解密** (`CcbRSA.php`):支持 1024 位 RSA,117/128 字节分段处理 -- ✅ **MD5 签名** (`CcbMD5.php`):API 消息签名和支付串签名 -- ✅ **URL 参数解密** (`CcbUrlDecrypt.php`):双层 BASE64 + DES 解密 - -### 2. 业务服务类 -- ✅ **HTTP 客户端** (`CcbHttpClient.php`):处理与建行 API 的通信 -- ✅ **订单服务** (`CcbOrderService.php`):订单推送、查询、状态更新、退款 -- ✅ **支付服务** (`CcbPaymentService.php`):支付串生成、回调处理、支付验证 - -### 3. 控制器接口 -- ✅ **用户登录** (`Ccblife.php`):建行用户登录、自动登录、用户绑定 -- ✅ **支付处理** (`Ccbpayment.php`):生成支付串、处理回调、推送订单 -- ✅ **测试接口** (`Ccbtest.php`):全面的功能测试接口 - -### 4. 前端集成 -- ✅ **JSBridge 库** (`ccblife-bridge.js`):封装建行原生交互功能 -- ✅ **示例页面** (`ccblife-demo.html`):演示如何使用 JSBridge - -## 数据库结构(已执行) - -### 用户表改造 -```sql -ALTER TABLE `fa_user` -ADD COLUMN `ccb_user_id` varchar(50) DEFAULT NULL COMMENT '建行用户ID'; -``` - -### 订单表改造 -```sql -ALTER TABLE `fa_shopro_order` -ADD COLUMN `ccb_user_id` varchar(30) DEFAULT NULL COMMENT '建行用户ID', -ADD COLUMN `ccb_pay_flow_id` varchar(50) DEFAULT NULL COMMENT '建行支付流水号', -ADD COLUMN `ccb_sync_status` tinyint(1) DEFAULT '0' COMMENT '建行同步状态', -ADD COLUMN `ccb_sync_time` int(11) DEFAULT NULL COMMENT '建行同步时间'; -``` - -### 支付日志表 -```sql --- fa_ccb_payment_log 表用于记录支付流水 -``` - -### 同步日志表 -```sql --- fa_ccb_sync_log 表用于记录与建行的同步日志 -``` - -## 配置说明 - -配置文件位置:`/addons/shopro/config/ccblife.php` - -关键配置项(从 .env 读取): -- `CCB_SERVICE_ID`: 服务方编号(生产:YS44000009001853) -- `CCB_MERCHANT_ID`: 商户号 -- `CCB_POS_ID`: POS 号 -- `CCB_BRANCH_ID`: 分行号 -- `CCB_PRIVATE_KEY`: RSA 私钥(BASE64 格式) -- `CCB_PUBLIC_KEY`: RSA 公钥(BASE64 格式) - -## 接口调用流程 - -### 1. 用户登录流程 - -```mermaid -sequenceDiagram - participant U as 用户 - participant CCB as 建行App - participant H5 as H5商城 - participant API as 后端API - participant DB as 数据库 - - U->>CCB: 点击进入商城 - CCB->>H5: 跳转URL(携带ccbParamSJ) - H5->>API: 调用 /ccblife/login - API->>API: 解密ccbParamSJ参数 - API->>DB: 查询/创建用户 - DB-->>API: 返回用户信息 - API->>API: 生成Token - API-->>H5: 返回Token和用户信息 - H5-->>U: 显示商城首页 -``` - -### 2. 支付流程 - -```mermaid -sequenceDiagram - participant U as 用户 - participant H5 as H5商城 - participant API as 后端API - participant CCB as 建行App - participant CCBAPI as 建行API - - U->>H5: 提交订单 - H5->>API: 创建订单 - API-->>H5: 返回订单信息 - H5->>API: 请求支付串 /ccbpayment/createPayment - API->>API: 生成支付串和MAC签名 - API-->>H5: 返回支付串 - H5->>CCB: 调用JSBridge发起支付 - CCB->>U: 显示收银台 - U->>CCB: 确认支付 - CCB->>CCBAPI: 处理支付 - CCBAPI-->>CCB: 支付结果 - CCB->>H5: 返回支付结果 - H5->>API: 回调 /ccbpayment/callback - API->>CCBAPI: 验证支付结果 - API->>API: 更新订单状态 - API->>CCBAPI: 推送订单信息 - API-->>H5: 返回最终结果 -``` - -## 测试步骤 - -### 1. 环境检查 -访问:`http://fengketrade.test/addons/shopro/ccbtest` - -检查项: -- 配置检查:`/ccbtest/checkConfig` -- 数据库检查:`/ccbtest/checkDatabase` - -### 2. 功能测试 - -#### 基础功能 -- RSA 测试:`/ccbtest/testRsa` -- MD5 测试:`/ccbtest/testMd5` -- URL 解密:`/ccbtest/testUrlDecrypt` - -#### 业务功能 -- 用户登录:`POST /ccbtest/testUserLogin` -- 订单推送:`/ccbtest/testOrderPush?order_id=1` -- 支付串生成:`/ccbtest/testPaymentString?order_id=1` - -### 3. 前端测试 -1. 访问示例页面:`http://fengketrade.test/ccblife-demo.html` -2. 测试各项功能: - - 检测运行环境 - - 获取用户信息 - - 自动登录 - - 模拟支付 - -## 前端集成示例 - -### 初始化 JSBridge -```javascript -// 在页面加载时初始化 -CcbLifeBridge.init({ - debug: true, - apiBaseUrl: '/addons/shopro' -}); -``` - -### 获取用户信息 -```javascript -CcbLifeBridge.getUserInfo(function(result) { - if (result.success) { - console.log('用户信息:', result.data); - // result.data 包含: ccb_user_id, mobile, nickname, avatar - } -}); -``` - -### 调起支付 -```javascript -// 先调用后端生成支付串 -fetch('/addons/shopro/ccbpayment/createPayment', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'token': localStorage.getItem('ccb_token') - }, - body: JSON.stringify({ order_id: orderId }) -}) -.then(response => response.json()) -.then(data => { - if (data.code === 1) { - // 调起建行支付 - CcbLifeBridge.payment({ - payment_string: data.data.payment_string - }, function(result) { - if (result.success) { - // 支付成功,通知后端 - notifyPaymentSuccess(orderId); - } - }); - } -}); -``` - -## 注意事项 - -### 1. 安全要求 -- 所有敏感配置必须存储在 `.env` 文件中 -- RSA 密钥必须是 BASE64 格式(不含 PEM 头尾) -- 生产环境必须启用 HTTPS - -### 2. 数据同步 -- 订单状态变更必须同步到建行 -- 使用 `fa_ccb_sync_log` 表记录所有同步操作 -- 支持批量同步未同步的订单 - -### 3. 错误处理 -- 所有 API 调用都有重试机制(默认 3 次) -- 失败的同步任务会记录到日志表 -- 支付失败需要明确提示用户 - -### 4. 兼容性 -- iOS:通过 URL Scheme 调起支付(comccbpay://) -- Android:通过 window.mbspay 对象调用 -- H5:在非建行环境降级处理 - -## 部署检查清单 - -- [ ] 确认 `.env` 配置正确 -- [ ] 数据库表结构已更新 -- [ ] RSA 密钥已配置 -- [ ] HTTPS 证书已安装 -- [ ] 前端 JS 文件已部署 -- [ ] 测试接口访问正常 -- [ ] 日志目录可写 - -## 常见问题 - -### Q: RSA 加密失败 -A: 检查密钥格式是否为 BASE64,不要包含 PEM 头尾标记 - -### Q: 用户登录失败 -A: 确认 ccbParamSJ 参数正确,服务方编号与配置一致 - -### Q: 订单推送失败 -A: 检查必需的 34 个字段是否都已填充,特别注意日期格式 - -### Q: 支付无响应 -A: 确认在建行 App 内访问,检查 JSBridge 是否就绪 - -## 技术支持 - -如有问题,请检查: -1. 系统日志:`runtime/log/ccblife/` -2. 同步日志:`fa_ccb_sync_log` 表 -3. 支付日志:`fa_ccb_payment_log` 表 - ---- - -*最后更新:2025-01-17* -*作者:Billy* \ No newline at end of file diff --git a/doc/ccblife-test-summary.md b/doc/ccblife-test-summary.md deleted file mode 100644 index 9a0daa4..0000000 --- a/doc/ccblife-test-summary.md +++ /dev/null @@ -1,321 +0,0 @@ -# 建行生活前端测试摘要 - -## 📋 测试环境配置完成 - -**日期**: 2025-01-17 -**状态**: ✅ 就绪 - -## 📁 已创建/修改的文件 - -### 新建文件 (6个) - -1. `/frontend/sheep/platform/provider/ccblife/index.js` (347行) - - 建行生活平台核心模块 - - 环境检测、JSBridge、用户信息、支付功能 - -2. `/frontend/sheep/platform/provider/ccblife/api.js` (69行) - - 建行 API 接口封装 - - 登录、支付相关接口 - -3. `/frontend/pages/ccblife/index.vue` (373行) - - 建行生活专属页面 - - 用户信息、专属活动、专享商品展示 - -4. `/frontend/static/ccb-test.html` (620行) - - 前端集成测试页面 - - 环境检测、模块加载、功能测试 - -5. `/doc/ccblife-uniapp-integration.md` - - uni-app 集成指南文档 - -6. `/doc/ccblife-frontend-testing-guide.md` - - 完整的测试指南文档 - -### 修改文件 (3个) - -1. `/frontend/sheep/platform/index.js` - - ✅ 添加 ccblife 模块导入 - - ✅ 添加建行环境检测 - - ✅ 添加 ccb provider 支持 - -2. `/frontend/sheep/platform/pay.js` - - ✅ 添加建行支付方式 - - ✅ 实现 ccbPay() 方法 - - ✅ 集成支付流程 - -3. `/frontend/pages.json` - - ✅ 添加建行页面路由配置 - -## ✅ 文件验证结果 - -| 检查项 | 状态 | 说明 | -|--------|------|------| -| JavaScript 语法 | ✅ 通过 | 所有 JS 文件语法正确 | -| JSON 格式 | ✅ 通过 | pages.json 格式正确 | -| 文件完整性 | ✅ 通过 | 所有文件已创建 | -| 代码行数 | ✅ 正常 | 共 516 行核心代码 | - -## 🚀 快速开始测试 - -### 方式 1: 使用测试页面(最简单) - -```bash -# 1. 访问后端测试页面 -http://fengketrade.test/ccblife-demo.html - -# 2. 或访问前端测试页面(需启动前端服务) -http://localhost:3000/static/ccb-test.html - -# 3. 模拟建行环境 -添加 URL 参数: ?from=ccblife&ccbParamSJ=test -``` - -### 方式 2: 使用 HBuilderX(推荐) - -```bash -# 1. 打开 HBuilderX -# 2. 文件 → 打开目录 → 选择 frontend -# 3. 运行 → 运行到浏览器 → Chrome -# 4. 访问: http://localhost:8080/#/pages/ccblife/index -``` - -### 方式 3: 使用命令行(开发者) - -```bash -cd frontend - -# 安装依赖(如果还没安装) -npm install - -# 启动开发服务器(如果项目支持) -npm run dev:h5 -# 或 -npx vite -``` - -## 🧪 核心测试点 - -### 1. 环境检测测试 ⏳ - -**测试目标**: 验证系统能否正确识别建行生活 App - -**测试方法**: -```javascript -// 在浏览器 Console 中执行 -console.log(sheep.$platform); -// 预期输出: { name: 'CcbLife', provider: 'ccb', ... } -``` - -**测试场景**: -- ✅ 普通浏览器 → 应识别为 H5 -- ✅ 带 from=ccblife 参数 → 应识别为 CcbLife -- ✅ 带 ccbParamSJ 参数 → 应识别为 CcbLife - -### 2. 页面路由测试 ⏳ - -**测试目标**: 验证建行专属页面可以正常访问 - -**测试方法**: -``` -访问: http://localhost:3000/#/pages/ccblife/index -``` - -**预期结果**: -- ✅ 页面正常加载 -- ✅ 显示"建行生活"标题 -- ✅ 导航栏背景色为建行红 (#F51C13) - -### 3. 模块导入测试 ⏳ - -**测试目标**: 验证建行模块正确导入 - -**测试方法**: -```javascript -// 检查文件是否可访问 -fetch('/sheep/platform/provider/ccblife/index.js') - .then(res => console.log('建行模块:', res.ok ? '✓ 存在' : '✗ 不存在')); -``` - -### 4. API 接口测试 ⏳ - -**测试目标**: 验证前端能调用后端建行接口 - -**测试方法**: -```bash -# 方法1: 使用 curl -curl http://fengketrade.test/addons/shopro/ccbtest - -# 方法2: 浏览器访问 -http://fengketrade.test/addons/shopro/ccbtest/checkConfig -``` - -**预期结果**: -- ✅ 返回 JSON 数据 -- ✅ code 字段为 1(成功) - -### 5. 支付流程测试 ⏳ - -**测试目标**: 验证建行支付调用流程 - -**前置条件**: 在建行环境中 - -**测试方法**: -```javascript -// 模拟支付调用 -sheep.$platform.pay('ccb', 'goods', 'ORDER_TEST_001'); -``` - -**预期结果**: -- ✅ 非建行环境: 提示"请在建行生活App内使用" -- ✅ 建行环境: 尝试调用 JSBridge - -## 📊 测试覆盖范围 - -``` -核心功能模块 -├── 环境检测 ✅ 已实现 ⏳ 待测试 -├── 用户信息获取 ✅ 已实现 ⏳ 待测试 -├── 自动登录 ✅ 已实现 ⏳ 待测试 -├── 支付集成 ✅ 已实现 ⏳ 待测试 -├── JSBridge 调用 ✅ 已实现 ⏳ 待测试 -└── 专属页面 ✅ 已实现 ⏳ 待测试 - -平台兼容 -├── H5 环境 ✅ 已实现 ⏳ 待测试 -├── iOS JSBridge ✅ 已实现 ⏳ 需真机 -├── Android 集成 ✅ 已实现 ⏳ 需真机 -└── 小程序 ⚠️ 不支持(符合预期) - -API 接口 -├── 用户登录 ✅ 已实现 ⏳ 待测试 -├── 自动登录 ✅ 已实现 ⏳ 待测试 -├── 支付串生成 ✅ 已实现 ⏳ 待测试 -└── 支付回调 ✅ 已实现 ⏳ 待测试 -``` - -## 🔍 已知限制 - -1. **真机测试依赖** - - JSBridge 功能需要在真实建行 App 中测试 - - 支付功能需要建行生产环境 - -2. **小程序限制** - - 微信小程序不支持建行支付 - - 系统会自动降级处理 - -3. **开发环境限制** - - 本地无法完整模拟建行 JSBridge - - 可以通过 URL 参数模拟环境判断 - -## 📝 下一步行动 - -### 立即可以测试(本地) - -- [x] 文件语法检查 -- [x] JSON 格式验证 -- [ ] 启动开发服务器 -- [ ] 访问测试页面 -- [ ] 验证环境检测 -- [ ] 验证页面路由 -- [ ] 测试 API 接口调用 - -### 需要建行环境(真机) - -- [ ] JSBridge 功能测试 -- [ ] 获取建行用户信息 -- [ ] 自动登录流程 -- [ ] 支付功能完整流程 -- [ ] URL 参数解密 - -### 建议测试顺序 - -1. **第一阶段: 本地基础测试** - ```bash - # 1. 访问测试页面 - http://localhost:3000/static/ccb-test.html - - # 2. 点击"测试模块导入" - # 3. 点击"测试环境检测" - # 4. 点击"测试URL参数解析" - ``` - -2. **第二阶段: 接口联调测试** - ```bash - # 1. 测试配置检查 - curl http://fengketrade.test/addons/shopro/ccbtest/checkConfig - - # 2. 测试数据库检查 - curl http://fengketrade.test/addons/shopro/ccbtest/checkDatabase - - # 3. 测试 RSA 加密 - curl http://fengketrade.test/addons/shopro/ccbtest/testRsa - ``` - -3. **第三阶段: 真机环境测试** - - 在建行生活 App 内打开测试链接 - - 验证 JSBridge 功能 - - 测试完整的支付流程 - -## 📚 相关文档 - -- **集成指南**: `/doc/ccblife-uniapp-integration.md` -- **测试指南**: `/doc/ccblife-frontend-testing-guide.md` -- **实施指南**: `/doc/ccblife-implementation-guide.md` -- **技术方案**: `/fangan.md` - -## 💡 测试技巧 - -### 快速验证模块加载 - -```javascript -// 在浏览器 Console 中粘贴执行 -(() => { - console.group('🏦 建行生活模块验证'); - - // 1. 平台对象 - console.log('平台对象:', window.sheep?.$platform || '未找到'); - - // 2. 平台名称 - console.log('平台名称:', window.sheep?.$platform?.name || '未知'); - - // 3. 提供商 - console.log('提供商:', window.sheep?.$platform?.provider || '未知'); - - // 4. 是否建行环境 - const isCcb = window.sheep?.$platform?.provider === 'ccb'; - console.log('建行环境:', isCcb ? '✓ 是' : '✗ 否'); - - console.groupEnd(); -})(); -``` - -### 模拟建行参数 - -```javascript -// 方式1: 修改 URL(刷新后生效) -const url = new URL(window.location); -url.searchParams.set('from', 'ccblife'); -url.searchParams.set('ccbParamSJ', 'test123'); -window.location.href = url.toString(); - -// 方式2: 直接访问 -window.location.href = '?from=ccblife&ccbParamSJ=test123'; -``` - -## ❓ 问题排查 - -遇到问题?查看**测试指南**的"常见问题排查"部分: -``` -/doc/ccblife-frontend-testing-guide.md#常见问题排查 -``` - ---- - -**测试状态图例**: -- ✅ 已完成 -- ⏳ 待测试 -- ❌ 失败 -- ⚠️ 有限制 - -*摘要生成时间: 2025-01-17* -*作者: Billy* \ No newline at end of file diff --git a/doc/ccblife-uniapp-integration.md b/doc/ccblife-uniapp-integration.md deleted file mode 100644 index ca0f5b2..0000000 --- a/doc/ccblife-uniapp-integration.md +++ /dev/null @@ -1,363 +0,0 @@ -# 建行生活 uni-app 前端集成指南 - -## 概述 - -本文档介绍了如何在 Shopro uni-app 前端项目中集成建行生活功能,包括用户登录、支付、专属页面等功能。 - -## 已实现的前端功能 - -### 1. 平台集成模块 - -**位置**: `/frontend/sheep/platform/provider/ccblife/` - -- `index.js` - 建行生活平台核心模块 - - 环境检测(检测是否在建行App内) - - JSBridge 初始化和管理 - - 用户信息获取 - - 自动登录 - - 支付调起 - - 原生方法调用 - -- `api.js` - API 接口封装 - - URL 跳转登录 - - 自动登录 - - 支付串生成 - - 支付回调 - -### 2. 支付集成 - -**位置**: `/frontend/sheep/platform/pay.js` - -已在现有支付系统中添加了建行支付(ccb)支持: - -```javascript -// 支付方式现在支持 -payment = ['wechat','alipay','ccb','money','offline'] -``` - -### 3. 平台识别 - -**位置**: `/frontend/sheep/platform/index.js` - -系统会自动识别建行生活环境: -- 平台名称:`CcbLife` -- 提供商:`ccb` -- 平台标识:`ccblife` - -### 4. 建行专属页面 - -**位置**: `/frontend/pages/ccblife/index.vue` - -建行生活专属页面,展示: -- 建行用户信息 -- 专属优惠活动 -- 专享商品 -- 建行特色功能 - -## 使用指南 - -### 1. 检测建行环境 - -```javascript -import sheep from '@/sheep'; - -// 检查是否在建行App内 -if (sheep.$platform.provider === 'ccb') { - console.log('在建行生活App内'); -} - -// 或直接使用平台对象 -import CcbLifePlatform from '@/sheep/platform/provider/ccblife/index'; - -if (CcbLifePlatform.isInCcbApp) { - console.log('在建行生活App内'); -} -``` - -### 2. 获取建行用户信息 - -```javascript -import CcbLifePlatform from '@/sheep/platform/provider/ccblife/index'; - -// 获取用户信息 -CcbLifePlatform.getUserInfo().then(result => { - if (result.code === 0) { - console.log('用户信息:', result.data); - // data包含: ccb_user_id, mobile, nickname, avatar - } -}).catch(error => { - console.error('获取用户信息失败:', error); -}); -``` - -### 3. 自动登录流程 - -平台会自动处理登录流程,无需手动调用: - -1. 用户从建行App进入H5页面 -2. 系统自动检测建行环境 -3. 自动获取用户信息并登录 -4. 保存Token到本地存储 - -手动触发登录: - -```javascript -import CcbLifePlatform from '@/sheep/platform/provider/ccblife/index'; - -// 手动触发自动登录 -CcbLifePlatform.autoLogin(); -``` - -### 4. 建行支付集成 - -#### 在订单支付页面 - -```javascript -// 支付方式选择 -const payMethods = [ - { - value: 'wechat', - name: '微信支付', - icon: 'wechat' - }, - { - value: 'alipay', - name: '支付宝', - icon: 'alipay' - }, - { - value: 'ccb', - name: '建行支付', - icon: 'ccb', - // 只在建行App内显示 - show: sheep.$platform.provider === 'ccb' - } -]; -``` - -#### 发起支付 - -```javascript -import sheep from '@/sheep'; - -// 调起支付 -sheep.$platform.pay('ccb', 'goods', orderSN); -``` - -### 5. 监听事件 - -```javascript -// 监听登录成功事件 -uni.$on('ccb:login:success', (data) => { - console.log('建行用户登录成功', data); - // 更新UI或执行其他操作 -}); - -// 在页面销毁时移除监听 -onUnmounted(() => { - uni.$off('ccb:login:success'); -}); -``` - -### 6. 条件编译 - -在需要区分平台的代码中使用条件编译: - -```javascript -// #ifdef H5 -import CcbLifePlatform from '@/sheep/platform/provider/ccblife/index'; - -if (CcbLifePlatform.isInCcbApp) { - // 建行App内的特殊处理 -} -// #endif -``` - -## 页面路由配置 - -在 `pages.json` 中添加建行相关页面: - -```json -{ - "pages": [ - { - "path": "pages/ccblife/index", - "style": { - "navigationBarTitleText": "建行生活", - "navigationBarBackgroundColor": "#F51C13", - "navigationBarTextStyle": "white" - } - } - ] -} -``` - -## 测试流程 - -### 1. 本地测试 - -```bash -# 进入前端目录 -cd frontend - -# 安装依赖 -npm install - -# 运行H5 -npm run dev:h5 -``` - -### 2. 模拟建行环境 - -在浏览器中添加URL参数模拟建行环境: -``` -http://localhost:3000/#/pages/index/index?from=ccblife&ccbParamSJ=xxx -``` - -### 3. 真机测试 - -1. 使用 HBuilderX 打包H5应用 -2. 部署到测试服务器 -3. 在建行生活App内访问测试地址 - -## 注意事项 - -### 1. 平台判断优先级 - -系统按以下优先级判断平台: -1. 建行生活 (`CcbLife`) -2. 微信公众号 (`WechatOfficialAccount`) -3. 普通H5 (`H5`) - -### 2. JSBridge 兼容性 - -- iOS:使用 WebViewJavascriptBridge -- Android:使用 window.mbspay 对象 - -### 3. 支付流程 - -1. 用户选择建行支付 -2. 调用后端生成支付串 -3. 通过JSBridge调起建行收银台 -4. 支付完成后回调通知后端 -5. 更新订单状态 - -### 4. 错误处理 - -```javascript -try { - const result = await CcbLifePlatform.payment(options); - // 处理成功 -} catch (error) { - if (error.code === -1) { - // 不在建行App内 - uni.showToast({ - title: '请在建行生活App内使用', - icon: 'none' - }); - } else { - // 其他错误 - console.error(error); - } -} -``` - -## 常见问题 - -### Q: 如何判断是否在建行App内? - -```javascript -import sheep from '@/sheep'; - -// 方式1:通过平台对象 -if (sheep.$platform.provider === 'ccb') { - // 在建行App内 -} - -// 方式2:直接使用建行平台模块 -import CcbLifePlatform from '@/sheep/platform/provider/ccblife/index'; -if (CcbLifePlatform.isInCcbApp) { - // 在建行App内 -} -``` - -### Q: 自动登录失败怎么办? - -1. 检查是否在建行App内 -2. 确认后端接口正常 -3. 查看控制台错误信息 -4. 清除本地缓存重试 - -```javascript -// 清除缓存 -uni.removeStorageSync('token'); -uni.removeStorageSync('userInfo'); - -// 重新触发登录 -CcbLifePlatform.autoLogin(); -``` - -### Q: 支付无响应? - -1. 确认在建行App内 -2. 检查JSBridge是否就绪 -3. 验证支付串格式 -4. 查看原生日志 - -```javascript -// 检查JSBridge状态 -CcbLifePlatform.ready(() => { - console.log('JSBridge已就绪'); -}); -``` - -### Q: 如何调试? - -在建行平台模块中开启调试模式: - -```javascript -// frontend/sheep/platform/provider/ccblife/index.js -const config = { - debug: true // 开启调试 -}; -``` - -## 扩展开发 - -### 添加新的原生方法调用 - -```javascript -// 在 CcbLifePlatform 中添加新方法 -async customMethod() { - return new Promise((resolve, reject) => { - this.callNative('customMethod', { - // 参数 - }, (result) => { - if (result.success) { - resolve(result); - } else { - reject(result); - } - }); - }); -} -``` - -### 添加新的API接口 - -```javascript -// 在 api.js 中添加 -newApi: (data) => { - return request({ - url: '/ccblife/newApi', - method: 'POST', - data: data - }); -} -``` - ---- - -*文档版本:1.0.0* -*最后更新:2025-01-17* -*作者:Billy* \ No newline at end of file diff --git a/doc/ccblife_bugfix_summary.md b/doc/ccblife_bugfix_summary.md deleted file mode 100644 index 98ad4e5..0000000 --- a/doc/ccblife_bugfix_summary.md +++ /dev/null @@ -1,539 +0,0 @@ -# 建行生活H5商城对接 - Bug修复总结 - -> 修复时间: 2025-01-18 -> 审查人员: Billy (Claude Code) - ---- - -## 📊 Bug统计 - -| 级别 | 数量 | 说明 | -|------|------|------| -| P0 - 致命 | 7 | 导致功能完全无法使用 | -| P1 - 严重 | 0 | 影响核心功能但有workaround | -| P2 - 一般 | 0 | 影响次要功能 | -| **总计** | **7** | - | - ---- - -## 🔴 P0级Bug清单 - -### Bug #1: MD5签名大小写混用 -**文件**: `CcbMD5.php` -**行号**: 未区分 -**发现时间**: 初次代码审查 - -**问题描述**: -代码未区分API接口签名和支付串签名的MD5大小写要求,全部使用大写。 - -**错误代码**: -```php -// 错误:API和支付串都用大写 -public static function sign($message, $privateKey) -{ - return strtoupper(md5($message . $privateKey)); -} -``` - -**根本原因**: -根据建行文档: -- **API接口签名**: MD5签名后转大写 -- **支付串签名**: MD5签名后保持小写 - -**修复方案**: -```php -/** - * API接口签名(使用大写MD5) - */ -public static function signApiMessage($message, $privateKey) -{ - return strtoupper(md5($message . $privateKey)); -} - -/** - * 支付串签名(使用小写MD5) - */ -public static function signPaymentString($params, $privateKey) -{ - return md5($paymentString); // 保持小写 -} -``` - -**影响范围**: -- ❌ 支付串签名验证失败 -- ❌ 建行收银台无法打开 -- ❌ 支付回调签名验证失败 - -**验证方法**: -```bash -# 测试支付串生成 -php addons/shopro/test/ccblife_test.php -# 检查输出的MAC签名是否为小写32位字符串 -``` - ---- - -### Bug #2: 支付串缺少必需参数 -**文件**: `CcbPaymentService.php` -**行号**: 原66-86(已修复) -**发现时间**: 初次代码审查 - -**问题描述**: -支付串只包含10个参数,缺少建行要求的必需参数。 - -**错误代码**: -```php -// 原代码只有10个参数 -$paymentParams = [ - 'MERCHANTID' => $this->config['merchant_id'], - 'POSID' => $this->config['pos_id'], - 'ORDERID' => $payFlowId, - 'PAYMENT' => $order['total_fee'], - // ... 只有10个参数 -]; -``` - -**根本原因**: -未按照建行支付串规范包含所有必需字段,特别是: -- CLIENTIP(客户端IP) -- PROINFO(商品信息) -- THIRDAPPINFO(第三方应用信息) -- USER_ORDERID(商户订单号) -- TIMEOUT(超时时间) - -**修复方案**: -```php -// 修复后包含18个核心参数 -$paymentParams = [ - 'MERCHANTID' => $this->config['merchant_id'], - 'POSID' => $this->config['pos_id'], - 'BRANCHID' => $this->config['branch_id'], - 'ORDERID' => $payFlowId, // 支付流水号 - 'PAYMENT' => number_format($order['total_fee'], 2, '.', ''), - 'CURCODE' => '01', - 'TXCODE' => '520100', - 'REMARK1' => '', - 'REMARK2' => $this->config['service_id'], - 'TYPE' => '1', - 'GATEWAY' => '0', - 'CLIENTIP' => request()->ip(), - 'REGINFO' => '', - 'PROINFO' => $this->buildProductInfo($order), - 'REFERER' => '', - 'THIRDAPPINFO' => 'comccbpay1234567890cloudmerchant', - 'USER_ORDERID' => $order['order_sn'], // 商户订单号 - 'TIMEOUT' => date('YmdHis', strtotime('+30 minutes')) -]; -``` - -**影响范围**: -- ❌ 支付串验证失败 -- ❌ 建行收银台拒绝请求 -- ❌ 无法完成支付 - ---- - -### Bug #3: 支付回调notify()方法空实现 -**文件**: `Ccbpayment.php` -**行号**: 原217-230(已修复) -**发现时间**: 初次代码审查 - -**问题描述**: -异步通知方法只有TODO注释,没有实际业务逻辑。 - -**错误代码**: -```php -public function notify() -{ - // TODO: 处理建行异步通知 - echo 'FAIL'; - return; -} -``` - -**根本原因**: -开发时未实现异步通知处理逻辑。 - -**修复方案**: -```php -public function notify() -{ - try { - // 1. 获取原始请求数据 - $rawData = file_get_contents('php://input'); - Log::info('[建行通知] 收到异步通知: ' . $rawData); - - // 2. 解析POST参数 - $params = $this->request->post(); - if (empty($params) && $rawData) { - parse_str($rawData, $params); - } - - // 3. 验证必需参数 - if (empty($params['ORDERID'])) { - echo 'FAIL'; - return; - } - - // 4. 调用支付服务处理通知 - $result = $this->paymentService->handleNotify($params); - - // 5. 返回结果 - echo $result; // 'SUCCESS' 或 'FAIL' - } catch (Exception $e) { - Log::error('[建行通知] 处理失败 error:' . $e->getMessage()); - echo 'FAIL'; - } -} -``` - -**影响范围**: -- ❌ 异步通知处理失败 -- ❌ 订单状态无法自动更新 -- ❌ 建行会持续重发通知 - ---- - -### Bug #4: 支付流水号与订单号混淆 -**文件**: -- `CcbPaymentService.php` (多处) -- `Ccbpayment.php` (多处) - -**行号**: 409, 98, 111, 187-212, 272-283 -**发现时间**: 深度代码审查 - -**问题描述**: -代码将支付流水号(pay_flow_id)和订单号(order_sn)混用,特别是在: -1. 日志记录时用了 `order_sn` 代替 `pay_flow_id` -2. 返回数据时用了 `order_sn` 代替 `pay_flow_id` -3. 回调处理时用 `ORDERID` 查询 `order_sn` 字段 - -**错误代码**: -```php -// 错误1: 日志记录 -Db::name('ccb_payment_log')->insert([ - 'pay_flow_id' => $order['order_sn'], // ❌ 应该用真实的pay_flow_id -]); - -// 错误2: 返回数据 -$this->success('支付串生成成功', [ - 'pay_flow_id' => $result['data']['order_sn'], // ❌ 应该用真实的pay_flow_id -]); - -// 错误3: 回调处理 -$orderSn = $params['ORDERID'] ?? ''; // ❌ ORDERID是支付流水号,不是订单号! -$order = Order::where('order_sn', $orderSn)->find(); // ❌ 永远查不到订单 -``` - -**核心概念区分**: -``` -支付流水号 (pay_flow_id): -- 格式: PAY + YmdHis + 6位随机数 -- 示例: PAY20250118143000123456 -- 用途: 建行支付唯一标识 -- 对应建行字段: ORDERID - -订单号 (order_sn): -- 格式: Shopro系统生成 -- 示例: SO20250118001 -- 用途: 商城内部订单标识 -- 对应建行字段: USER_ORDERID -``` - -**修复方案**: -```php -// 修复1: 日志记录 -'pay_flow_id' => $paymentData['pay_flow_id'] ?? '', // ✅ 使用真实的支付流水号 - -// 修复2: 返回数据 -'pay_flow_id' => $result['data']['pay_flow_id'], // ✅ 返回真实的支付流水号 - -// 修复3: 回调处理 -$payFlowId = $params['ORDERID'] ?? ''; // 支付流水号 -$userOrderId = $params['USER_ORDERID'] ?? ''; // 商户订单号 - -// 优先使用USER_ORDERID查询 -if (!empty($userOrderId)) { - $order = Order::where('order_sn', $userOrderId)->find(); -} else { - $order = Order::where('ccb_pay_flow_id', $payFlowId)->find(); -} -``` - -**影响范围**: -- ❌ 支付日志记录错误 -- ❌ 前端无法获取正确的支付流水号 -- ❌ **支付回调100%失败**(订单永远查不到) -- ❌ 异步通知100%失败 -- ❌ 对账数据不准确 - -**严重程度**: 🔥🔥🔥 致命(会导致所有支付失败) - ---- - -### Bug #5: 平台公钥字段不存在 -**文件**: `CcbPaymentService.php` -**行号**: 原94-97(已修复) -**发现时间**: 用户反馈 - -**问题描述**: -代码尝试在支付串签名中追加 `PLATFORMPUB` 字段,但建行文档中没有这个参数。 - -**错误代码**: -```php -// 追加平台公钥(如果有) -if (!empty($this->config['platform_public_key'])) { - $signString .= '&PLATFORMPUB=' . $this->config['platform_public_key']; -} -$mac = md5($signString . $this->config['private_key']); -``` - -**根本原因**: -对建行签名规则理解错误,误以为需要平台公钥参与签名。 - -**正确规则**: -``` -支付串签名 = MD5(参数字符串 + 服务方私钥) -不需要平台公钥! -``` - -**修复方案**: -```php -// ⚠️ 注意:建行支付串签名规则 -// 签名 = MD5(参数字符串 + 服务方私钥) -// 不需要PLATFORMPUB字段,直接使用私钥签名 -$mac = md5($signString . $this->config['private_key']); -``` - -**影响范围**: -- ⚠️ 签名算法错误(如果配置了platform_public_key) -- ⚠️ 可能导致签名验证失败 - ---- - -### Bug #6: 订单状态更新字段错误 -**文件**: `CcbPaymentService.php` -**行号**: 原363-374(已修复) -**发现时间**: 数据库Schema审查 - -**问题描述**: -更新订单支付状态时使用了错误的字段名和数据类型。 - -**错误代码**: -```php -Order::where('id', $order['id'])->update([ - 'status' => 'paid', - 'pay_type' => 'ccb', // ❌ 枚举中没有'ccb' - 'paytime' => time(), // ❌ 字段名错误,应为paid_time - 'transaction_id' => $params['ORDERID'] ?? '', -]); -``` - -**根本原因**: -1. Shopro订单表使用 `paid_time` 字段,不是 `paytime` -2. `paid_time` 是bigint(16)毫秒时间戳,不是秒级 -3. `pay_type` 枚举值中没有 `'ccb'` 选项 - -**数据库Schema**: -```sql --- Shopro订单表字段 -`paid_time` bigint(16) NULL DEFAULT NULL COMMENT '支付成功时间', -- 毫秒时间戳 - -`pay_type` enum('wechat','alipay','money','score','offline') NULL DEFAULT NULL -``` - -**修复方案**: -```php -Order::where('id', $order['id'])->update([ - 'status' => 'paid', - 'pay_type' => 'offline', // ✅ 建行支付归类为线下银行支付 - 'paid_time' => time() * 1000, // ✅ 毫秒时间戳 - 'transaction_id' => $params['ORDERID'] ?? '', -]); -``` - -**影响范围**: -- ❌ 订单状态更新SQL执行失败 -- ❌ 支付时间无法记录 -- ❌ 订单列表显示异常 - ---- - -### Bug #7: 订单字段映射错误 -**文件**: `CcbOrderService.php` -**行号**: 原276-331(已在之前修复) -**发现时间**: 初次代码审查 - -**问题描述**: -订单同步到建行时,使用了错误的Shopro字段名。 - -**错误代码**: -```php -return [ - 'PAY_AMT' => $order['pay_amount'], // ❌ Shopro没有这个字段 - 'PAY_TIME' => date('YmdHis', $order['paytime']), // ❌ 字段名错误 -]; -``` - -**正确字段映射**: -| Shopro字段 | 建行字段 | 说明 | -|------------|----------|------| -| total_fee | PAY_AMT | 实际支付金额 | -| paid_time | PAY_TIME | 支付时间(需除以1000) | -| discount_fee | - | 优惠金额 | -| aftersale_status | REFUND_STATUS | 退款状态 | - -**修复方案**: -```php -// 计算金额(Shopro使用total_fee作为实际支付) -$payAmount = number_format($order['total_fee'] ?? 0, 2, '.', ''); - -// 处理支付时间(Shopro paid_time是毫秒时间戳) -$payTime = ''; -if (!empty($order['paid_time'])) { - $payTime = date('YmdHis', intval($order['paid_time'] / 1000)); // ✅ 除以1000转为秒 -} - -return [ - 'PAY_AMT' => $payAmount, - 'PAY_TIME' => $payTime, - 'REFUND_STATUS' => $this->mapRefundStatus($order['aftersale_status'] ?? 0), -]; -``` - -**影响范围**: -- ❌ 订单同步到建行失败 -- ❌ 建行后台订单数据错误 -- ❌ 对账金额不一致 - ---- - -## 📈 修复效果 - -### 修复前 -- ❌ 支付串签名错误 -- ❌ 支付回调100%失败 -- ❌ 异步通知无法处理 -- ❌ 订单状态无法更新 -- ❌ 订单同步失败 - -### 修复后 -- ✅ 支付串生成正确 -- ✅ 支付回调正确处理 -- ✅ 异步通知正确处理 -- ✅ 订单状态正确更新 -- ✅ 订单同步正确执行 -- ✅ 所有自动化测试通过 - ---- - -## 🧪 验证方法 - -### 1. 运行自动化测试 -```bash -cd /Users/billy/Code/fengketrade.com -php addons/shopro/test/ccblife_test.php -``` - -**预期结果**: -``` -======================================== -总计: 10 项测试 -通过: 10 项 -失败: 0 项 -======================================== -🎉 所有测试通过!系统运行正常。 -``` - -### 2. 手动验证关键点 - -**验证支付流水号格式**: -```sql -SELECT - order_sn, - ccb_pay_flow_id, - LENGTH(ccb_pay_flow_id) as length, - SUBSTRING(ccb_pay_flow_id, 1, 3) as prefix -FROM fa_shopro_order -WHERE ccb_pay_flow_id IS NOT NULL; - --- 预期: --- length = 23 --- prefix = 'PAY' -``` - -**验证订单状态更新**: -```sql -SELECT - order_sn, - status, - pay_type, - paid_time, - FROM_UNIXTIME(paid_time/1000) as paid_datetime, - transaction_id -FROM fa_shopro_order -WHERE status = 'paid' AND pay_type = 'offline'; - --- 预期: --- status = 'paid' --- pay_type = 'offline' --- paid_time > 0 (毫秒时间戳) --- transaction_id = ccb_pay_flow_id -``` - -**验证支付日志**: -```sql -SELECT - order_sn, - pay_flow_id, - LENGTH(pay_flow_id) as length, - status, - amount -FROM fa_ccb_payment_log -ORDER BY id DESC -LIMIT 10; - --- 预期: --- pay_flow_id 不等于 order_sn --- length = 23 --- pay_flow_id 格式: PAY开头 -``` - ---- - -## 📝 总结 - -本次代码审查发现并修复了**7个P0级致命bug**,这些bug会导致: - -1. **支付功能完全无法使用**(Bug #2, #4) -2. **订单状态无法更新**(Bug #6, #7) -3. **回调和通知100%失败**(Bug #3, #4) -4. **签名验证失败**(Bug #1, #5) - -修复后,系统核心逻辑已完全正确,可以进行模拟测试。 - -**下一步**: -1. ✅ 运行自动化测试脚本验证修复 -2. ⏸️ 等待建行提供测试环境配置 -3. 🔄 进行真实环境联调测试 - ---- - -## 附录:修改文件清单 - -| 文件 | 修改行数 | 修改类型 | 影响 | -|------|----------|----------|------| -| CcbMD5.php | ~50 | 重构 | 区分API和支付串签名 | -| CcbPaymentService.php | ~200 | 重大修复 | 修复支付串生成、回调处理、状态更新 | -| CcbOrderService.php | ~50 | 修复 | 修复字段映射 | -| Ccbpayment.php | ~100 | 补充实现 | 实现notify()方法 | -| CcbPaymentLog.php | +150 | 新建 | 创建支付日志模型 | -| CcbSyncLog.php | +150 | 新建 | 创建同步日志模型 | -| ccblife_test.php | +600 | 新建 | 创建自动化测试脚本 | -| ccblife_test_guide.md | +800 | 新建 | 创建测试指南文档 | - -**总修改**: ~2100行代码 - ---- - -**文档结束** diff --git a/doc/ccblife_test_guide.md b/doc/ccblife_test_guide.md deleted file mode 100644 index cd597c8..0000000 --- a/doc/ccblife_test_guide.md +++ /dev/null @@ -1,744 +0,0 @@ -# 建行生活H5商城对接 - 测试指南 - -> 文档版本: v1.0 -> 更新时间: 2025-01-18 -> 作者: Billy - ---- - -## 📋 目录 - -1. [测试概述](#测试概述) -2. [测试环境准备](#测试环境准备) -3. [自动化测试流程](#自动化测试流程) -4. [手动测试流程](#手动测试流程) -5. [常见问题排查](#常见问题排查) -6. [测试检查清单](#测试检查清单) - ---- - -## 测试概述 - -本测试方案分为两个阶段: - -### 🤖 阶段一:模拟测试(当前可用) -**目的**: 在没有建行加密数据的情况下,验证系统核心逻辑是否正确 - -**范围**: -- ✅ 环境配置检查 -- ✅ 数据库表结构验证 -- ✅ 用户和订单创建 -- ✅ 支付串生成逻辑 -- ✅ 支付流水号规则 -- ✅ 订单状态更新逻辑 -- ✅ 字段映射正确性 - -**不包含**: -- ❌ 真实RSA加密/解密 -- ❌ 真实建行API调用 -- ❌ 建行回调签名验证 -- ❌ JSBridge通信 - -### 🌐 阶段二:真实环境测试(需建行配置) -**前提**: 已获得建行提供的测试/生产环境配置 - -**范围**: -- ✅ 完整RSA加密流程 -- ✅ 建行API接口调用 -- ✅ 订单同步到建行 -- ✅ 建行收银台支付 -- ✅ 支付回调验证 -- ✅ 异步通知验证 - ---- - -## 测试环境准备 - -### 1. 系统要求 - -```bash -# 检查PHP版本(需要 >= 7.0) -php -v - -# 检查必需扩展 -php -m | grep -E 'openssl|pdo|json|mbstring' - -# 检查数据库连接 -mysql -h -u -p -e "SELECT VERSION();" -``` - -### 2. 配置文件检查 - -确保已创建配置文件:`/application/extra/ccblife.php` - -```php - '***', // 商户代码 - 'pos_id' => '***', // 柜台代码 - 'branch_id' => '***', // 分行代码 - 'service_id' => '***', // 服务方编号 - - // 密钥配置 - 'private_key' => '***', // 服务方私钥(1024位) - 'merchant_public_key' => '***', // 商户公钥(1024位) - - // API地址(测试环境) - 'api_url' => 'http://test.ccb.com/api', - 'cashier_url' => 'comccbpay://pay', - - // 回调地址 - 'callback_url' => 'https://your-domain.com/addons/shopro/ccbpayment/callback', - 'notify_url' => 'https://your-domain.com/addons/shopro/ccbpayment/notify', -]; -``` - -### 3. 数据库表检查 - -确保已执行以下SQL(如未执行): - -```sql --- 订单表添加建行支付流水号字段 -ALTER TABLE `fa_shopro_order` -ADD COLUMN `ccb_pay_flow_id` VARCHAR(50) NULL DEFAULT NULL COMMENT '建行支付流水号' -AFTER `order_sn`; - --- 用户表添加建行用户ID字段 -ALTER TABLE `fa_user` -ADD COLUMN `ccb_user_id` VARCHAR(50) NULL DEFAULT NULL COMMENT '建行用户ID' -AFTER `mobile`; - --- 支付日志表(已在install.sql中) --- fa_ccb_payment_log - --- 同步日志表(已在install.sql中) --- fa_ccb_sync_log -``` - ---- - -## 自动化测试流程 - -### 运行自动化测试脚本 - -```bash -cd /Users/billy/Code/fengketrade.com - -# 运行完整测试套件 -php addons/shopro/test/ccblife_test.php -``` - -### 预期输出示例 - -``` -======================================== - 建行生活H5商城对接 - 自动化测试 -======================================== -测试时间: 2025-01-18 14:30:00 -======================================== - -【环境检查】 - ✓ PHP版本检查 (7.4.33 >= 7.0) - ✓ 检查OpenSSL扩展 - ✓ 检查PDO扩展 - ✓ 检查JSON扩展 - ✓ 检查文件: CcbPaymentService.php - ✓ 检查文件: CcbOrderService.php - ✓ 检查文件: CcbRSA.php - ✓ 检查文件: CcbMD5.php - ✓ 检查文件: CcbEncryption.php - -【配置文件检查】 - ✓ 配置文件存在 - ✓ 商户ID配置 - ✓ POS ID配置 - ✓ 分行代码配置 - ✓ 服务方编号配置 - ✓ 服务方私钥配置 - ✓ 商户公钥配置 - -【数据库表检查】 - ✓ 检查表: fa_ccb_payment_log - ✓ 检查表: fa_ccb_sync_log - ✓ 检查表: fa_shopro_order - ✓ 检查表: fa_user - ✓ 检查订单表ccb_pay_flow_id字段 - -【创建测试用户】 - ✓ 用户创建成功 (ID: 123) - -【创建测试订单】 - ✓ 订单创建成功 (ID: 456, SN: SO20250118143000001) - -【支付串生成测试】 - ✓ 支付串生成状态 - ✓ 支付串不为空 - ✓ 支付流水号不为空 - ✓ MAC签名不为空 - ✓ 支付流水号长度正确 (23位) - ✓ 支付流水号前缀正确 (PAY) - ✓ 订单表支付流水号已更新 - ✓ 支付日志已记录 - 支付串长度: 512 字节 - 支付流水号: PAY20250118143000123456 - MAC签名: a1b2c3d4e5f6... - -【支付回调测试】 - ✓ 回调处理成功 - ✓ 回调消息正确 - ✓ 订单状态已更新为已支付 - ✓ 支付方式正确 (offline代表建行) - ✓ 支付时间已记录 - ✓ 交易单号正确 - 订单状态: paid - 支付时间: 2025-01-18 14:30:05 - -【异步通知测试】 - 通知处理结果: fail - ✓ 通知处理逻辑测试完成 - -【订单同步测试】 - ✓ 订单字段 ORD_NUM 存在 - ✓ 订单字段 PAY_AMT 存在 - ✓ 订单字段 ORD_TIME 存在 - ✓ 订单字段 PAY_TIME 存在 - ✓ 订单字段 ORD_STATUS 存在 - ✓ 订单字段 REFUND_STATUS 存在 - ✓ 订单号正确 - ✓ 支付金额正确 - 订单号: SO20250118143000001 - 支付金额: 100.00 - 订单状态: 02 - -【清理测试数据】 - ✓ 删除测试订单 (ID: 456) - ✓ 删除支付日志 - ✓ 删除测试用户 (ID: 123) - -======================================== - 测试报告 -======================================== -✓ 通过 环境检查 - 所有环境检查通过 -✓ 通过 配置文件检查 - 所有配置项完整 -✓ 通过 数据库表检查 - 所有表结构完整 -✓ 通过 创建测试用户 - 用户ID: 123 -✓ 通过 创建测试订单 - 订单ID: 456 -✓ 通过 支付串生成测试 - 支付流水号: PAY20250118143000123456 -✓ 通过 支付回调测试 - 支付回调处理成功 -✓ 通过 异步通知测试 - 通知处理逻辑测试完成 -✓ 通过 订单同步测试 - 订单数据构建正确 -✓ 通过 清理测试数据 - 所有测试数据已清理 - -======================================== -总计: 10 项测试 -通过: 10 项 -失败: 0 项 -耗时: 1.23 秒 -======================================== - -🎉 所有测试通过!系统运行正常。 -``` - ---- - -## 手动测试流程 - -### 测试1: 前端环境检测 - -**目的**: 验证建行生活APP环境识别 - -**步骤**: -1. 在建行生活APP内打开H5商城链接(待建行提供测试链接) -2. 打开浏览器控制台 -3. 查看输出: - -```javascript -// 预期输出 -[CcbLife] 初始化完成, 是否在建行App内: true -[CcbLife] JSBridge 已就绪 -``` - -**验证点**: -- `isInCcbApp` 应为 `true` -- `isReady` 应为 `true` -- User-Agent 包含 'ccblife' 或 'ccb' - ---- - -### 测试2: 用户自动登录 - -**目的**: 验证建行用户自动登录流程 - -**前提**: -- 测试1通过 -- 已在建行APP内打开H5商城 - -**步骤**: -1. 清除商城登录态:`localStorage.clear()` -2. 刷新页面 -3. 观察控制台输出 - -**预期输出**: -``` -[CcbLife] 自动登录成功 -``` - -**验证点**: -- `localStorage.getItem('token')` 不为空 -- `localStorage.getItem('userInfo')` 包含用户信息 -- 数据库 `fa_user` 表中 `ccb_user_id` 已绑定 - ---- - -### 测试3: 创建订单 - -**目的**: 验证订单创建流程 - -**步骤**: -1. 选择商品加入购物车 -2. 进入结算页 -3. 填写收货地址 -4. 提交订单 - -**验证点**: -- 订单创建成功,返回订单ID和订单号 -- 数据库 `fa_shopro_order` 表中订单状态为 `unpaid` -- `ccb_pay_flow_id` 字段为空 - ---- - -### 测试4: 生成支付串 - -**目的**: 验证支付串生成逻辑 - -**接口**: `POST /addons/shopro/ccbpayment/pay` - -**请求参数**: -```json -{ - "order_id": 123 -} -``` - -**预期响应**: -```json -{ - "code": 1, - "msg": "支付串生成成功", - "data": { - "payment_string": "MERCHANTID=...&POSID=...&MAC=...", - "payment_url": "comccbpay://pay?MERCHANTID=...&MAC=...", - "mac": "a1b2c3d4e5f6...", - "order_id": 123, - "order_sn": "SO20250118001", - "pay_flow_id": "PAY20250118143000123456", - "amount": "100.00" - } -} -``` - -**验证点**: -1. `payment_string` 包含所有必需参数 -2. `pay_flow_id` 格式正确(PAY + 14位时间戳 + 6位随机数) -3. `mac` 签名不为空 -4. 数据库订单表 `ccb_pay_flow_id` 已更新 -5. `fa_ccb_payment_log` 表中已记录支付请求 - -**关键参数检查**: -```javascript -// 从 payment_string 中提取参数 -const params = new URLSearchParams(payment_string); - -console.log('ORDERID:', params.get('ORDERID')); // 应等于 pay_flow_id -console.log('USER_ORDERID:', params.get('USER_ORDERID')); // 应等于 order_sn -console.log('PAYMENT:', params.get('PAYMENT')); // 应等于订单实际支付金额 -console.log('MAC:', params.get('MAC')); // MD5签名 -console.log('PLATFORMID:', params.get('PLATFORMID')); // 服务方编号 -``` - ---- - -### 测试5: 调起支付(需建行环境) - -**目的**: 验证JSBridge调起建行收银台 - -**前提**: -- 测试1-4全部通过 -- 已在建行APP内 - -**步骤**: -1. 点击"去支付"按钮 -2. 前端调用 `CcbLifePlatform.payment()` - -**前端代码示例**: -```javascript -// sheep/platform/provider/ccblife/index.js - -const paymentResult = await CcbLifePlatform.payment({ - payment_string: '支付串内容' -}); - -console.log('支付结果:', paymentResult); -``` - -**iOS预期行为**: -- 跳转到 `comccbpay://pay?支付参数` -- 打开建行收银台 - -**Android预期行为**: -- 调用 `window.mbspay.payment()` -- 打开建行收银台 - -**验证点**: -- 收银台显示正确的订单金额 -- 收银台显示正确的商品信息 - ---- - -### 测试6: 支付回调(需建行环境) - -**目的**: 验证支付完成后的同步回调 - -**触发方式**: -- 在建行收银台完成支付(或取消支付) -- 建行会重定向到 `callback_url` - -**回调URL示例**: -``` -https://your-domain.com/addons/shopro/ccbpayment/callback?ccbParamSJ=加密参数 -``` - -**预期流程**: -1. 解密 `ccbParamSJ` 参数 -2. 获取支付结果参数 -3. 根据 `SUCCESS=Y/N` 判断支付成功或失败 -4. 更新订单状态 - -**支付成功验证点**: -- 订单状态 → `paid` -- 支付时间 `paid_time` 已记录(毫秒时间戳) -- 支付方式 `pay_type` → `offline` -- 交易单号 `transaction_id` → 支付流水号 -- 页面跳转到订单详情或支付成功页 - -**支付失败验证点**: -- 订单状态保持 `unpaid` -- 页面显示失败原因 - ---- - -### 测试7: 异步通知(需建行环境) - -**目的**: 验证建行异步通知处理 - -**触发方式**: -- 支付成功后,建行会POST请求到 `notify_url` - -**接口**: `POST /addons/shopro/ccbpayment/notify` - -**建行会发送的参数**(示例): -``` -POST数据: -ORDERID=PAY20250118143000123456 -&USER_ORDERID=SO20250118001 -&POSID=100001 -&PAYMENT=100.00 -&SUCCESS=Y -&SIGN=签名值 -``` - -**预期处理逻辑**: -1. 验证签名 `SIGN` -2. 验证 `POSID` 是否匹配 -3. 根据 `USER_ORDERID` 或 `ccb_pay_flow_id` 查询订单 -4. 更新订单状态 -5. 返回 `SUCCESS` 或 `FAIL` - -**验证点**: -- 响应体必须返回字符串 `SUCCESS` 或 `FAIL` -- 订单状态正确更新 -- 日志 `runtime/log/` 中记录通知详情 - -**测试异步通知的方法**(模拟建行POST请求): -```bash -curl -X POST 'https://your-domain.com/addons/shopro/ccbpayment/notify' \ - -H 'Content-Type: application/x-www-form-urlencoded' \ - -d 'ORDERID=PAY20250118143000123456&USER_ORDERID=SO20250118001&POSID=100001&PAYMENT=100.00&SUCCESS=Y&SIGN=mock_sign' -``` - ---- - -### 测试8: 订单同步到建行(需建行环境) - -**目的**: 验证支付成功后订单推送到建行 - -**触发时机**: -- 支付回调成功后自动触发 -- 或手动调用同步接口 - -**手动触发方式**: -```php -$orderService = new \addons\shopro\library\ccblife\CcbOrderService(); -$result = $orderService->pushOrder($orderId); - -var_dump($result); -``` - -**验证点**: -1. API请求成功(HTTP 200) -2. 返回 `CLD_HEAD.ERR_CODE === '0'` -3. `fa_ccb_sync_log` 表中记录同步成功 -4. 订单同步状态更新 - -**失败处理**: -- 如果同步失败,日志中记录错误原因 -- 支持重试机制(通过定时任务) - ---- - -## 常见问题排查 - -### 问题1: 支付串生成失败 - -**错误信息**: "订单状态不正确" - -**原因**: 订单已支付或已关闭 - -**解决**: -```sql --- 检查订单状态 -SELECT id, order_sn, status, pay_status FROM fa_shopro_order WHERE id = ; - --- 如需重测,重置订单状态 -UPDATE fa_shopro_order SET status = 'unpaid', pay_status = 'unpaid' WHERE id = ; -``` - ---- - -### 问题2: 支付串签名错误 - -**错误信息**: "签名验证失败" - -**排查步骤**: -1. 检查配置文件中私钥是否正确 -2. 检查参数是否按ASCII排序 -3. 检查MD5签名是否为小写 - -**验证签名算法**: -```php -// 测试代码 -$params = [/* 支付参数 */]; -ksort($params); -$signString = http_build_query($params); -$mac = md5($signString . config('ccblife.private_key')); - -echo "签名字符串: {$signString}\n"; -echo "MAC签名: {$mac}\n"; -``` - ---- - -### 问题3: 订单查询失败 - -**错误信息**: "订单不存在" - -**原因**: -- 使用了错误的订单号字段 -- ORDERID(支付流水号)和 USER_ORDERID(商户订单号)混淆 - -**排查**: -```sql --- 检查订单数据 -SELECT - id, - order_sn, -- 商户订单号 - ccb_pay_flow_id, -- 建行支付流水号 - status -FROM fa_shopro_order -WHERE order_sn = 'SO20250118001' -- 使用商户订单号查询 - OR ccb_pay_flow_id = 'PAY20250118143000123456'; -- 使用支付流水号查询 -``` - ---- - -### 问题4: 订单状态未更新 - -**可能原因**: -1. 字段名错误(`paytime` vs `paid_time`) -2. 时间戳单位错误(秒 vs 毫秒) -3. `pay_type` 枚举值不存在 - -**验证**: -```sql --- 检查订单表字段 -SHOW COLUMNS FROM fa_shopro_order LIKE 'paid_time'; - --- 检查pay_type枚举值 -SHOW COLUMNS FROM fa_shopro_order LIKE 'pay_type'; - --- 查看订单更新时间 -SELECT - id, - paid_time, -- 应为毫秒时间戳 - FROM_UNIXTIME(paid_time/1000) as paid_datetime, - pay_type, -- 应为 'offline' - status -FROM fa_shopro_order -WHERE id = ; -``` - ---- - -### 问题5: RSA加密失败 - -**错误信息**: "RSA加密失败" 或 "key size too small" - -**原因**: -- 密钥格式不正确 -- 密钥长度不是1024位 -- 数据块大小超过117字节 - -**验证密钥**: -```bash -# 检查私钥 -echo "<私钥内容>" | openssl rsa -text -noout - -# 检查公钥 -echo "<公钥内容>" | openssl rsa -pubin -text -noout - -# 查看密钥长度 -# 应显示: Private-Key: (1024 bit) -``` - -**修复**: -1. 确保密钥包含完整的 `-----BEGIN` 和 `-----END` 标记 -2. 数据分块处理,每块不超过117字节 -3. 使用正确的填充模式 `OPENSSL_PKCS1_PADDING` - ---- - -## 测试检查清单 - -### ✅ 阶段一:模拟测试(可立即执行) - -- [ ] 运行自动化测试脚本,所有测试通过 -- [ ] 配置文件已正确配置 -- [ ] 数据库表结构完整 -- [ ] 订单表 `ccb_pay_flow_id` 字段存在 -- [ ] 用户表 `ccb_user_id` 字段存在 -- [ ] 支付日志表 `fa_ccb_payment_log` 存在 -- [ ] 同步日志表 `fa_ccb_sync_log` 存在 -- [ ] 支付串生成逻辑正确 -- [ ] 支付流水号格式正确(PAY+14位+6位) -- [ ] 订单字段映射正确(paid_time、total_fee等) - -### ✅ 阶段二:真实环境测试(需建行配置) - -- [ ] 已获得建行测试环境配置 -- [ ] RSA密钥对配置正确(1024位) -- [ ] API地址配置正确 -- [ ] 回调地址已配置且可访问 -- [ ] 在建行APP内能正确识别环境 -- [ ] JSBridge初始化成功 -- [ ] 用户自动登录成功 -- [ ] ccb_user_id正确绑定 -- [ ] 支付串加密正确(ENCPUB字段) -- [ ] 建行收银台能正确打开 -- [ ] 支付回调能正确接收和处理 -- [ ] 异步通知能正确接收和处理 -- [ ] 签名验证通过 -- [ ] 订单状态正确更新 -- [ ] 订单同步到建行成功 -- [ ] 重复通知幂等性处理正确 -- [ ] 小额支付测试通过(0.01元) -- [ ] 正常金额支付测试通过 -- [ ] 支付取消测试通过 -- [ ] 支付超时测试通过 - ---- - -## 附录 - -### A. 支付流水号规则 - -``` -格式: PAY + YmdHis(14位) + 随机数(6位) -示例: PAY20250118143000123456 -长度: 23位固定长度 - -组成: -- PAY: 固定前缀(3位) -- 20250118143000: 时间戳 yyMMddHHmmss (14位) -- 123456: 随机数 (6位) -``` - -### B. 重要字段映射表 - -| Shopro字段 | 建行字段 | 说明 | 示例 | -|------------|----------|------|------| -| order_sn | USER_ORDERID | 商户订单号 | SO20250118001 | -| ccb_pay_flow_id | ORDERID | 建行支付流水号 | PAY20250118143000123456 | -| total_fee | PAYMENT / PAY_AMT | 实际支付金额 | 100.00 | -| discount_fee | - | 优惠金额 | 5.00 | -| paid_time | PAY_TIME | 支付时间(毫秒) | 1737183000000 | -| createtime | ORD_TIME | 订单创建时间(秒) | 20250118143000 | -| status | ORD_STATUS | 订单状态 | paid → 02 | -| pay_type | - | 支付方式 | offline(代表建行) | -| transaction_id | ORDERID | 交易单号 | PAY20250118143000123456 | - -### C. 订单状态映射 - -| Shopro状态 | 建行状态码 | 说明 | -|-----------|-----------|------| -| unpaid | 01 | 未支付 | -| paid | 02 | 已支付 | -| closed | 03 | 已关闭 | -| cancelled | 04 | 已取消 | - -### D. 日志文件位置 - -``` -建行支付日志: -- runtime/log/202501/18.log - -关键日志标识: -- [建行支付] 支付串生成 -- [建行支付] 生成支付串失败 -- [建行回调] 收到同步回调 -- [建行通知] 收到异步通知 -- [建行订单同步] 推送订单 -``` - ---- - -## 总结 - -本测试指南提供了完整的测试方案,分为两个阶段: - -1. **模拟测试阶段**(当前可用) - - 运行自动化测试脚本即可验证核心逻辑 - - 无需建行真实配置 - - 适合开发阶段自测 - -2. **真实环境测试阶段**(需建行配置) - - 需要建行提供测试环境 - - 包含完整的支付流程 - - 适合联调和上线前测试 - -**下一步行动**: -1. ✅ 立即运行自动化测试脚本 -2. ⏸️ 等待建行提供测试环境配置 -3. 🔄 获得配置后执行阶段二测试 - -**如有问题,请查看"常见问题排查"章节或联系开发人员。** diff --git a/doc/frontend-deployment-guide.md b/doc/frontend-deployment-guide.md deleted file mode 100644 index 20eab5a..0000000 --- a/doc/frontend-deployment-guide.md +++ /dev/null @@ -1,579 +0,0 @@ -# Shopro 前端项目部署指南 - -## 概述 - -本文档描述如何将 Shopro uni-app 前端项目打包并部署到生产环境 `https://app.fengketrade.com`。 - -## 环境配置 - -### 生产环境配置已完成 ✅ - -`.env` 文件已配置: -```env -# 正式环境接口域名 -SHOPRO_BASE_URL = https://app.fengketrade.com - -# 开发环境接口域名 -SHOPRO_DEV_BASE_URL = https://app.fengketrade.com - -# 开发环境运行端口 -SHOPRO_DEV_PORT = 3000 - -# 接口地址前缀 -SHOPRO_API_PATH = /addons/shopro/ -``` - -## 打包方法 - -### 方法一:使用 HBuilderX 打包(推荐) - -这是 uni-app 官方推荐的打包方式,最稳定可靠。 - -#### 步骤 1: 准备 HBuilderX - -1. **下载 HBuilderX** - - 官网:https://www.dcloud.io/hbuilderx.html - - 选择"正式版" → "标准版"即可 - -2. **安装必要插件** - - 启动 HBuilderX - - 工具 → 插件安装 → 搜索"uni-app编译" - - 安装"uni-app (Vue3)"编译器 - -#### 步骤 2: 打开项目 - -1. 文件 → 打开目录 -2. 选择:`/Users/billy/Code/fengketrade.com/frontend` -3. 等待项目加载完成 - -#### 步骤 3: H5 打包 - -1. **发行到 H5** - - 发行 → 网站-H5手机版 - - 或者点击菜单:发行 → 网站-PC Web或App(适用于宽屏应用) - -2. **配置打包选项** - ``` - 网站标题:风刻商城 - 网站域名:https://app.fengketrade.com - 路由模式:hash(推荐)或 history - ``` - -3. **点击"发行"** - - 等待编译完成 - - 控制台会显示编译进度 - -4. **查看打包结果** - ``` - 打包目录:/Users/billy/Code/fengketrade.com/frontend/unpackage/dist/build/h5 - ``` - -#### 步骤 4: 部署到服务器 - -```bash -cd /Users/billy/Code/fengketrade.com/frontend - -# 压缩打包文件(排除 macOS 元数据) -tar --no-mac-metadata --no-xattrs --exclude='.DS_Store' \ - -czf h5-dist.tar.gz -C unpackage/dist/build/h5 . - -# 上传到服务器 -scp h5-dist.tar.gz user@app.fengketrade.com:/tmp/ - -# 登录服务器 -ssh user@app.fengketrade.com - -# 解压到 Web 目录 -cd /path/to/web/root -mkdir -p h5 -tar -xzf /tmp/h5-dist.tar.gz -C h5/ - -# 设置权限 -chown -R www-data:www-data h5/ -chmod -R 755 h5/ -``` - -### 方法二:使用命令行打包 - -如果已安装 uni-app CLI 工具,可以使用命令行打包。 - -#### 步骤 1: 安装 uni-app CLI - -```bash -cd /Users/billy/Code/fengketrade.com/frontend - -# 安装 uni-app 编译器(如果还没安装) -npm install -g @dcloudio/uvm -uvm - -# 或直接安装依赖 -npm install -``` - -#### 步骤 2: 添加构建脚本 - -编辑 `package.json`,添加以下脚本: - -```json -{ - "scripts": { - "dev:h5": "uni -p h5", - "build:h5": "uni build -p h5", - "build:h5:prod": "cross-env NODE_ENV=production uni build -p h5", - "prettier": "prettier --write \"{pages,sheep}/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"" - } -} -``` - -#### 步骤 3: 执行打包 - -```bash -# 开发环境打包 -npm run build:h5 - -# 生产环境打包(压缩优化) -npm run build:h5:prod -``` - -#### 步骤 4: 查看打包结果 - -```bash -ls -lh unpackage/dist/build/h5/ -``` - -### 方法三:快速部署脚本(自动化) - -创建自动化部署脚本。 - -#### 创建部署脚本 - -```bash -cat > deploy-frontend.sh << 'EOF' -#!/bin/bash - -# 前端自动化部署脚本 -# 作者: Billy -# 日期: 2025-01-17 - -set -e - -echo "=== 前端部署脚本 ===" - -# 配置 -PROJECT_DIR="/Users/billy/Code/fengketrade.com/frontend" -BUILD_DIR="$PROJECT_DIR/unpackage/dist/build/h5" -SERVER_USER="your_user" -SERVER_HOST="app.fengketrade.com" -SERVER_PATH="/var/www/html/h5" -BACKUP_DIR="/var/www/backup" - -# 颜色输出 -GREEN='\033[0;32m' -RED='\033[0;31m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -echo -e "${YELLOW}步骤 1: 检查环境...${NC}" -cd "$PROJECT_DIR" - -if [ ! -d "node_modules" ]; then - echo -e "${YELLOW}安装依赖...${NC}" - npm install -fi - -echo -e "${GREEN}✓ 环境检查完成${NC}" - -echo -e "${YELLOW}步骤 2: 清理旧文件...${NC}" -rm -rf unpackage/dist/build/h5 - -echo -e "${YELLOW}步骤 3: 开始打包...${NC}" -# 如果有 HBuilderX CLI -if command -v cli &> /dev/null; then - cli publish --platform h5 --project "$PROJECT_DIR" -# 或使用 npm -elif [ -f "package.json" ]; then - npm run build:h5:prod || npm run build:h5 -else - echo -e "${RED}✗ 找不到构建工具,请使用 HBuilderX 手动打包${NC}" - exit 1 -fi - -echo -e "${GREEN}✓ 打包完成${NC}" - -echo -e "${YELLOW}步骤 4: 压缩文件...${NC}" -cd "$PROJECT_DIR" -tar --no-mac-metadata --no-xattrs --exclude='.DS_Store' \ - -czf h5-dist-$(date +%Y%m%d-%H%M%S).tar.gz -C "$BUILD_DIR" . - -ARCHIVE_NAME=$(ls -t h5-dist-*.tar.gz | head -1) -echo -e "${GREEN}✓ 压缩完成: $ARCHIVE_NAME${NC}" - -echo -e "${YELLOW}步骤 5: 上传到服务器...${NC}" -scp "$ARCHIVE_NAME" "$SERVER_USER@$SERVER_HOST:/tmp/" - -echo -e "${YELLOW}步骤 6: 部署到服务器...${NC}" -ssh "$SERVER_USER@$SERVER_HOST" << ENDSSH - set -e - - # 创建备份 - if [ -d "$SERVER_PATH" ]; then - echo "备份当前版本..." - sudo mkdir -p "$BACKUP_DIR" - sudo tar -czf "$BACKUP_DIR/h5-backup-\$(date +%Y%m%d-%H%M%S).tar.gz" \ - -C "$SERVER_PATH" . || true - fi - - # 解压新版本 - echo "部署新版本..." - sudo mkdir -p "$SERVER_PATH" - sudo tar -xzf "/tmp/$ARCHIVE_NAME" -C "$SERVER_PATH" - - # 设置权限 - sudo chown -R www-data:www-data "$SERVER_PATH" - sudo chmod -R 755 "$SERVER_PATH" - - # 清理临时文件 - rm -f "/tmp/$ARCHIVE_NAME" - - echo "部署完成!" -ENDSSH - -echo -e "${GREEN}=== 部署成功!===${NC}" -echo -e "${GREEN}访问地址: https://app.fengketrade.com/h5${NC}" - -# 清理本地临时文件 -rm -f "$ARCHIVE_NAME" -EOF - -chmod +x deploy-frontend.sh -``` - -#### 使用部署脚本 - -```bash -# 修改脚本中的服务器配置 -vim deploy-frontend.sh - -# 执行部署 -./deploy-frontend.sh -``` - -## 部署后配置 - -### Nginx 配置 - -#### H5 项目配置 - -```nginx -# /etc/nginx/sites-available/app.fengketrade.com - -server { - listen 80; - listen 443 ssl http2; - server_name app.fengketrade.com; - - # SSL 证书配置 - ssl_certificate /path/to/ssl/cert.pem; - ssl_certificate_key /path/to/ssl/key.pem; - - # 强制 HTTPS - if ($scheme != "https") { - return 301 https://$server_name$request_uri; - } - - # H5 前端 - location /h5 { - alias /var/www/html/h5; - index index.html; - - # 解决 Vue Router history 模式 404 - try_files $uri $uri/ /h5/index.html; - - # 静态资源缓存 - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 30d; - add_header Cache-Control "public, immutable"; - } - - # HTML 不缓存 - location ~* \.html$ { - expires -1; - add_header Cache-Control "no-cache, no-store, must-revalidate"; - } - } - - # API 代理到 PHP 后端 - location /addons/shopro { - proxy_pass http://127.0.0.1:8080; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # 建行测试页面 - location /ccblife-demo.html { - alias /var/www/html/public/ccblife-demo.html; - } - - # 静态资源 - location /static { - alias /var/www/html/public; - } -} -``` - -#### 重启 Nginx - -```bash -# 测试配置 -sudo nginx -t - -# 重启服务 -sudo systemctl reload nginx -``` - -### Apache 配置(如果使用 Apache) - -```apache - - ServerName app.fengketrade.com - DocumentRoot /var/www/html - - # SSL 配置 - SSLEngine on - SSLCertificateFile /path/to/ssl/cert.pem - SSLCertificateKeyFile /path/to/ssl/key.pem - - # H5 前端 - Alias /h5 /var/www/html/h5 - - Options -Indexes +FollowSymLinks - AllowOverride All - Require all granted - - # Vue Router history 模式 - RewriteEngine On - RewriteBase /h5 - RewriteRule ^index\.html$ - [L] - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-d - RewriteRule . /h5/index.html [L] - - - # 静态资源缓存 - - Header set Cache-Control "max-age=2592000, public" - - - # HTML 不缓存 - - Header set Cache-Control "no-cache, no-store, must-revalidate" - - -``` - -## 部署检查清单 - -### 打包前检查 - -- [ ] ✅ `.env` 文件配置正确(生产环境域名) -- [ ] 检查 `manifest.json` 配置 -- [ ] 移除开发环境的 console.log -- [ ] 移除调试工具(如 vconsole) -- [ ] 检查建行生活模块是否正确集成 - -### 打包检查 - -```bash -# 检查打包文件大小 -du -sh unpackage/dist/build/h5 - -# 检查关键文件是否存在 -ls -lh unpackage/dist/build/h5/index.html -ls -lh unpackage/dist/build/h5/static/ - -# 检查是否有 source map(生产环境应该移除) -find unpackage/dist/build/h5 -name "*.map" -``` - -### 部署后检查 - -```bash -# 1. 检查文件是否上传成功 -ssh user@app.fengketrade.com "ls -lh /var/www/html/h5" - -# 2. 检查权限 -ssh user@app.fengketrade.com "ls -ld /var/www/html/h5" - -# 3. 测试访问 -curl -I https://app.fengketrade.com/h5/ - -# 4. 检查 API 接口 -curl https://app.fengketrade.com/addons/shopro/ccbtest -``` - -### 功能测试 - -- [ ] 访问首页是否正常 -- [ ] API 接口是否可以调用 -- [ ] 建行生活模块是否正常 -- [ ] 支付功能是否正常 -- [ ] 图片资源是否加载 -- [ ] 移动端适配是否正常 - -## 快速部署命令 - -### 本地到服务器一键部署 - -```bash -cd /Users/billy/Code/fengketrade.com/frontend - -# 1. 打包(使用 HBuilderX 或命令行) -# HBuilderX: 发行 → 网站-H5手机版 - -# 2. 压缩 -tar --no-mac-metadata --no-xattrs --exclude='.DS_Store' \ - -czf h5-$(date +%Y%m%d-%H%M%S).tar.gz \ - -C unpackage/dist/build/h5 . - -# 3. 上传 -ARCHIVE=$(ls -t h5-*.tar.gz | head -1) -scp $ARCHIVE user@app.fengketrade.com:/tmp/ - -# 4. 部署(在服务器上执行) -ssh user@app.fengketrade.com << 'EOF' -ARCHIVE=$(ls -t /tmp/h5-*.tar.gz | head -1) -sudo mkdir -p /var/www/html/h5 -sudo tar -xzf $ARCHIVE -C /var/www/html/h5 -sudo chown -R www-data:www-data /var/www/html/h5 -sudo chmod -R 755 /var/www/html/h5 -rm -f $ARCHIVE -echo "部署完成!" -EOF - -# 5. 测试 -curl -I https://app.fengketrade.com/h5/ -``` - -## 回滚方案 - -### 快速回滚 - -```bash -# 在服务器上执行 -cd /var/www/backup - -# 查看备份 -ls -lht h5-backup-*.tar.gz | head -5 - -# 回滚到指定版本 -BACKUP_FILE="h5-backup-20250117-143000.tar.gz" -sudo rm -rf /var/www/html/h5/* -sudo tar -xzf $BACKUP_FILE -C /var/www/html/h5 -sudo systemctl reload nginx - -echo "已回滚到: $BACKUP_FILE" -``` - -## 性能优化 - -### 打包优化 - -1. **启用 Gzip 压缩**(在 `vite.config.js`) -```javascript -import viteCompression from 'vite-plugin-compression'; - -export default { - plugins: [ - viteCompression({ - verbose: true, - disable: false, - threshold: 10240, - algorithm: 'gzip', - ext: '.gz' - }) - ] -} -``` - -2. **代码分割** - - uni-app 会自动进行代码分割 - - 按页面分包加载 - -3. **图片优化** - - 压缩图片资源 - - 使用 WebP 格式 - - 启用懒加载 - -### CDN 配置 - -修改 `.env`: -```env -# 使用 CDN 加速静态资源 -SHOPRO_STATIC_URL = https://cdn.fengketrade.com -``` - -## 常见问题 - -### Q: 打包后页面空白? - -**原因**: 路径配置错误 - -**解决**: -```javascript -// manifest.json 中检查 -{ - "h5": { - "router": { - "mode": "hash", // 推荐使用 hash 模式 - "base": "/h5/" // 根据实际部署路径调整 - } - } -} -``` - -### Q: API 请求 404? - -**原因**: 接口地址配置错误 - -**解决**: 检查 `.env` 中的 `SHOPRO_BASE_URL` - -### Q: 静态资源 404? - -**原因**: Nginx 配置问题 - -**解决**: 确保 Nginx 配置正确映射静态资源路径 - -### Q: 打包文件太大? - -**解决**: -1. 移除无用的依赖 -2. 启用代码压缩 -3. 使用 CDN 加载大文件 -4. 按需引入组件 - -## 监控和维护 - -### 访问日志 - -```bash -# Nginx 访问日志 -sudo tail -f /var/log/nginx/access.log | grep "GET /h5" - -# 错误日志 -sudo tail -f /var/log/nginx/error.log -``` - -### 性能监控 - -使用浏览器开发者工具: -1. Network - 查看资源加载时间 -2. Performance - 分析页面性能 -3. Lighthouse - 综合评分 - ---- - -*文档版本: 1.0.0* -*最后更新: 2025-01-17* -*作者: Billy* \ No newline at end of file