建行微信支付 代码参考
java实现对接建行支付及其回调
参考链接一 微信小程序使用建行支付
参考链接二 微信小程序,调用建行支付
参开链接三 集成建行聚合支付踩过的坑,有些槽不吐不快
建行支付里三大步骤与坑 验签也有顺序
Java实现建行聚合支付对接及其回调
这里我也得吐槽一下,2022年了 建行的回调验签居然还是把jar自己导入到本次仓库进行依赖添加,居然没有上传自己的pom依赖,jdk还是1.5的。。。还是说有新的,我不知道?
微信支付使用建行流程
链接一
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
链接二
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"
链接一、链接二、支付参数 获取方式
- 拼接建行链接,链接包括建行参数跟mac(字段MD5信息) post请求
- 请求后 建行返回字段中包含 一个链接,再次请求返回支付信息
链接一 通过 POST
方式 第一次请求 字段信息是建行等配置
组装加密字段,拼接MAC,组成完整的请求链接
拼接建行md5加密 字段是有顺序的,不然请求后会返回mac校验失败,PUB(公钥后三十位)字段只参与加密,不参与参数传递
加密字段 加密字段 包含PUB字段
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
建行请求链接 建行请求链接 不包含PUB字段
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加密字信息
请求后 返回信息 这里返回 payUrl
是链接二
{
"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绑定
3. 记录支付登记簿异常 escape编码长度超长 。 这里面有的字段中文需要通过escape转码,但是转码长度不能过长
{
"SUCCESS":"true",
"ERRCODE":"0130Z1109001",
"ERRMSG":"记录支付登记簿异常!",
"TXCODE":"530590",
"appId":"",
"timeStamp":"",
"nonceStr":"",
"package":"",
"signType":"",
"paySign":"",
"partnerid":"",
"prepayid":"",
"mweb_url":""
}
重点
拼接建行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 小程序用小程序的依赖,公众号用公众号的依赖 公众号配置 小程序配置
- natapp 配置内网穿透,测试时建议购买一个月的通道,配置到建行的回调地址,方便测试支付后回调
建行验签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;
}
参数赋值
package com.kdzt.wxmp.convert;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.kdzt.wxmp.config.CcbProperties;
import com.kdzt.wxmp.model.ccb.req.CcbTakeMacReq;
import com.kdzt.wxmp.util.FiledConvertUtils;
import com.kdzt.wxmp.util.IpUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* 建行参数转换
*
* @author Administrator
* @date 2022/08/17
*/
@Component
@RequiredArgsConstructor
public class CcbParamConvert {
private final CcbProperties ccbProperties;
public String createCcbParam(String amount, String description, String openId, String orderNo, String zhnum, String appId) {
// 微信下单 ==》 建行下单
CcbTakeMacReq ccbTakeReq = new CcbTakeMacReq();
this.toCcbMacSetting(ccbTakeReq);
ccbTakeReq.setPAYMENT(amount);
ccbTakeReq.setORDERID(orderNo);
ccbTakeReq.setSUB_OPENID(openId);
ccbTakeReq.setSUB_APPID(appId);
ccbTakeReq.setREMARK1(“”);
ccbTakeReq.setREMARK2("");
ccbTakeReq.setREFERER("");
// ccbTakeReq.setTIMEOUT("");
ccbTakeReq.setPROINFO("");
ccbTakeReq.setREGINFO("");
// 拼接参数
String value = FiledConvertUtils.getCcbParam(ccbTakeReq, false);
ccbTakeReq.setPUB(ccbProperties.getPub());
// 获取md5加密后的MAC字段
String md5 = FiledConvertUtils.getCcbParam(ccbTakeReq, true);
return value + md5;
}
private 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("");
}
}
反射工具类
package com.kdzt.wxmp.util;
import cn.hutool.crypto.SecureUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 字段转换工具类
*
* @author Administrator
* @date 2022/08/12
*/
@Slf4j
@Component
public class FiledConvertUtils {
/**
* 得到建行参数
*
* @param object 对象
* @param md5 md5 是否md5
* @return {@link String}
*/
public static String getCcbParam(Object object, boolean md5) {
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);
// 判断如果不是算mac MD5 并且字段是PUB 跳过
if (!md5 && "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();
log.error("转换建行参数错误 {} {}", md5, e.getMessage());
}
String value = String.join("&", values);
if (md5) {
System.out.println("加密字符串" + value);
return String.format("&MAC=%s", SecureUtil.md5(value));
}
return String.join("&", values);
}
}
方式二 直接拼接
拼接工具类
@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);
}
}
escape转码工具类
public static String escape(String src) {
int i;
char j;
StringBuffer tmp = new StringBuffer();
tmp.ensureCapacity(src.length() * 6);
for (i = 0; i < src.length(); i++) {
j = src.charAt(i);
if (Character.isDigit(j) || Character.isLowerCase(j)
|| Character.isUpperCase(j))
tmp.append(j);
else if (j < 256) {
tmp.append("%");
if (j < 16)
tmp.append("0");
tmp.append(Integer.toString(j, 16));
} else {
tmp.append("%u");
tmp.append(Integer.toString(j, 16));
}
}
return tmp.toString();
}
public static String unescape(String src) {
StringBuffer tmp = new StringBuffer();
tmp.ensureCapacity(src.length());
int lastPos = 0, pos = 0;
char ch;
while (lastPos < src.length()) {
pos = src.indexOf("%", lastPos);
if (pos == lastPos) {
if (src.charAt(pos + 1) == 'u') {
ch = (char) Integer.parseInt(src
.substring(pos + 2, pos + 6), 16);
tmp.append(ch);
lastPos = pos + 6;
} else {
ch = (char) Integer.parseInt(src
.substring(pos + 1, pos + 3), 16);
tmp.append(ch);
lastPos = pos + 3;
}
} else {
if (pos == -1) {
tmp.append(src.substring(lastPos));
lastPos = src.length();
} else {
tmp.append(src.substring(lastPos, pos));
lastPos = pos;
}
}
}
return tmp.toString();
}
请求返回链接 获取返回参数
通过post请求链接到建行,建行返回信息,信息里有微信请求连接,再次通过get方式请求,获取支付调起参数
- 请求建行连接
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加密字信息
// 1 拼接参数请求建行api
String ccbParam = ccbParamConvert.createCcbParam(amountStr, description, openId, orderNo, zhnum);
// 2 请求建行 返回请求结果
String result = HttpUtil.post(String.format("%s&%s", ccbProperties.getUrl(), ccbParam), "");
- 注:ccbParam就是拼接的字符串参数
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加密字信息
- ccbProperties.getUrl() 是
https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6
建行前缀信息 -
HttpUtil
hutool的http方法
返回的对象
@Data
public class CcbVo {
@SerializedName("SUCCESS")
private String success;
@SerializedName("PAYURL")
private String payUrl;
}
- 再次请求
payUrl
中的连接
String wxResult = HttpUtil.get(ccbVo.getPayUrl());
返回对象 这里package是关键字, 使用 @JSONField(name = "package")
转一下
@Data
public class CcbWxJsapiResult {
@SerializedName("SUCCESS")
private String success;
@SerializedName("ERRCODE")
private String errCode;
@SerializedName("ERRMSG")
private String errMsg;
@SerializedName("TXCODE")
private String txCode;
private String appId;
private String timeStamp;
private String nonceStr;
@JSONField(name = "package")
private String packageValue;
private String signType;
private String paySign;
private String partnerid;
private String prepayid;
@SerializedName("mweb_url")
private String mwebUrl;
}
最后将参数传给前端,前端通过参数调起支付
字段 | 名称 | 说明 |
---|---|---|
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 |
前端调起支付
小程序
微信H5
APP
公众号
举例:
公众号支付,复制下面方法,将参数改成你返回的参数就可以调起支付了
回调方法
需要在建行配置你的回调地址,域名+请求路径,本地测试的话可以使用内网穿透,通过微信支付调起支付,付款成功后会发起回调
回调方法
/**
* 建行支付结果通知
*
* @param request
* @return
*/
@PostMapping("/callBackCcb")
public ParamMap callBackCcb(HttpServletRequest request) {
log.debug("callBackCCB start!");
Map<String, String> map = Maps.newHashMap();
Enumeration<String> parameterNames = request.getParameterNames();
StringBuilder data = new StringBuilder();
// 通过request获取返回信息,构建map
while (parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement();
String value = request.getParameter(name);
map.put(name, value);
data.append(name).append("=").append(value).append("&");
}
if(log.isDebugEnabled()) {
log.debug("request 回调信息=====》{} {}", data, map);
}
// 验签
callBackCheck(map);
return “你的具体实现”;
}
验签 回调最主要的就是验签,验签成功后才能做具体操作文章来源:https://www.toymoban.com/news/detail-403773.html
private void callBackCheck(Map<String, String> map) {
// 这里顺序必须是这样,不然验签会不成功
String sb = "POSID=" + map.get("POSID") + "&BRANCHID=" + map.get("BRANCHID") +
"&ORDERID=" + map.get("ORDERID") + "&PAYMENT=" + map.get("PAYMENT") +
"&CURCODE=" + map.get("CURCODE") + "&REMARK1=" + map.get("REMARK1") +
"&REMARK2=" + map.get("REMARK2") + "&ACC_TYPE=" + map.get("ACC_TYPE") +
"&SUCCESS=" + map.get("SUCCESS") + "&ACCDATE=" + map.get("ACCDATE");
RSASig rsaSig = new RSASig();
// 公钥
rsaSig.setPublicKey(ccbProperties.getPublicKey());
boolean flag = rsaSig.verifySigature(map.get("SIGN"), sb);
log.info("callBackCCB Check :[{}]", flag);
//表示支付成功
if ("Y".equals(map.get("SUCCESS")) && flag) {
CallbackResult callbackResult = new CallbackResult();
callbackResult.setReturnCode("Y");
callbackResult.setReturnMsg("Ok");
callbackResult.setOrderNo(map.get("ORDERID"));
} else {
throw new MpCcbException("建行回调异常");
}
}
之后就可以做具体的操作了,修改订单状态等文章来源地址https://www.toymoban.com/news/detail-403773.html
到了这里,关于微信建行支付对接的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!