【建行生活】记录一次建行生活接入的坑

最近公司的项目接了个建行生活生活支付,文档诟病良多,把一些坑记录一下防止大家踩坑。
一、首先需要接入授权登录,需要服务方公私钥加解密建行生活跳转带过来的参数,光这个就浪费了两天时间,建行的工作人员提供各种钥匙无果,我就是各种解密失败,直到第三天才提供了正确的服务方公私钥,然后第一个坑就来了,这是建行官方文档提供的解密方法:

/**
 * 解密及验签
 */
// base64逆处理并用私钥解密
BASE64Decoder decoder = new BASE64Decoder();
enc_msg = new String(decoder.decodeBuffer(enc_msg),"UTF-8");
String dec_msg = RSAUtil.decrypt(enc_msg, privateKey);

但是文档上根本没有写在base64解密之前必须要先解码,这个又浪费了两个小时,要先加上

enc_msg = new String(URLDecoder.decode(enc_msg));

二、接入完登录就要接支付了,这里来第二个坑是自己踩得,这个坑浪费了几次三天时间,面壁思过…就是拉起支付的时候页面上加了JSON.Stringfy(paramStr),死活拉起不了支付,最后在别人的提醒下才解决

这里还有一个要注意的点,前面授权登录解密ccbParamSj 的时候需要使用privateKey私钥解密,到这里传参加密MAC的时候不需要加privateKey,只用 MD5Util.getMD5这个方法就可以了

三、接好支付后配置回调地址需要配置的是第二个反馈地址,并且这个地址有半个小时左右的延迟,配置了不会立刻生效,不知道是建行这样还是所有银行都这样的,不多试几次很难知道

四、支付回调验签的时候又是一个大坑,拉起支付的时候无论是否传了clientip参数,回调通知的时候就是不给你,诶,气不气,关键支付回调验签文档里这个参数又是必须参加验签的,却没地儿拿值
加粗的必须参加验签
然后试了好多次才发现不传才能通过验签,不然就是过不了,真是大无语了

五、正式上线后是从首页banner点进去,建行的运营人员不熟悉业务,配置链接的时候没有勾选授权登录,新用户根本无法获取用户信息,得,又浪费我几个小时调试时间才把问题拋回去

最后放一下公用的代码把,这东西开发人员不能放一套示例代码到材料文档里吗,重复造轮子没有意思,放一下节约时间,要用的直接复制就行了

授权登录

		// 解码
        userCode = new String(URLDecoder.decode(userCode));
        // base64逆处理并用私钥解密
        BASE64Decoder decoder = new BASE64Decoder();
        userCode = new String(decoder.decodeBuffer(userCode),"UTF-8");
        String dec_msg = RSAUtil.decrypt(userCode, privateKey);
        String arr[] = dec_msg.split("&");
        String userId = null;
        String mobile = null;
        String timeStamp = null;
        for(String str :arr){
            if("USERID".equals(str.split("=")[0])){
                userId = str.split("=")[1];
            }
            if("MOBILE".equals(str.split("=")[0])){
                mobile = str.split("=")[1];
            }
            if("TIMESTAMP".equals("")){
                timeStamp = str.split("=")[1];
            }
        }
        logger.info("accessToken:{},获取用户信息返回报文:{}", dec_msg);

