微信建行支付对接

建行微信支付 代码参考

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&REGINFO=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&REGINFO=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"

链接一、链接二、支付参数 获取方式

  1. 拼接建行链接,链接包括建行参数跟mac(字段MD5信息) post请求
  2. 请求后 建行返回字段中包含 一个链接,再次请求返回支付信息

链接一 通过 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&REGINFO=&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&REGINFO=&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&REGINFO=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&REGINFO=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

建行请求流程图

可能会遇到的问题

  1. MAC校验失败, 可能是加密字段顺序不一致
  2. “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&REGINFO=&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&REGINFO=&PROINFO=&REFERER=&TRADE_TYPE=JSAPI&SUB_APPID=0000&SUB_OPENID=0000&MAC=md5加密字信息

开发前置准备

  1. md5加密使用的是hutool工具集的 SecureUtil.md5() 方法 添加依赖即可 hutool工具集
  2. 建行jar包,以供调用接口回调验签
  3. 配置微信小程序信息 可以使用 wx-java 小程序用小程序的依赖,公众号用公众号的依赖 公众号配置 小程序配置
  4. 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&REGINFO=&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信息,这里提供两种方式

  1. 实体类定义字段,通过反射拼接字符串
  2. 通过map、append拼接字符串

拼接建行请求链接

方式一 利用反射拼接

基础配置

  1. 可以定义在常量类中 直接写死
  2. 我这里写在配置类里进行了读取
    两种方式都可以

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=&REGINFO=&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=&REGINFO=&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方式请求,获取支付调起参数

  1. 请求建行连接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&REGINFO=&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&REGINFO=&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;
}
  1. 再次请求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 “你的具体实现”;
    }

验签 回调最主要的就是验签,验签成功后才能做具体操作

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("建行回调异常");
        }
    }

之后就可以做具体的操作了,修改订单状态等

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 39
    评论
评论 39
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值