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] 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 @@
-
-
-
-
-
CCBLife小程序API使用说明_v1.1_20230511
文档修订记录
| 版本 | 日期 | 修订说明 |
|---|
| 1.0 | 2023.02.14 | 同步在线文档接口说明 |
| 1.1 | 2023.05.11 | 新增实名认证api |
文档目录
1. 文档说明
本文档所描述API适用于建行生活App端内运行的JUMP小程序。
2. 接口说明
回调函数统一格式:
回调结果参数(Object res)
| 属性 | 类型 | 说明 | 最低版本 |
|---|
| data | object | 返回内容 | - |
| state | string | 状态码 | - |
| msg | string | 状态信息|报错信息 | - |
响应内容封装在data的Json对象里
2.1 login
用途说明
登录|获取用户信息。提供客户端认证模式与服务端认证模式两种模式。若建行生活处于未登录状态会跳转建行生活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:用户中心预授权编码) | - |
注意
2.2 ccblife_login
用途说明
登录|获取用户信息。提供客户端认证模式与服务端认证模式两种模式。若建行生活处于未登录状态会跳转建行生活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:用户选择城市码) | - |
注意
2.3 checkSession
用途说明
检查登录态是否过期。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|
| PLATFORM_ID | string | - | - | 服务方ID | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|
| isVaild | boolean | 登录态是否有效 | - |
2.4 getUserInfo
用途说明
获取用户信息。目前能返回的信息均为登录态敏感信息,加密。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|
| 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仅客户端认证模式有效
2.5 authorize
用途说明
提前向用户发起授权请求。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|
| scope | string | - | 是 | 需要获取权限的 scope | - |
| success | function | - | 否 | 接口调用成功的回调函数(授权成功) | - |
| fail | function | - | 否 | 接口调用失败的回调函数(授权失败) | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
scope说明:
| scope | 对应接口 | 说明 | 最低版本 |
|---|
| scope.userInfo | getUserInfo、login | 用户信息 | - |
| scope.camera | scanCode | 摄像头 | - |
2.6 requestPayment
用途说明
调用建行生活收银台。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|
| 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服务方接入文档》。
2.7 ccblife_requestPayment
用途说明
调用建行生活收银台。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|
| 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服务方接入文档》。
2.8 navigateTo
用途说明
跳转建行生活页面 | 外部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 的合法值:
miniVersion 的合法值:
响应内容
无
state 的合法值:
2.9 scanCode
用途说明
建行生活App的扫码功能,扫描建行生活提供的业务二维码(扫码支付等)需要用本接口而非jump.scanCode。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|
| content | string | 扫码结果 | - |
state 的合法值:
| 值 | 说明 | 最低版本 |
|---|
| 0 | 扫码成功 | - |
| 1 | 非有效业务二维码,无法解析 | - |
2.10 openPayCode
用途说明
打开支付码。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
无
state 的合法值:
2.11 callMap
用途说明
调起手机内的地图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 | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
注意:
响应内容
无
state 的合法值:
2.12 startFaceScan
用途说明
刷脸认证|人脸校验
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|
| 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 | 全局流水号 | - |
2.13 userStatus
用途说明
获取用户状态信息
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|
| PLATFORM_ID | string | - | - | 服务方ID | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|
| userType | string | 用户类型:00游客/未登录 01钱包用户 02已注册未开钱包 | - |
| isLogin | string | 登录状态:0:未登录 1:已登录 | - |
2.14 share
用途说明
分享。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|
| 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:分享到朋友圈 | - |
2.15 checkUser
用途说明
校验用户身份。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|
| 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后,应由场景方服务端按服务方协议调用校验验密流水号交易,该交易返回验证通过后方能做后续涉密业务,否则应终止流程。
2.15 RealNameAuthorization
用途说明
实名认证。
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|
| platformId | string | - | 是 | 服务方编号,非空 | - |
| success | function | - | 否 | 接口调用成功的回调函数 | - |
| fail | function | - | 否 | 接口调用失败的回调函数 | - |
| complete | function | - | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | - |
响应内容
| 属性 | 类型 | 说明 | 最低版本 |
|---|
| success | string | 实名结果 0-失败,1-成功 | - |
-
-
\ No newline at end of file
diff --git a/doc/UrlMain跳转链接解密可参考此demo2.java b/doc/UrlMain跳转链接解密可参考此demo2.java
deleted file mode 100644
index 1fdd5cc..0000000
--- a/doc/UrlMain跳转链接解密可参考此demo2.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.example.filedemo.util.fuwufang;
-
-import com.example.filedemo.util.RSAUtil;
-import sun.misc.BASE64Decoder;
-import sun.misc.BASE64Encoder;
-
-public class UrlMain {
- public static void main(String[] args) throws Exception {
- String msg = "BGCOLOR=&userid=YSM202111170063936&mobile=18242028306&cityid=330100&userCityId=330100&orderid=&PLATFLOWNO=0000A2UNK1639016304462982&openid=&lgt=113.3295774824442<t=23.12339638654285&Usr_Name=&USERID=YSM202111170063936&MOBILE=18242028306&CITYID=330100&USERCITYID=330100&ORDERID=&OPENID=&LGT=113.3295774824442<T=23.12339638654285";
-
- //String enc_msg = "SDB0dllqYmxFS2xHRmlqa1ZaOFk0OHBXY0I5TitoREdJaVB3K1pjM2M3dy9jek4zN016ZUoxZENTNTVLWVFFV3VSYzlYOVlXRkpBcQpWRUgwaDJUMG04V2lmNHJyS3krdG5QUDJHalhEQlNma21oR3JrV0lsbFRibC9vbWJONGxqeVk1TXZQWjVWc2t5N2ZVRlZTYlNlYjIzCnJ5cFN4dTRNSDUrTjFRTU5NVFE9Cg%3D%3D";
- // 公钥
- String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClMNB2rs4PMyxHdV+HeISWBbe55WQkmSYQQvFq8M4MMczhYihhp1Z9p723wD8cv9m/PQQcQZuNIehGGIIbZnMZFkqwDYUODH0DF8N5o7BiUhw/XUr3nl49/hsjlE6L7k/7jYzxZ+r3CXhz7qVXZNW6tD2RM+AI4qomQr0p1VNxhQIDAQAB";
- // 私钥
- String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKUw0Hauzg8zLEd1X4d4hJYFt7nlZCSZJhBC8WrwzgwxzOFiKGGnVn2nvbfAPxy/2b89BBxBm40h6EYYghtmcxkWSrANhQ4MfQMXw3mjsGJSHD9dSveeXj3+GyOUTovuT/uNjPFn6vcJeHPupVdk1bq0PZEz4AjiqiZCvSnVU3GFAgMBAAECgYAyTZQdoAulu0qPlCF8CmotmR4ioMUHFA/wQcJsc1n7gqrGM3LikeeXqh3ut79ATPfM8ZKv3Ba3Oo0V017DY0ZG7j2stXxFhm2ln/q6nfaDsfx5ae22kIdNFCrDfwYByBiVsZPNCrj+8qDb/DPiVveEpsj7hn6thZY8QnjwEi0O3QJBAOia3cqup/rLMTYwtl43OREyMDt3qWS+aRQz1jQJlQSONV76qsZpZZUVxQEglvf6+afRCyn1mAqNa2dek6gbHTMCQQC1zijBYb6b4kghbKg/ZC37A79kBuRKtl/yIMYtFLWrtIntv047HavVPHZLEl++44Hk+9rfzNw1J12uXigGVoZnAkBGh6745jzJLxOc+uhRaS1EqZM2dPJIOfRiy9UHsmAdIYHNavSddRf4PMGfteIRD2jkGd7oui+AA6Gtll/veUlBAkAwybEwK/3NsUywA4um70hTiy7qNds/nW9j952W7W7PNDSrY2IoBQ9eusn33WdqP31VKK0Uz9HsRbMjHstY4BFTAkEAisda+CJkO/Epdj693ewIr4GbGORGSVB2pCjLGPqhuvu37d/T9+9T85BoeaMwm31aVNGOPIUCSPOMelKRUoj3Gw==";
-
- // 公钥加密得到密文并使用base64处理
- String enc_msg = RSAUtil.encrypt(msg, publicKey);
- //enc_msg = "";
- //enc_msg = "TVpoZ040QTdVcTZMem11cGdTVjdWeFEvWU5kZWZqcFVBK3JSS0hGK0NsSFo0Ly82RnV3blVvT1hrMXNlZ25odXlkVytvRkdGT0xoYwpRSlhjYWI4eWJkUWh3UTgzUU1MQzBzYWtManZCOFNhR29VWDhiMHZoRXhWdkVIQ3BTcnlVRUQwUU9zQzVodXoxQTk3djRIeXNlMFR5CjFnWm1zaTRLZm9sWHM1TndJcXMvQ3lVdE9MTjNqZGZoajNHTXQvSHN1c01PVzFBekwxbTFTWVk2VGhuL3lvODA4NGRRaGN2aC9KVU0KdUdWZUNtdEJJbytHVVJaOXNCZW5BaUZVTEFWTHJINHhRK0pWUnpRRUZvRlhhNVVaRVQ0cDJTME1TWXBTZ1R4eStOeFloTER1S280OQpsV3RONFVKVkJiTVY1dUJrdEthMEF6MEROczA1bndDTnJtL3FReFdQMmFtV3pRT24ybHl6MVRCMXhjZGpaVVpDc3prOVZoM3FtS05SCmNVTnV2M0x6cDB2SzIzOVFyM205R0taWGl6TFdKcktQcE9UaDkrODQyY0s3L1ZmTDh6S2NBZG1QN21QMXhKZE5NVlEzZFZnaW9XWlQKd2xWWVc4N2FLZ0xLNlZIaEhJNTVGV3RNaFYrTzdNbUZlSzBWckhaS3NHSDJIUGg5ZUQyVkFVWVNWaU9HdisyUDRFczJ5V0lCNjkxNgpjMStRWjc5RXdwRmwrQjBJUG0yLy9rbDVXSXVYaDdXY1FPUzBBMm4yMnYxWnlMK29vdVNUTWNGMm9TNWx1RjRzRVc5VDkrb0tjRjU5CjBVOGVvY1E0R2Q5dkxwTnVpSG1CNUpvWFF6NG1EZGFBdG13eS9zMHFXVTgyQjlWZEVETlNPZGx0NnNoQXBTZ01ObGtGS0hyQllHMD0K";
-
- BASE64Encoder encoder = new BASE64Encoder();
- enc_msg = encoder.encode(enc_msg.getBytes("UTF-8"));
- enc_msg = enc_msg.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
-
-
- System.out.println("公钥加密得到密文并使用base64处理:");
- //enc_msg = "TVpoZ040QTdVcTZMem11cGdTVjdWeFEvWU5kZWZqcFVBK3JSS0hGK0NsSFo0Ly82RnV3blVvT1hrMXNlZ25odXlkVytvRkdGT0xoYwpRSlhjYWI4eWJkUWh3UTgzUU1MQzBzYWtManZCOFNhR29VWDhiMHZoRXhWdkVIQ3BTcnlVRUQwUU9zQzVodXoxQTk3djRIeXNlMFR5CjFnWm1zaTRLZm9sWHM1TndJcXMvQ3lVdE9MTjNqZGZoajNHTXQvSHN1c01PVzFBekwxbTFTWVk2VGhuL3lvODA4NGRRaGN2aC9KVU0KdUdWZUNtdEJJbytHVVJaOXNCZW5BaUZVTEFWTHJINHhRK0pWUnpRRUZvRlhhNVVaRVQ0cDJTME1TWXBTZ1R4eStOeFloTER1S280OQpsV3RONFVKVkJiTVY1dUJrdEthMEF6MEROczA1bndDTnJtL3FReFdQMmFtV3pRT24ybHl6MVRCMXhjZGpaVVpDc3prOVZoM3FtS05SCmNVTnV2M0x6cDB2SzIzOVFyM205R0taWGl6TFdKcktQcE9UaDkrODQyY0s3L1ZmTDh6S2NBZG1QN21QMXhKZE5NVlEzZFZnaW9XWlQKd2xWWVc4N2FLZ0xLNlZIaEhJNTVGV3RNaFYrTzdNbUZlSzBWckhaS3NHSDJIUGg5ZUQyVkFVWVNWaU9HdisyUDRFczJ5V0lCNjkxNgpjMStRWjc5RXdwRmwrQjBJUG0yLy9rbDVXSXVYaDdXY1FPUzBBMm4yMnYxWnlMK29vdVNUTWNGMm9TNWx1RjRzRVc5VDkrb0tjRjU5CjBVOGVvY1E0R2Q5dkxwTnVpSG1CNUpvWFF6NG1EZGFBdG13eS9zMHFXVTgyQjlWZEVETlNPZGx0NnNoQXBTZ01ObGtGS0hyQllHMD0K";
- System.out.println(enc_msg);
-
- //enc_msg = "UVRreXJPVm1GRlgrYldkWFNjd1pwM0dTMWxuNkJYMThUZEs1U1dLQWU2cFdkV0JoUXBFeU1nci90L1J1YWpTSks0RXo2a250cXJGK0hoclRXQ3I5Nk8vUEw4aWFKS3J5SllpUm9jTE1NMVdEcWsyakIvMWxkWXE1WGx4Qk9lenR3aTI0alV4MVV4dTBZY0ZWaUIvdGFRd0xIaWdzdE1nT1pEYnlqcnhKdUdGdkpJVG9hNkJDbGM4RXpnRWN3bzZiQnBDR3BXSCtkQk5LZE5yN0dDYnAzRTltUGRxOEh4Y01NNFBiNXdyeFBrZlpCMkl2NXpGRnNmOStUTHEzajVQT2JTa2t6dXR2VGhVS2VKN1dYZ01Vdzhvbk9rYzE2S3Q3VTg3dEFJVlpJYTY2RDdTMGd5ZWNrN01oVE5KM2tkYXNhUmVtbEQ2cys0WkNtb2NqYWVWbVpuT09yNGtsS3Z6U2VZVE5sOWNpMXRCblFBV0M0VzE5dVN0RXp6OGxFY21idHBqZHVSbUxGODNyNm83YWZ6N1dDbGMwUDEyakMxODRxZDNNUUpRQ0l0OE1OZThzNTZsNVJ1blJnRmNGNGJEb2UyTU94QUUzb05Rd3JCMldRemcxNE9mRFp2UVdlMW9JVUNMU0cyZGc1OUNUN09KdG5lZndDaEJQUGNmc2tBVE8%3D";
- //enc_msg = "T2tpcTVMeC9uVCt6VXNSRWxaT09VVGh1YlZtMkpXQXhzcWErZkxsQ0pUWktMQVZNdTFTYm1VZnN6aVVGaTNnbUE0VEx6LzJLL3pkVmpzY0pHSDlzUmJ6MFRWTUM3QkZ4ZXV5bVJZMW43bGVNOG1wRVhheGNpTEIyVzNMV0lmakh0d0o0QTRUNWtwMnhUOVprRXFhZ1RKUEZ3RUgwSmdqem9CRHpjMzZNWkxlRS9DUzBCR0RoQzdTODFweXBMaktuUWdhK0RJNUFOQUdrQnhjeHcrQWFGeUdNRmRVMWVaMU9GWUtYVjRzeUJVZnZ6dk1UN2ZmODIvLzZBa1VRMFN3a2p5TmliRjg4VkJCODJGckRCOC9TRW1CWVJnWWtRVklhWEFPZXo1aXlSR0laam1KN3Z6bXFKTDZSVzVGWTFPYms2YWJaU1FnVnZwNXoxbStHdG1KdkRYczJxeE01Unk2N0RtNlhpOGRyRERvVW83YUdzbW5Tamp5VzNUSVE0WS9iSzVyMEo5UndwcjUvTTFYMGg1T3d4MWJoRWVVTUJVZlMzV1BZTVNwMVR4WFVsRkFjTk8yQk9wZ3lvcWJYcmRFV0c2RmFIUXNxYS82ay80SmpseVpCbDd6cUUwYU9SM3lZMXY0ZG9iVHlDb0JENkNhcFp1SWs0NFlibDRaMFdTRmFlRE1lcndiZmdUcU1nWmFNL0RjWmVWN2V1akVGNytaWjNLTExZdmU2VlV3bVlJbmM2bHg2N2FwdG5UM0hic3BWei8rTnlHek1FRWRmQlpGTVFnbDhSeTBoeTlDcGRxRng2dUhrdm5wRHJrenZkUVAzWm55bkRzZHgwdlBoUW9XeEQyQWRDVi9UdVdIOTIxeG52b0NVa3U2UCtkSFJyUm9kd1BVSjBWOURiYnc9";
- // base64逆处理并用私钥解密
- BASE64Decoder decoder = new BASE64Decoder();
- enc_msg = new String(decoder.decodeBuffer(enc_msg),"UTF-8");
- String dec_msg = RSAUtil.decrypt(enc_msg, privateKey);
-
- System.out.println("base64逆处理并用私钥解密:");
- System.out.println(dec_msg);
-
-
-
- }
-}
diff --git a/doc/建行支付对接修复报告.md b/doc/建行支付对接修复报告.md
deleted file mode 100644
index ea968f9..0000000
--- a/doc/建行支付对接修复报告.md
+++ /dev/null
@@ -1,538 +0,0 @@
-# 建行支付对接修复报告
-
-**项目**: Shopro商城建行支付集成
-**修复时间**: 2025-01-20
-**文档版本**: v2.0 (修订版)
-**建行接口版本**: v2.20 (2025-07-25)
-
----
-
-## ⚠️ 重要修订说明
-
-本报告v2.0版本修正了v1.0中关于"建行平台公钥"的**严重错误理解**:
-
-- ❌ **错误**: 文档中不存在"建行平台公钥"这个概念
-- ✅ **正确**: 应该是"建行生活支付验签公钥"(需联系建行生活技术支持获取)
-
----
-
-## 📋 修复概览
-
-本次对建行支付对接代码进行了**5项严重错误修复**和**1项性能优化**,基于建行官方Java示例代码和接口文档v2.20规范。
-
-### 修复文件清单
-
-| 文件路径 | 修复项 | 风险等级 |
-|---------|--------|---------|
-| `addons/shopro/library/ccblife/CcbPaymentService.php` | MAC签名算法、SIGN验签逻辑 | 🔴 致命 |
-| `addons/shopro/library/ccblife/CcbEncryption.php` | ENCPUB生成、RSA分段加密 | 🔴 致命 |
-| `addons/shopro/controller/Ccbpayment.php` | 防重复支付、notify返回格式 | 🟡 严重 |
-
----
-
-## 🔴 致命错误修复
-
-### 1. 支付串MAC签名算法错误
-
-**位置**: `CcbPaymentService.php:148-153`
-
-#### 修复前 ❌
-```php
-// 错误: 使用私钥签名
-$mac = md5($signString . $this->config['private_key']);
-```
-
-#### 修复后 ✅
-```php
-// 正确: 使用服务方公钥参与MD5计算(建行v2.2规范)
-$platformPubKey = $this->config['public_key']; // 服务方公钥
-$mac = strtoupper(md5($signString . '&PLATFORMPUB=' . $platformPubKey));
-```
-
-#### 技术说明
-根据建行文档v2.2版本和官方MD5Util.java示例:
-- **PLATFORMPUB字段**: 仅参与MD5摘要计算,不作为HTTP参数传递
-- **签名格式**: `MD5(参数串 + &PLATFORMPUB= + 服务方公钥内容)`
-- **输出格式**: 32位**大写**MD5字符串 (对照MD5Util.java第30行: `toUpperCase()`)
-
-**影响**: 修复前建行会拒绝所有支付请求,因签名验证100%失败。
-
----
-
-### 2. ENCPUB字段生成逻辑错误
-
-**位置**: `CcbEncryption.php:387-420`
-
-#### 修复前 ❌
-```php
-// 错误: 加密整个商户公钥
-return $this->rsaEncrypt($this->publicKey);
-```
-
-#### 修复后 ✅
-```php
-// 正确: 只加密商户公钥后30位
-$publicKeyContent = str_replace([
- '-----BEGIN PUBLIC KEY-----',
- '-----END PUBLIC KEY-----',
- "\r", "\n", " "
-], '', $this->publicKey);
-
-$last30Chars = substr($publicKeyContent, -30);
-return $this->rsaEncrypt($last30Chars);
-```
-
-#### 技术说明
-建行文档明确要求:
-> "使用服务方公钥对**商户公钥后30位**进行RSA加密并base64后的密文"
-
-**影响**: 修复前ENCPUB字段内容错误,可能导致建行无法验证商户公钥。
-
----
-
-### 3. 异步通知SIGN验签逻辑优化
-
-**位置**: `CcbPaymentService.php:467-570`
-
-#### 修复前 ❌
-```php
-// 错误: 使用MD5验签
-$expectedSign = md5($signStr . $this->config['private_key']);
-return strtolower($signature) === strtolower($expectedSign);
-```
-
-#### 修复后 ✅
-```php
-// 智能验签方案: 如果配置了验签公钥则使用RSA,否则降级为POSID验证
-$ccbVerifyPublicKey = $this->config['ccb_payment_verify_public_key'] ?? '';
-
-if (empty($ccbVerifyPublicKey)) {
- // 降级方案: POSID验证
- return ($params['POSID'] ?? '') === $this->config['pos_id'];
-}
-
-// 完整方案: RSA验签(尝试SHA256和SHA1)
-$signBinary = hex2bin($params['SIGN']);
-$pubKey = openssl_pkey_get_public($ccbVerifyPublicKey);
-
-$result = openssl_verify($signStr, $signBinary, $pubKey, OPENSSL_ALGO_SHA256);
-if ($result !== 1) {
- $result = openssl_verify($signStr, $signBinary, $pubKey, OPENSSL_ALGO_SHA1);
-}
-
-return $result === 1;
-```
-
-#### 技术说明
-根据建行文档7.2.3章节:
-- **SIGN字段**: 256个十六进制字符 (2048位RSA签名)
-- **验签密钥**: "建行生活分配的服务商支付验签公钥" (NT_TYPE=YS时)
-- **验签算法**: RSA-SHA256或SHA1 (文档未明确,代码会自动尝试)
-- **获取方式**: 联系建行生活平台技术支持
-
-#### 降级方案说明
-由于建行未提供验签公钥和示例代码,代码实现了两级验证:
-
-1. **优先**: 如果配置了`ccb_payment_verify_public_key`,使用RSA验签
-2. **降级**: 如果未配置,验证POSID和订单号是否匹配
-
-**建议**: 尽快联系建行技术支持获取验签公钥,补全配置后获得完整安全保障。
-
-**影响**: 修复后验签逻辑更健壮,未配置公钥时也能正常运行(安全性降低但不会中断业务)。
-
----
-
-## 🟡 严重问题修复
-
-### 4. 订单状态更新缺少防重复逻辑
-
-**位置**: `Ccbpayment.php:170-197`
-
-#### 修复前 ❌
-```php
-// 直接更新,没有并发控制
-$order->status = 'paid';
-$order->save();
-```
-
-#### 修复后 ✅
-```php
-// 使用原子性更新,防止并发重复支付
-$affectedRows = Db::name('shopro_order')
- ->where('id', $order->id)
- ->where('status', 'unpaid') // 只更新未支付的订单
- ->update([
- 'status' => 'paid',
- 'paid_time' => time() * 1000,
- 'updatetime' => time()
- ]);
-
-if ($affectedRows === 0) {
- // 订单已支付或状态异常
- throw new Exception('订单状态异常,无法更新为已支付');
-}
-```
-
-**影响**: 修复前在高并发场景下可能出现重复支付或状态覆盖。
-
----
-
-### 5. notify接口返回格式不规范
-
-**位置**: `Ccbpayment.php:271-283`
-
-#### 修复前 ❌
-```php
-// ThinkPHP框架会追加额外内容
-echo $result; // 'SUCCESS' 或 'FAIL'
-```
-
-#### 修复后 ✅
-```php
-// 直接exit,确保只返回纯文本
-exit(strtoupper($result)); // 'SUCCESS' 或 'FAIL'
-```
-
-#### 技术说明
-建行要求异步通知响应:
-- **HTTP 200** 状态码
-- **纯文本** 响应体: `SUCCESS` 或 `FAIL`
-- **不允许**任何额外字符(HTML/JSON等)
-
-**影响**: 修复前ThinkPHP框架可能追加调试信息,导致建行认为通知失败并重复推送。
-
----
-
-## ⚡ 性能优化
-
-### 6. RSA加密分段大小动态计算
-
-**位置**: `CcbEncryption.php:102-129`
-
-#### 优化前 ⚠️
-```php
-// 写死1024位RSA的chunk size
-$chunkSize = 117; // 1024位RSA密钥,每次最多加密117字节
-```
-
-#### 优化后 ✅
-```php
-// 动态获取RSA密钥大小
-$keyDetails = openssl_pkey_get_details($pubKeyId);
-$keySize = $keyDetails['bits'] / 8; // 1024位=128字节, 2048位=256字节
-$chunkSize = $keySize - 11; // PKCS1填充需要预留11字节
-```
-
-**优势**:
-- 自动适配1024位/2048位/4096位RSA密钥
-- 减少不必要的分段次数,提升加密性能
-- 避免密钥升级后的兼容性问题
-
----
-
-## 🔐 建行接口签名规则总结
-
-### 支付串生成流程
-
-```mermaid
-graph LR
- A[34个参数] --> B[按ASCII排序ksort]
- B --> C[http_build_query拼接]
- C --> D[追加&PLATFORMPUB=服务方公钥]
- D --> E[MD5签名,32位小写]
- E --> F[ENCPUB=RSA加密商户公钥后30位]
- F --> G[最终支付串=参数+MAC+PLATFORMID+ENCPUB]
-```
-
-### 异步通知验签流程
-
-```mermaid
-graph LR
- A[接收SIGN字段] --> B[hex2bin转二进制]
- B --> C[移除SIGN,剩余参数ksort排序]
- C --> D[拼接签名原串]
- D --> E[使用建行公钥RSA-SHA256验签]
- E --> F{验签结果}
- F -->|成功| G[返回SUCCESS]
- F -->|失败| H[返回FAIL]
-```
-
----
-
-## ✅ 验证检查清单
-
-修复完成后,请逐项检查以下配置:
-
-### 1. 配置文件检查
-
-**文件**: `addons/shopro/config/ccblife.php`
-
-```php
-return [
- // 建行商户信息
- 'merchant_id' => 'YOUR_MERCHANT_ID',
- 'pos_id' => 'YOUR_POS_ID',
- 'branch_id' => 'YOUR_BRANCH_ID',
- 'service_id' => 'YOUR_SERVICE_ID',
-
- // ✅ 服务方公钥(用于MAC签名)
- 'public_key' => '-----BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
------END PUBLIC KEY-----',
-
- // ✅ 服务方私钥(用于解密)
- 'private_key' => '-----BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
------END PRIVATE KEY-----',
-
- // ✅ 建行平台公钥(用于SIGN验签) - 新增必填!
- 'platform_public_key' => '-----BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
------END PUBLIC KEY-----',
-
- // 建行收银台URL
- 'cashier_url' => 'https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain',
-];
-```
-
-### 2. 密钥格式验证
-
-运行以下PHP脚本验证密钥格式:
-
-```php
-generatePaymentString($orderId);
-
-// 验证点:
-// 1. MAC长度为32位
-// 2. ENCPUB字段存在且不为空
-// 3. 支付串包含所有34个必需参数
-```
-
-#### TC2: 异步通知验签测试
-```php
-// 模拟建行回调数据
-$params = [
- 'ORDERID' => 'test123',
- 'PAYMENT' => '100.00',
- 'SUCCESS' => 'Y',
- 'SIGN' => '256字符十六进制字符串...'
-];
-
-$result = $service->handleNotify($params);
-// 预期: 返回'success'或'fail'
-```
-
-#### TC3: 并发支付测试
-使用Apache Bench进行并发测试:
-```bash
-ab -n 100 -c 10 http://your-domain/api/ccbpayment/callback
-```
-验证订单状态不会重复更新。
-
----
-
-## ⚠️ 上线前必读
-
-### 1. 建行生活支付验签公钥获取(重要!)
-
-**关键**: 需要向建行生活技术支持索要**"建行生活支付验签公钥"**,用于异步通知SIGN验签。
-
-#### 为什么需要这个公钥?
-
-- 建行用自己的私钥对异步通知进行RSA签名(生成SIGN字段)
-- 你需要用建行的公钥来验证SIGN,确保通知是建行发送的
-- 这个公钥**不是**你自己生成的公钥,是建行生活平台分配给你的
-
-#### 如何获取?
-
-1. 联系建行生活平台运营人员或技术支持
-2. 说明需要获取"建行生活支付验签公钥"(NT_TYPE=YS的验签公钥)
-3. 提供你的商户号和服务方编号
-4. 获取后配置到`.env`文件中
-
-```ini
-# .env文件
-ccb_payment_verify_public_key="-----BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
------END PUBLIC KEY-----"
-```
-
-#### 未配置的影响
-
-- 异步通知验签会降级为POSID验证
-- 安全性降低,无法完全确认通知来源
-- 但不会中断业务,系统仍可正常运行
-
-### 2. 验证密钥格式
-
-运行以下PHP脚本验证密钥配置是否正确:
-
-```php
->Backend: POST /createPayment(生成支付串)
- Backend->>Backend: 保存订单(未支付状态)
-
- Note over Backend,CCBBackend: 步骤2: 推送订单 ✅ 推送未支付订单!
- Backend->>Merchant: 调用订单推送接口(A3341TP01)
- Merchant-->>Backend: 返回推送结果
- Backend-->>H5: {payment_string}
-
- Note over H5,CCBApp: 步骤3: 调起建行收银台
- H5->>CCBApp: JSBridge.ccbpay(支付串)
- activate CCBBackend
- CCBApp->>CCBBackend: 校验登录
- CCBApp->>CCBApp: 调用支付组件
- H5->>CCBApp: 确认支付、输入密码
- CCBApp->>CCBBackend: 发送支付请求
- Note right of CCBBackend: 用户在建行APP中完成支付
-
- Note over CCBBackend,Backend: 步骤10-12: 建行异步通知
- CCBBackend->>Merchant: 返回支付成功通知
- Merchant->>Backend: 推送服务器通知(notify)
- Backend->>Backend: 验证SIGN签名
- Backend->>Backend: 原子更新本地订单状态为paid
- deactivate CCBBackend
-
- Note over Backend,Merchant: 步骤13: 更新订单状态 ✅ 更新为已支付!
- Backend->>Merchant: 调用订单更新接口(A3341TP02)
- Merchant-->>Backend: 返回更新结果
- Backend-->>Merchant: 返回SUCCESS
-
- Note over H5,Backend: 步骤15-16: 前端轮询查询状态 (未收到通知时)
- loop 每2秒轮询(最多60秒)
- H5->>Backend: GET /queryPaymentStatus
- Backend-->>H5: {status: 'paid'或'unpaid'}
- alt status == 'paid'
- H5->>H5: 跳转到支付成功页
- end
- end
-```
-
-**关键流程说明**:
-1. **步骤2**: 生成支付串后**立即推送未支付订单**到建行外联系统(A3341TP01)
-2. **步骤13**: 收到支付成功通知后**更新订单状态为已支付**(A3341TP02)
-3. **步骤15**: 前端轮询查询订单状态(用于未收到通知的降级方案)
-
-### 修复前的错误实现
-
-```mermaid
-sequenceDiagram
- participant H5 as 前端H5页面
- participant Callback as callback接口
- participant Notify as notify接口
- participant CCBApp as 建行APP(黑盒)
-
- Note over H5,Callback: ❌ 错误1: 前端callback通知支付成功
- H5->>Callback: POST callback(order_id, trans_id) ❌ 可伪造!
- Callback->>Callback: verifyPayment()主动查询建行?
- Callback->>Callback: ❌ 更新订单为已支付
- Callback->>Callback: ❌ 推送订单到外联
- Callback-->>H5: 返回success
-
- Note over CCBApp,Notify: ❌ 错误2: 建行异步通知被边缘化
- CCBApp-->>Notify: POST notify(ORDERID, SIGN等)
- Notify->>Notify: ❌ 再次更新订单?
- Notify->>Notify: ❌ 再次推送订单?
- Notify-->>CCBApp: 返回SUCCESS
-
- Note over H5,Callback: ⚠️ 严重问题: 两条并行路径!
- rect rgb(255, 200, 200)
- Note right of Callback: 路径A: 前端callback触发更新