拉起支付报文

		String MERCHANTID ="商户代码";         // 商户代码 
        String POSID = "柜台代码";             // 柜台代码
        String BRANCHID = "分行代码";          // 分行代码
        String ORDERID = orderSn;   // 订单号
        String PAYMENT = realPay;    // 付款金额
        String CURCODE = "01";      // 币种 缺省为01-人民币(只支持人民币支付)
        String TXCODE = "520100";   // 交易码
        String REMARK1 = "ccbpay";  // 备注1
        String REMARK2 = serviceNo; // 备注2 上送YS开头的服务方编号
        String TYPE = "1";          // 接口类型 默认送1 - 防钓鱼接口
        String GATEWAY = "0";       // 网关类型 默认送0
        String CLIENTIP = "";       // 客户端IP 客户在商户系统中的IP,送空值即可
        String REGINFO = "";        // 客户注册信息
        String PROINFO = "";        //
        String REFERER = "";        // 商户URL 商户送空值即可
        String THIRDAPPINFO = "comccbpay1234567890cloudmerchant"; // 客户端标识
        String TIMEOUT =cn.hutool.core.date.DateUtil.format(cn.hutool.core.date.DateUtil.offsetHour(new Date(), 1), DatePattern.PURE_DATETIME_PATTERN);     // 订单超时时间 YYYYMMDDHHMMSS
        String PLATFORMPUB = publicKey;             //  服务方公钥 仅作为源串参加MD5摘要,不作为参数传递
        String MAC = "";                            // MD5加密串 采用标准MD5算法,对以上字段进行MAC加密(32位小写)
        String PLATFORMID = serviceNo;              // 服务方编号 仅作为参数传递,不参与MAC校验
        String ENCPUB = "";                         // 商户公钥密文 仅作为参数传递,不参与MAC校验
        StringBuffer macBuffer = new StringBuffer();
        macBuffer.append("MERCHANTID="+MERCHANTID);
        macBuffer.append("&POSID="+POSID);
        macBuffer.append("&BRANCHID="+BRANCHID);
        macBuffer.append("&ORDERID="+ORDERID);
        macBuffer.append("&PAYMENT="+PAYMENT);
        macBuffer.append("&CURCODE="+CURCODE);
        macBuffer.append("&TXCODE="+TXCODE);
        macBuffer.append("&REMARK1="+REMARK1);
        macBuffer.append("&REMARK2="+REMARK2);
        macBuffer.append("&TYPE="+TYPE);
        macBuffer.append("&GATEWAY="+GATEWAY);
        macBuffer.append("&CLIENTIP="+CLIENTIP);
        macBuffer.append("&REGINFO="+REGINFO);
        macBuffer.append("&PROINFO="+PROINFO);
        macBuffer.append("&REFERER="+REFERER);
        macBuffer.append("&THIRDAPPINFO="+THIRDAPPINFO);
        macBuffer.append("&TIMEOUT="+TIMEOUT);
        macBuffer.append("&PLATFORMPUB="+PLATFORMPUB);
        MAC = MD5Util.getMD5(macBuffer.toString()).toLowerCase();
        StringBuffer paramBuffer = new StringBuffer();
        paramBuffer.append("MERCHANTID="+MERCHANTID);
        paramBuffer.append("&POSID="+POSID);
        paramBuffer.append("&BRANCHID="+BRANCHID);
        paramBuffer.append("&ORDERID="+ORDERID);
        paramBuffer.append("&PAYMENT="+PAYMENT);
        paramBuffer.append("&CURCODE="+CURCODE);
        paramBuffer.append("&TXCODE="+TXCODE);
        paramBuffer.append("&REMARK1="+REMARK1);
        paramBuffer.append("&REMARK2="+REMARK2);
        paramBuffer.append("&TYPE="+TYPE);
        paramBuffer.append("&GATEWAY="+GATEWAY);
        paramBuffer.append("&CLIENTIP="+CLIENTIP);
        paramBuffer.append("&REGINFO="+REGINFO);
        paramBuffer.append("&PROINFO="+PROINFO);
        paramBuffer.append("&REFERER="+REFERER);
        paramBuffer.append("&THIRDAPPINFO="+THIRDAPPINFO);
        paramBuffer.append("&TIMEOUT="+TIMEOUT);
        paramBuffer.append("&MAC="+MAC);
        paramBuffer.append("&PLATFORMID="+PLATFORMID);
        String enc_msg = RSAUtil.encrypt(merPublicKeyStr, publicKey);
        BASE64Encoder encoder = new BASE64Encoder();
        enc_msg = encoder.encode(enc_msg.getBytes("UTF-8"));
        enc_msg = enc_msg.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
        ENCPUB = enc_msg;
        paramBuffer.append("&ENCPUB="+ENCPUB);

支付通知回调

