参考链接一 微信小程序使用建行支付 参考链接二 微信小程序,调用建行支付 参开链接三 集成建行聚合支付踩过的坑,有些槽不吐不快
这里我也得吐槽一下,2022年了 建行的回调验签居然还是把jar自己导入到本次仓库进行依赖添加,居然没有上传自己的pom依赖,jdk还是1.5的。。。还是说有新的,我不知道?
微信支付使用建行流程
- 拼接建行链接,链接包括建行参数跟mac(字段MD5信息) post请求
- 请求后 建行返回字段中包含 一个链接,再次请求返回支付信息
例: 通过 POST 方式 第一次请求 字段信息是建行等配置
https://ibsbjstar.ccb.com.cn/CCBIS/ccbMainCCB_IBSVersion=V6&MERCHANTID=105320148140002&POSID=100001135&BRANCHID=320000000&ORDERID=88487&PAYMENT=0.01&CURCODE=01&TXCODE=530590&REMARK1=&REMARK2=&TYPE=1&GATEWAY=0&CLIENTIP=128.128.80.125®INFO=xiaofeixia&PROINFO=digital&REFERER=&SMERID=111&SMERNAME=%u5DE5%u827A%u7F8E%u672F%u5546%u5E97&SMERTYPEID=112&SMERTYPE=%u5BBE%u9986%u9910%u5A31%u7C7B&TRADECODE=001&TRADENAME=%u6D88%u8D39&SMEPROTYPE=1&PRONAME=%u5DE5%u827A%u54C1&TIMEOUT=20161028101226&TRADE_TYPE=MINIPRO&SUB_APPID=wx8888888888888888&SUB_OPENID=oUpF8uMuAJO_M2pxb1Q9zNjWeS6o&WX_CHANNELID=wx902937628837&USERPARAM=1U2xb%2FdMepRIs0KcM53xns%2Chdg2xBh3qwJ%2F%2F%2FHi%2FjMfPcbUYjQdxJKe%2CnoHnBgXppyQqPVPdDf8p%0D%0AEwxoLdkWjvdj2QUXJ5Hb&RETURN_FIELD=10000000000000000000&MAC=b2a1adfc9f9a44b57731440e31710740
请求后 返回信息
{
"SUCCESS": "true",
"PAYURL": "https://ibsbjstar.ccb.com.cn/CCBIS/B2CMainPlat_02?CCB_IBSVersion=V6&MERCHANTID=105320148140002&POSID=100001135&BRANCHID=320000000&ORDERID=88487&PAYMENT=0.01&CURCODE=01&TXCODE=530590&REMARK1=&REMARK2=&TYPE=1&GATEWAY=0&CLIENTIP=128.128.80.125®INFO=xiaofeixia&PROINFO=digital&REFERER=&SMERID=111&SMERNAME=%u5DE5%u827A%u7F8E%u672F%u5546%u5E97&SMERTYPEID=112&SMERTYPE=%u5BBE%u9986%u9910%u5A31%u7C7B&TRADECODE=001&TRADENAME=%u6D88%u8D39&SMEPROTYPE=1&PRONAME=%u5DE5%u827A%u54C1&TIMEOUT=20161028101226&TRADE_TYPE=MINIPRO&SUB_APPID=wx8888888888888888&SUB_OPENID=oUpF8uMuAJO_M2pxb1Q9zNjWeS6o&MAC=b2a1adfc9f9a44b57731440e31710740&QRCODE=1&CHANNEL=1"
}
获取 PAYURL 链接 再次请求
https://ibsbjstar.ccb.com.cn/CCBIS/B2CMainPlat_02?CCB_IBSVersion=V6&MERCHANTID=105320148140002&POSID=100001135&BRANCHID=320000000&ORDERID=88487&PAYMENT=0.01&CURCODE=01&TXCODE=530590&REMARK1=&REMARK2=&TYPE=1&GATEWAY=0&CLIENTIP=128.128.80.125®INFO=xiaofeixia&PROINFO=digital&REFERER=&SMERID=111&SMERNAME=%u5DE5%u827A%u7F8E%u672F%u5546%u5E97&SMERTYPEID=112&SMERTYPE=%u5BBE%u9986%u9910%u5A31%u7C7B&TRADECODE=001&TRADENAME=%u6D88%u8D39&SMEPROTYPE=1&PRONAME=%u5DE5%u827A%u54C1&TIMEOUT=20161028101226&TRADE_TYPE=MINIPRO&SUB_APPID=wx8888888888888888&SUB_OPENID=oUpF8uMuAJO_M2pxb1Q9zNjWeS6o&MAC=b2a1adfc9f9a44b57731440e31710740&QRCODE=1&CHANNEL=1
此时返回 请求结果
{
"appId": "wxad35f06adfdsgre3"
"errcode": "000000"
"errmsg": ""
"mweb_url": ""
"mypackage": "prepay_id=wx15155254732131244559e3cb82f0000"
"nonceStr": "wYggZEgyfdfdsfasdsaJpPOSw61sG"
"partnerid": "54523121"
"paySign": "Hn/e4XM7gOnfhADoN6ccVh2BnAX09zs2IjlqPs5PfckIkUXFRSwprCd9g94FU4NwoZd58tjtwFjiI/7z2qaXhMwNKlxthjgasavUWhhHd3Nb1JPIORiRXlN/lyElmDj4RQ/6+bheixrQmT7NlIX/gCcpRxJbIw+lmoNMbgJWB8nNZ4YOIkS8B9ybBjluNa4bqePwKxSfLJnDJmlm95IDIVcJ/+uuTED97peHPbEI39t966wFbibXxUi6cbeOtYieW7TkwDIt3LGX6SqvLlMybXDyuKGseyY0wG80UNOFShvOt60iFiFJhAuE0OXHFw=="
"prepayid": ""
"signType": "RSA"
"success": true
"timeStamp": "1605426774"
"txcode": "530590"
}
参数介绍
字段 | 名称 | 说明 |
---|
SUCCESS | 返回状态码 | 此字段是通信标识,表示通信成功 | ERRCODE | 错误码 | 000000表示交易成功,非000000表示交易失败,错误信息可以查看ERRMSG字段 | ERRMSG | 错误信息 | 错误信息描述 | TXCODE | 交易码 | 530590 | appId | 微信分配的APPID | 参考微信对应的调起支付API | timeStamp | 时间戳 | 参考微信对应的调起支付API | nonceStr | 随机串 | 参考微信对应的调起支付API | package | 数据包 | 参考微信对应的调起支付API | signType | 签名方式 | 参考微信对应的调起支付API | paySign | 签名数据 | 参考微信对应的调起支付API | partnerid | 子商户的商户号 | 参考微信对应的调起支付API | prepayid | 预支付交易会话ID | 参考微信对应的调起支付API | mweb_url | 微信H5支付中间页面URL | 参考微信对应的调起支付API |
可能会遇到的问题
MAC校验失败 , 可能是加密字段顺序不一致- “ERRMSG”:“支付不成功,@@INVALID_REQUEST~~sub_mch_id与sub_appid不匹配@@”, 没有配置商户id绑定
重点 拼接建行md5加密 字段是有顺序的,不然请求后会返回mac校验失败,PUB(公钥后三十位)字段只参与加密,不参与参数传递
例: 加密字段有PUB 字段,请求不需要加 PUB 字段, MAC 是加密信息md5后的信息
加密字段
MERCHANTID=0000&POSID=0000&BRANCHID=0000&ORDERID=0000&PAYMENT=0.01&CURCODE=01&TXCODE=530590&REMARK1=2&REMARK2=&TYPE=1&PUB=0000&GATEWAY=0&CLIENTIP=192.168.56.1®INFO=&PROINFO=&REFERER=&TRADE_TYPE=JSAPI&SUB_APPID=0000&SUB_OPENID=0000
建行请求链接
https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6&MERCHANTID=0000&POSID=0000&BRANCHID=0000&ORDERID=0000&PAYMENT=0.01&CURCODE=01&TXCODE=530590&REMARK1=2&REMARK2=&TYPE=1&GATEWAY=0&CLIENTIP=192.168.56.1®INFO=&PROINFO=&REFERER=&TRADE_TYPE=JSAPI&SUB_APPID=0000&SUB_OPENID=0000&MAC=md5加密字信息
开发前置准备
- md5加密使用的是
hutool 工具集的 SecureUtil.md5() 方法 添加依赖即可 hutool工具集 - 建行jar包,以供调用接口回调验签
- 配置微信小程序信息 可以使用 wx-java 小程序用小程序的依赖,公众号用公众号的依赖 公众号配置 小程序配置
本地jar包导入命令
mvn install:install-file "-Dfile=F:\work\java\ccb\netpay.jar" "-DgroupId=CCBSign" "-DartifactId=RSASig" "-Dversion=1.0" "-Dpackaging=jar"
导入到本地仓库后添加到pom文件
<dependency>
<groupId>CCBSign</groupId>
<artifactId>RSASig</artifactId>
<version>1.0</version>
</dependency>
建行开发
mac 加密的字段及顺序
$host = 'https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6&';
$string = 'MERCHANTID=00000&POSID=0000&BRANCHID=000&ORDERID=0000&PAYMENT=0.01&CURCODE=01&TXCODE=530590&REMARK1=&REMARK2=&TYPE=1&PUB=00000000&GATEWAY=0&CLIENTIP=127.0.0.1®INFO=&PROINFO=%u94bb%u77f3%u4f1a%u5458%u4f18%u60E0%u5927%u4fC3&REFERER=&TRADE_TYPE=MINIPRO&SUB_APPID=0000&SUB_OPENID=0000';
echo $host.$string."&MAC=".md5($string);
流程看的差不多了,接下来就是对接建行。 如何拼接建行请求字段以及如何拼接生成md5信息,这里提供两种方式
- 实体类定义字段,通过反射拼接字符串
- 通过map、append拼接字符串
拼接建行请求链接
方式一 利用反射拼接
基础配置
- 可以定义在常量类中 直接写死
- 我这里写在配置类里进行了读取
两种方式都可以
yml配置属性
ccb:
url: https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6
merchant-id: 你的建行商户id
pos-id: '你的建行posid' # 这里使用单引号是因为前面如果是0的话 读取过去会去掉
branch-id: 你的建行branch-id
cur-code: '01'
tx-code: 530590
gateway: 0
type: 1
pub: 公钥后三十位
yml配置类
/**
* 建行url属性
*
* @author Administrator
* @date 2022/07/28
*/
@Data
@Component
@ConfigurationProperties(prefix = "ccb")
public class CcbProperties {
/**
* 建行支付请求连接
*/
private String url;
/**
* 商户代码
*/
private String merchantId;
/**
* 商户柜台代码
*/
private String posId;
/**
* BRANCHID
*/
private String branchId;
/**
* 币种类型 01 人民币
*/
private String curCode;
/**
* 交易码 由建行统一分配为530590
*/
private String txCode;
/**
* 网关 默认0
*/
private String gateway;
/**
* 接口类型 1- 防钓鱼接口
*/
private String type;
/**
* 公钥后三十位
*/
private String pub;
}
参与加密的字段及顺序 实体类展现
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CcbTakeMacReq {
/**
* 商户代码
*/
private String MERCHANTID;
/**
* 商户柜台代码
*/
private String POSID;
/**
* 分行代码
*/
private String BRANCHID;
/**
* 定单号 由商户提供,最长30位
*/
private String ORDERID;
/**
* 付款金额 由商户提供,按实际金额给出
*/
private String PAYMENT;
/**
* 币种 缺省为01-人民币
* (只支持人民币支付)
*/
private String CURCODE;
/**
* 交易码
*/
private String TXCODE;
/**
* 备注1 一般作为商户自定义备注信息使用,可在对账单中显示。
*/
private String REMARK1;
/**
* 备注2 一般作为商户自定义备注信息使用,可在对账单中显示。
*/
private String REMARK2;
/**
* 接口类型 分行业务人员在P2员工渠道后台设置防钓鱼的开关。
* 1- 防钓鱼接口
*/
private String TYPE;
/**
* 公钥后30位 商户从建行商户服务平台下载,截取后30位。
* 仅作为源串参加MD5摘要,不作为参数传递
*/
private String PUB;
/**
* 网关类型 默认送0
*/
private String GATEWAY;
/**
* 客户端IP 客户在商户系统中的IP,即客户登陆(访问)商户系统时使用的ip)
*/
private String CLIENTIP;
/**
* 客户注册信息 客户在商户系统中注册的信息,中文需使用escape编码
*/
private String REGINFO;
/**
* 商品信息 客户购买的商品
* 中文需使用escape编码
*/
private String PROINFO;
/**
* 商户URL 商户送空值即可;
* 具体请看REFERER设置说明
*/
private String REFERER;
/**
* 交易类型 JSAPI--公众号支付、APP--app支付、MINIPRO--小程序、H5--H5跳转支付
* @see WxPayEnum
*/
private String TRADE_TYPE;
/**
* 当前调起支付的小程序APPID
*/
private String SUB_APPID;
/**
* 用户在小程序appid下的唯一标识,小程序通过wx.login获取
*/
private String SUB_OPENID;
}
反射工具类
@Component
public class FiledConvertUtils {
/**
* 获取对象的所有字段名和字段值
*
* @param object
* @return
*/
public static String getFieldNameAndValue(Object object) {
List<String> values = Lists.newArrayList();
Class<?> clazz = object.getClass();
List<Field> fieldList = new ArrayList<>();
while (clazz != null) {
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = clazz.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
fieldList.toArray(fields);
try {
for (Field field : fields) {
field.setAccessible(true);
if("PUB".equalsIgnoreCase( field.getName())) {
continue;
}
values.add(String.format("%s=%s", field.getName(), (null == field.get(object)) ? "" : field.get(object)));
}
} catch (Exception e) {
e.printStackTrace();
}
String value = String.join("&", values);
return value;
}
// 获取md5加密字段
public static String getMd5(Object object) {
List<String> values = Lists.newArrayList();
Class<?> clazz = object.getClass();
List<Field> fieldList = new ArrayList<>();
while (clazz != null) {
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = clazz.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
fieldList.toArray(fields);
try {
for (Field field : fields) {
field.setAccessible(true);
values.add(String.format("%s=%s", field.getName(), (null == field.get(object)) ? "" : field.get(object)));
}
} catch (Exception e) {
e.printStackTrace();
}
String value = String.join("&", values);
System.out.println("加密字符串" + value);
return String.format("&MAC=%s", SecureUtil.md5(value));
}
@Autowired
private CcbProperties ccbProperties;
// 对数据进行赋值
public void toCcbMacSetting(CcbTakeMacReq macReq) {
macReq.setMERCHANTID(ccbProperties.getMerchantId());
macReq.setPOSID(ccbProperties.getPosId());
macReq.setBRANCHID(ccbProperties.getBranchId());
macReq.setCURCODE(ccbProperties.getCurCode());
macReq.setTXCODE(ccbProperties.getTxCode());
macReq.setGATEWAY(ccbProperties.getGateway());
macReq.setTYPE(ccbProperties.getType());
macReq.setCLIENTIP(IpUtils.getHostIp());
macReq.setTRADE_TYPE(TradeTypeEnum.JSAPI.name());
macReq.setREFERER("");
}
}
方式二 直接拼接
拼接工具类
@Component
@RequiredArgsConstructor
public class CcbParamUtils {
private final CcbProperties ccbProperties;
public String getPayRequestParam(String orderNo, BigDecimal payMoney, String openid, String appId) {
//md5加密 MD5加密后生成32位(小写字母 + 数字)字符串
String sb1 = "MERCHANTID=" + ccbProperties.getMerchantId() + "&" +
"POSID=" + ccbProperties.getPosId() + "&" +
"BRANCHID=" + ccbProperties.getBranchId() + "&" +
"ORDERID=" + orderNo.trim() + "&" +
"PAYMENT=" + payMoney + "&" +
"CURCODE=" + ccbProperties.getCurCode() + "&" +
"TXCODE=530590&" +
"REMARK1=&REMARK2=&" +
"TYPE=" + ccbProperties.getType() + "&" +
"PUB=" + ccbProperties.getPub() + "&" +
"GATEWAY=" + ccbProperties.getGateway() + "&" +
"CLIENTIP=®INFO=&PROINFO=&REFERER=&" +
"TRADE_TYPE=" + TradeTypeEnum.JSAPI.name() + "&" +
"SUB_APPID=" + appId + "&" +
"SUB_OPENID=" + openid;
String sb2 = "MERCHANTID=" + ccbProperties.getMerchantId() + "&" +
"POSID=" + ccbProperties.getPosId() + "&" +
"BRANCHID=" + ccbProperties.getBranchId() + "&" +
"ORDERID=" + orderNo.trim() + "&" +
"PAYMENT=" + payMoney + "&" +
"CURCODE=" + ccbProperties.getCurCode() + "&" +
"TXCODE=530590&" +
"REMARK1=&REMARK2=&" +
"TYPE=" + ccbProperties.getType() + "&" +
"GATEWAY=" + ccbProperties.getGateway() + "&" +
"CLIENTIP=®INFO=&PROINFO=&REFERER=&" +
"TRADE_TYPE=" + TradeTypeEnum.JSAPI.name() + "&" +
"SUB_APPID=" + appId + "&" +
"SUB_OPENID=" + openid;
return sb2 + "&MAC=" + SecureUtil.md5(sb1);
}
}
请求返回链接 回去返回参数
回调方法
|