• 微信建行支付对接


    参考链接一 微信小程序使用建行支付
    参考链接二 微信小程序,调用建行支付
    参开链接三 集成建行聚合支付踩过的坑,有些槽不吐不快

    这里我也得吐槽一下,2022年了 建行的回调验签居然还是把jar自己导入到本次仓库进行依赖添加,居然没有上传自己的pom依赖,jdk还是1.5的。。。还是说有新的,我不知道?

    微信支付使用建行流程

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

    例:
    通过 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
    
    • 1

    请求后 返回信息

    {
      "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"
    }
    
    • 1
    • 2
    • 3
    • 4

    获取 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
    
    • 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    参数介绍

    字段名称说明
    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绑定

    商户使用小程序

    重点
    拼接建行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加密字信息
    
    • 1
    • 2
    • 3
    • 4
    • 5

    开发前置准备

    1. md5加密使用的是hutool工具集的 SecureUtil.md5() 方法 添加依赖即可 hutool工具集
    2. 建行jar包,以供调用接口回调验签
    3. 配置微信小程序信息 可以使用 wx-java 小程序用小程序的依赖,公众号用公众号的依赖 公众号配置 小程序配置

    本地jar包导入命令

    mvn install:install-file "-Dfile=F:\work\java\ccb\netpay.jar" "-DgroupId=CCBSign" "-DartifactId=RSASig" "-Dversion=1.0" "-Dpackaging=jar"
    
    
    • 1
    • 2

    导入到本地仓库后添加到pom文件

    
       CCBSign
       RSASig
       1.0
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    建行开发

    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);
    
    • 1
    • 2
    • 3

    流程看的差不多了,接下来就是对接建行。
    如何拼接建行请求字段以及如何拼接生成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: 公钥后三十位
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    参与加密的字段及顺序 实体类展现

    @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;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    反射工具类

    @Component
    public class FiledConvertUtils {
    
        /**
         * 获取对象的所有字段名和字段值
         *
         * @param object
         * @return
         */
        public static String getFieldNameAndValue(Object object) {
            List values = Lists.newArrayList();
            Class clazz = object.getClass();
            List 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 values = Lists.newArrayList();
            Class clazz = object.getClass();
            List 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("");
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    方式二 直接拼接

    拼接工具类

    
    @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);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    请求返回链接 回去返回参数

    回调方法

  • 相关阅读:
    python开发之个人微信机器人的开发
    Eureka
    writev()与readv()
    Android在AMS中拦截某个Activity的启动
    「游戏引擎 浅入浅出」4.2 顶点着色器
    SRT简介
    电商平台如何实现分账功能?
    【DBC专题】-8-经典CAN2.0 DBC快速切换为CANFD DBC
    cv算法工程师学习教程
    写给新用户-Mac软件指南篇:让你的Mac更好用
  • 原文地址:https://blog.csdn.net/wonder_dog/article/details/126358394