@Transactional(rollbackFor = Exception.class)
    public void payCallback(Map<String, String> reqParam, HttpServletResponse resp) throws Exception {
        String SUCCESS = reqParam.get("SUCCESS");
        try {
            if ("Y".equals(SUCCESS)) {
                logger.info("建行生活支付通知-1-[支付]-回调成功");
                // 必填字段
                String POSID = reqParam.get("POSID");
                String BRANCHID = reqParam.get("BRANCHID");
                String ORDERID = reqParam.get("ORDERID");
                String PAYMENT = reqParam.get("PAYMENT");
                String CURCODE = reqParam.get("CURCODE");
                String REMARK1 = reqParam.get("REMARK1");
                String REMARK2 = reqParam.get("REMARK2");
                String ACC_TYPE = reqParam.get("ACC_TYPE");
//            String CLIENTIP = reqParam.get("CLIENTIP");
                String SIGN = reqParam.get("SIGN");

                // 优惠
                String  CCB_DISCOUNT_AMT = reqParam.get("CCB_DISCOUNT_AMT");
                String  CCB_DISCOUNT_AMT_DESC = reqParam.get("CCB_DISCOUNT_AMT_DESC");

                StringBuilder signBuilder = new StringBuilder();
                signBuilder.append("POSID=" + POSID);
                signBuilder.append("&BRANCHID=" + BRANCHID);
                signBuilder.append("&ORDERID=" + ORDERID);
                signBuilder.append("&PAYMENT=" + PAYMENT);
                signBuilder.append("&CURCODE=" + CURCODE);
                signBuilder.append("&REMARK1=" + REMARK1);
                signBuilder.append("&REMARK2=" + REMARK2);
                signBuilder.append("&ACC_TYPE=" + ACC_TYPE);
                signBuilder.append("&SUCCESS=" + SUCCESS);
//                signBuilder.append("&CLIENTIP="+CLIENTIP); // 这个不能加才能验签通过 是个坑
                LsOrder order = lsOrderService.getByOrderSn(ORDERID);
                // 订单实付价格
                BigDecimal realPrice = new BigDecimal(order.getRealPrice());
                BigDecimal divide = realPrice.divide(new BigDecimal("100"));
                DecimalFormat decimalFormat = new DecimalFormat("0.00");
                String realPay = decimalFormat.format(divide.doubleValue());
                RSASig rsaSig = new RSASig();
                rsaSig.setPublicKey(merPublicKey);
                boolean res = rsaSig.verifySigature(SIGN, signBuilder.toString());
                if (res) {
                    logger.info("建行生活支付通知-2-[签名]-校验成功");
                    // 无优惠券
                    if(StringUtils.isBlank(CCB_DISCOUNT_AMT)){
                        if (realPay.equals(PAYMENT)) {
                            logger.info("建行生活支付通知-3-[金额]-校验成功");
                            callBackHandler(ORDERID);
                        } else {
                            logger.error("建行生活支付通知-3-[金额]-校验失败");
                        }
                    }else {
                        // 优惠金额
                        BASE64Decoder decoder = new BASE64Decoder();
                        CCB_DISCOUNT_AMT = RSAUtil.decrypt(new String(decoder.decodeBuffer(CCB_DISCOUNT_AMT), "UTF-8"), privateKey);
                        // 商户获取金额 = 建行实付 + 优惠金额
                        BigDecimal getMoney = new BigDecimal(PAYMENT).add(new BigDecimal(CCB_DISCOUNT_AMT));
                        String getMoneyStr = decimalFormat.format(getMoney.doubleValue());
                        if(realPay.equals(getMoneyStr)){
                            logger.info("建行生活支付通知-3-[金额]-校验成功-[使用优惠券]");
                            callBackHandler(ORDERID);
                        }else{
                            logger.info("建行生活支付通知-3-[金额]-校验失败-[使用优惠券]");
                        }
                    }
                } else {
                    logger.error("建行生活支付通知-2-[签名]-校验失败");
                }
            } else {
                logger.error("建行生活支付通知-1-[支付失败]");
            }
        }catch (Exception e){
            logger.error("建行生活app-[支付通知回调接口异常]:{}", ExceptionUtils.getStackTrace(e));
        }
    }

其他的订单状态更新、退款都差不多,就不赘述了

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值