From ca82c79c7876115f2431c8910b6ffe5e7f9f3778 Mon Sep 17 00:00:00 2001
From: Billy <641833868@qq.com>
Date: Tue, 21 Oct 2025 09:02:55 +0800
Subject: [PATCH 01/10] =?UTF-8?q?=E7=94=9F=E6=88=90=E6=94=AF=E4=BB=98?=
=?UTF-8?q?=E4=B8=B2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
addons/shopro/controller/Ccblife.php | 50 +------------------
.../shopro/library/ccblife/CcbEncryption.php | 13 ++---
2 files changed, 5 insertions(+), 58 deletions(-)
diff --git a/addons/shopro/controller/Ccblife.php b/addons/shopro/controller/Ccblife.php
index 7e0c8a7..d436e07 100644
--- a/addons/shopro/controller/Ccblife.php
+++ b/addons/shopro/controller/Ccblife.php
@@ -140,55 +140,7 @@ class Ccblife extends Common
]);
}
}
-
- /**
- * 建行用户自动登录(JSBridge方式)
- * H5在建行App内打开时,通过JSBridge获取用户信息后调用
- *
- * POST /addons/shopro/ccblife/autoLogin
- */
- public function autoLogin()
- {
- try {
- // 获取请求参数
- $ccbUserId = $this->request->post('ccb_user_id', '');
- $mobile = $this->request->post('mobile', '');
- $nickname = $this->request->post('nickname', '');
- $avatar = $this->request->post('avatar', '');
-
- // 验证必需参数
- if (empty($ccbUserId)) {
- $this->error('建行用户ID不能为空');
- }
-
- // 处理用户登录/注册
- $userInfo = $this->processUserLogin($ccbUserId, $mobile, [
- 'nickname' => $nickname,
- 'avatar' => $avatar
- ]);
-
- // 使用Auth系统登录并生成Token
- $this->auth->direct($userInfo['user_id']);
- $token = $this->auth->getToken();
-
- // 返回结果
- $this->success('登录成功', [
- 'token' => $token,
- 'user_id' => $userInfo['user_id'],
- 'is_new_user' => $userInfo['is_new'],
- 'userInfo' => $userInfo
- ]);
-
- } catch (\think\exception\HttpResponseException $e) {
- // HttpResponseException 是框架正常的响应机制,直接向上抛出
- throw $e;
- } catch (\Exception $e) {
- Log::error('建行自动登录失败: ' . $e->getMessage());
- Log::error('错误堆栈: ' . $e->getTraceAsString());
- $this->error('登录失败: ' . $e->getMessage());
- }
- }
-
+
/**
* 处理用户登录/注册
*
diff --git a/addons/shopro/library/ccblife/CcbEncryption.php b/addons/shopro/library/ccblife/CcbEncryption.php
index 30dcee5..7a0ee64 100644
--- a/addons/shopro/library/ccblife/CcbEncryption.php
+++ b/addons/shopro/library/ccblife/CcbEncryption.php
@@ -36,11 +36,6 @@ class CcbEncryption
*/
private $publicKey;
- /**
- * 建行平台公钥
- * @var string
- */
- private $platformPublicKey;
/**
* 构造函数
@@ -75,8 +70,8 @@ class CcbEncryption
throw new Exception('服务方私钥未配置');
}
- if (empty($this->platformPublicKey)) {
- throw new Exception('建行平台公钥未配置');
+ if (empty($this->publicKey)) {
+ throw new Exception('服务方公钥未配置');
}
}
@@ -90,7 +85,7 @@ class CcbEncryption
public function rsaEncrypt($data)
{
// 格式化公钥
- $publicKey = $this->formatKey($this->platformPublicKey, 'PUBLIC');
+ $publicKey = $this->formatKey($this->publicKey);
// 获取公钥资源
$pubKeyId = openssl_pkey_get_public($publicKey);
@@ -382,7 +377,7 @@ class CcbEncryption
$signString = http_build_query($params);
// 3. 追加平台公钥
- $signString .= '&PLATFORMPUB=' . $this->platformPublicKey;
+ $signString .= '&PLATFORMPUB=' . $this->publicKey;
// 4. 生成MD5签名 (转为大写,与Java一致)
return strtoupper(md5($signString . $this->privateKey));
From a4b8b710d5d8c7fccf9fb7c735a0f086057f757c Mon Sep 17 00:00:00 2001
From: Billy <641833868@qq.com>
Date: Tue, 21 Oct 2025 09:51:19 +0800
Subject: [PATCH 02/10] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E8=AE=A2=E5=8D=95?=
=?UTF-8?q?=E7=8A=B6=E6=80=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../library/ccblife/CcbOrderService.php | 243 +++++++-----------
1 file changed, 94 insertions(+), 149 deletions(-)
diff --git a/addons/shopro/library/ccblife/CcbOrderService.php b/addons/shopro/library/ccblife/CcbOrderService.php
index 350837e..1a4427f 100644
--- a/addons/shopro/library/ccblife/CcbOrderService.php
+++ b/addons/shopro/library/ccblife/CcbOrderService.php
@@ -112,10 +112,23 @@ class CcbOrderService
// 获取订单商品列表
$orderItems = Db::name('shopro_order_item')
->where('order_id', $orderId)
- ->select();
+ ->select()
+ ->toArray();
+
+ // 获取支付流水号(PAY_FLOW_ID必填字段)
+ $payInfo = Db::name('shopro_pay')
+ ->where('order_id', $orderId)
+ ->where('status', 'paid')
+ ->find();
+
+ if (!$payInfo || empty($payInfo['pay_sn'])) {
+ throw new \Exception('订单未支付或支付流水号不存在');
+ }
+
+ $payFlowId = $payInfo['pay_sn'];
// 构建订单数据(符合A3341TP01接口规范)
- $orderData = $this->buildOrderData($order, $orderItems, $ccbUserId);
+ $orderData = $this->buildOrderData($order, $orderItems, $ccbUserId, $payFlowId);
// 记录请求数据(同步日志)
$txSeq = CcbMD5::generateTransactionSeq();
@@ -270,13 +283,13 @@ class CcbOrderService
{
try {
// 获取订单信息
- $order = Order::find($orderId);
+ $order = Db::name('shopro_order')->where('id', $orderId)->find();
if (!$order) {
throw new \Exception('订单不存在');
}
// 验证退款金额
- if ($refundAmount > $order['total_amount']) {
+ if ($refundAmount > $order['order_amount']) {
throw new \Exception('退款金额不能超过订单总额');
}
@@ -307,148 +320,112 @@ class CcbOrderService
}
/**
- * 构建符合建行要求的订单数据
+ * 构建符合建行 A3341TP01 接口规范的订单数据
*
- * ⚠️ 注意:Shopro字段说明
- * - pay_fee: 实际支付金额(Shopro字段)
- * - order_amount: 订单总金额(Shopro字段)
- * - total_discount_fee: 优惠总金额(Shopro字段)
- * - paid_time: 支付时间(毫秒时间戳!需除以1000)
- * - createtime: 创建时间(毫秒时间戳!需除以1000)
+ * 📋 建行生活订单推送接口规范说明:
+ *
+ * 必填字段(11个):
+ * - USER_ID: 客户编号(建行用户ID)
+ * - ORDER_ID: 订单号
+ * - ORDER_DT: 订单日期(yyyyMMddHHmmss格式)
+ * - TOTAL_AMT: 订单原金额
+ * - ORDER_STATUS: 订单状态
+ * - MCT_NM: 商户名称
+ * - CUS_ORDER_URL: 订单详情链接
+ * - PAY_FLOW_ID: 支付流水号(从 shopro_pay.pay_sn 获取)
+ * - PRPFTL_MRCH_ID: 门店商户号(使用 merchant_id)
+ * - PAY_MRCH_ID: 支付商户号(使用 merchant_id)
+ * - SKU_LIST: 商品信息JSON字符串
+ *
+ * 非必填但推荐字段:
+ * - PAY_AMT: 订单实际支付金额
+ * - DISCOUNT_AMT: 第三方平台优惠金额
+ * - DISCOUNT_AMT_DESC: 第三方平台优惠说明
+ * - REFUND_STATUS: 退款状态
+ * - PAY_MODE: 支付方式
+ * - PLAT_ORDER_TYPE: 服务方订单类型
+ * - COUPON_AMT: 优惠券金额
+ *
+ * ⚠️ 注意:Shopro字段映射
+ * - pay_fee → PAY_AMT(实际支付金额)
+ * - order_amount → TOTAL_AMT(订单总金额)
+ * - total_discount_fee → DISCOUNT_AMT(优惠总金额)
+ * - createtime → ORDER_DT(毫秒时间戳需除以1000)
*
* @param array $order 订单数组
* @param array $orderItems 订单商品列表
* @param string $ccbUserId 建行用户ID
+ * @param string $payFlowId 支付流水号(从shopro_pay表获取)
* @return array
*/
- private function buildOrderData($order, $orderItems, $ccbUserId)
+ private function buildOrderData($order, $orderItems, $ccbUserId, $payFlowId)
{
- // 构建商品列表
- $goodsList = $this->buildGoodsList($orderItems);
-
- // 计算各项金额(Shopro字段:pay_fee=实付金额,order_amount=订单总金额)
+ // 构建SKU商品列表(JSON字符串格式)
+ $skuList = $this->buildSkuList($orderItems);
+ // 计算各项金额(保留2位小数)
$totalAmount = number_format($order['order_amount'] ?? 0, 2, '.', '');
- $payAmount = number_format($order['pay_fee'] ?? 0, 2, '.', '');
- $discountAmount = number_format($order['total_discount_fee'] ?? 0, 2, '.', '');
-
- // 处理支付时间(Shopro的paid_time是毫秒时间戳,需要除以1000)
- $payTime = '';
- if (!empty($order['paid_time']) && is_numeric($order['paid_time'])) {
- $payTime = date('YmdHis', intval($order['paid_time'] / 1000));
- }
-
- // 处理创建时间(Shopro的createtime是毫秒时间戳,需要除以1000)
+ // 处理订单时间(Shopro的createtime是毫秒时间戳,需要除以1000)
$createTimeValue = $order['createtime'] ?? null;
if (empty($createTimeValue) || !is_numeric($createTimeValue)) {
- $createTimeValue = time() * 1000; // 当前时间的毫秒时间戳
+ $createTimeValue = time() * 1000;
}
- $createTime = date('YmdHis', intval($createTimeValue / 1000));
+ $orderDt = date('YmdHis', intval($createTimeValue / 1000));
- // 获取订单地址信息(Shopro将地址存储在单独的表中)
- $orderAddress = Db::name('shopro_order_address')
- ->where('order_id', $order['id'])
- ->find();
-
- // 获取支付方式(Shopro将支付信息存储在单独的表中)
- $payInfo = Db::name('shopro_pay')
- ->where('order_id', $order['id'])
- ->where('status', 'paid')
- ->find();
-
- // 获取快递信息(Shopro将快递信息存储在单独的表中)
- $expressInfo = Db::name('shopro_order_express')
- ->where('order_id', $order['id'])
- ->find();
-
- // 构建订单数据(34个必填字段)
+ // 构建符合A3341TP01接口规范的订单数据
return [
- 'USER_ID' => $ccbUserId, // 建行用户ID
+ // ========== 必填字段 ==========
+ 'USER_ID' => $ccbUserId, // 客户编号
'ORDER_ID' => $order['order_sn'], // 订单号
- 'ORDER_DT' => $createTime, // 订单时间
+ 'ORDER_DT' => $orderDt, // 订单日期(yyyyMMddHHmmss)
'TOTAL_AMT' => $totalAmount, // 订单原金额
- 'PAY_AMT' => $payAmount, // 实付金额
- 'DISCOUNT_AMT' => $discountAmount, // 优惠金额
'ORDER_STATUS' => $this->mapOrderStatus($order['status']), // 订单状态
- 'REFUND_STATUS' => '0', // 退款状态(默认无退款)
+ 'REFUND_STATUS' => $this->mapRefundStatus($order['refund_status'] ?? 0), // 退款状态
'MCT_NM' => $this->config['merchant']['name'] ?? '商户名称', // 商户名称
- 'MCT_ORDER_ID' => $order['id'], // 商户订单ID
- 'GOODS_LIST' => json_encode($goodsList, JSON_UNESCAPED_UNICODE), // 商品列表
- 'PAY_TYPE' => $this->mapPayType($payInfo['pay_type'] ?? ''), // 支付方式(从支付表获取)
- 'PAY_TIME' => $payTime, // 支付时间(毫秒转秒)
- 'DELIVERY_TYPE' => '01', // 配送方式(01快递)
- 'DELIVERY_STATUS' => $this->mapDeliveryStatus($order['status']), // 配送状态
- 'DELIVERY_TIME' => !empty($expressInfo['createtime']) ? date('YmdHis', intval($expressInfo['createtime'] / 1000)) : '', // 发货时间
- 'RECEIVE_NAME' => $orderAddress['consignee'] ?? '', // 收货人姓名(从地址表获取)
- 'RECEIVE_PHONE' => $orderAddress['mobile'] ?? '', // 收货人电话(从地址表获取)
- 'RECEIVE_ADDRESS' => $this->buildAddress($order), // 收货地址(从地址表获取)
- 'EXPRESS_COMPANY' => $expressInfo['express_name'] ?? '', // 快递公司(从快递表获取)
- 'EXPRESS_NO' => $expressInfo['express_no'] ?? '', // 快递单号(从快递表获取)
- 'REMARK' => $order['remark'] ?? '', // 备注
- 'ORDER_TYPE' => '01', // 订单类型(01普通订单)
- 'IS_VIRTUAL' => '0', // 是否虚拟商品
- 'ORDER_URL' => $this->config['merchant']['order_detail_url'] . $order['id'], // 订单详情链接
- 'CREATE_TIME' => $createTime, // 创建时间
- 'UPDATE_TIME' => date('YmdHis'), // 更新时间
- 'SHOP_ID' => '1', // 店铺ID
- 'SHOP_NAME' => $this->config['merchant']['name'] ?? '', // 店铺名称
- 'ACTIVITY_ID' => '', // 活动ID
- 'ACTIVITY_NAME' => '', // 活动名称
- 'COUPON_AMT' => number_format($order['coupon_discount_fee'] ?? 0, 2, '.', ''), // 优惠券金额
- 'FREIGHT_AMT' => number_format($order['dispatch_amount'] ?? 0, 2, '.', ''), // 运费(Shopro字段名为dispatch_amount)
+ 'CUS_ORDER_URL' => $this->config['merchant']['order_detail_url'] . $order['id'], // 订单详情链接
+ 'PAY_FLOW_ID' => $payFlowId, // 支付流水号(必填!)
+ 'PAY_MRCH_ID' => $this->config['merchant_id'], // 支付商户号(必填!)
+ 'SKU_LIST' => $skuList, // 商品信息JSON字符串(必填!)
+ // ========== 非必填字段 ==========
+ 'PLAT_ORDER_TYPE' => "T0000", // 服务方订单类型
];
}
/**
- * 构建商品列表
+ * 构建符合建行规范的SKU商品列表(JSON字符串格式)
*
- * @param array $items 订单商品项
- * @return array
+ * 📋 建行 SKU_LIST 字段规范:
+ *
+ * 必填字段(4个):
+ * - SKU_NAME: 商品名称(必填)
+ * - SKU_REF_PRICE: 商品参考价(必填,支持小数最多2位)
+ * - SKU_NUM: 商品数量(必填,支持小数最多1位)
+ * - SKU_SELL_PRICE: 商品售价(必填,支持小数最多2位)
+ *
+ * ⚠️ 注意:Shopro字段映射
+ * - goods_title → SKU_NAME(商品名称)
+ * - goods_original_price → SKU_REF_PRICE(商品原价作为参考价)
+ * - goods_num → SKU_NUM(购买数量)
+ * - goods_price → SKU_SELL_PRICE(商品实际售价)
+ *
+ * @param array $items 订单商品项数组
+ * @return string JSON字符串格式的SKU列表
*/
- private function buildGoodsList($items)
+ private function buildSkuList($items)
{
- $goodsList = [];
+ $skuList = [];
foreach ($items as $item) {
- $goodsList[] = [
- 'goods_id' => $item['goods_id'],
- 'goods_name' => $item['goods_title'],
- 'goods_price' => number_format($item['goods_price'], 2, '.', ''),
- 'goods_num' => $item['goods_num'],
- 'goods_amount' => number_format($item['goods_amount'], 2, '.', ''),
- 'goods_image' => $item['goods_image'] ?? '',
- 'goods_sku' => $item['goods_sku_text'] ?? ''
+ $skuList[] = [
+ 'SKU_NAME' => $item['goods_title'], // 商品名称(必填)
+ 'SKU_REF_PRICE' => number_format($item['goods_original_price'] ?? $item['goods_price'], 2, '.', ''), // 商品参考价(必填)
+ 'SKU_NUM' => $item['goods_num'], // 商品数量(必填)
+ 'SKU_SELL_PRICE' => number_format($item['goods_price'], 2, '.', ''), // 商品售价(必填)
];
}
- return $goodsList;
+
+ // 返回JSON字符串(不转义Unicode,保持中文可读)
+ return json_encode($skuList, JSON_UNESCAPED_UNICODE);
}
-
- /**
- * 构建收货地址
- *
- * ⚠️ 注意:Shopro的收货地址存储在单独的表 shopro_order_address 中
- *
- * @param array $order 订单数组
- * @return string
- */
- private function buildAddress($order)
- {
- // 从订单地址表获取地址信息
- $orderAddress = Db::name('shopro_order_address')
- ->where('order_id', $order['id'])
- ->find();
-
- if (!$orderAddress) {
- return '';
- }
-
- $address = '';
- if (!empty($orderAddress['province_name'])) $address .= $orderAddress['province_name'];
- if (!empty($orderAddress['city_name'])) $address .= $orderAddress['city_name'];
- if (!empty($orderAddress['district_name'])) $address .= $orderAddress['district_name'];
- if (!empty($orderAddress['address'])) $address .= $orderAddress['address'];
-
- return $address;
- }
-
+
/**
* 记录同步日志
*
@@ -543,38 +520,6 @@ class CcbOrderService
return '0';
}
- /**
- * 映射支付方式
- *
- * @param string $payType 支付类型
- * @return string
- */
- private function mapPayType($payType)
- {
- $payMap = [
- 'wechat' => '01', // 微信支付
- 'alipay' => '02', // 支付宝
- 'ccb' => '03', // 建行支付
- 'balance' => '04' // 余额支付
- ];
-
- return $payMap[$payType] ?? '00';
- }
-
- /**
- * 映射配送状态
- *
- * @param string $status 订单状态
- * @return string
- */
- private function mapDeliveryStatus($status)
- {
- if (in_array($status, ['unpaid', 'paid'])) return '0'; // 未发货
- if ($status == 'shipped') return '1'; // 已发货
- if (in_array($status, ['received', 'completed'])) return '2'; // 已收货
- return '0';
- }
-
/**
* 批量同步订单
* 用于初始化或定时同步
From 184be5a1d15b602556115950970b77be1d805e00 Mon Sep 17 00:00:00 2001
From: Billy <641833868@qq.com>
Date: Tue, 21 Oct 2025 10:17:40 +0800
Subject: [PATCH 03/10] =?UTF-8?q?=E6=8E=A8=E9=80=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../shopro/library/ccblife/CcbEncryption.php | 17 +++-
.../library/ccblife/CcbOrderService.php | 67 ++------------
.../library/ccblife/CcbPaymentService.php | 92 +++++++++----------
addons/shopro/library/ccblife/CcbRSA.php | 10 +-
4 files changed, 69 insertions(+), 117 deletions(-)
diff --git a/addons/shopro/library/ccblife/CcbEncryption.php b/addons/shopro/library/ccblife/CcbEncryption.php
index 7a0ee64..c3efbd9 100644
--- a/addons/shopro/library/ccblife/CcbEncryption.php
+++ b/addons/shopro/library/ccblife/CcbEncryption.php
@@ -7,12 +7,19 @@ use think\Exception;
/**
* 建行生活加密解密核心类
*
- * 功能:
- * - RSA加密与解密
- * - MD5签名生成与验证
- * - 报文构造与解析
- * - 交易流水号生成
+ * ⚠️ 已废弃:请使用 CcbRSA、CcbMD5、CcbHttpClient 类替代
*
+ * 废弃原因:
+ * 1. formatKey() 方法存在密钥格式化错误(PKCS#1 vs PKCS#8 混淆)
+ * 2. chunk_split() 使用不当导致 OpenSSL ASN1 解析错误
+ * 3. 与 CcbRSA 类功能重复,维护成本高
+ *
+ * 迁移指南:
+ * - RSA加密/解密 → 使用 CcbRSA::encrypt() / CcbRSA::decrypt()
+ * - MD5签名 → 使用 CcbMD5::signApiMessage() / CcbMD5::verifyApiSignature()
+ * - 加密商户公钥 → 在 CcbPaymentService 中使用 encryptPublicKeyLast30()
+ *
+ * @deprecated 2025-01-21 统一使用 CcbRSA、CcbMD5 类
* @author Billy
* @date 2025-01-16
*/
diff --git a/addons/shopro/library/ccblife/CcbOrderService.php b/addons/shopro/library/ccblife/CcbOrderService.php
index 1a4427f..8352489 100644
--- a/addons/shopro/library/ccblife/CcbOrderService.php
+++ b/addons/shopro/library/ccblife/CcbOrderService.php
@@ -34,50 +34,13 @@ class CcbOrderService
throw new \Exception('建行生活配置文件不存在');
}
- // 处理BASE64格式的密钥
- $this->config = $this->processPemKeys($this->config);
+ // ✅ 修复: 删除processPemKeys()调用
+ // 密钥格式化统一由CcbRSA类处理,避免重复格式化导致OpenSSL ASN1解析错误
+ // CcbRSA::formatPublicKey/formatPrivateKey 会在加密/解密时自动处理密钥格式
$this->httpClient = new CcbHttpClient($this->config);
}
- /**
- * 处理PEM格式密钥
- *
- * @param array $config
- * @return array
- */
- private function processPemKeys($config)
- {
- if (!empty($config['private_key']) && strpos($config['private_key'], '-----BEGIN') === false) {
- $config['private_key'] = "-----BEGIN PRIVATE KEY-----\n"
- . chunk_split($config['private_key'], 64, "\n")
- . "-----END PRIVATE KEY-----";
- }
-
- if (!empty($config['public_key']) && strpos($config['public_key'], '-----BEGIN') === false) {
- $config['public_key'] = "-----BEGIN PUBLIC KEY-----\n"
- . chunk_split($config['public_key'], 64, "\n")
- . "-----END PUBLIC KEY-----";
- }
-
- if (empty($config['merchant_public_key'])) {
- $config['merchant_public_key'] = $config['public_key'];
- }
-
- // 处理平台公钥
- if (!empty($config['platform_public_key'])) {
- if (strpos($config['platform_public_key'], '-----BEGIN') === false) {
- $config['platform_public_key'] = "-----BEGIN PUBLIC KEY-----\n"
- . chunk_split($config['platform_public_key'], 64, "\n")
- . "-----END PUBLIC KEY-----";
- }
- } else {
- $config['platform_public_key'] = $config['public_key'];
- }
-
- return $config;
- }
-
/**
* 推送订单到建行生活平台
* 当用户下单后调用此方法同步订单信息
@@ -112,23 +75,10 @@ class CcbOrderService
// 获取订单商品列表
$orderItems = Db::name('shopro_order_item')
->where('order_id', $orderId)
- ->select()
- ->toArray();
-
- // 获取支付流水号(PAY_FLOW_ID必填字段)
- $payInfo = Db::name('shopro_pay')
- ->where('order_id', $orderId)
- ->where('status', 'paid')
- ->find();
-
- if (!$payInfo || empty($payInfo['pay_sn'])) {
- throw new \Exception('订单未支付或支付流水号不存在');
- }
-
- $payFlowId = $payInfo['pay_sn'];
+ ->select();
// 构建订单数据(符合A3341TP01接口规范)
- $orderData = $this->buildOrderData($order, $orderItems, $ccbUserId, $payFlowId);
+ $orderData = $this->buildOrderData($order, $orderItems, $ccbUserId);
// 记录请求数据(同步日志)
$txSeq = CcbMD5::generateTransactionSeq();
@@ -355,10 +305,9 @@ class CcbOrderService
* @param array $order 订单数组
* @param array $orderItems 订单商品列表
* @param string $ccbUserId 建行用户ID
- * @param string $payFlowId 支付流水号(从shopro_pay表获取)
* @return array
*/
- private function buildOrderData($order, $orderItems, $ccbUserId, $payFlowId)
+ private function buildOrderData($order, $orderItems, $ccbUserId)
{
// 构建SKU商品列表(JSON字符串格式)
$skuList = $this->buildSkuList($orderItems);
@@ -382,7 +331,7 @@ class CcbOrderService
'REFUND_STATUS' => $this->mapRefundStatus($order['refund_status'] ?? 0), // 退款状态
'MCT_NM' => $this->config['merchant']['name'] ?? '商户名称', // 商户名称
'CUS_ORDER_URL' => $this->config['merchant']['order_detail_url'] . $order['id'], // 订单详情链接
- 'PAY_FLOW_ID' => $payFlowId, // 支付流水号(必填!)
+ 'PAY_FLOW_ID' => $order['ccb_pay_flow_id'], // 支付流水号(必填!)
'PAY_MRCH_ID' => $this->config['merchant_id'], // 支付商户号(必填!)
'SKU_LIST' => $skuList, // 商品信息JSON字符串(必填!)
// ========== 非必填字段 ==========
@@ -425,7 +374,7 @@ class CcbOrderService
// 返回JSON字符串(不转义Unicode,保持中文可读)
return json_encode($skuList, JSON_UNESCAPED_UNICODE);
}
-
+
/**
* 记录同步日志
*
diff --git a/addons/shopro/library/ccblife/CcbPaymentService.php b/addons/shopro/library/ccblife/CcbPaymentService.php
index c0cc2a9..92b6b7f 100644
--- a/addons/shopro/library/ccblife/CcbPaymentService.php
+++ b/addons/shopro/library/ccblife/CcbPaymentService.php
@@ -35,56 +35,13 @@ class CcbPaymentService
throw new \Exception('建行生活配置文件不存在');
}
- // 处理BASE64格式的密钥,添加PEM包装
- $this->config = $this->processPemKeys($this->config);
+ // ✅ 修复: 删除processPemKeys()调用
+ // 密钥格式化统一由CcbRSA类处理,避免重复格式化导致OpenSSL ASN1解析错误
+ // CcbRSA::formatPublicKey/formatPrivateKey 会在加密/解密时自动处理密钥格式
$this->orderService = new CcbOrderService();
}
- /**
- * 处理PEM格式密钥
- * 如果密钥是BASE64格式(不含-----BEGIN-----),则添加PEM包装
- *
- * @param array $config 配置数组
- * @return array
- */
- private function processPemKeys($config)
- {
- // 处理私钥
- if (!empty($config['private_key']) && strpos($config['private_key'], '-----BEGIN') === false) {
- $config['private_key'] = "-----BEGIN PRIVATE KEY-----\n"
- . chunk_split($config['private_key'], 64, "\n")
- . "-----END PRIVATE KEY-----";
- }
-
- // 处理公钥
- if (!empty($config['public_key']) && strpos($config['public_key'], '-----BEGIN') === false) {
- $config['public_key'] = "-----BEGIN PUBLIC KEY-----\n"
- . chunk_split($config['public_key'], 64, "\n")
- . "-----END PUBLIC KEY-----";
- }
-
- // 兼容merchant_public_key字段
- if (empty($config['merchant_public_key'])) {
- $config['merchant_public_key'] = $config['public_key'];
- }
-
- // 处理平台公钥
- if (!empty($config['platform_public_key'])) {
- // 如果有配置平台公钥且是BASE64格式,添加PEM包装
- if (strpos($config['platform_public_key'], '-----BEGIN') === false) {
- $config['platform_public_key'] = "-----BEGIN PUBLIC KEY-----\n"
- . chunk_split($config['platform_public_key'], 64, "\n")
- . "-----END PUBLIC KEY-----";
- }
- } else {
- // 如果没有配置平台公钥,使用商户公钥作为默认值
- $config['platform_public_key'] = $config['public_key'];
- }
-
- return $config;
- }
-
/**
* 生成建行支付串
* 用于前端JSBridge调用建行收银台
@@ -155,9 +112,9 @@ class CcbPaymentService
$platformPubKey = $this->config['public_key']; // 服务方公钥
$mac = strtoupper(md5($signString . '&PLATFORMPUB=' . $platformPubKey));
- // 使用RSA加密商户公钥后30位(用于ENCPUB字段)
- $encryption = new CcbEncryption($this->config);
- $encpub = $encryption->encryptMerchantPublicKeyLast30();
+ // ✅ 修复:使用 CcbRSA 加密商户公钥后30位(用于ENCPUB字段)
+ // 删除 CcbEncryption 类,统一使用 CcbRSA 处理密钥格式化
+ $encpub = $this->encryptPublicKeyLast30();
// 组装最终支付串
$finalPaymentString = $signString . '&MAC=' . $mac . '&PLATFORMID=' . $this->config['service_id'] . '&ENCPUB=' . urlencode($encpub);
@@ -247,6 +204,43 @@ class CcbPaymentService
return implode(',', $orderItems);
}
+ /**
+ * 加密商户公钥后30位(用于支付串的ENCPUB字段)
+ *
+ * 根据建行文档v2.2规范:
+ * "使用服务方公钥对商户公钥后30位进行RSA加密并base64后的密文"
+ *
+ * @return string BASE64编码的加密密文
+ * @throws \Exception
+ */
+ private function encryptPublicKeyLast30()
+ {
+ $publicKey = $this->config['public_key'] ?? '';
+
+ if (empty($publicKey)) {
+ throw new \Exception('服务方公钥未配置');
+ }
+
+ // 去除 PEM 格式的头尾和空白字符,获取纯 BASE64 内容
+ $publicKeyContent = str_replace([
+ '-----BEGIN PUBLIC KEY-----',
+ '-----END PUBLIC KEY-----',
+ '-----BEGIN RSA PUBLIC KEY-----',
+ '-----END RSA PUBLIC KEY-----',
+ "\r", "\n", " ", "\t"
+ ], '', $publicKey);
+
+ // 取后30位
+ $last30Chars = substr($publicKeyContent, -30);
+
+ if (strlen($last30Chars) < 30) {
+ throw new \Exception('商户公钥长度不足30位');
+ }
+
+ // ✅ 使用 CcbRSA 类进行加密(统一密钥格式化逻辑)
+ return CcbRSA::encrypt($last30Chars, $publicKey);
+ }
+
/**
* 处理支付回调
* 建行支付完成后的同步回调
diff --git a/addons/shopro/library/ccblife/CcbRSA.php b/addons/shopro/library/ccblife/CcbRSA.php
index 402e3f4..69437a2 100644
--- a/addons/shopro/library/ccblife/CcbRSA.php
+++ b/addons/shopro/library/ccblife/CcbRSA.php
@@ -140,9 +140,10 @@ class CcbRSA
return $publicKey;
}
- // 格式化为PEM格式
+ // ✅ 修复: chunk_split()会在末尾添加换行符,需要用rtrim()去除
+ // 否则会导致PEM格式中密钥内容和尾部之间有多余空行,OpenSSL解析失败
$pem = "-----BEGIN PUBLIC KEY-----\n";
- $pem .= chunk_split($publicKey, 64, "\n");
+ $pem .= rtrim(chunk_split($publicKey, 64, "\n"), "\n") . "\n";
$pem .= "-----END PUBLIC KEY-----\n";
return $pem;
@@ -166,9 +167,10 @@ class CcbRSA
return $privateKey;
}
- // 格式化为PEM格式
+ // ✅ 修复: chunk_split()会在末尾添加换行符,需要用rtrim()去除
+ // 否则会导致PEM格式中密钥内容和尾部之间有多余空行,OpenSSL解析失败
$pem = "-----BEGIN RSA PRIVATE KEY-----\n";
- $pem .= chunk_split($privateKey, 64, "\n");
+ $pem .= rtrim(chunk_split($privateKey, 64, "\n"), "\n") . "\n";
$pem .= "-----END RSA PRIVATE KEY-----\n";
return $pem;
From 2dc5ff423f5f84c1f5e48e0e6e23eec72d542595 Mon Sep 17 00:00:00 2001
From: Billy <641833868@qq.com>
Date: Tue, 21 Oct 2025 10:29:22 +0800
Subject: [PATCH 04/10] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
doc/建行生活API接口文档.md | 568 +++++++++++++++++++++++++++++
1 file changed, 568 insertions(+)
create mode 100644 doc/建行生活API接口文档.md
diff --git a/doc/建行生活API接口文档.md b/doc/建行生活API接口文档.md
new file mode 100644
index 0000000..628edc3
--- /dev/null
+++ b/doc/建行生活API接口文档.md
@@ -0,0 +1,568 @@
+# 建行生活API接口文档
+
+> 文档版本: v1.1.6
+> 生成时间: 2025-01-21
+> 来源: 建行生活输入通讯报文v1.1.6【最新】.xlsx
+
+---
+
+## 📑 目录
+
+- [1. 修订记录](#1-修订记录)
+- [2. 接口规范](#2-接口规范)
+ - [2.1 公共报文头](#21-公共报文头)
+ - [2.2 通讯地址](#22-通讯地址)
+ - [2.3 接口索引](#23-接口索引)
+- [3. 核心接口](#3-核心接口)
+ - [3.1 A3341TP01 - 服务方订单推送](#31-a3341tp01---服务方订单推送)
+ - [3.2 A3341TP02 - 服务方订单更新](#32-a3341tp02---服务方订单更新)
+ - [3.3 A3341TP03 - 服务方订单查询](#33-a3341tp03---服务方订单查询)
+ - [3.4 A3341TP04 - 服务方订单退款](#34-a3341tp04---服务方订单退款)
+ - [3.5 A3341TP05 - 订单支付权益查询](#35-a3341tp05---订单支付权益查询)
+ - [3.6 A3341TP13 - 服务方订单补充信息](#36-a3341tp13---服务方订单补充信息)
+
+---
+
+## 1. 修订记录
+
+| 修订日期 | 修订内容 | 上线状态 |
+|---------|---------|----------|
+| 2021-11-22 | 接口增加字段PLAT_MCT_ID、增加退款和订单查询接口 | 已上线 |
+| 2022-11-11 | 增加订单子系统新接口 | 已上线 |
+| 2023-03-28 | 订单推送新增GOODS_NM/PLATFORM_POINT两个字段 | 已上线 |
+| 2024-01-24 | 修改报文顺序、内容 | 已上线 |
+| 2024-02-23 | 订单更新新增PLATFORM_POINT、PAY_MODE、GOODS_NM字段 | 已上线 |
+| 2024-02-26 | 增加权益信息查询接口 | 已上线 |
+| 2025-06-09 | 增加SKU_LIST字段 | 待上线 |
+
+---
+
+## 2. 接口规范
+
+### 2.1 公共报文头
+
+#### 请求体(CLD_HEADER)
+
+| 栏位项目名称 | 中文名称 | 栏位属性 | 必须 | 数据项说明 |
+|------------|---------|---------|------|------------|
+| CLD_TX_CHNL | 通讯渠道号 | C | Y | 跟svcid相同,YS44开头的字符串 |
+| CLD_TX_TIME | 通讯时间 | C | Y | yyyyMMddHHmmss(请求通讯时间) |
+| CLD_TX_CODE | 服务ID | C | Y | 如A3341TP01 |
+| CLD_TX_SEQ | 全局事件流水号 | C | Y | 唯一流水号 |
+
+#### 响应体(CLD_HEADER)
+
+| 栏位项目名称 | 中文名称 | 栏位属性 | 必须 | 数据项说明 |
+|------------|---------|---------|------|------------|
+| CLD_TX_CHNL | 通讯渠道号 | C | Y | 跟svcid相同,YS44开头的字符串 |
+| CLD_TX_TIME | 通讯时间 | C | Y | yyyyMMddHHmmss(响应通讯时间) |
+| CLD_TX_CODE | 服务ID | C | Y | 如A3341TP01 |
+| CLD_TX_SEQ | 全局事件流水号 | C | Y | 唯一流水号 |
+| CLD_CODE | 响应码 | C | Y | 详见响应字典 |
+| CLD_DESC | 响应描述 | C | N | 响应描述信息 |
+
+### 2.2 通讯地址
+
+#### UAT 测试环境
+```
+http://128.192.179.60/uat_new/tp_service/txCtrl/server?txcode=XX
+```
+
+#### 生产环境
+```
+https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=XX
+```
+
+> **注意**: XX 为服务ID,每个接口不同
+
+#### 接口地址示例
+
+| 接口 | 服务ID | 完整URL |
+|------|--------|---------|
+| 订单推送 | A3341TP01 | https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP01 |
+| 订单更新 | A3341TP02 | https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP02 |
+| 订单查询 | A3341TP03 | https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP03 |
+| 订单退款 | A3341TP04 | https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP04 |
+| 权益查询 | A3341TP05 | https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP05 |
+| 订单补充信息 | A3341TP13 | https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP13 |
+
+> **提示**: 当调用接口出现404时,请求头中加上:
+> - `Accept: application/json`
+> - `Content-Type: application/json`
+
+### 2.3 接口索引
+
+| 服务码 | 服务名称 | 服务类型 | 版本 | 备注 |
+|--------|---------|---------|------|------|
+| A3341TP01 | 服务方订单推送 | 直接调用 | 01 | 新接口 |
+| A3341TP02 | 服务方订单更新 | 直接调用 | 01 | 新接口 |
+| A3341TP03 | 服务方订单查询 | 直接调用 | 01 | 新接口 |
+| A3341TP04 | 服务方订单退款 | 直接调用 | 01 | 新接口 |
+| A3341TP05 | 订单支付权益查询 | 直接调用 | 01 | 新接口 |
+| A3341TP13 | 服务方订单补充信息 | 直接调用 | 01 | 新接口 |
+
+---
+
+## 3. 核心接口
+
+### 3.1 A3341TP01 - 服务方订单推送
+
+#### 接口说明
+
+| 项目 | 说明 |
+|------|------|
+| **服务码** | A3341TP01 |
+| **服务名称** | 服务方订单推送 |
+| **服务类型** | 直接调用 |
+| **功能描述** | 用户在服务方下单成功后推送,支付时会校验订单信息 |
+| **URL** | https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP01 |
+
+#### 请求参数(CLD_BODY)
+
+| 字段名 | 中文名称 | 数据类型 | 必须 | 说明 |
+|--------|---------|----------|------|------|
+| USER_ID | 客户编号 | varChar(30) | Y | 建行生活的会员编号 |
+| ORDER_ID | 订单编号 | varChar(30) | Y | 用户订单号,建行生活订单列表展示的订单订单号(对应收银台USER_ORDERID字段,支付时会校验订单信息) |
+| ORDER_DT | 订单日期 | Char(14) | Y | yyyyMMddHHmmss |
+| TOTAL_AMT | 订单原金额 | number(15,2) | Y | 第三方原始金额 |
+| PAY_AMT | 订单实际支付金额 | number(15,2) | N | 支付网关支付金额。此处如果为空必须在状态变更时推送 |
+| DISCOUNT_AMT | 第三方平台优惠金额 | number(15,2) | N | 第三方平台优惠金额。此处如果为空必须在状态变更时推送 |
+| DISCOUNT_AMT_DESC | 第三方平台优惠定义 | varChar(1000) | N | 各金额之和等于第三方平台优惠金额,格式:名称=金额\|@\|名称=金额 |
+| ORDER_STATUS | 订单状态 | Char(1) | Y | 0-待支付 1-支付成功 2-已过期 3-支付失败 4-取消 |
+| REFUND_STATUS | 退款状态 | Char(1) | Y | 0-无退款 1-退款申请 2-已退款 3-部分退款 |
+| INV_DT | 订单过期日期 | Char(14) | N | yyyyMMddHHmmss |
+| MCT_NM | 商户名称 | varChar(218) | Y | 商户名称 |
+| CUS_ORDER_URL | 自定义订单链接 | varChar(256) | N | 订单详情地址(需要推送完整的订单详情URL) |
+| OCC_MCT_LOGO_URL | 服务方商户logo图片地址 | varChar(512) | N | 必须以http://或https://开头 |
+| PAY_FLOW_ID | 支付流水号 | varChar(30) | Y | 支付流水号,调用收银台时上送的支付流水号(对应收银台ORDERID字段) |
+| PAY_USER_ID | 支付客户编号 | varChar(30) | N | 支付客户编号 |
+| TOTAL_REFUND_AMT | 累计退款金额 | number(15,2) | N | 若支持多次退款,此次推送的金额为多次退款累计已退金额 |
+| PREFTL_MRCH_ID | 门店商户号 | varChar(50) | N | 999的门店商户号,商户终端对应的建行生活门店编号 |
+| PAY_MRCH_ID | 支付商户号 | Char(15) | Y | 调用收银台时上送的商户号 |
+| PLAT_MCT_ID | 服务商门店编号 | varChar(21) | N | 外部平台商户号,不为空以这个字段为准 |
+| OCCCOUP_DISCOUNT_AMT | 建行专属优惠金额 | number(15,2) | N | 第三方平台的建行专属优惠金额 |
+| OCCCOUP_DISCOUNT_AMT_DESC | 建行专属优惠定义 | varChar(1000) | N | 格式:券实例号=金额\|@\|券实例号=金额 |
+| SPECIAL_STATUS | 特殊附加状态 | Char(5) | N | P0000-正常状态 P0001-待使用 P0002-待审核 P0003-已支付 P0004-待退款 P0005-退款中 P0006-待发货 P0007-已发货 P0008-进行中 P0009-预定中 P0010-预定成功 P0011-预定失败 P0012-已入住 P0013-已离店 P0014-未入住 P0015-配送中 P0016-出券中 |
+| PLAT_ORDER_TYPE | 服务方订单类型 | Char(5) | N | T0000-普通类型 T0001-洗车 T0002-加油 T0003-停车 T0004-修车 T0005-充电 T0006-年检代办 T0007-道路救援 T0008-云南中石油充值 |
+| GOODS_NM | 商品名称 | varChar(200) | N | 用户购买商品名称 |
+| PLATFORM_POINT | 积分值 | number(15,2) | N | 订单使用积分抵扣积分值 |
+| PAY_MODE | 支付方式 | varChar(8) | N | STSL-刷脸 STSK-刷卡 |
+| PLATFORM | 下单场景 | Char(2) | N | 99-建行生活APP 98-微应用 |
+| **SKU_LIST** | **商品信息** | **varchar(3000)** | **N** | **商品信息列表(JSON字符串),商品售价SKU_SELL_PRICE不能大于商品定价SKU_REF_PRICE** |
+
+#### SKU_LIST 字段说明(JSON数组)
+
+```json
+[
+ {
+ "SKU_NAME": "商品名称",
+ "SKU_REF_PRICE": 15.01,
+ "SKU_NUM": 1,
+ "SKU_SELL_PRICE": 10.01
+ },
+ {
+ "SKU_NAME": "商品名称2",
+ "SKU_REF_PRICE": 15.01,
+ "SKU_NUM": 2,
+ "SKU_SELL_PRICE": 10.01
+ }
+]
+```
+
+| 字段名 | 中文名称 | 数据类型 | 必须 | 说明 |
+|--------|---------|----------|------|------|
+| SKU_NAME | 商品名称 | varChar | Y | 商品名称 |
+| SKU_REF_PRICE | 商品参考价 | number(15,2) | Y | 商品参考价,支持两位小数 |
+| SKU_NUM | 商品数量 | number(11,2) | Y | 商品数量 |
+| SKU_SELL_PRICE | 商品单价 | number(15,2) | Y | 商品单价,支持两位小数 |
+
+#### 响应参数
+
+| 字段名 | 中文名称 | 数据类型 | 必须 | 说明 |
+|--------|---------|----------|------|------|
+| CCB_DISCOUNT_AMT | 建行支付侧优惠金额 | number(15,2) | N | 因业务调整,后续下线字段 |
+| CCB_DISCOUNT_AMT_DESC | 建行支付侧优惠定义 | varChar(1000) | N | 因业务调整,后续下线字段 |
+
+---
+
+### 3.2 A3341TP02 - 服务方订单更新
+
+#### 接口说明
+
+| 项目 | 说明 |
+|------|------|
+| **服务码** | A3341TP02 |
+| **服务名称** | 服务方订单更新 |
+| **服务类型** | 直接调用 |
+| **功能描述** | 在用户订单状态有变动时调用,比如电影出票成功、外卖配送中等 |
+| **URL** | https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP02 |
+| **重要说明** | 通知类型为"支付状态修改"时,支付状态不能为空,退款状态为空;通知类型为"退款状态修改"时,退款状态不能为空,支付状态为空 |
+
+#### 请求参数
+
+| 字段名 | 中文名称 | 数据类型 | 必须 | 说明 |
+|--------|---------|----------|------|------|
+| ORDER_ID | 订单编号 | varChar(30) | Y | 用户订单号(对应收银台USER_ORDERID字段) |
+| INFORM_ID | 通知类型 | Char(1) | Y | 0-支付状态修改 1-退款状态修改 |
+| PAY_STATUS | 支付状态 | Char(1) | N | 0-待支付 1-支付成功 2-已过期 3-支付失败 4-取消 |
+| REFUND_STATUS | 退款状态 | Char(1) | N | 0-无退款 1-退款申请 2-已退款 3-部分退款 |
+| PAY_AMT | 订单实际支付金额 | number(15,2) | N | 订单实际支付金额 |
+| DISCOUNT_AMT | 第三方平台优惠金额 | number(15,2) | N | 第三方平台优惠金额 |
+| DISCOUNT_AMT_DESC | 第三方平台优惠定义 | varChar(1000) | N | 格式:名称=金额\|@\|名称=金额 |
+| CUS_ORDER_URL | 自定义订单链接 | varChar(256) | N | 订单详情地址(需要推送完整的订单详情URL) |
+| OCC_MCT_LOGO_URL | 服务方商户logo图片地址 | varChar(512) | N | http:// 或 https:// 为起始 |
+| PAY_FLOW_ID | 支付流水号 | varChar(30) | Y | 调用收银台时上送的支付流水号(对应收银台ORDERID字段) |
+| PAY_USER_ID | 支付客户编号 | varChar(150) | N | 支付客户编号 |
+| TOTAL_REFUND_AMT | 累计退款金额 | number(15,2) | N | 若支持多次退款,此次推送的金额为多次退款累计已退金额 |
+| PREFTL_MRCH_ID | 门店商户号 | varChar(50) | N | 999的门店商户号 |
+| PAY_MRCH_ID | 支付商户号 | Char(15) | Y | 调用收银台时上送的商户号 |
+| PLAT_MCT_ID | 服务商门店编号 | varChar(21) | N | 外部平台商户号,不为空以这个字段为准 |
+| OCCCOUP_DISCOUNT_AMT | 建行专属优惠金额 | number(15,2) | N | 第三方平台的建行专属优惠金额 |
+| OCCCOUP_DISCOUNT_AMT_DESC | 建行专属优惠定义 | varChar(1000) | N | 格式:券实例号=金额\|@\|券实例号=金额 |
+| SPECIAL_STATUS | 特殊附加状态 | Char(5) | N | 同订单推送 |
+| PLAT_ORDER_TYPE | 服务方订单类型 | Char(5) | N | 同订单推送 |
+| GOODS_NM | 商品名称 | varChar(200) | N | 用户购买商品名称 |
+| PLATFORM_POINT | 积分值 | number(15,2) | N | 订单使用积分抵扣积分值 |
+| PAY_MODE | 支付方式 | varChar(8) | N | STSL-刷脸 STSK-刷卡 |
+| PLATFORM | 下单场景 | Char(2) | N | 99-建行生活APP 98-微应用 |
+| SKU_LIST | 商品信息 | varchar(3000) | N | 商品信息列表(JSON字符串),格式同订单推送 |
+
+#### 响应参数
+
+| 字段名 | 中文名称 | 数据类型 | 必须 | 说明 |
+|--------|---------|----------|------|------|
+| IS_SUCCESS | 是否更新成功 | Char(1) | Y | 0-否 1-是 |
+| CCB_DISCOUNT_AMT | 建行支付侧优惠金额 | number(15,2) | N | 因业务调整,后续下线字段 |
+| CCB_DISCOUNT_AMT_DESC | 建行支付侧优惠定义 | varChar(1000) | N | 因业务调整,后续下线字段 |
+
+---
+
+### 3.3 A3341TP03 - 服务方订单查询
+
+#### 接口说明
+
+| 项目 | 说明 |
+|------|------|
+| **服务码** | A3341TP03 |
+| **服务名称** | 服务方订单查询 |
+| **服务类型** | 直接调用 |
+| **功能描述** | 订单主动查询,在未收到支付结果通知时使用。查询结果存在延时情况,接口返回未查到可以适当重复查询 |
+| **URL** | https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP03 |
+| **流控限制** | 默认流控值为 100/TPM |
+
+#### 请求参数
+
+| 字段名 | 中文名称 | 数据类型 | 必须 | 说明 |
+|--------|---------|----------|------|------|
+| TX_TYPE | 交易类型 | varChar(1) | Y | 0-支付交易 1-退款交易 a-查询可退款的订单 |
+| TXN_PRD_TPCD | 查询时间范围类型 | varChar(2) | Y | 99-自定义时间段查询 |
+| STDT_TM | 开始日期时间 | Char(14) | N | 查询时间范围类型为99时必输,格式yyyyMMddhhmiss |
+| EDDT_TM | 结束日期时间 | Char(14) | N | 查询时间范围类型为99时必输,格式yyyyMMddhhmiss |
+| ONLN_PY_TXN_ORDR_ID | 订单编号 | varChar(60) | N | 调用收银台时支付流水号,对应字段ORDERID |
+| SCN_IDR | 场景标识 | varChar(3) | N | BHK-本行卡 THK-他行卡 ZFB-支付宝 CFT-微信 |
+| PLAT_MCT_ID | 服务商门店编号 | varChar(21) | N | 外部平台商户号,不为空以这个字段为准 |
+| CUSTOMERID | 商户号 | varChar(21) | N | 建行商户编号,与外部平台商户号不能同时为空 |
+| BRANCHID | 一级分行号 | varChar(9) | N | 商户一级分行号,用建行商户编号时不能为空 |
+| POS_CODE | 柜台号 | varChar(9) | N | 柜台号 |
+| POS_ID | POS终端编号 | varChar(19) | N | POS终端编号 |
+| TXN_STATUS | 交易状态 | varChar(2) | Y | 00-交易成功 01-交易失败 02-不确定 |
+| MSGRP_JRNL_NO | 商户的流水号 | varChar(60) | N | 商户支付流水号或者退款流水号 |
+| PAGE | 当前页次 | number(10) | Y | 当前页次 |
+
+#### 响应参数(摘要)
+
+| 字段名 | 中文名称 | 数据类型 | 必须 | 说明 |
+|--------|---------|----------|------|------|
+| CUR_PAGE | 当前页次 | number(10) | N | 每页最多返回10条记录 |
+| PAGE_COUNT | 总页次 | number(10) | N | 总页次 |
+| ED_CRD_PRTY_IDR_CD | 商户号 | varChar(15) | N | 商户号 |
+| PY_AMT | 支付金额 | number(16,2) | N | 单位:元 |
+| MRCH_RFND_AMT | 商户退款金额 | number(16,2) | N | 单位:元 |
+| LIST | 订单列表 | Array | N | 订单详情列表(见下表) |
+
+#### LIST 数组字段(订单列表)
+
+| 字段名 | 中文名称 | 数据类型 | 说明 |
+|--------|---------|----------|------|
+| ONLN_PY_TXN_ORDR_ID | 订单编号 | varChar(120) | 订单编号 |
+| CLRG_STM_DT_TM | 交易时间 | varChar(14) | 格式yyyyMMddhhmiss |
+| ACQ_FNDS_CLRG_DT | 记账日期 | varChar(8) | 格式yyyyMMdd |
+| ORDR_TM | 原支付订单时间 | varChar(42) | 格式yyyyMMddhhmiss |
+| AHN_TXNAMT | 交易金额 | number(16,2) | 单位:元 |
+| ORDR_PYRFD_AMT | 退款总额 | number(16,2) | 单位:元 |
+| TXN_CLRGAMT | 结算金额 | number(16,2) | 单位:元 |
+| MRCHCMSN_AMT | 手续费金额 | number(16,2) | 单位:元 |
+| ORIG_AMT | 订单金额 | number(16,2) | 单位:元 |
+| DISCOUNT_AMT | 优惠金额 | number(16,2) | 订单金额-交易金额=优惠金额 |
+| RETGDS_ORIG_TXNAMT | 原支付金额 | number(16,2) | 单位:元 |
+| CST_ACCNO | 支付卡号 | varChar(32) | 支付卡号 |
+| CCYCD | 币种 | varChar(3) | 币种 |
+| TXN_STATUS | 交易状态 | varChar(2) | 00-成功 01-失败 02/04-不确定 TO-超时 |
+| ORIOVRLSTTNEV_TRCK_NO | 银行流水号 | varChar(25) | 银行流水号 |
+| MSGRP_JRNL_NO | 商户流水号 | varChar(240) | 退款时商户上送的退款流水号 |
+| PAY_MODE | 支付方式 | Char(3) | BHK-建行 THK-他行 ZFB-支付宝 CFT-微信 |
+
+> **更多字段请参考Excel原文档**
+
+---
+
+### 3.4 A3341TP04 - 服务方订单退款
+
+#### 接口说明
+
+| 项目 | 说明 |
+|------|------|
+| **服务码** | A3341TP04 |
+| **服务名称** | 服务方订单退款 |
+| **服务类型** | 直接调用 |
+| **功能描述** | 订单退款时使用。在退款结果未返回结果时,建议先查询退款结果再做后续处理 |
+| **URL** | https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP04 |
+
+#### 请求参数
+
+| 字段名 | 中文名称 | 数据类型 | 必须 | 说明 |
+|--------|---------|----------|------|------|
+| PLAT_MCT_ID | 服务商门店编号 | varChar(21) | N | 外部平台商户号,不为空以这个字段为准 |
+| CUSTOMERID | 商户号 | varChar(21) | N | 建行商户编号,与外部平台商户号不能同时为空 |
+| BRANCHID | 一级分行号 | varChar(9) | N | 商户一级分行号,用建行商户编号时不能为空 |
+| MONEY | 退款金额 | number(16,2) | Y | 单位:元 |
+| ORDER | 订单号 | varChar(30) | Y | 调用收银台时支付流水号,对应字段ORDERID |
+| STDT_TM | 开始日期时间 | Char(14) | Y | 根据支付时间往前加4小时,格式yyyyMMddhhmiss |
+| EDDT_TM | 结束日期时间 | Char(14) | Y | 根据支付时间往后加4小时,但日期不能超过当前日期,格式yyyyMMddhhmiss |
+| REFUND_CODE | 退款流水号 | varChar(30) | N | 可不填,商户可根据需要填写,退款流水号由商户的系统生成(在未收到退款接口返回时,可用此字段查询退款结果) |
+
+#### 响应参数
+
+| 字段名 | 中文名称 | 数据类型 | 必须 | 说明 |
+|--------|---------|----------|------|------|
+| ORDER_NUM | 订单号 | varChar(30) | Y | 订单号 |
+| PAY_AMOUNT | 支付金额 | number(16,2) | Y | 单位:元 |
+| AMOUNT | 退款金额 | number(16,2) | Y | 单位:元 |
+| Cst_AccNo | 客户账户 | varChar(32) | N | 脱敏后客户退款账号 |
+
+---
+
+### 3.5 A3341TP05 - 订单支付权益查询
+
+#### 接口说明
+
+| 项目 | 说明 |
+|------|------|
+| **服务码** | A3341TP05 |
+| **服务名称** | 订单支付权益查询 |
+| **服务类型** | 直接调用 |
+| **功能描述** | 查询订单支付时使用的权益信息。此接口跟订单支付结果一起计算流控TPM |
+| **URL** | https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP05 |
+
+#### 请求参数
+
+| 字段名 | 中文名称 | 数据类型 | 必须 | 说明 |
+|--------|---------|----------|------|------|
+| PLAT_MCT_ID | 服务商门店编号 | varChar(21) | N | 外部平台商户号(暂不支持) |
+| CUSTOMERID | 商户号 | varChar(21) | N | 建行商户编号,与外部平台商户号不能同时为空 |
+| BRANCHID | 一级分行号 | varChar(9) | N | 商户一级分行号,用建行商户编号时不能为空 |
+| TX_SPECIAL_EC | 交易类型 | varChar(1) | Y | 0-支付交易 1-退款交易 |
+| ORDER_ID | 订单号 | varChar(30) | Y | 调用收银台时支付流水号,对应字段ORDERID |
+| OriOvrlsttnEV_Trck_No | 原银行流水号 | varChar(25) | N | 原银行流水号 |
+| Txn_Prd_TpCd | 交易期间类型代码 | varChar(2) | N | 08-1个小时内,送空则为08 |
+| POS_ID | 终端号 | varChar(19) | N | 终端号 |
+| Jrnl_TpCd | 流水类型代码 | varChar(2) | N | XM-数字人民币 PT-普通收单 YH-单纯查券优惠 AL-全部,送空则为YH |
+
+#### 响应参数(摘要)
+
+| 字段名 | 中文名称 | 数据类型 | 说明 |
+|--------|---------|----------|------|
+| Py_Amt | 总支付金额 | varChar(19) | 总支付金额 |
+| Mrch_Rfnd_Amt | 总退款金额 | varChar(10) | 总退款金额 |
+| Py_Ordr_Amt | 总支付订单金额 | varChar(18) | 总支付订单金额 |
+| Rght_Cgy_Inf | 权益类信息 | varChar(3000) | 权益信息(见下方说明) |
+| LIST | 订单列表 | Array | 订单详情列表 |
+
+#### Rght_Cgy_Inf 权益类信息说明
+
+权益位图格式示例:
+```
+P6:{FL3:有价折扣券,AM1:30.00,AM2:30.00},P3:{FL2:02,FL3:满减活动,AM1:30.00,AM2:30.00}
+```
+
+**字段说明**:
+- P6: 商户券
+- FL1: 优惠度量(如积分数、券数量)
+- FL2: 清算方式(01-后结算 02-在线一笔清算 03-在线异步清算)
+- FL3: 优惠类型(如满减活动、满减券、有价券等)
+- FL4: 优惠名称(如活动描述或券名称)
+- AM1: 优惠金额
+- AM2: 清算金额
+- AM3: 银行出资金额
+- AM4: 商户出资金额
+- AM5: 优惠券面额
+- AM6: 客户实付金额
+- AM7: 积分抵扣金额
+
+**P6 优惠券类型**:
+- 免费折扣券
+- 免费代金券
+- 免费实物券
+- 免费定额券
+- 有价折扣券
+- 有价代金券
+- 有价实物券
+- 有价定额券
+
+---
+
+### 3.6 A3341TP13 - 服务方订单补充信息
+
+#### 接口说明
+
+| 项目 | 说明 |
+|------|------|
+| **服务码** | A3341TP13 |
+| **服务名称** | 服务方订单补充信息 |
+| **服务类型** | 直接调用 |
+| **功能描述** | 补充订单的商品信息(SKU_LIST) |
+| **URL** | https://yunbusiness.ccb.com/tp_service/txCtrl/server?txcode=A3341TP13 |
+
+#### 请求参数
+
+| 字段名 | 中文名称 | 数据类型 | 必须 | 说明 |
+|--------|---------|----------|------|------|
+| USER_ID | 会员编号 | varChar(40) | Y | 会员编号 |
+| ORDER_ID | 订单编号 | varChar(40) | Y | 订单编号 |
+| SKU_LIST | 商品信息列表 | varChar(3000) | N | 商品信息列表(JSON字符串),格式同订单推送接口 |
+
+#### SKU_LIST 字段说明
+
+同订单推送接口的 SKU_LIST 字段,包含:
+- SKU_NAME:商品名称
+- SKU_REF_PRICE:商品参考价
+- SKU_NUM:商品数量
+- SKU_SELL_PRICE:商品单价
+
+⚠️ **重要提示**:商品售价 SKU_SELL_PRICE 不能大于商品定价 SKU_REF_PRICE,字段长度为字节,注意中文字段长度。
+
+---
+
+## 附录
+
+### A. 订单状态枚举
+
+#### 订单状态(ORDER_STATUS / PAY_STATUS)
+- `0` - 待支付
+- `1` - 支付成功
+- `2` - 已过期
+- `3` - 支付失败
+- `4` - 取消
+
+#### 退款状态(REFUND_STATUS)
+- `0` - 无退款
+- `1` - 退款申请
+- `2` - 已退款
+- `3` - 部分退款
+
+#### 特殊附加状态(SPECIAL_STATUS)
+- `P0000` - 正常状态
+- `P0001` - 待使用
+- `P0002` - 待审核
+- `P0003` - 已支付
+- `P0004` - 待退款
+- `P0005` - 退款中
+- `P0006` - 待发货
+- `P0007` - 已发货
+- `P0008` - 进行中
+- `P0009` - 预定中
+- `P0010` - 预定成功
+- `P0011` - 预定失败
+- `P0012` - 已入住
+- `P0013` - 已离店
+- `P0014` - 未入住
+- `P0015` - 配送中
+- `P0016` - 出券中
+
+### B. 服务方订单类型(PLAT_ORDER_TYPE)
+- `T0000` - 普通类型
+- `T0001` - 洗车
+- `T0002` - 加油
+- `T0003` - 停车
+- `T0004` - 修车
+- `T0005` - 充电
+- `T0006` - 年检代办
+- `T0007` - 道路救援
+- `T0008` - 云南中石油充值
+
+### C. 支付方式(PAY_MODE / Scn_Idr)
+- `STSL` - 刷脸
+- `STSK` - 刷卡
+- `BHK` - 本行卡
+- `THK` - 他行卡
+- `ZFB` - 支付宝
+- `CFT` - 微信
+
+### D. 交易状态(TXN_STATUS / Txn_Status)
+- `00` - 交易成功
+- `01` - 交易失败
+- `02` - 不确定
+- `04` - 不确定(前端无须发冲正)
+- `TO` - 交易超时
+
+---
+
+## 开发指南
+
+### 1. 接口调用流程
+
+```
+1. 构建公共报文头(CLD_HEADER)
+ ├── CLD_TX_CHNL:服务方ID(如YS44000009001853)
+ ├── CLD_TX_TIME:当前时间(YmdHis格式)
+ ├── CLD_TX_CODE:服务码(如A3341TP01)
+ └── CLD_TX_SEQ:唯一流水号
+
+2. 构建业务参数(CLD_BODY)
+ └── 根据具体接口填充业务参数
+
+3. 整体报文加密
+ ├── 将CLD_HEADER + CLD_BODY转JSON
+ ├── 使用建行公钥RSA加密
+ └── 生成MD5签名(mac字段)
+
+4. 发送HTTP POST请求
+ ├── URL:基础地址 + ?txcode=服务码
+ ├── 参数:cnt(加密内容)、mac(签名)
+ └── 请求头:Content-Type: application/x-www-form-urlencoded
+
+5. 处理响应
+ ├── 使用商户私钥解密响应cnt字段
+ ├── 验证响应mac签名
+ └── 解析CLD_HEADER和CLD_BODY
+```
+
+### 2. 重要提示
+
+⚠️ **密钥管理**
+- 商户私钥:用于解密建行返回的数据
+- 商户公钥:参与支付串的MD5签名计算
+- 建行公钥:用于加密发送给建行的数据
+
+⚠️ **字段校验**
+- SKU_SELL_PRICE(商品售价)不能大于 SKU_REF_PRICE(商品定价)
+- 通知类型为"支付状态修改"时,PAY_STATUS 必填,REFUND_STATUS 为空
+- 通知类型为"退款状态修改"时,REFUND_STATUS 必填,PAY_STATUS 为空
+
+⚠️ **流控限制**
+- 订单查询接口(A3341TP03):默认流控值为 100/TPM
+- 权益查询接口(A3341TP05):与订单支付结果一起计算流控TPM
+
+⚠️ **时间格式**
+- 所有时间字段统一使用:`yyyyMMddHHmmss` 格式
+- 示例:`20250121120000`
+
+### 3. 错误处理
+
+- 接口返回404:请在请求头中添加 `Accept: application/json` 和 `Content-Type: application/json`
+- 查询结果延时:订单查询接口存在延时,返回未查到时可适当重复查询
+- 退款未返回:建议先调用订单查询接口查询退款结果,再做后续处理
+
+---
+
+**文档结束**
From 8de5d880915bdefe7d2901f70b3106882359d6dc Mon Sep 17 00:00:00 2001
From: Billy <641833868@qq.com>
Date: Tue, 21 Oct 2025 11:11:59 +0800
Subject: [PATCH 05/10] =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
addons/shopro/config/ccblife.php | 2 +-
.../shopro/library/ccblife/CcbHttpClient.php | 42 +++++++
.../library/ccblife/CcbOrderService.php | 107 +++++++++++++++---
3 files changed, 134 insertions(+), 17 deletions(-)
diff --git a/addons/shopro/config/ccblife.php b/addons/shopro/config/ccblife.php
index 8d98b0b..82d9e03 100644
--- a/addons/shopro/config/ccblife.php
+++ b/addons/shopro/config/ccblife.php
@@ -108,7 +108,7 @@ return [
// 商户信息
'merchant' => [
- 'name' => Env::get('ccb.merchant_name', '商户名称'),
+ 'name' => Env::get('ccb.merchant_name', '丰科贸易(荷西嘉园店)'),
'logo_url' => Env::get('ccb.merchant_logo', ''),
'order_detail_url' => Env::get('app_url', 'http://fengketrade.test') . '/pages/order/detail?id=',
],
diff --git a/addons/shopro/library/ccblife/CcbHttpClient.php b/addons/shopro/library/ccblife/CcbHttpClient.php
index 0d46db5..a7fe9a2 100644
--- a/addons/shopro/library/ccblife/CcbHttpClient.php
+++ b/addons/shopro/library/ccblife/CcbHttpClient.php
@@ -2,6 +2,8 @@
namespace addons\shopro\library\ccblife;
+use think\Log;
+
/**
* 建行生活HTTP客户端
* 处理与建行API的通信,包括加密、签名、发送请求和解密响应
@@ -48,6 +50,10 @@ class CcbHttpClient
// 构建请求报文
$message = $this->buildMessage($txCode, $body, $txSeq);
+ // 📝 记录原始请求报文(加密前)
+ Log::info('建行生活API原始请求报文 [txCode=' . $txCode . '] [txSeq=' . $txSeq . ']');
+ Log::info('原始报文内容: ' . $message);
+
// 加密报文
$encryptedMessage = CcbRSA::encryptForCcb($message, $this->config['public_key']);
@@ -99,6 +105,9 @@ class CcbHttpClient
*/
private function sendHttpRequest($txCode, $cnt, $mac)
{
+ // 记录请求开始时间
+ $startTime = microtime(true);
+
// 构建完整的API URL(基础URL + ?txcode=交易代码)
$apiUrl = $this->config['api_base_url'] . '?txcode=' . $txCode;
@@ -108,6 +117,11 @@ class CcbHttpClient
'mac' => $mac
];
+ // 📝 记录请求参数(加密后的完整内容)
+ Log::info('建行生活API加密请求参数 [txCode=' . $txCode . '] [url=' . $apiUrl . '] [timeout=' . self::DEFAULT_TIMEOUT . 's]');
+ Log::info('加密参数 cnt: ' . $cnt);
+ Log::info('加密参数 mac: ' . $mac);
+
// 初始化CURL
$ch = curl_init();
@@ -130,17 +144,32 @@ class CcbHttpClient
$response = curl_exec($ch);
$error = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $curlInfo = curl_getinfo($ch);
curl_close($ch);
+ // 计算请求耗时
+ $costTime = round((microtime(true) - $startTime) * 1000, 2); // 毫秒
+
// 检查错误
if ($error) {
+ // 📝 记录错误日志
+ Log::error('建行生活API请求失败 [txCode=' . $txCode . '] [url=' . $apiUrl . '] [error=' . $error . '] [cost_time=' . $costTime . 'ms] [total_time=' . ($curlInfo['total_time'] ?? 0) . 's] [connect_time=' . ($curlInfo['connect_time'] ?? 0) . 's]');
throw new \Exception('HTTP请求失败: ' . $error);
}
if ($httpCode !== 200) {
+ // 📝 记录HTTP状态码异常日志
+ $responsePreview = mb_substr($response, 0, 300);
+ Log::error('建行生活API响应状态码异常 [txCode=' . $txCode . '] [url=' . $apiUrl . '] [http_code=' . $httpCode . '] [cost_time=' . $costTime . 'ms] [response=' . $responsePreview . ']');
throw new \Exception('HTTP状态码异常: ' . $httpCode . ', 响应内容: ' . $response);
}
+ // 📝 记录成功响应日志
+ $totalTime = round(($curlInfo['total_time'] ?? 0) * 1000, 2);
+ $connectTime = round(($curlInfo['connect_time'] ?? 0) * 1000, 2);
+ $responseLength = mb_strlen($response);
+ Log::info('建行生活API请求成功 [txCode=' . $txCode . '] [url=' . $apiUrl . '] [http_code=' . $httpCode . '] [response_length=' . $responseLength . '] [cost_time=' . $costTime . 'ms] [total_time=' . $totalTime . 'ms] [connect_time=' . $connectTime . 'ms]');
+
return $response;
}
@@ -153,6 +182,9 @@ class CcbHttpClient
*/
private function handleResponse($response)
{
+ // 📝 记录原始响应内容
+ Log::info('建行生活API原始响应内容: ' . $response);
+
// 解析JSON响应
$responseData = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
@@ -164,15 +196,25 @@ class CcbHttpClient
throw new \Exception('响应格式错误,缺少cnt或mac字段');
}
+ // 📝 记录加密响应参数
+ Log::info('加密响应参数 cnt: ' . $responseData['cnt']);
+ Log::info('加密响应参数 mac: ' . $responseData['mac']);
+
// 解密响应内容
$decryptedContent = CcbRSA::decryptFromCcb($responseData['cnt'], $this->config['private_key']);
+ // 📝 记录解密后的响应内容
+ Log::info('解密后响应内容: ' . $decryptedContent);
+
// 验证签名
$isValid = CcbMD5::verifyApiSignature($decryptedContent, $responseData['mac'], $this->config['private_key']);
if (!$isValid) {
+ Log::error('响应签名验证失败 [expected_mac=' . $responseData['mac'] . ']');
throw new \Exception('响应签名验证失败');
}
+ Log::info('响应签名验证成功');
+
// 解析解密后的内容
$decryptedData = json_decode($decryptedContent, true);
if (json_last_error() !== JSON_ERROR_NONE) {
diff --git a/addons/shopro/library/ccblife/CcbOrderService.php b/addons/shopro/library/ccblife/CcbOrderService.php
index 8352489..86fe33b 100644
--- a/addons/shopro/library/ccblife/CcbOrderService.php
+++ b/addons/shopro/library/ccblife/CcbOrderService.php
@@ -272,7 +272,7 @@ class CcbOrderService
/**
* 构建符合建行 A3341TP01 接口规范的订单数据
*
- * 📋 建行生活订单推送接口规范说明:
+ * 📋 建行生活订单推送接口规范说明(v1.1.6):
*
* 必填字段(11个):
* - USER_ID: 客户编号(建行用户ID)
@@ -280,27 +280,30 @@ class CcbOrderService
* - ORDER_DT: 订单日期(yyyyMMddHHmmss格式)
* - TOTAL_AMT: 订单原金额
* - ORDER_STATUS: 订单状态
+ * - REFUND_STATUS: 退款状态
* - MCT_NM: 商户名称
* - CUS_ORDER_URL: 订单详情链接
- * - PAY_FLOW_ID: 支付流水号(从 shopro_pay.pay_sn 获取)
- * - PRPFTL_MRCH_ID: 门店商户号(使用 merchant_id)
- * - PAY_MRCH_ID: 支付商户号(使用 merchant_id)
+ * - PAY_FLOW_ID: 支付流水号
+ * - PAY_MRCH_ID: 支付商户号
* - SKU_LIST: 商品信息JSON字符串
*
- * 非必填但推荐字段:
- * - PAY_AMT: 订单实际支付金额
- * - DISCOUNT_AMT: 第三方平台优惠金额
+ * 重要可选字段(建议必填):
+ * - PAY_AMT: 订单实际支付金额(文档要求:如为空必须在状态变更时推送)
+ * - DISCOUNT_AMT: 第三方平台优惠金额(文档要求:如为空必须在状态变更时推送)
* - DISCOUNT_AMT_DESC: 第三方平台优惠说明
- * - REFUND_STATUS: 退款状态
- * - PAY_MODE: 支付方式
+ * - INV_DT: 订单过期日期
+ * - GOODS_NM: 商品名称
+ * - PREFTL_MRCH_ID: 门店商户号
+ * - PLAT_MCT_ID: 服务商门店编号
* - PLAT_ORDER_TYPE: 服务方订单类型
- * - COUPON_AMT: 优惠券金额
+ * - PLATFORM: 下单场景
*
* ⚠️ 注意:Shopro字段映射
* - pay_fee → PAY_AMT(实际支付金额)
* - order_amount → TOTAL_AMT(订单总金额)
* - total_discount_fee → DISCOUNT_AMT(优惠总金额)
* - createtime → ORDER_DT(毫秒时间戳需除以1000)
+ * - expiry_time → INV_DT(过期时间)
*
* @param array $order 订单数组
* @param array $orderItems 订单商品列表
@@ -309,10 +312,21 @@ class CcbOrderService
*/
private function buildOrderData($order, $orderItems, $ccbUserId)
{
+ // ⚠️ 验证必填字段:PAY_FLOW_ID(支付流水号)
+ // 这个字段在 createPayment 时设置,推送订单前必须存在
+ if (empty($order['ccb_pay_flow_id'])) {
+ throw new \Exception('订单支付流水号(ccb_pay_flow_id)不存在,请先调用createPayment生成支付串');
+ }
+
// 构建SKU商品列表(JSON字符串格式)
$skuList = $this->buildSkuList($orderItems);
+
// 计算各项金额(保留2位小数)
$totalAmount = number_format($order['order_amount'] ?? 0, 2, '.', '');
+ $payAmount = number_format($order['pay_fee'] ?? $order['order_amount'] ?? 0, 2, '.', '');
+ $discountAmount = number_format($order['total_discount_fee'] ?? 0, 2, '.', '');
+ $totalRefundAmount = number_format($order['refund_fee'] ?? 0, 2, '.', '');
+
// 处理订单时间(Shopro的createtime是毫秒时间戳,需要除以1000)
$createTimeValue = $order['createtime'] ?? null;
if (empty($createTimeValue) || !is_numeric($createTimeValue)) {
@@ -320,8 +334,42 @@ class CcbOrderService
}
$orderDt = date('YmdHis', intval($createTimeValue / 1000));
+ // 处理订单过期时间
+ $invDt = '';
+ if (!empty($order['expiry_time'])) {
+ // Shopro 的 expiry_time 可能是时间戳或日期字符串
+ if (is_numeric($order['expiry_time'])) {
+ // 如果是毫秒时间戳,需要除以1000
+ $timestamp = intval($order['expiry_time']);
+ if ($timestamp > 9999999999) {
+ $timestamp = intval($timestamp / 1000);
+ }
+ $invDt = date('YmdHis', $timestamp);
+ } else {
+ $invDt = date('YmdHis', strtotime($order['expiry_time']));
+ }
+ }
+
+ // 获取商品名称(取第一个商品)
+ $goodsName = '';
+ if (!empty($orderItems)) {
+ $goodsName = $orderItems[0]['goods_title'] ?? '';
+ // 如果有多个商品,可以拼接
+ if (count($orderItems) > 1) {
+ $goodsName .= ' 等' . count($orderItems) . '件商品';
+ }
+ }
+
+ // 构建优惠说明(如果有优惠金额)
+ $discountAmtDesc = '';
+ if ($discountAmount > 0) {
+ // 格式:名称=金额|@|名称=金额
+ // 这里简化处理,实际应该根据具体优惠券信息构建
+ $discountAmtDesc = '平台优惠=' . $discountAmount;
+ }
+
// 构建符合A3341TP01接口规范的订单数据
- return [
+ $orderData = [
// ========== 必填字段 ==========
'USER_ID' => $ccbUserId, // 客户编号
'ORDER_ID' => $order['order_sn'], // 订单号
@@ -330,13 +378,40 @@ class CcbOrderService
'ORDER_STATUS' => $this->mapOrderStatus($order['status']), // 订单状态
'REFUND_STATUS' => $this->mapRefundStatus($order['refund_status'] ?? 0), // 退款状态
'MCT_NM' => $this->config['merchant']['name'] ?? '商户名称', // 商户名称
- 'CUS_ORDER_URL' => $this->config['merchant']['order_detail_url'] . $order['id'], // 订单详情链接
- 'PAY_FLOW_ID' => $order['ccb_pay_flow_id'], // 支付流水号(必填!)
- 'PAY_MRCH_ID' => $this->config['merchant_id'], // 支付商户号(必填!)
+ 'PAY_FLOW_ID' => $order['ccb_pay_flow_id'], // 支付流水号(必填!已在上方验证)
+ 'PAY_MRCH_ID' => $this->config['merchant_id'], // 支付商户号(必填!)
'SKU_LIST' => $skuList, // 商品信息JSON字符串(必填!)
- // ========== 非必填字段 ==========
- 'PLAT_ORDER_TYPE' => "T0000", // 服务方订单类型
+
+ // ========== 重要可选字段(强烈建议填写) ==========
+ 'PAY_AMT' => $payAmount, // 订单实际支付金额
+ 'DISCOUNT_AMT' => $discountAmount, // 第三方平台优惠金额
+ 'PLAT_ORDER_TYPE' => 'T0000', // 服务方订单类型(T0000-普通类型)
+ 'PLATFORM' => '99', // 下单场景(99-建行生活APP)
];
+
+ // ========== 条件可选字段(有值才添加) ==========
+
+ // 优惠说明
+ if (!empty($discountAmtDesc)) {
+ $orderData['DISCOUNT_AMT_DESC'] = $discountAmtDesc;
+ }
+
+ // 订单过期时间
+ if (!empty($invDt)) {
+ $orderData['INV_DT'] = $invDt;
+ }
+
+ // 商品名称
+ if (!empty($goodsName)) {
+ $orderData['GOODS_NM'] = mb_substr($goodsName, 0, 200); // 限制长度200字符
+ }
+
+ // 累计退款金额(如果有退款)
+ if ($totalRefundAmount > 0) {
+ $orderData['TOTAL_REFUND_AMT'] = $totalRefundAmount;
+ }
+
+ return $orderData;
}
/**
From eb0fe44cbd0e44eec00874a22ce0688ae6ef76e5 Mon Sep 17 00:00:00 2001
From: Billy <641833868@qq.com>
Date: Tue, 21 Oct 2025 11:14:27 +0800
Subject: [PATCH 06/10] 1
---
addons/shopro/controller/Pay.php | 4 ++--
addons/shopro/controller/order/Order.php | 1 -
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/addons/shopro/controller/Pay.php b/addons/shopro/controller/Pay.php
index b69bc38..c1cf25e 100755
--- a/addons/shopro/controller/Pay.php
+++ b/addons/shopro/controller/Pay.php
@@ -344,7 +344,7 @@ class Pay extends Common
*
* @param object|string $result
* @param string|null $payment
- * @return void
+ * @return object|string|\think\Response|null
*/
private function payResponse($result = null, $payment = null)
{
@@ -363,7 +363,7 @@ class Pay extends Common
* 根据订单号获取订单实例
*
* @param [type] $order_sn
- * @return void
+ * @return array
*/
private function getOrderInstance($order_sn)
{
diff --git a/addons/shopro/controller/order/Order.php b/addons/shopro/controller/order/Order.php
index a8501aa..1f278e0 100755
--- a/addons/shopro/controller/order/Order.php
+++ b/addons/shopro/controller/order/Order.php
@@ -77,7 +77,6 @@ class Order extends Common
$this->error(__('No Results were found'));
}
- $order->pay_types_text = $order->pay_types_text;
// 处理未支付订单 item status_code
$order = $order->setOrderItemStatusByOrder($order); // 这里订单转 数组了
From 461083cb7935d95788480fd5aa005f1794549fc8 Mon Sep 17 00:00:00 2001
From: Billy <641833868@qq.com>
Date: Tue, 21 Oct 2025 13:42:55 +0800
Subject: [PATCH 07/10] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=8E=A5=E5=8F=A3?=
=?UTF-8?q?=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
doc/建行生活API接口文档.md | 996 ++++++++++++++++++++++++++++-
1 file changed, 993 insertions(+), 3 deletions(-)
diff --git a/doc/建行生活API接口文档.md b/doc/建行生活API接口文档.md
index 628edc3..ed3a366 100644
--- a/doc/建行生活API接口文档.md
+++ b/doc/建行生活API接口文档.md
@@ -1,8 +1,11 @@
# 建行生活API接口文档
-> 文档版本: v1.1.6
-> 生成时间: 2025-01-21
-> 来源: 建行生活输入通讯报文v1.1.6【最新】.xlsx
+> 文档版本: v2.0.0
+> 更新时间: 2025-10-21
+> 来源:
+> - 建行生活输入通讯报文v1.1.6【最新】.xlsx
+> - 建行生活原生与h5交互规范接口1.3(新).html
+> - 建行相关App服务方接入文档v2.20_20250725.html
---
@@ -20,6 +23,23 @@
- [3.4 A3341TP04 - 服务方订单退款](#34-a3341tp04---服务方订单退款)
- [3.5 A3341TP05 - 订单支付权益查询](#35-a3341tp05---订单支付权益查询)
- [3.6 A3341TP13 - 服务方订单补充信息](#36-a3341tp13---服务方订单补充信息)
+- [4. H5 与原生交互接口(JS Bridge)](#4-h5-与原生交互接口js-bridge)
+ - [4.1 交互规范说明](#41-交互规范说明)
+ - [4.2 callCamera - 调用相机/相册](#42-callcamera---调用相机相册)
+ - [4.3 getPosition - 获取定位](#43-getposition---获取定位)
+- [5. 服务方接入规范](#5-服务方接入规范)
+ - [5.1 跳转 URL 规范](#51-跳转-url-规范)
+ - [5.2 建行相关 App 环境识别](#52-建行相关-app-环境识别)
+ - [5.3 收银台调用方法](#53-收银台调用方法)
+ - [5.4 商户下单支付参数定义(完整)](#54-商户下单支付参数定义完整)
+ - [5.5 支付成功页面回调设置](#55-支付成功页面回调设置)
+ - [5.6 其他说明](#56-其他说明)
+- [6. 报文规范与加密](#6-报文规范与加密)
+ - [6.1 报文结构](#61-报文结构)
+ - [6.2 报文加密及签名](#62-报文加密及签名)
+- [7. 回调通知接口](#7-回调通知接口)
+ - [7.1 建行生活支付通知接口](#71-建行生活支付通知接口)
+ - [7.2 建行生活退款操作通知接口](#72-建行生活退款操作通知接口)
---
@@ -34,6 +54,7 @@
| 2024-02-23 | 订单更新新增PLATFORM_POINT、PAY_MODE、GOODS_NM字段 | 已上线 |
| 2024-02-26 | 增加权益信息查询接口 | 已上线 |
| 2025-06-09 | 增加SKU_LIST字段 | 待上线 |
+| 2025-10-21 | **整合多个文档**:新增 H5 与原生交互接口(JS Bridge)、服务方接入规范、报文加密与签名、回调通知接口等章节 | 文档整合 |
---
@@ -565,4 +586,973 @@ P6:{FL3:有价折扣券,AM1:30.00,AM2:30.00},P3:{FL2:02,FL3:满减活动,AM1:30.
---
+## 4. H5 与原生交互接口(JS Bridge)
+
+> 版本: v1.1
+> 来源: 建行生活原生与h5交互规范接口1.3(新).html
+
+### 4.1 交互规范说明
+
+#### 4.1.1 H5 请求 JS Bridge API 格式
+
+用于 H5 调用由 Native 提供的 js api 能力,如扫一扫、定位、登录等能力。
+
+**调用格式:**
+
+```javascript
+// H5 请求
+/**
+ * H5 与 native api 交互
+ * service: api类型
+ * action: api名称
+ * params: 参数
+ * callBack: 回调方法名,H5自定义,通过变量的方式传递到客户端,供客户端执行指令后回调
+ */
+CCBMofeBridge.exec(service, action, params, callBackTmp)
+```
+
+**Native 回调格式:**
+
+```javascript
+// native 回调 callBackTmp 格式定义
+/**
+ * 客户端回调 H5 调用的 js 方法示例
+ * data: 返回的数据,json格式
+ * status: 返回状态,0:成功,1:失败,-2:功能未开通
+ */
+callBackTmp('{
+ "data":"", // jsonStr api响应字典转Json
+ "status":"0", // 状态码
+}')
+```
+
+---
+
+### 4.2 callCamera - 调用相机/相册
+
+#### 接口说明
+
+调用手机相机拍照或选择相册图片。
+
+#### 请求参数
+
+| 字段名 | 类型 | 默认值 | 必填 | 说明 |
+|--------|------|--------|------|------|
+| sourceType | string | - | 是 | `album` 相册 / `camera` 拍照 |
+| maxSize | string | - | 否 | 图片最大大小,单位为 k |
+| count | string | 1 | 否 | 最多可以选择的图片张数,目前仅建行生活支持,仅调用相册时支持 |
+
+#### 成功返回参数
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| imgs | array | 图片 base64 串数组 |
+
+#### 错误返回参数
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| errCode | string | 错误码:`1014` 用户未授权,`0001` 参数错误,`-1` 用户取消 |
+| errMsg | string | 错误信息 |
+
+#### 调用示例
+
+```javascript
+var param = {
+ "sourceType": "album",
+ "maxSize": "1024",
+ "count": "2"
+}
+function callback(res) {
+ console.log(res);
+}
+window.callback = callback
+window.CCBMofeBridge.exec("baseAPI", "callCamera", JSON.stringify(param), 'callback');
+```
+
+#### 响应示例
+
+**成功响应:**
+
+```json
+{
+ "data": {
+ "imgs": ["data:image/png;base64,xxxxxxxx", "data:image/png;base64,xxxxxxxx"]
+ },
+ "status": "0" // 0:成功,1:失败,-2:功能未开通
+}
+```
+
+**失败响应:**
+
+```json
+{
+ "data": {
+ "errCode": "1014",
+ "errMsg": "用户未授权"
+ },
+ "status": "1"
+}
+```
+
+---
+
+### 4.3 getPosition - 获取定位
+
+#### 接口说明
+
+获取用户当前位置信息。
+
+#### 请求参数
+
+| 字段名 | 类型 | 默认值 | 建行生活必填 | 手机银行必填 | 说明 |
+|--------|------|--------|--------------|--------------|------|
+| Can_Ahn_Inst | string | - | 否 | 是 | 机构号 |
+| appName | string | - | 否 | 是 | 名称 |
+| cdnLogoUrl | string | - | 否 | 否 | logoUrl 地址 |
+| mode | string | 1 | 否 | 否 | 定位模式:
`0` 缓存优先,先返回上一次定位缓存再异步更新缓存
`1` 即时定位,loading等待即时定位后返回,并更新缓存(默认)
`2` 兜底定位,先实时定位,超时后返回缓存定位 |
+| timeout | string | 2 | 否 | 否 | 定位 API 超时时间,默认值为 2s,支持通用技术参数配置,字段为 locationCacheTime |
+
+#### 成功返回参数
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| cityName | string | 城市名称,如:广州市 |
+| cityCode | string | 城市代码,如:440100 |
+| latitude | string | 纬度 |
+| longitude | string | 经度 |
+| address | string | 地址 |
+| adCode | string | 区域行政编码,如:440106 |
+| gpsType | string | 坐标系,`bd09` 为百度,`gcj02` 为高德 |
+
+#### 错误返回参数
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| gpsType | string | 坐标系,`bd09` 为百度,`gcj02` 为高德 |
+| bizCode | string | 地图 sdk 原始定位结果错误码 |
+| errCode | string | 错误码:
`1014` 用户未授权
`1016` 手机GPS开关未开启
`1017` 地图sdk返回失败
`0001` 参数错误 |
+| errMsg | string | 错误信息 |
+
+#### 调用示例
+
+```javascript
+var param = {}
+function callback(res) {
+ console.log(res);
+}
+window.callback = callback
+window.CCBMofeBridge.exec("baseAPI", "getPosition", JSON.stringify(param), 'callback');
+```
+
+#### 响应示例
+
+**成功响应:**
+
+```json
+{
+ "data": {
+ "gpsType": "bd09",
+ "adCode": "440106",
+ "address": "中国广东省广州市天河区猎德街道珠江新城华夏路10号",
+ "cityCode": "440100",
+ "cityName": "广州市",
+ "latitude": "23.123691",
+ "longitude": "113.329941"
+ },
+ "status": "0"
+}
+```
+
+**失败响应:**
+
+```json
+{
+ "data": {
+ "gpsType": "bd09",
+ "bizCode": "06",
+ "errCode": "1017",
+ "errMsg": "定位失败"
+ },
+ "status": "1"
+}
+```
+
+---
+
+## 5. 服务方接入规范
+
+> 版本: v2.20
+> 来源: 建行相关App服务方接入文档v2.20_20250725.html
+
+### 5.1 跳转 URL 规范
+
+建行相关 App 通过分类入口跳转或订单详情跳转至服务方链接时,使用 URL 格式为:
+
+```
+url?platform=ccblife&channel=mbs&ccbParamSJ=xxxxxx&CITYID=330100&USERCITYID=440100
+```
+
+**参数说明:**
+
+- **url**: 服务方页面链接地址(一般为中间页面,用于对跳转参数的解密验签处理及二次跳转)
+- **platform**: `ccblife` 为建行生活平台标识符
+- **channel**: 当前运行环境所在的 App
+ - `channel=mbs` 表示在中国建设银行 App 运行
+ - 无此参数则默认为在建行生活 App 运行
+- **CITYID**: 用户选择城市代码(6位中国城市代码)
+- **USERCITYID**: 用户定位城市代码(6位中国城市代码)
+- **ccbParamSJ**: 使用服务方公钥 RSA 加密后再 base64 + encodeURIComponent 的加密密文
+
+#### ccbParamSJ 密文携带的参数
+
+解密后明文格式为: `USERID=xxx&MOBILE=xxx...`(先进行 URLDecode 之后,再进行解密)
+
+| 参数名 | 参数类型 | 中文名 | 备注 |
+|--------|----------|--------|------|
+| BGCOLOR | String | 背景色 | 默认为空 |
+| PLATFLOWNO | String | 登录校验流水号 | |
+| TIMESTAMP | String | 跳转服务方时间戳 | 13位毫秒级 |
+| USERID | String | 建行生活用户ID | |
+| MOBILE | String | 手机号码 | |
+| CITYID | String | 用户选择城市代码 | 使用标准的中国城市代码 |
+| USERCITYID | String | 用户定位城市代码 | |
+| LGT | String | 经度 | |
+| LTT | String | 纬度 | |
+| GPS_TYPE | String | GPS坐标系 | `gcj02`(高德地图坐标)、`bd09`(百度地图坐标) |
+| APPID | String | 小程序appid | 小程序专用参数,APP不存在 |
+| OPENID | String | 用户在小程序下唯一标识 | 小程序专用参数,APP不存在 |
+| ORDERID | String | 订单号 | 通过订单详情跳转会携带此字段 |
+| TOKEN | String | 建行生活用户TOKEN | 仅通过手机银行访问建行生活场景时携带 |
+
+#### 加密串验证建议
+
+⚠️ **重要提示**:为防止加密串被非法窃取导致的越权风险,建议服务方对加密串进行唯一请求校验。
+
+可选校验方案:
+1. 使用 PLATFLOWNO 作为一次性令牌,使用后立即失效
+2. 验证 TIMESTAMP 时间戳,拒绝超过一定时间(如5分钟)的请求
+3. 结合 USERID 和 PLATFLOWNO 进行会话绑定
+
+---
+
+### 5.2 建行相关 App 环境识别
+
+服务方生活场景,按本章节方法识别当前运行的 App 环境是否为建行相关 App。
+
+#### 5.2.1 建行生活 App 环境判断
+
+通过检测 URL 参数中的 `platform=ccblife` 和 `channel` 参数来判断:
+
+```javascript
+// 判断是否为建行生活 App
+function isCCBLifeApp() {
+ const urlParams = new URLSearchParams(window.location.search);
+ const platform = urlParams.get('platform');
+ const channel = urlParams.get('channel');
+
+ return platform === 'ccblife' && !channel;
+}
+```
+
+#### 5.2.2 中国建设银行 App 环境判断
+
+通过检测 URL 参数中的 `channel=mbs` 来判断:
+
+```javascript
+// 判断是否为中国建设银行 App
+function isCCBApp() {
+ const urlParams = new URLSearchParams(window.location.search);
+ const channel = urlParams.get('channel');
+
+ return channel === 'mbs';
+}
+```
+
+---
+
+### 5.3 收银台调用方法
+
+#### 5.3.1 在建行生活 App 环境收银台调用
+
+##### (1) iPhone 系统调用
+
+```javascript
+// iOS 系统调用收银台
+function callCashierIOS(paymentParams) {
+ // paymentParams 为支付参数串
+ window.location.href = 'ccbpayment://openCashier?params=' + encodeURIComponent(paymentParams);
+}
+```
+
+##### (2) Android 系统调用
+
+```javascript
+// Android 系统调用收银台
+function callCashierAndroid(paymentParams) {
+ // paymentParams 为支付参数串
+ window.CCBAndroid.callCashier(paymentParams);
+}
+```
+
+#### 5.3.2 在中国建设银行 App 环境收银台调用
+
+```javascript
+// 在建设银行 App 中调用收银台
+function callCashierInCCBApp(paymentParams) {
+ window.CCBMofeBridge.exec("paymentAPI", "callCashier", paymentParams, 'paymentCallback');
+}
+
+function paymentCallback(result) {
+ // 处理支付结果
+ console.log('支付结果:', result);
+}
+```
+
+---
+
+### 5.5 支付成功页面回调设置
+
+#### 5.5.1 回调地址设置方法
+
+支付完成后默认跳转到建行生活的支付成功页面,如服务方需要跳转到自己的成功页,请调用 `setCache` 方法来设置。
+
+⚠️ **重要提示**:`setCache` 操作须在调起收银台(5.3.1 在建行生活App环境收银台调用)**之前**进行。
+
+**设置方法(仅在建行生活 App 环境有效):**
+
+```javascript
+var requestObj = {
+ action: 'setCache', // 设置回调地址
+ params: {
+ key: '', // 回调的 key,与下单参数的 remark2 保持一致,上送服务方编号
+ value: '' // 回调的 url,支付成功后将由建行生活收银台跳转至此 url
+ }
+}
+
+window.CCBBridge.requestNative(JSON.stringify(requestObj), 'callBackName');
+
+// 注:callBackName 是回调函数的名称(可自行定义),请先将回调函数挂载到 window 对象下
+// 该回调仅作为地址设置成功的参考
+```
+
+**在中国建设银行 App 环境:**
+
+在中国建设银行 App 环境需通过《5.4 商户下单支付参数定义》中的 **PAYSUCCESSURL** 参数提供支付成功页面 URL。
+
+#### 5.5.2 回调 URL 携带参数
+
+支付成功跳转后,设置的回调 URL 会增加携带如下参数:
+
+| 字段名 | 中文名 | 备注 |
+|--------|--------|------|
+| MERCHANTID | 商户号 | |
+| POSID | 柜台号 | |
+| ORDERID | 订单号 | |
+| PAYMENT | 订单金额 | |
+| SUCCESS | 支付成功标识 | 固定为 Y |
+| REMARK2 | 支付备注二 | |
+| realPayment | 实付金额 | 优惠后实际支付的金额 |
+| ccbParamSJ | 用户信息加密串 | 与跳转服务方携带的相同 |
+
+⚠️ **重要提示**:最终支付结果请以服务器通知(7.1 建行生活支付通知接口)为准,前端回调仅作为页面跳转使用。
+
+---
+
+### 5.6 其他说明
+
+#### 5.6.1 支付流程要点
+
+1. **调起支付**:建行相关 App 根据订单信息调起支付
+2. **完成支付**:在支付模块完成支付后,点击完成支付,关闭支付模块,跳转到支付成功页面
+3. **结果确认**:⚠️ **支付成功结果最终以服务器通知为准**(参考 7.1 建行生活支付通知接口)
+
+#### 5.6.2 业务流程说明
+
+**以建行生活 App 环境为例(仅供参考):**
+
+**重点步骤说明:**
+
+1. **步骤2 - 订单推送**:
+ - 由服务方调用订单推送接口(**A3341TP01**)向建行生活推送订单信息
+ - 参考《3.1 A3341TP01 - 服务方订单推送》
+
+2. **步骤3 - 调用收银台**:
+ - 参考《5.3 收银台调用方法》调用收银台
+ - 参考《5.4 商户下单支付参数定义》构建支付参数
+
+3. **步骤10、11 - 支付通知**:
+ - 支付通知有两种推送方式,具体细节参考《7.1 建行生活支付通知接口》:
+ - **方式1**:网银通知建行生活,再由建行生活转发给服务方(推荐)
+ - **方式2**:网银直接通知服务方
+
+4. **步骤13、16 - 订单状态更新**:
+ - 由服务方调用订单更新接口(**A3341TP02**)向建行生活更新订单状态
+ - 参考《3.2 A3341TP02 - 服务方订单更新》
+
+5. **步骤14、15 - 主动查询**:
+ - 服务方可以向外联平台主动查询订单交易流水,判断订单的支付状态
+ - 也可以使用订单查询接口(**A3341TP03**)查询订单状态
+ - 参考《3.3 A3341TP03 - 服务方订单查询》
+
+**业务流程图:**
+
+```
+用户 → 服务方H5页面 → 调用订单推送接口(A3341TP01) → 建行生活
+ ↓
+ 调用收银台(支付参数)
+ ↓
+ 建行生活/建行App收银台
+ ↓
+ 用户支付
+ ↓
+ ┌───────────┴───────────┐
+ ↓ ↓
+ 支付成功页面 支付通知(方式1或方式2)
+ ↓ ↓
+ 回调服务方H5 服务方后台接收通知
+ ↓ ↓
+ 用户确认订单 更新订单状态(A3341TP02)
+ ↓
+ 主动查询确认(A3341TP03)
+```
+
+---
+
+### 5.4 商户下单支付参数定义(完整)
+
+#### 参数说明
+
+| 字段名 | 中文名 | 类型 | 是否非空 | 是否必送 | 备注 |
+|--------|--------|------|----------|----------|------|
+| **MERCHANTID** | 商户代码 | CHAR(15) | Y | F | 由建行统一分配 |
+| **POSID** | 柜台代码 | CHAR(9) | Y | F | 由建行统一分配,9位柜台号 |
+| **BRANCHID** | 分行代码 | CHAR(9) | Y | F | 由建行统一分配 |
+| POSID19 | 商户19位终端号 | CHAR(19) | N | F | 由建行统一分配,使用微信支付时上送。仅作为参数传递,不参与MAC校验 |
+| **PLATMCTID** | 外部平台商户号 | CHAR(19) | Y | F | 当使用外部商户号时,建行商户号、柜台号、分行号及终端号无需上送。当该字段有值时参与MAC校验,否则不参与MAC校验 |
+| **ORDERID** | 支付流水号 | CHAR(30) | Y | T | 由商户提供,最长30位,支付时上送到支付中台,支付结果查询和退款使用 |
+| **USER_ORDERID** | 用户订单号 | CHAR(30) | Y | T | 由商户提供,最长30位,用户订单列表订单信息,订单推送和更新时订单号,支付校验订单信息 |
+| **PAYMENT** | 付款金额 | NUMBER(16,2) | Y | T | 由商户提供,最长30位 |
+| **CURCODE** | 币种 | CHAR(2) | Y | T | 缺省为 01-人民币(只支持人民币支付) |
+| **TXCODE** | 交易码 | CHAR(6) | Y | T | 由建行统一分配为 520100 |
+| REMARK1 | 备注1 | CHAR(30) | N | T | 网银不处理,直接传到城综网,该字段只支持送数字和英文 |
+| **REMARK2** | 备注2 | CHAR(30) | Y | T | 上送 YS 开头的服务方编号,与 PLATFORMID 保持一致 |
+| **TYPE** | 接口类型 | CHAR(1) | Y | T | 默认送 1 - 防钓鱼接口 |
+| **GATEWAY** | 网关类型 | CHAR(100) | Y | T | 默认送 0 |
+| CLIENTIP | 客户端IP | CHAR(40) | N | T | 送空值即可 |
+| REGINFO | 客户注册信息 | CHAR(256) | N | T | 客户在商户系统中注册的信息,中文需使用 escape 编码。送空值即可 |
+| **PROINFO** | 商品信息 | CHAR(256) | N | T | 客户购买的商品信息,收银台会展示该信息,中文需使用 escape 编码。建议编码前长度不超过50位 |
+| REFERER | 商户URL | CHAR(100) | N | T | 商户送空值即可 |
+| INSTALLNUM | 分期期数 | CHAR(2) | N | F | 信用卡支付分期期数,一般为 3、6、12 等,必须为大于 1 的整数。仅当分期支付时上送该字段,无此字段上送时,则视为普通支付 |
+| **THIRDAPPINFO** | 客户端标识 | CHAR(40) | Y | T | 通过建行相关App下单场景,订单中客户端标识固定设为 `comccbpay1234567890cloudmerchant` |
+| TIMEOUT | 订单超时时间 | CHAR(14) | N | F | 格式:YYYYMMDDHHMMSS(如:20120214143005)银行系统时间 > TIMEOUT 时拒绝交易,若送空值则不判断超时。当该字段有值时参与MAC校验,否则不参与MAC校验 |
+| USERID | 建行生活用户ID | CHAR(32) | N | F | 仅在中国建设银行App环境使用。当该字段有值时参与MAC校验,否则不参与MAC校验 |
+| TOKEN | 建行生活用户TOKEN | CHAR(32) | N | F | 仅在中国建设银行App环境使用。当该字段有值时参与MAC校验,否则不参与MAC校验 |
+| PAYSUCCESSURL | 支付成功页面URL | CHAR(128) | N | F | 仅在中国建设银行App环境使用,如需指定支付成功页面时提供,需对URL编码,生产环境必须为HTTPS。未提供则默认跳转到建行生活的支付成功页面。当该字段有值时参与MAC校验,否则不参与MAC校验 |
+| PAYBITMAP | 支付位图 | CHAR(10) | N | F | 默认为空,特定场景使用 |
+| ACCOUNTBITMAP | 支付账户位图 | CHAR(10) | N | F | 默认为空,特定场景使用 |
+| POINTAVYID | 积分二级活动编号 | VARCHAR(6) | N | F | 默认为空,特定场景使用。龙支付积分二级活动上送 010051 |
+| DCEPDEPACCNO | 数字人民币收款钱包编号 | VARCHAR(32) | N | F | 默认为空,特定场景使用。数字人民币商户绑定的收款钱包编号 |
+| COUPONAVYID | 有价券活动编号 | VARCHAR(32) | N | F | 默认为空,特定场景使用 |
+| ONLY_CREDIT_PAY_FLAG | 限制信用卡支付标志 | CHAR(1) | N | F | 默认为空,特定场景使用。当有价券活动编号不为空时生效,送Y限制仅信用卡能支付,送N或空不作限制 |
+| FIXEDPOINTVAL | 固定抵扣积分值 | VARCHAR(16) | N | F | 默认为空,特定场景使用。上送该值时,若用户不满足积分使用条件将拒绝支付 |
+| MINPOINTLIMIT | 最小使用积分抵扣限制 | VARCHAR(16) | N | F | 默认为空,特定场景使用。上送整数值时,视为最小积分抵扣数额;上送小于1的小数时,视为最小积分抵扣比例。若用户不满足积分使用条件将拒绝支付 |
+| IDENTITYCODE | 身份证后6位 | VARCHAR(256) | N | F | 默认为空,特定场景使用。仅中石化服务方可用。身份证号后6位加密串,用于身份识别。加密说明:用服务方公钥对身份证后6位进行RSA加密,再进行base64,再进行encodeURIComponent一次 |
+| NOTIFY_URL | 支付异步通知地址 | VARCHAR(512) | N | F | 默认为空,特定场景使用。仅中石化服务方可用。支付结果异步通知地址的encodeURIComponent编码值,多个通知地址时,分隔符用英文半角符号 , 分隔 |
+| DCEP_MCT_TYPE | 数币商户类型 | CHAR(1) | N | F | 默认为空,特定场景使用。0\空-不识别为数币商户;1-融合商户;2-非融合商户 |
+| DCEP_MERCHANTID | 数字人民币商户号 | CHAR(15) | N | F | 默认为空,特定场景使用。当 DCEP_MCT_TYPE 为 2 时上送值 |
+| DCEP_POSID | 数字人民币柜台号 | CHAR(9) | N | F | 默认为空,特定场景使用。当 DCEP_MCT_TYPE 为 2 时上送值 |
+| DCEP_BRANCHID | 数字人民币分行号 | CHAR(9) | N | F | 默认为空,特定场景使用。当 DCEP_MCT_TYPE 为 2 时上送值 |
+| SUB_MCT_ID | 服务方二级商户编号 | VARCHAR(20) | N | F | 默认为空,涉及银联反欺诈时使用。若支付发起方为平台类服务方,需上送第三方平台下实际发起支付的二级商户所对应的商户编号 |
+| SUB_MCT_NAME | 服务方二级商户名称 | VARCHAR(40) | N | F | 默认为空,涉及银联反欺诈时使用。若支付发起方为平台类服务方,需上送第三方平台下实际发起支付的二级商户所对应的商户名称 |
+| SUB_MCT_MCC | 服务方二级商户类别 | CHAR(4) | N | F | 默认为空,涉及银联反欺诈时使用。若支付发起方为平台类服务方,需上送第三方平台下实际发起支付的二级商户所对应的商户MCC码 |
+| EXTENDPARAMS | 扩展域 | VARCHAR(256) | N | F | 默认为空,特定场景使用。上送约定JSON格式字符串 |
+| **PLATFORMPUB** | 服务方公钥 | VARCHAR(256) | Y | F | 仅作为源串参加 MD5 摘要,不作为参数传递 |
+| **MAC** | MD5加密串 | CHAR(32) | Y | T | 采用标准 MD5 算法,对以上字段进行 MAC 加密(32位小写),由商户实现 |
+| **PLATFORMID** | 服务方编号 | CHAR(16) | Y | T | 以 YS 开头的16位编号。仅作为参数传递,不参与MAC校验 |
+| ENCPUB | 商户公钥密文 | VARCHAR(512) | Y | F | 使用服务方公钥对商户公钥后30位进行 RSA 加密并 base64 后的密文。若商户已经上架建行生活并同步公钥,或是使用外部商户号时,可以不再上送商户公钥。仅作为参数传递,不参与MAC校验 |
+| SCNID | 场景编号 | VARCHAR(32) | N | F | 默认为空,埋点使用。特色场景的唯一标识。仅作为参数传递,不参与MAC校验 |
+| SCN_PLTFRM_ID | 场景平台编号 | VARCHAR(32) | N | F | 默认为空,埋点使用。场景平台唯一标识。仅作为参数传递,不参与MAC校验 |
+
+#### 字段说明
+
+**是否非空**:
+- `Y` - 上送该字段时,值不可为空
+- `N` - 值可以为空
+
+**是否必送**:
+- `T` - 该字段必须上送,无论是否有值
+- `F` - 字段可以不送
+
+**重点字段**(已用粗体标注):
+1. **商户标识**:MERCHANTID、POSID、BRANCHID、PLATMCTID
+2. **订单标识**:ORDERID、USER_ORDERID
+3. **金额信息**:PAYMENT、CURCODE
+4. **交易信息**:TXCODE、TYPE、GATEWAY
+5. **服务方信息**:REMARK2、PLATFORMID、PLATFORMPUB
+6. **客户端标识**:THIRDAPPINFO
+7. **商品信息**:PROINFO
+8. **签名信息**:MAC
+
+#### MD5 签名计算详解
+
+**签名规则:**
+
+采用标准 MD5 摘要算法对字符串数据签名(32位小写),得到 MAC。
+
+> **注意**:和交易通讯报文的 mac 区分,签名时不需要额外拼接 privateKey
+
+**参与签名的字段:**
+
+1. **必须参与签名的字段**(按顺序):
+ - MERCHANTID(或 PLATMCTID)
+ - POSID
+ - BRANCHID
+ - ORDERID
+ - USER_ORDERID
+ - PAYMENT
+ - CURCODE
+ - TXCODE
+ - REMARK1
+ - REMARK2
+ - TYPE
+ - GATEWAY
+ - CLIENTIP
+ - REGINFO
+ - PROINFO
+ - REFERER
+ - THIRDAPPINFO
+
+2. **有值时参与签名的字段**(橙色字段):
+ - PLATMCTID(使用外部平台商户号时)
+ - TIMEOUT
+ - USERID
+ - TOKEN
+ - PAYSUCCESSURL
+ - DCEP_MCT_TYPE
+ - 其他特定场景字段
+
+3. **最后拼接**:
+ - PLATFORMPUB(服务方公钥)
+
+**签名计算示例:**
+
+```
+参与签名的字符串(按顺序拼接,格式:字段名=值&字段名=值):
+
+MERCHANTID=105910100194086&POSID=313368474&BRANCHID=441000000&ORDERID=202209020000000061&USER_ORDERID=202209020000000061&PAYMENT=22.39&CURCODE=01&TXCODE=520100&REMARK1=&REMARK2=YS44000098000600&TYPE=1&GATEWAY=0&CLIENTIP=®INFO=&PROINFO=&REFERER=&THIRDAPPINFO=comccbpay1234567890cloudmerchant&TIMEOUT=20220902103420&DCEP_MCT_TYPE=1&PLATFORMPUB=MIGfMA0GCSqGSIb......
+
+然后对整个字符串进行 MD5 加密(32位小写):
+MAC=f07ef63236e3bbbc1dc221b06e631f3d
+```
+
+#### 中文信息编码
+
+**REGINFO(客户注册信息)** 和 **PROINFO(商品信息)** 中的中文需要使用 JavaScript 的 `escape()` 方法进行编码,数字字母信息不需编码。
+
+**编码示例:**
+
+```javascript
+escape("小飞侠") = "%u5C0F%u98DE%u4FA0"
+escape("A1级牛排") = "A1%u7EA7%u725B%u6392"
+```
+
+#### 商户公钥密文(ENCPUB)
+
+**ENCPUB** 是各服务方使用自己的**服务方公钥**对**商户公钥后30位**进行 RSA 加密,再进行 base64 后,生成的密文串。
+
+**生成步骤:**
+
+1. 取商户公钥的后30位字符
+2. 使用服务方公钥进行 RSA 加密
+3. 对加密结果进行 base64 编码
+4. 得到 ENCPUB 密文串
+
+> **注意**:若商户已经上架建行生活并同步公钥,或是关联了外部平台商户号,可以不再上送商户公钥。
+>
+> 公钥加密方法见《6.2 报文加密及签名》章节。
+
+#### 外部平台商户号说明(PLATMCTID)
+
+外部平台商户号用于服务方已在建行生活完成商户关联,且收款商户已上架建行生活的场景。
+
+**使用规则:**
+
+- **使用外部平台商户号时**:
+ - 必须上送 PLATMCTID
+ - 无需上送 MERCHANTID、POSID、BRANCHID
+ - PLATMCTID 参与 MAC 校验
+
+- **不使用外部平台商户号时**:
+ - 必须上送 MERCHANTID、POSID、BRANCHID
+ - 无需上送 PLATMCTID
+
+#### 扩展域说明(EXTENDPARAMS)
+
+该字段仅提供给特定服务方场景使用,内容为双方约定值,格式为 **JSON 字符串**,上送时需要 **encodeURI 编码**。
+
+**示例 - 善融积分:**
+
+```json
+{
+ "scene": "pointPay", // 场景
+ "pointAmount": "1.50", // 积分金额
+ "pointNum": "1050" // 积分数量
+}
+```
+
+**使用步骤:**
+
+1. 构建 JSON 字符串
+2. 使用 encodeURI 进行编码
+3. 将编码后的字符串作为 EXTENDPARAMS 的值
+
+> 更多扩展域格式请参考原文档的"扩展域列表"章节
+
+#### PAYBITMAP 和 ACCOUNTBITMAP 说明
+
+两个字段分别为定长 **10位数字的位图**,位图为 `1` 表示可用,为 `0` 表示不可用。
+
+**PAYBITMAP(支付方式位图):**
+
+| 位数 | 支付方式 | 说明 |
+|------|----------|------|
+| 第1位 | 生活钱包支付 | 1=可用,0=不可用 |
+| 第2位 | 龙支付 | 1=可用,0=不可用 |
+| 第3位 | 微信支付 | 1=可用,0=不可用 |
+| 第4位 | 数币支付 | 1=可用,0=不可用 |
+| 第5位 | 信用付 | 1=可用,0=不可用 |
+| 第6位 | 快贷支付 | 1=可用,0=不可用 |
+| 第7-10位 | 保留 | 预留位 |
+
+**ACCOUNTBITMAP(支付账户位图,仅对龙支付及生活钱包下的卡账户生效):**
+
+| 位数 | 账户类型 | 说明 |
+|------|----------|------|
+| 第1位 | 建行借记卡 | 1=可用,0=不可用 |
+| 第2位 | 建行贷记卡 | 1=可用,0=不可用 |
+| 第3位 | 他行借记卡 | 1=可用,0=不可用 |
+| 第4位 | 他行贷记卡 | 1=可用,0=不可用 |
+| 第5位 | 建行钱包 | 1=可用,0=不可用 |
+| 第6-10位 | 保留 | 预留位 |
+
+**使用说明:**
+
+- 两个字段可单独使用
+- 未上送时默认全部支付方式都支持,即全为 `1`(1111111111)
+- 该位图字段在判断支付方式是否可用时**优先级最低**
+- 仅当商户已支持某一种支付方式时,服务方可对其决定是否使用
+- 例如商户不支持微信支付,即使位图上送 `1` 也不会生效
+
+**示例:**
+
+```
+PAYBITMAP=1110000000 // 仅支持生活钱包、龙支付、微信支付
+ACCOUNTBITMAP=1100000000 // 仅支持建行借记卡和建行贷记卡
+```
+
+#### ORDERID 和 USER_ORDERID 的区别
+
+| 字段 | 用途 | 说明 |
+|------|------|------|
+| **ORDERID** | 商户支付流水号 | 用于建行收单支付流程,包括在支付、查询支付结果及退款交易中使用 |
+| **USER_ORDERID** | 用户订单号 | 用于同步用户在建行生活中的订单状态,包括在订单推送及状态更新时使用 |
+
+**使用场景:**
+
+- **ORDERID**:调用收银台时上送的支付流水号(对应收银台 ORDERID 字段)
+- **USER_ORDERID**:建行生活订单列表展示的订单号(对应收银台 USER_ORDERID 字段,支付时会校验订单信息)
+
+#### 最终参数串示例
+
+完整的支付参数串由以下部分组成:
+
+```
+参与验签字符串 + MAC + PLATFORMID + ENCPUB
+
+示例:
+MERCHANTID=105910100194086&POSID=313368474&BRANCHID=441000000&ORDERID=202209020000000061&USER_ORDERID=202209020000000061&PAYMENT=22.39&CURCODE=01&TXCODE=520100&REMARK1=&REMARK2=YS44000098000600&TYPE=1&GATEWAY=0&CLIENTIP=®INFO=&PROINFO=&REFERER=&THIRDAPPINFO=comccbpay1234567890cloudmerchant&TIMEOUT=20220902103420&DCEP_MCT_TYPE=1&MAC=f07ef63236e3bbbc1dc221b06e631f3d&PLATFORMID=YS44000098000600&ENCPUB=YzNxRGtKSkFYZURRczYvNDN6WVZkYk......
+```
+
+> **注意**:
+> - MAC 不参与 MAC 校验,但需要包含在最终参数串中
+> - PLATFORMID 仅作为参数传递,不参与 MAC 校验
+> - ENCPUB 仅作为参数传递,不参与 MAC 校验
+
+---
+
+## 6. 报文规范与加密
+
+### 6.1 报文结构
+
+#### 6.1.1 请求报文格式
+
+请求报文由两部分组成:**交易请求报文头(CLD_HEADER)** 和 **交易请求报文体(CLD_BODY)**。
+
+**报文头字段(CLD_HEADER):**
+
+| 字段标识 | 字段中文名 | 字段说明 | 数据属性 | 是否必填 | 备注 |
+|----------|------------|----------|----------|----------|------|
+| CLD_TX_CHNL | 报文来源渠道 | 消息来源渠道 | CHAR(20) | Y | 以 YS 开头的服务方编号 |
+| CLD_TX_TIME | 报文交易时间 | 交易时间 | CHAR(14) | Y | 上送交易时间,格式: yyyyMMddHHmmss |
+| CLD_TX_CODE | 交易服务ID | 服务ID | CHAR(64) | Y | 调用的交易名称 |
+| CLD_TX_SEQ | 交易流水标识 | 流水号 | CHAR(32) | Y | 用于标识唯一性 |
+
+**请求报文示例:**
+
+```json
+{
+ "CLD_HEADER": {
+ "CLD_TX_CHNL": "YSTEST",
+ "CLD_TX_TIME": "20241231090000",
+ "CLD_TX_CODE": "A3341TP01",
+ "CLD_TX_SEQ": "1010114131620897033814262"
+ },
+ "CLD_BODY": {
+ // 具体业务参数
+ }
+}
+```
+
+#### 6.1.2 响应报文格式
+
+响应报文由两部分组成:**交易响应报文头(CLD_HEADER)** 和 **交易响应报文体(CLD_BODY)**。
+
+**报文头字段(CLD_HEADER):**
+
+| 字段标识 | 字段中文名 | 字段说明 | 数据属性 | 是否必填 | 备注 |
+|----------|------------|----------|----------|----------|------|
+| CLD_TX_CHNL | 报文来源渠道 | 消息来源渠道 | CHAR(20) | Y | 以 YS 开头的服务方编号 |
+| CLD_TX_TIME | 报文交易时间 | 交易时间 | CHAR(14) | Y | 格式: yyyyMMddHHmmss |
+| CLD_TX_CODE | 交易服务ID | 服务ID | CHAR(64) | Y | 调用的交易名称 |
+| CLD_TX_SEQ | 交易流水标识 | 流水号 | CHAR(32) | Y | 用于标识唯一性 |
+| CLD_CODE | 交易响应码 | 响应码 | CHAR(32) | Y | 详见响应字典 |
+| CLD_DESC | 交易响应内容 | 响应描述 | CHAR(32) | Y | 响应描述信息 |
+
+**响应报文示例:**
+
+```json
+{
+ "CLD_HEADER": {
+ "CLD_TX_CHNL": "YSTEST",
+ "CLD_TX_TIME": "20151231090000",
+ "CLD_TX_CODE": "A3341TP01",
+ "CLD_TX_SEQ": "1010114131620897033914221",
+ "CLD_TX_RESP": {
+ "CLD_CODE": "0000",
+ "CLD_DESC": "成功"
+ }
+ },
+ "CLD_BODY": {
+ // 具体业务返回数据
+ }
+}
+```
+
+#### 6.1.3 报文编码说明
+
+- 报文使用 **UTF-8** 编码
+- 非二进制类型报元单个报元长度建议在 9999 位以内
+- 整体报文大小建议在 20K 以内
+- 登录类交易可以在 2M 以内(考虑到账户数量较多的情况)
+
+---
+
+### 6.2 报文加密及签名
+
+#### 6.2.1 加密流程说明
+
+服务方主动调用建行生活的接口时,需传递的是 **服务方编号**、**RSA密文** 及 **签名** 三个数据。
+
+**加密后报文格式:**
+
+```json
+{
+ "svcid": "服务方ID,以YS开头",
+ "cnt": "密文xxxx",
+ "mac": "签名yyyy"
+}
+```
+
+**错误响应格式:**
+
+```json
+{
+ "code": "错误码xxxx",
+ "msg": "错误描述yyyy"
+}
+```
+
+#### 6.2.2 加密签名方法(Java 示例)
+
+**加密及获取签名:**
+
+```java
+// 公钥
+String publicKey = "";
+// 私钥
+String privateKey = "";
+
+// 源报文(未加密)
+String msg = "";
+
+// 1. 公钥加密得到密文并使用 base64 处理
+String enc_msg = RSAUtil.encrypt(msg, publicKey);
+Encoder encoder = Base64.getEncoder();
+enc_msg = encoder.encodeToString(enc_msg.getBytes("UTF-8"));
+enc_msg = enc_msg.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
+
+// 2. 根据源报文+私钥获得 MD5 签名
+String mac_info = MD5Util.getMD5(msg + privateKey);
+```
+
+**解密及验签:**
+
+```java
+// 1. base64 逆处理并用私钥解密
+Decoder decoder = Base64.getMimeDecoder();
+enc_msg = new String(decoder.decode(enc_msg), "UTF-8");
+String dec_msg = RSAUtil.decrypt(enc_msg, privateKey);
+
+// 2. 验签
+String dec_mac = MD5Util.getMD5(dec_msg + privateKey);
+if (mac_info.equals(dec_mac)) {
+ System.out.println("验签通过");
+} else {
+ System.out.println("验签失败");
+}
+```
+
+> **提示**:如果使用的 JDK 版本高于 1.8,请将 base64 方法由 `sun.misc.BASE64Encoder` 替换为 `java.util.Base64`
+
+#### 6.2.3 加密报文完整示例
+
+**原始报文:**
+
+```json
+{
+ "CLD_HEADER": {
+ "CLD_TX_CHNL": "YSTEST",
+ "CLD_TX_TIME": "20191112145911",
+ "CLD_TX_CODE": "A3341TP01",
+ "CLD_TX_SEQ": ""
+ },
+ "CLD_BODY": {
+ "USER_ID": "user123",
+ "ORDER_ID": "order123",
+ "ORDER_DT": "20191112145811",
+ "TOTAL_AMT": "100.00",
+ "PAY_AMT": "90.00",
+ "DISCOUNT_AMT": "10.00",
+ "ORDER_STATUS": "1",
+ "REFUND_STATUS": "0",
+ "MCT_NM": "XXX商户"
+ }
+}
+```
+
+**加密后报文:**
+
+```json
+{
+ "cnt": "UWl3VVVIT0IzSHp6VUtpNFVmb25VcWJjMnBKdU9IclJUZ3lISGJLR3Myd21saWQvMTVvenNMNk5xWmFwUzl1clh5K2VkVmJuU1BYMg0KTzUwamNkZWRmdjc5NHdDRmJSdjF4WDl4bzAwejMwU2k2NXJ6cTVaVVU4dUt2ZmNPeVJ1cDJkTC9SOUgvODVUblNLbEVXUnErSGo3dw0KMzB2YjFaeU1acU5iRVZnb25VSTUrcmlPV2wweWUwb2xQTVFoei9CVWhDcU9qOExqR21hTDBCdGpVbVdiTyttU3Vpa0s5Y25md3g0SQ0KV0xUSk1BWjl3OHdkZ0FqamI5bXhJbFJUM3JrU3VId1RMdGF0VkdJcGovVDAvdk5nQzNUcWgvODJOQnZvYXdoYnl0Z3AwRW9FNWp5bw0KdVhmYVZDZE9IeGp4ZkpyclFFSCt5THFoZXl2MEx2OGJ1b0x0N0pYMi9zM0pJS0J1bFdSZWJHRTMrcmpEMm9sUVJiYklONkdpeDlodQ0KUEprWkFkK0UrcTV1ZFR4OXpFRlJyWVBHVURzL0RLK0JFSk5iYWZGRnllZTBrRzh2YVlmU1NxeFBWcVBrblRzZ2oxdXFqME0xTGFCNQ0KZ1drVU9vVnl4bUtXUEpYd3JqYXlkSUxVeEZ2dkwzZEpHYmV6cnUwK3d1V3A2QXBQVnRkOElaSm4NCg==",
+ "mac": "1659E42FC9D2A2186228C4607A566D78",
+ "svcid": "YSTEST"
+}
+```
+
+---
+
+## 7. 回调通知接口
+
+### 7.1 建行生活支付通知接口
+
+#### 7.1.1 背景描述
+
+如服务方生活场景涉及支付环节,必须按本章节要求完成支付回调通知对接。
+
+⚠️ **重要提示**:
+- 支付结果推送接口属于建行生活主动推送给服务方,所以不会附带服务方编号
+- 服务方接收支付通知的系统地址,需要配置在建行生活运营后台,请联系运维进行配置
+- 因网络问题存在支付通知丢失的可能性,请服务方考虑通过主动查询支付流水的方式作为兜底(参考《3.3 A3341TP03 - 服务方订单查询》)
+
+#### 7.1.2 接口推送报文示例
+
+```json
+{
+ "ACC_TYPE": "WX",
+ "BRANCHID": "310000000",
+ "CCB_DISCOUNT_AMT": "",
+ "CCB_DISCOUNT_AMT_DESC": "",
+ "CLIENTIP": "11.123.44.55",
+ "CURCODE": "01",
+ "MRCH_ID": "",
+ "NT_TYPE": "YS",
+ "ORDERID": "210520133844test3yhv",
+ "PAYMENT": "0.01",
+ "PAY_TYPE": "01",
+ "POSID": "123442025",
+ "REFERER": "",
+ "REMARK1": "",
+ "REMARK2": "YS1234009000111**WXZF",
+ "SIGN": "1a4aca34f02c0efab014ff823153a2a07a35a8a8410591a992d4aa9cd7fa1f961b952236a45b4fe37867ad4a7214ecbeae21957dee9c19d3d2f5dcf09c8faca81ceb68119c8dcd53bd1e70c23f60fc03e7cd96b8c0082a1243d688c13aac0bcd9e177c559e40e782e3856ada32de8d8ce0054e35fca2022b8f91b2a1ca2c5e10",
+ "SUCCESS": "Y",
+ "TYPE": "1"
+}
+```
+
+#### 7.1.3 字段说明
+
+##### REMARK2 字段说明
+
+发起支付时(调用商户下单支付接口),把服务方编号通过 `REMARK2` 字段送到支付渠道,让支付渠道再推送结果给建行生活时,建行生活根据该字段来判断订单归属哪个服务方,然后指定推送订单结果给对应的服务方(需要在后台维护服务方系统地址)。
+
+##### SIGN 字段说明
+
+使用商户私钥对报文进行 RSA 签名,服务方需要使用建行公钥验签。
+
+##### CCB_DISCOUNT_AMT 和 CCB_DISCOUNT_AMT_DESC 字段说明
+
+- 由建行生活推送给服务方的支付通知会额外携带该笔订单所使用的优惠金额
+- 但是由网银直接推送的支付通知,只含原订单金额,不包含优惠金额,需要服务方自行向外联平台查询
+
+---
+
+### 7.2 建行生活退款操作通知接口
+
+#### 7.2.1 背景描述
+
+如服务方场景需要接收退款操作消息通知,必须按本章节要求完成退款回调通知对接。
+
+⚠️ **重要提示**:
+- **不能用于退款结果判断**:退款操作消息推送仅推送商户号、订单号等少量用于查询使用的信息
+- 若需获知退款状态、金额、优惠信息和支付方式等信息,参考《3.3 A3341TP03 - 服务方订单查询》
+- 如服务方场景仅通过 A3341TP04 服务方退款交易完成退款,已实现退款流程的闭环,可略过本章节
+- 订单退款操作消息推送属于建行生活主动推送给服务方,不会附带服务方编号
+- 服务方接收退款操作消息通知的回调地址,需要配置在建行生活运营后台,请联系运维进行配置
+
+#### 7.2.2 接口推送报文示例
+
+```json
+{
+ "MRCH_ID": "1050000000000000000",
+ "NT_TYPE": "YS",
+ "ORDERID": "210520133844test3yhv",
+ "REFUND_DTM": "20250601120000",
+ "SIGN": "317b7dd349c1fbcabc26a20ba117a778da5a685c588be5e7378682651062a25b0885e36ee237c19a143f7271c9529a0e9bf198c8735709dc74233d96e1a276cec9d4835422bee597100b0a47d11b44dbba74bdf9cbde0587f138141ce79a3536733d5f6b53ed119c13708dca52ee8d3fcf7e67dcdb20053889adff1989a8c859"
+}
+```
+
+#### 7.2.3 字段说明
+
+| 字段名 | 中文名 | 类型 | 说明 |
+|--------|--------|------|------|
+| MRCH_ID | 商户号 | String | 建行商户编号 |
+| NT_TYPE | 通知类型 | String | 固定值 "YS" |
+| ORDERID | 订单号 | String | 支付流水号 |
+| REFUND_DTM | 退款时间 | String | 格式: yyyyMMddHHmmss |
+| SIGN | 签名 | String | RSA 签名,需使用建行公钥验签 |
+
+---
+
**文档结束**
From b4e76a58ab165ce8a079aba84cbb5af28fe89a9e Mon Sep 17 00:00:00 2001
From: Billy <641833868@qq.com>
Date: Tue, 21 Oct 2025 13:45:10 +0800
Subject: [PATCH 08/10] 1
---
...程序API使用说明_v1.1_20230511.html | 599 -------------
...in跳转链接解密可参考此demo2.java | 44 -
doc/建行支付对接修复报告.md | 538 ------------
doc/建行支付架构修复报告.md | 825 ------------------
4 files changed, 2006 deletions(-)
delete mode 100644 doc/CCBLife小程序API使用说明_v1.1_20230511.html
delete mode 100644 doc/UrlMain跳转链接解密可参考此demo2.java
delete mode 100644 doc/建行支付对接修复报告.md
delete mode 100644 doc/建行支付架构修复报告.md
diff --git a/doc/CCBLife小程序API使用说明_v1.1_20230511.html b/doc/CCBLife小程序API使用说明_v1.1_20230511.html
deleted file mode 100644
index b1f76f2..0000000
--- a/doc/CCBLife小程序API使用说明_v1.1_20230511.html
+++ /dev/null
@@ -1,599 +0,0 @@
-
-
-
文档修订记录
| 版本 | 日期 | 修订说明 |
|---|---|---|
| 1.0 | 2023.02.14 | 同步在线文档接口说明 |
| 1.1 | 2023.05.11 | 新增实名认证api |
文档目录
CCBLife小程序API使用说明_v1.1_202305111. 文档说明2. 接口说明2.1 login2.2 ccblife_login2.3 checkSession2.4 getUserInfo2.5 authorize2.6 requestPayment2.7 ccblife_requestPayment2.8 navigateTo2.9 scanCode2.10 openPayCode2.11 callMap2.12 startFaceScan2.13 userStatus2.14 share2.15 checkUser2.15 RealNameAuthorization
本文档所描述API适用于建行生活App端内运行的JUMP小程序。
回调函数统一格式:
回调结果参数(Object res)
| 属性 | 类型 | 说明 | 最低版本 |
|---|---|---|---|
| data | object | 返回内容 | - |
| state | string | 状态码 | - |
| msg | string | 状态信息|报错信息 | - |
响应内容封装在data的Json对象里
用途说明
登录|获取用户信息。提供客户端认证模式与服务端认证模式两种模式。若建行生活处于未登录状态会跳转建行生活APP的登录页进行登录(行内单点登录使用)。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| type | number | - | 是 | 登录类型 | - |
| PLATFORM_ID | string | - | - | 服务方ID | - |
| Opn_Chnl_ID | string | - | 是 | 合作方渠道编号 | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
type 的合法值:
| 值 | 说明 | 最低版本 |
|---|---|---|
| 0 | 客户端认证模式 | - |
| 1 | 服务端认证模式 | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|---|---|---|
| encryptedData | string | "userid=xxx&mobile=xxx&PreAhr_ID=xxx"的加密字符串。(userid:建行生活用户编号,mobile:手机号,PreAhr_ID:用户中心预授权编码) | - |
注意
服务方IDPLATFORM_ID于客户端认证模式(type=0)时必填。
用途说明
登录|获取用户信息。提供客户端认证模式与服务端认证模式两种模式。若建行生活处于未登录状态会跳转建行生活APP的登录页进行登录。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| type | number | - | 是 | 登录类型 | - |
| PLATFORM_ID | string | - | - | 服务方ID | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
type 的合法值:
| 值 | 说明 | 最低版本 |
|---|---|---|
| 0 | 客户端认证模式 | - |
| 1 | 服务端认证模式 | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|---|---|---|
| encryptedData | string | "unionid=xxx&phone=xxx&locationCityCode=xxx"的加密字符串。(unionid:建行生活用户编号,phone:手机号,locationCityCode:用户选择城市码) | - |
注意
服务方IDPLATFORM_ID于客户端认证模式(type=0)时必填。
用途说明
检查登录态是否过期。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| PLATFORM_ID | string | - | - | 服务方ID | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|---|---|---|
| isVaild | boolean | 登录态是否有效 | - |
用途说明
获取用户信息。目前能返回的信息均为登录态敏感信息,加密。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| withCredentials | boolean | true | 否 | 是否带上登录态信息。 | - |
| loginType | number | - | 是 | 当前登录类型 | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
loginType的合法值:
| 值 | 说明 | 最低版本 |
|---|---|---|
| 0 | 客户端认证模式 | - |
| 1 | 服务端认证模式 | - |
响应内容
state的合法值:
| 值 | 说明 | 最低版本 |
|---|---|---|
| 0 | 获取成功 | - |
| 1 | 获取失败 | - |
| 2 | 获取失败:未授权 | - |
data:
| 属性 | 类型 | 说明 | 最低版本 |
|---|---|---|---|
| userInfo | Object | 用户信息,不包含敏感数据 | - |
| encryptedData | string | 包括敏感数据在内的完整用户信息的加密数据 | - |
| signature | string | 用户数据签名 | - |
| salt | string | 签名使用的字符串 | - |
| iv | string | 加密算法的初始向量 | - |
encryptedData 解密:
| 属性 | 类型 | 说明 | 最低版本 |
|---|---|---|---|
| unionid | string | 建行生活平台帐号的唯一标识 | - |
| openid | string | 用户在当前小程序的唯一标识 | - |
| cityCode | String | 用户选择城市编码 | |
| locationCityCode | string | 用户当前定位城市编码 | - |
| registerCityCode | string | 用户归属城市编码,即用户注册地 | - |
| phone | string | 用户手机号 | - |
注意:
当采用客户端认证模式时,数据使用服务方公钥加密,需使用服务方私钥解密。
当采用服务端认证模式时,数据使用session_key加密。
res.data的signature 、salt、iv仅当采用服务端认证模式时有效
signature = sha1( salt + session_key )
城市编码使用6位全国地区行政编码,仅市级编码有效
res.data.encryptedData 解密后得到的accessToken仅客户端认证模式有效
用途说明
提前向用户发起授权请求。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| scope | string | - | 是 | 需要获取权限的 scope | - |
| success | function | - | 否 | 接口调用成功的回调函数(授权成功) | - |
| fail | function | - | 否 | 接口调用失败的回调函数(授权失败) | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
scope说明:
| scope | 对应接口 | 说明 | 最低版本 |
|---|---|---|---|
| scope.userInfo | getUserInfo、login | 用户信息 | - |
| scope.camera | scanCode | 摄像头 | - |
用途说明
调用建行生活收银台。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| payInfo | string | - | 是 | 支付参数 | - |
payInfo参数内容:
| 属性 | 类型 | 可为空 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| MERCHANTID | char(15) | Y | F | 商户代码;由建行统一分配 | - |
| POSID | char(9) | Y | F | 柜台代码;由建行统一分配 | - |
| BRANCHID | char(9) | Y | F | 分行代码;由建行统一分配 | - |
| POSID19 | char(19) | N | F | 商户19位终端号;由建行统一分配,使用微信支付时上送。仅作为参数传递,不参与MAC校验 | - |
| PLATMCTID | char(19) | Y | F | 外部平台商户号;当使用外部商户号时,建行商户号、柜台号、分行号及终端号无需上送。当该字段有值时参与MAC校验,否则不参与MAC校验 | - |
| ORDERID | char(30) | Y | T | 订单号;由商户提供,最长30位 | - |
| PAYMENT | number(16,2) | Y | T | 付款金额;由商户提供,最长30位 | - |
| CURCODE | char(2) | Y | T | 币种;缺省为01-人民币(只支持人民币支付) | - |
| TXCODE | char(6) | Y | T | 交易码;由建行统一分配为520100 | - |
| REMARK1 | char(30) | N | T | 备注1;网银不处理,直接传到城综网,该字段只支持送数字和英文 | - |
| REMARK2 | char(30) | N | T | 备注2;上送YS开头的服务方编号,与PLATFORMID保持一致 | - |
| TYPE | char(1) | Y | T | 接口类型;1- 防钓鱼接口 | - |
| GATEWAY | char(100) | Y | T | 网关类型;默认送0 | - |
| CLIENTIP | char(40) | N | T | 客户端IP;客户在商户系统中的IP | - |
| REGINFO | char(256) | N | T | 客户注册信息;客户在商户系统中注册的信息,中文需使用escape编码 | - |
| PROINFO | char(256) | N | T | 商品信息;客户购买的商品,中文需使用escape编码 | - |
| REFERER | char(100) | N | T | 商户URL;商户送空值即可 | - |
| INSTALLNUM | char(2) | N | F | 分期期数;信用卡支付分期期数,一般为 3、6、12 等,必须为大于 1 的整数。 仅当分期支付时上送该字段,无此字段上送时,则视为普通支付。 | - |
| THIRDAPPINFO | char(40) | Y | T | 客户端标识;通过建行生活APP下单场景,订单中客户端标识固定设为comccbpay1234567890cloudmerchant | - |
| TIMEOUT | char(14) | N | F | 订单超时时间;格式:YYYYMMDDHHMMSS(如:20120214143005) 银行系统时间> TIMEOUT时拒绝交易,若送空值则不判断超时。 当该字段有值时参与MAC校验,否则不参与MAC校验。 | - |
| USERID | char(100) | N | F | 在中国建设银行App环境需提供。 当该字段有值时参与MAC校验,否则不参与MAC校验 | - |
| TOKEN | char(100) | N | F | 在中国建设银行App环境需提供。 当该字段有值时参与MAC校验,否则不参与MAC校验 | - |
| PAYSUCCESSURL | char(100) | N | F | 在中国建设银行App环境考虑,如需指定支付成功页面需提供,需对URL编码,生产环境必须为HTTPS。未提供则默认跳转到建行生活的支付成功页面 当该字段有值时参与MAC校验,否则不参与MAC校验 | - |
| PAYBITMAP | char(10) | N | F | 支付位图;默认为空,只需要展示龙支付时请送0100000000 当该字段有值时参与MAC校验,否则不参与MAC校验。 | - |
| POINTAVYID | varchar(6) | N | F | 积分二级活动编号;默认为空,特定场景使用。龙支付积分二级活动上送 010051 | - |
| DCEPDEPACCNO | varchar(32) | N | F | 数字人民币收款钱包编号;默认为空,特定场景使用。数字人民币商户绑定的收款钱包编号 | - |
| COUPONAVYID | varchar(32) | N | F | 有价券活动编号;默认为空,特定场景使用。 | - |
| ONLY_CREDIT_PAY_FLAG | varchar(1) | N | F | 限制信用卡支付标志;默认为空,特定场景使用。当有价券活动编号不为空时生效,送Y限制仅信用卡能支付,送N或空不作限制 | - |
| FIXEDPOINTVAL | varchar(16) | N | F | 固定抵扣积分值;默认为空,特定场景使用。上送该值时,若用户不满足积分使用条件将拒绝支付 | - |
| EXTENDPARAMS | varchar(256) | N | F | 积分二级活动编号;默认为空,特定场景使用。上送约定JSON格式字符串 | - |
| PLATFORMPUB | varchar(256) | Y | F | 服务方公钥;仅作为源串参加MD5摘要,不作为参数传递 | - |
| MAC | char(32) | T | T | MD5加密串;采用标准MD5算法,对以上字段进行MAC加密(32位小写),由商户实现。 | - |
| PLATFORMID | char(16) | Y | T | 服务方编号;仅作为参数传递,不参与MAC校验 | - |
| ENCPUB | varchar(512) | Y | F | 商户公钥密文;使用服务方公钥对商户公钥后30位进行RSA加密并base64后的密文。 若商户已经上架建行生活并同步公钥,可以不再上送商户公钥。仅作为参数传递,不参与MAC校验 | - |
| SCNID | char(32) | N | F | 场景编号;默认为空,埋点使用。特色场景的唯一标识。仅作为参数传递,不参与MAC校验 | - |
| SCN_PLTFRM_ID | char(32) | N | F | 场景平台编号;默认为空,埋点使用。场景平台唯一标识。仅作为参数传递,不参与MAC校验 |
注意:
字符串中变量名必须是大写字母。
中文信息需要escape编码:
使用js的escape()方法对payInfo.REGINFO(客户注册信息)和payInfo.PROINFO(商品信息)进行转码,数字字母信息不需转码。
payInfo.MAC采用标准MD5摘要算法对字符串数据签名(32位小写)
参与签名的字符串及其顺序如下(为上表中MAC字段之上的所有字段按顺序拼接):
MERCHANTID=123456789&POSID=000000000&BRANCHID=110000000&ORDERID=19991101234&PAYMENT=0.01&CURCODE=01&TXCODE=520100&REMARK1=&REMARK2=&TYP>E=1&GATEWAY=&CLIENTIP=172.0.0.1®INFO=%u5C0F%u98DE%u4FA0&PROINFO=%u5145%u503C%u5361&REFERER=&THIRDAPPINFO=comccbpay1234567890cloudmerchant&TIMEOUT=20161028101226&PAYBITMAP=0100000000&MAC=4f9033be946e7dcd78886fdb4b2b0ec3&PLATFORMID=YS0000000000000001&ENCPUB=ZGdXNUo3MXA5allmY0dUQm1mRW...
黑色字体对应的字段必须参与MAC,橙色的字段请根据需要上送,且有值时才参与MAC,否则无需参与MAC
如有和网银约定好的必传参数,请拼接在MAC和PLATFORMID字段之间,如PAYMAP
payInfo.ENCPUB:各服务方使用自己的服务方公钥对商户公钥后30位进行RSA加密,再进行base64后,生成的密文串。若商户已经上架建行生活并同步公钥,可以不再上送商户公钥。
公钥加密方法见《建行生活输入通讯报文接口规范》的报文加密章节。
支付通知等接口见《建行生活APP服务方接入文档》。
用途说明
调用建行生活收银台。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| miniId | string | - | 是 | 本小程序id | - |
| successPage | string | - | 否 | 成功页面的路径,不设置则跳转建行生活APP的支付成功页面 | - |
| payInfo | object | - | 是 | 支付参数 | - |
payInfo参数内容
| 属性 | 类型 | 可为空 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| MERCHANTID | char(15) | 是 | 是 | 商户代码;由建行统一分配 | - |
| POSID | char(9) | 是 | 是 | 柜台代码;由建行统一分配 | - |
| BRANCHID | char(9) | 是 | 是 | 分行代码;由建行统一分配 | - |
| POSID19 | char(19) | 否 | 否 | 商户19位终端号;由建行统一分配,仅作为参数传递,不参与MAC校验 | - |
| ORDERID | char(30) | 是 | 是 | 订单号;由商户提供,最长30位 | - |
| PAYMENT | number(16,2) | 是 | 是 | 付款金额;由商户提供,最长30位 | - |
| CURCODE | char(2) | 是 | 是 | 币种;缺省为01-人民币(只支持人民币支付) | - |
| TXCODE | char(6) | 是 | 是 | 交易码;由建行统一分配为520100 | - |
| REMARK1 | char(30) | 是 | 是 | 备注1;网银不处理,直接传到城综网,该字段只支持送数字和英文 | - |
| REMARK2 | char(30) | 是 | 是 | 备注2;上送YS开头的服务方编号 | - |
| TYPE | char(1) | 是 | 是 | 接口类型;1- 防钓鱼接口 | - |
| GATEWAY | char(100) | 是 | 是 | 网关类型;默认送0 | - |
| CLIENTIP | char(40) | 否 | 是 | 客户端IP;客户在商户系统中的IP | - |
| REGINFO | char(256) | 否 | 是 | 客户注册信息;客户在商户系统中注册的信息,中文需使用escape编码 | - |
| PROINFO | char(256) | 否 | 是 | 商品信息;客户购买的商品,中文需使用escape编码 | - |
| REFERER | char(100) | 否 | 是 | 商户URL;商户送空值即可 | - |
| THIRDAPPINFO | char(40) | 是 | 是 | 客户端标识;通过建行生活APP下单场景,订单中客户端标识固定设为comccbpay1234567890cloudmerchant | - |
| TIMEOUT | char(14) | 否 | 否 | 订单超时时间;格式:YYYYMMDDHHMMSS(如:20120214143005) 银行系统时间> TIMEOUT时拒绝交易,若送空值则不判断超时。 当该字段有值时参与MAC校验,否则不参与MAC校验。 | - |
| PAYBITMAP | char(10) | 否 | 否 | 支付位图;默认为空,只需要展示龙支付时请送0100000000 当该字段有值时参与MAC校验,否则不参与MAC校验。 | - |
| PLATFORMPUB | varchar(256) | 是 | 否 | 服务方公钥;仅作为源串参加MD5摘要,不作为参数传递 | - |
| MAC | char(32) | 是 | 是 | MD5加密串;采用标准MD5算法,对以上字段进行MAC加密(32位小写),由商户实现。 | - |
| PLATFORMID | char(16) | 是 | 是 | 服务方编号;仅作为参数传递,不参与MAC校验 | - |
| ENCPUB | varchar(512) | 是 | 否 | 商户公钥密文;使用服务方公钥对商户公钥后30位进行RSA加密并base64后的密文。 若商户已经上架建行生活并同步公钥,可以不再上送商户公钥。仅作为参数传递,不参与MAC校验 | - |
注意:
字符串中变量名必须是大写字母。
中文信息需要escape编码:
使用js的escape()方法对payInfo.REGINFO(客户注册信息)和payInfo.PROINFO(商品信息)进行转码,数字字母信息不需转码。
payInfo.MAC采用标准MD5摘要算法对字符串数据签名(32位小写)
参与签名的字符串及其顺序如下(为上表中MAC字段之上的所有字段按顺序拼接):
MERCHANTID=123456789&POSID=000000000&BRANCHID=110000000&ORDERID=19991101234&PAYMENT=0.01&CURCODE=01&TXCODE=520100&REMARK1=&REMARK2=&TYP>E=1&GATEWAY=&CLIENTIP=172.0.0.1®INFO=%u5C0F%u98DE%u4FA0&PROINFO=%u5145%u503C%u5361&REFERER=&THIRDAPPINFO=comccbpay1234567890cloudmerchant&TIMEOUT=20161028101226&PAYBITMAP=0100000000&MAC=4f9033be946e7dcd78886fdb4b2b0ec3&PLATFORMID=YS0000000000000001&ENCPUB=ZGdXNUo3MXA5allmY0dUQm1mRW...
黑色字体对应的字段必须参与MAC,橙色的字段请根据需要上送,且有值时才参与MAC,否则无需参与MAC
如有和网银约定好的必传参数,请拼接在MAC和PLATFORMID字段之间,如PAYMAP
payInfo.ENCPUB:各服务方使用自己的服务方公钥对商户公钥后30位进行RSA加密,再进行base64后,生成的密文串。若商户已经上架建行生活并同步公钥,可以不再上送商户公钥。
公钥加密方法见《建行生活输入通讯报文接口规范》的报文加密章节。
支付通知等接口见《建行生活APP服务方接入文档》。
用途说明
跳转建行生活页面 | 外部H5页|建信小程序 | 微信小程序。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| type | number | - | 是 | 跳转页面类型 | - |
| toPage | string | - | 是 | 跳转路径,类型为小程序本值为空时调起首页。 | - |
| isNewView | boolean | false | 否 | 是否打开新WebView,当type为1或2时有效。值为false时用进入小程序前入口所在页面的webview打开,若不存在则用新webview打开。 | - |
| isShowHeader | boolean | false | 否 | 是否展示通用标题栏,当type为1或2时有效 | - |
| headerName | string | - | 否 | 标题栏名称,isShowHeader为true时有效 | - |
| headerRightType | number | 0 | 否 | 展示标题栏时右边按钮的类型 | - |
| param | string | - | 否 | 跳转携带的参数,type非0时拼接到最终URL后 | - |
| miniId | string | - | 否 | 跳转小程序id,type为3或4时有效 | - |
| miniVersion | number | 0 | 否 | 微信小程序版本,type为4时有效 | |
| success | function | - | 否 | 接口调用的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
type 的合法值:
| 值 | 说明 | 最低版本 |
|---|---|---|
| 0 | 建行生活原生页面 | - |
| 1 | 建行生活H5页面 | - |
| 2 | 外部H5页面 | - |
| 3 | Jump小程序 | - |
| 4 | 微信小程序 | - |
headerRightType 的合法值:
| 值 | 说明 | 最低版本 |
|---|---|---|
| 0 | 关闭按钮 | - |
| 1 | 分享按钮 | - |
miniVersion 的合法值:
| 值 | 说明 | 最低版本 |
|---|---|---|
| 0 | 发布版 | - |
| 1 | 预览版 | - |
| 2 | 测试版 | - |
响应内容
无
state 的合法值:
| 值 | 说明 | 最低版本 |
|---|---|---|
| 0 | 跳转成功 | - |
| 1 | 跳转失败 | - |
用途说明
建行生活App的扫码功能,扫描建行生活提供的业务二维码(扫码支付等)需要用本接口而非jump.scanCode。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|---|---|---|
| content | string | 扫码结果 | - |
state 的合法值:
| 值 | 说明 | 最低版本 |
|---|---|---|
| 0 | 扫码成功 | - |
| 1 | 非有效业务二维码,无法解析 | - |
用途说明
打开支付码。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
无
state 的合法值:
| 值 | 说明 | 最低版本 |
|---|---|---|
| 0 | 打开成功 | - |
| 1 | 打开失败 | - |
用途说明
调起手机内的地图App。支持苹果地图|高德地图|百度地图。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| params | object | - | 否 | 参数 | - |
| needNavigation | boolean | false | 否 | 是否需要导航 | - |
| address | string | - | 是 | 商户地址 | - |
| lgt | number | - | 否 | 商户纬度 | - |
| ltt | number | - | 否 | 商户经度 | - |
| cityName | string | - | 否 | 城市名称 | - |
| business_name | string | - | 是 | 商户名称 | - |
| self_lgt | number | - | 否 | 客户维度 | - |
| self_ltt | number | - | 否 | 客户经度 | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
注意:
needNavigation为true时lgt 和ltt必填。
响应内容
无
state 的合法值:
| 值 | 说明 | 最低版本 |
|---|---|---|
| 0 | 调起地图成功 | - |
| 1 | 调起地图失败 | - |
用途说明
刷脸认证|人脸校验
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| PLATFORM_ID | string | - | 是 | 服务方ID | - |
| name | string | - | - | 姓名 | - |
| cardType | string | - | 是 | 卡片类型(身份证) | - |
| cardNum | string | - | 是 | 身份证号码 | - |
| phoneNum | string | - | 是 | 手机号 | - |
| showError | string | - | 否 | 报错弹窗:1-显示 0-不显示 | - |
| scanOnly | string | - | 否 | 仅刷脸:1-只刷脸,不发校验刷脸流水的交易 | - |
| Stm_Chnl_ID | string | - | 否 | 渠道号,默认为建行生活渠道 | - |
| Stm_Chnl_Txn_CD | string | - | 否 | 渠道交易码,默认为建行生活渠道交易码 | - |
| txCode | string | - | 否 | 安全交易码,默认为建行生活安全交易码 | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|---|---|---|
| success | string | 刷脸认证是否成功:0-失败,1-成功 | - |
| Comm_Auth_Fields | string | UUID | - |
| Apl_Aply_TrcNo | string | 全局流水号 | - |
用途说明
获取用户状态信息
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| PLATFORM_ID | string | - | - | 服务方ID | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|---|---|---|
| userType | string | 用户类型:00游客/未登录 01钱包用户 02已注册未开钱包 | - |
| isLogin | string | 登录状态:0:未登录 1:已登录 | - |
用途说明
分享。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| share_id | string | - | 是 | 分享id,存在分享ID时先调接口获取分享内容 | - |
| text | string | - | - | 分享的描述 | - |
| title | string | - | - | 标题 | - |
| url | string | - | - | 链接 | - |
| image | string | - | - | 图片链接 | - |
| type | string | - | - | 0--分享链接,1--分享微信朋友,2--分享朋友圈 | - |
| base64Pic | string | - | - | 图片base64格式化 | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|---|---|---|
| type | string | 0: 取消分享 1:分享到微信 2:分享到朋友圈 | - |
用途说明
校验用户身份。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| platformId | string | - | 是 | 服务方编号,非空 | - |
| sceneId | string | - | - | 场景ID | - |
| checkType | string | - | - | 校验类型 1-校验平台支付密码 2-校验平台登录密码 | - |
| checkScope | string | - | - | 验密有效范围,0-App内有效(默认值) 1-同类场景有效 2-场景内有效 3-场景内同功能有效 4-一次性有效 | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|---|---|---|
| token | string | 唯一验密流水号,每次重新生成 | - |
| code | string | 校验结果状态码 0:校验完成 1:已在其他功能场景校验通过,且在有效期内 -1:用户取消校验 -2:校验失败,当前场景未配置校验类型、用户状态异常等原因 | - |
注意:
当code为-1、-2时,token为空;为0、1时,token有值;
code为1时,若为弱金融场景,由业务和场景方决定可直接默认验密通过,若为强金融场景,由业务和场景方决定是否需要再次校验,如需再次校验,重新调起 ‘checkUser’ action并修改 ‘checkScope’ 参数为4即可;
收到 ‘checkUser’ action返回的token后,应由场景方服务端按服务方协议调用校验验密流水号交易,该交易返回验证通过后方能做后续涉密业务,否则应终止流程。
用途说明
实名认证。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| platformId | string | - | 是 | 服务方编号,非空 | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|---|---|---|
| success | string | 实名结果 0-失败,1-成功 | - |