• 工行(工银聚富通)-H5埋名支付


    上篇文章讲述了代理商注册商户号,因为接触的这个项目是一个微信小程序商城项目,里面包含用户小程序,门店App,代理商App三个端,每个代理商下有不同的门店,用户通过微信小程序下单,只会下单到具体的门店,所以说代理商有了商户号,用户才能支付成功.下面介绍聚富通H5埋名支付,说白了就是用户下单向工行发起支付,生成支付报文,然后由微信小程序/微信公众号来唤起微信支付,用户付款到代理商的商户号中.

    H5支付

    相关文档在以下链接中,测试代码里面也有给出,只需要配置相关参数就可以进行测试了
    聚富通H5埋名支付接口相关参数介绍
    下面是我的测试demo,其实就是把文档中的代码拿出来,相关参数配置下

    public class PayH5Test {
        protected static final String APP_ID ="APP的编号";
    	protected static final String MY_PRIVATE_KEY = "应用方私钥,由应用方生成公私钥对,并将公钥上传至工行API开放平台";
        protected static final String encryptKey = "应用方加密串,与工行API开放平台一致";
        protected static final String APIGW_PUBLIC_KEY =  "工行API网关公钥,请向工行联系获取";
    
        public static void main(String[] args) {
            DefaultIcbcClient client = new DefaultIcbcClient(APP_ID,"RSA2", MY_PRIVATE_KEY,"UTF-8",
                    "json",APIGW_PUBLIC_KEY,"AES",encryptKey,"","");
    
            JftApiPayAddTracelessOrderForH5RequestV2 request = new JftApiPayAddTracelessOrderForH5RequestV2();
            request.setServiceUrl("https://gw.open.icbc.com.cn/api/jft/api/pay/add/h5/traceless/order/V2");
            JftApiPayAddTracelessOrderForH5RequestV2.JftApiPayAddTracelessOrderForH5V2Biz bizContent = new JftApiPayAddTracelessOrderForH5RequestV2.JftApiPayAddTracelessOrderForH5V2Biz();
            bizContent.setAppId(APP_ID);//平台商户标识:提交支付请求的平台商户编号	10000000000000001
            bizContent.setOutVendorId("003");//子商户标识:提交支付请求的子商户编号
            bizContent.setOutUserId("A003");//用户标识
            bizContent.setNotifyUrl("http://xx.com");//商户通知URL:商户接受支付成功通知消息URL
            bizContent.setOutOrderId("20220610172830_1");//平台对接方ID:平台商户订单ID,确保唯一
            bizContent.setGoodsId("3212");//商品编号(长度单位:字节)
            bizContent.setGoodsName("测试商品");//商品名称(长度单位:字节)
            bizContent.setMac("a3-b4-c5");//手机Mac:支付提交用户手机Mac地址
            bizContent.setTrxIp("83.11.125.138");//交易IP:支付提交用户IP地址
            bizContent.setTrxIpCity("北京");//交易IP所在地:支付提交用户IP所在地
            bizContent.setTrxChannel("03");//交易渠道:固定取03
            bizContent.setPayAmount("1.01");//支付金额(元)整数长度不能超过8位,小数位不能超过2位
            bizContent.setSubMerRateWx("400");//子商户服务费率(微信),商户档案中支付费率类型为区间支付费率,则该字段为必输项
            bizContent.setSubMerRateZfb("400");//子商户服务费率(支付宝),商户档案中支付费率类型为区间支付费率,则该字段为必输项
            bizContent.setPayMode("01");//支付模式:微信小程序: 01,支付宝生活号: 02
            bizContent.setPayType("01");
            bizContent.setTpAppId("");//第三方应用ID;商户在微信公众号内或微信小程序内接入时必送,上送微信分配的公众账号ID;商户通过支付
            bizContent.setTpOpenId("");//第三方用户标识;商户在微信公众号内或微信小程序内接入时必送,上送用户在商户appid下的唯一标识;商户通过支付宝生活号接入时不送。
    //        bizContent.setUnionId("");//第三方用户标识;商户在支付宝生活号接入时必送,上送用户的唯一标识;商户在微信公众号内或微信小程序内选送,上送用户唯一标识。
            bizContent.setOutBusinessCode("10000000000000000001");//外部业务编码
            bizContent.setFixedCommissionWx("0");//定额佣金金额(元)(微信支付),商户档案中支付费率类型为固定费用时,该字段为必输项
            bizContent.setFixedCommissionZfb("0");//定额佣金金额(元)(支付宝支付),商户档案中支付费率类型为固定费用时,该字段为必输项
            bizContent.setAutoConfirm("0");//是否即时解冻 1是 0否 商户为担保模式有效,默认为0
            bizContent.setJDA("1.00");	//优惠金额
            bizContent.setJD("1");		//是否优惠
            bizContent.setJDT("1");		//聚富通优惠类型(1立减  2满减  3优惠券)
            request.setBizContent(bizContent);
            JftApiPayAddTracelessOrderForH5ResponseV2 response;
            try {
                response = client.execute(request, System.currentTimeMillis()+"");//msgId消息通讯唯一编号,要求每次调用独立生成,APP级唯一
                if (response.isSuccess()) {
                    //6、业务成功处理,请根据接口文档用response.getxxx()获取同步返回的业务数据
                    System.out.println("ReturnCode:"+response.getReturnCode());
                    System.out.println("response:" + response);
                    System.out.println(response.getPaySign());
                } else {
                    //失败
                    System.out.println("ReturnCode:"+response.getReturnCode());
                    System.out.println("ReturnMsg:"+response.getReturnMsg());
                }
            } catch (IcbcApiException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    • 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

    从代码中可以看到需要配置的参数,工行Api网关公钥,App编号,应用方私钥,应用方加密串,以及第三方应用id,TPAppId,以及第三方用户标识TpOpenId,其他参数可以根据任务需要进行相关配置,注意在代码中有个NotifyUrl商户通知URL,也就是支付成功通知的异步回调,由于工行对异步回调支持80/443端口,一般在开发或测试环境中测支付回调是无法成功的,只能在线上环境才能进行测试,这个问题会在后面给出解决方案,以上代码还未到支付阶段,此接口只是生成支付报文,就是如果请求返回成功,里面会有paySign这个参数,他是一个字符串,需要将它转换为map形式,可以使用以下代码进行转换

     String paySign = response.getPaySign();
     Map map = JSON.parseObject(paySign, Map.class);
    
    • 1
    • 2

    前端接收这个Map,就会发起微信支付,用户输入密码,支付完成.
    在项目中我是这样使用的,用户下单购买商品后会生成一条订单,然后会调用聚富通这个接口向工行发送请求,在发送请求前,我会根据订单号查询其代理商是否注册了工行商户号,然后调用聚富通的查询商户号接口判断是否注册过了,商户号状态是否正确,审核状态是否审核通过,等以上判断都是true再去调用聚富通支付接口发送请求,对返回的结果进行判断,首先会判断返回的code值是否为10100000,是的话代表请求成功,然后获取返回的paySign参数,将其转换为map返回给前端,前端拿到这个参数唤起支付页面,用户进行支付.业务中的代码如下所示

    **注意点:**在向工行发送支付请求时,不支持同一个订单号请求多次,一次请求一个订单号,这时可以关联系统订单号在随机生成一个不重复的订单号,随机订单号向工行发送请求,请求成功保存随机订单号,请求失败更新随机订单号,这样就能保证系统订单号关联到工行订单号了.

     /**
         * 工行支付
         * @param icbcConfig 工行参数
         * @param wechatConfig 微信参数
         * @param idbcOrderNo 请求工行随机订单号
         * @param orderNo 订单号
         * @param openId 用户标识
         * @return
         */
        private ResponseData ICBCH5(ICBCConfig icbcConfig,WechatConfig wechatConfig,String idbcOrderNo, String orderNo, String openId){
    
            //构建client
            DefaultIcbcClient client = new DefaultIcbcClient(icbcConfig.getAppId(), IcbcConstants.SIGN_TYPE_RSA2,
                    icbcConfig.getMyPrivateKey(), IcbcConstants.CHARSET_UTF8, IcbcConstants.FORMAT_JSON, icbcConfig.getApigwPublicKey(),
                    IcbcConstants.ENCRYPT_TYPE_AES, icbcConfig.getEncryptKey(), "", "");
            //构建查询请求对象
            JftApiVendorInfoQueryRequestV1 request = new JftApiVendorInfoQueryRequestV1();
            request.setServiceUrl(ICBCConfig.QUERY_INFO);
            JftApiVendorInfoQueryRequestV1.JftApiVendorInfoQueryRequestV1Biz queryBizContent = new JftApiVendorInfoQueryRequestV1.JftApiVendorInfoQueryRequestV1Biz();
            queryBizContent.setAppId(icbcConfig.getAppId());
            //查询代理商id
            TdCommodityUserOrder orderCommodity = tdCommodityUserOrderService.findByOrderNo(orderNo);
            log.info("代理商id:agentInfoId = {}", orderCommodity.getAgentId());
            SysAgentIcbcInfo icbcInfo = sysAgentIcbcInfoService.selectByAgentInfoId(orderCommodity.getAgentId());
            if (StringUtils.isEmpty(icbcInfo)) {
                return ResponseData.fail("代理商还没有绑定商户,请绑定商户");
            }
            if (StringUtils.isEmpty(icbcInfo.getOutVendorId())) {
                return ResponseData.fail("客户在合作方的平台自定义编号为空");
            }
            if (StringUtils.isEmpty(icbcInfo.getOutUserId())) {
                return ResponseData.fail("用户外部系统编号为空");
            }
            queryBizContent.setOutVendorId(icbcInfo.getOutVendorId());
            request.setBizContent(queryBizContent);
            log.info("查询工行商户请求参数 appId = {},outVendorId={},serviceUrl={}",icbcConfig.getAppId(),icbcInfo.getOutVendorId(),ICBCConfig.QUERY_INFO);
            try {
                JftApiVendorInfoQueryResponseV1 responseInfo = (JftApiVendorInfoQueryResponseV1) client.execute(request, System.currentTimeMillis() + "");
                System.out.println(responseInfo.isSuccess());
                if (!responseInfo.isSuccess()) {
                    log.info("该商户还没有注册,请先注册 returnCode={},returnMsg={}", responseInfo.getReturnCode(), responseInfo.getReturnMsg());
                    return ResponseData.fail(responseInfo.getReturnCode(), responseInfo.getReturnMsg());
                }
                //判断商户状态1-新建,2-正常,3-冻结,4-销户,5-预销户,6-预销户成功,7-待撤销,8-已撤销,9-作废
                if (!responseInfo.getVendorStatus().equals("2")) {
                    log.info("该商户状态不正确 vendorStatus={}", responseInfo.getVendorStatus());
                    return ResponseData.fail(responseInfo.getReturnCode(), responseInfo.getReturnMsg());
                }
                //审核状态01-新建,02-代送网金部审核,03-网金部审核中,04-网金部审核通过,05-网金部审核拒绝
                if (!responseInfo.getAuditStatus().equals("04")) {
                    log.info("审核状态不正确 auditStatus={}", responseInfo.getAuditStatus());
                    return ResponseData.fail(responseInfo.getReturnCode(), responseInfo.getReturnMsg());
                }
            } catch (IcbcApiException e) {
                e.printStackTrace();
            }
            JftApiPayAddTracelessOrderForH5RequestV2 requestPay = new JftApiPayAddTracelessOrderForH5RequestV2();
            requestPay.setServiceUrl(ICBCConfig.PAYH5_URL);
            JftApiPayAddTracelessOrderForH5RequestV2.JftApiPayAddTracelessOrderForH5V2Biz bizContent = new JftApiPayAddTracelessOrderForH5RequestV2.JftApiPayAddTracelessOrderForH5V2Biz();
            bizContent.setAppId(icbcConfig.getAppId());
            bizContent.setOutVendorId(icbcInfo.getOutVendorId());
            bizContent.setOutUserId(icbcInfo.getOutUserId());
            bizContent.setNotifyUrl(icbcConfig.getIcbcNotifyUrl());
            bizContent.setOutOrderId(idbcOrderNo);
            bizContent.setGoodsName("超微电池");
            bizContent.setTrxIp(getAttributeForIp());
            bizContent.setTrxChannel(IcbcParamEnum.ICBC_PARAM_ENUM.getFiveValue());
            bizContent.setPayAmount(String.valueOf(orderCommodity.getActualAmount().divide(new BigDecimal(100)).setScale(2,BigDecimal.ROUND_HALF_UP)));
            bizContent.setPayMode(IcbcParamEnum.ICBC_PARAM_ENUM.getThreeValue());
            bizContent.setPayType(IcbcParamEnum.ICBC_PARAM_ENUM.getThreeValue());
            bizContent.setTpAppId(wechatConfig.getAppId());
            bizContent.setTpOpenId(openId);
            bizContent.setAutoConfirm(IcbcParamEnum.ICBC_PARAM_ENUM.getFirstValue());
            requestPay.setBizContent(bizContent);
            JftApiPayAddTracelessOrderForH5ResponseV2 response = null;
            try {
                log.info("发起工行支付请求 request={}", JSONObject.toJSONString(requestPay));
                response = client.execute(requestPay,System.currentTimeMillis()+"");
                log.info("请求结束 response={}",response);
                log.info("判断请求结果");
            } catch (IcbcApiException e) {
                e.printStackTrace();
            }
            if (10100000 == response.getReturnCode()){
                log.info("请求结果:code={},msg={},msgId={}",response.getReturnCode(),response.getReturnMsg(),response.getMsgId());
                log.info("请求成功返回信息:response={}",JSONObject.toJSONString(response));
                log.info("paysign={}",response.getPaySign());
                String paySign = response.getPaySign();
                Map map = JSON.parseObject(paySign, Map.class);
                return new ResponseData<>(CodeIdEnum.Success, map);
            }
            log.info("请求失败,返回请求结果");
            log.info("请求结果:code={},msg={},msgId={}",response.getReturnCode(),response.getReturnMsg(),response.getMsgId());
            log.info("请求失败返回信息:response={}",JSONObject.toJSONString(response));
            return new ResponseData<>(CodeIdEnum.FAILED,response);
        }
    
    • 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
    • 94
    • 95
    • 96

    支付回调

    上面说了工行回调只支持线上回调,端口号为80/443,在开发环境/测试环境是无法进行测试的,我的解决方案是在开发环境中主动调工行查询订单接口,查询订单状态,如果订单状态为02时,我就主动修改系统中的订单状态,异步回调还是要写的,异步回调写好升到线上去测试,如果测试没问题,就用异步回调.毕竟支付成功都是通过异步回调来修改订单状态,主动查询的话,相当于支付完成后又向后台,后台又向工行发送一次请求,这样比较耗时,也可能出现其他问题不可控.两种方法也会在下面进行介绍

    主动查询

    聚富通支付订单查询文档

    以下是测试代码


    在这里插入图片描述

    项目中的使用

    在项目中通过订单号去查询代理商id,因为支付能成功,代理商肯定绑定了商户号
    根据代理商查询工行子商户信息
    根据订单号再去查询关联的发起支付的随机订单号
    然后构建工行聚富通请求对象发请求,对返回消息里的订单状态判断是否已支付成功,支付成功就去修改订单状态等相关业务逻辑,并将工行商户单号保存到系统数据库中,以便财务对账等
    相关代码会在下面展示:

     @Log
        @Token
        @ApiOperation("主动查询回调")
        @PostMapping("/activityCallBack")
        public ResponseData activityCallBack(@ApiParam(required = true, name = "orderNo",
                value = "订单编号") @RequestParam("orderNo") String orderNo){
            ConstraintViolation validate = BeanValidator.validate(req);
            if (validate != null) {
                return new ResponseData<>(CodeIdEnum.ParameterMalformed.getValue(), validate.getMessage());
            }
            //根据订单号查询代理商
            TdCommodityUserOrder tdCommodityUserOrder = tdCommodityUserOrderService.findByOrderNo(orderNo);
            //根据代理商查询子商户
            SysAgentIcbcInfo sysAgentIcbcInfo = sysAgentIcbcInfoService.selectByAgentInfoId(tdCommodityUserOrder.getAgentId());
            //根据订单号查询传入工行的订单号
            TdCommodityUserOrderIcbcResp resp = tdCommodityUserOrderIcbcService.selectByOrderNo(orderNo);
            return updateOrderNoStatus(sysAgentIcbcInfo.getOutVendorId(), orderNo,resp);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
     /**
         * 主动调支付修改订单状态
         * @param outVendorId
         * @param orderNo
         * @param resp
         * @return
         */
        private ResponseData updateOrderNoStatus(String outVendorId,String orderNo,TdCommodityUserOrderIcbcResp resp){
            //构建client
            DefaultIcbcClient client = new DefaultIcbcClient(icbcConfig.getAppId(), IcbcConstants.SIGN_TYPE_RSA2,
                    icbcConfig.getMyPrivateKey(), IcbcConstants.CHARSET_UTF8, IcbcConstants.FORMAT_JSON, icbcConfig.getApigwPublicKey(),
                    IcbcConstants.ENCRYPT_TYPE_AES, icbcConfig.getEncryptKey(), "", "");
    
            //构建查询请求对象
            QueryOrderRequestV1 request = new QueryOrderRequestV1();
            request.setServiceUrl(ICBCConfig.QUERY_ORDER_NO);
            QueryOrderRequestV1.QueryOrderRequestV1Biz bizContent = new QueryOrderRequestV1.QueryOrderRequestV1Biz();
            bizContent.setAppId(icbcConfig.getAppId());
            bizContent.setOutVendorId(outVendorId);
            bizContent.setOutOrderId(resp.getIcbcOrderNo());
            request.setBizContent(bizContent);
            QueryOrderResponseV1 response;
            try {
                response = client.execute(request,System.currentTimeMillis()+"");
                log.info("订单查询结果 response={}",JSONObject.toJSONString(response));
                if (("02").equals(response.getOrderStatus())){
                    //修改订单状态相关业务逻辑
                    payOrderFinish(orderNo,response.getIcbcOrderId(),(byte) 2);
                    TdCommodityUserOrderIcbcResp td1 = tdCommodityUserOrderIcbcService.selectByOrderNo(orderNo);
                    TdCommodityUserOrderIcbc td = new TdCommodityUserOrderIcbc();
                    td.setId(td1.getId());
                    td.setIcbcOrderId(response.getIcbcOrderId());
                    td.setjOrderId(response.getjOrderId());
                    td.setPayCompleteDateTime(response.getPayCompleteDate()+" "+response.getPayCompleteTime());
                    tdCommodityUserOrderIcbcService.updateByPrimaryKeySelective(td);
                }
    
            } catch (IcbcApiException e) {
                e.printStackTrace();
            }
            return ResponseData.success();
        }
    
    • 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

    异步回调

    异步通知报文格式{“notifyData”,“signData”},接收变量名为biz_content
    报文内容:Json解析为map,键值对说明如下

    key长度定义说明
    notifyData无限制通知消息,json格式,对其明文串进行了base64编码;
    signData无限制通过API签名验证,此字段可以忽略。

    notifyData数据定义,异步通知报文文档下载地址:异步通知文档

    变量名称变量名称长度说明
    交易代码txCode202:支付
    交易响应码respCodeMAX(10)成功为00000失败为00001
    通知信息respMsgMAX(200)支付平台返回的通知信息
    交易金额trxAmountMAX(22)交易金额
    支付方式payMethod201:工银e支付 02:微信支付 03:支付宝 04:预付卡 05:转账 06:二维码主扫支付 07:POS支付 08:e支付有协议小额免密 09:微信线下支付 10:会员卡小额免密 11:支付宝线下支付 12:二维码被扫支付 13:合作方钱包支付 99:其他
    订单状态orderStatus202:支付成功 03:支付失败
    订单号orderIdMAX(20)订单号
    支付完成日期completeDate8yyyyMMdd
    支付完成时间completeTime8HH:mm:ss

    从上面文档中可以看到我们需要的是notifyData这个数据,上面说明了为json格式的字符串,并进行了base64编码,首先对其参数进行验签操作,验签通过再对其进行base64解密操作,并将其转换为map从中获取需要的数据进行操作,最后再根据resCode和refundStatus判断是否支付成功,成功从中获取订单号执行修改业务系统订单号的操作.

    验签

     /**
         * 验签
         * @param request
         * @param path
         * @return
         * @throws IcbcApiException
         */
        private boolean verifySignData(HttpServletRequest request,String path) throws IcbcApiException {
            Map<String, String> params = new HashMap<String, String>();
            String api = request.getParameter("api");
            String from = request.getParameter("from");
            String app_id = request.getParameter("app_id");
            String charset = request.getParameter("charset");
            String format = request.getParameter("format");
            String encrypt_type = request.getParameter("encrypt_type");
            String timestamp = request.getParameter("timestamp");
            String biz_content = request.getParameter("biz_content");
            String sign_type = request.getParameter("sign_type");
            String sign = request.getParameter("sign");
            params.put("from", from);
            params.put("api", api);
            params.put("app_id", app_id);
            params.put("charset", charset);
            params.put("format", format);
            params.put("encrypt_type", encrypt_type);
            params.put("timestamp", timestamp);
            params.put("biz_content", biz_content);
            // 目前上行网关签名暂时仅支持RSA
            params.put("sign_type", sign_type);
            String signStr = WebUtils.buildOrderedSignStr(path, params);
            log.info("验签入参:signStr={},sign_type={},APIGW_PUBLIC_KEY={},charset={},sign={}",signStr,sign_type,icbcConfig.getApigwPublicKey(),charset,sign);
            boolean verify = IcbcSignature.verify(signStr, sign_type, icbcConfig.getApigwPublicKey(), charset, sign);
            log.info("验签结果:verify={}",verify);
            return verify;
        }
    
    • 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

    解密

     /**
         * 解密
         * @param request
         * @return
         */
        public String dencrypt(HttpServletRequest request) throws JsonProcessingException {
            String biz_content = request.getParameter("biz_content");
            String key = "%2B";
            String password = "+";
            String bizContent = biz_content.replaceAll(key, password);
            log.info("转换上送参数:bizContent={}",bizContent);
            //解析请求数据map
            ObjectMapper mapper = new ObjectMapper();
            Map transMap = mapper.readValue(bizContent, Map.class);
            //获取报文密文信息
            String notifyData = (String) transMap.get("notifyData");
            return new String(Base64.getDecoder().decode(notifyData));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    加签

     /**
         * 加签方法
         * @param request
         * @param copReturnCode
         * @param copReturnMsg
         * @return
         */
        public String sign(HttpServletRequest request,String copReturnCode,String copReturnMsg) throws IcbcApiException {
            String msg_id = new SimpleDateFormat("yyyyMMdd").format(new Date())+new SimpleDateFormat("HHmmss").format(new Date());
            String responseBizContent = "{\"return_code\":\"" + copReturnCode + "\",\"return_msg\":\"" + copReturnMsg + "\""
                    + ",\"msg_id\":\"" + msg_id + "\"}";
            //调用过程中使用的编码格式
            String charset = request.getParameter("charset");
            //商户对消息返回响应进行签名,签名方式需与在API平台登记的sign_type保持一致(RSA2)
            String signType = IcbcConstants.SIGN_TYPE_RSA2;
            String signStr = "\"response_biz_content\":" + responseBizContent + "," + "\"sign_type\":" + "\"" + signType
                    + "\"";
            String signParam = IcbcSignature.sign(signStr, signType, icbcConfig.getMyPrivateKey(), charset);
            String returnJson = "{\"response_biz_content\":" + responseBizContent + ",\"sign_type\":\"" + signType + "\""
                    + ",\"sign\":\"" + signParam + "\"}";
            return returnJson;
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    工行支付回调

    
     @Log
        @ApiOperation("工行支付回调")
        @RequestMapping("/receiveNotify")
        @ResponseBody
        public Object receiveNotify(HttpServletRequest request) throws IcbcApiException, JsonProcessingException {
            log.info("进入回调");
            log.info("回调地址...... url={}",icbcConfig.getIcbcNotifyUrl());
            String returnJson = null;
            boolean verifyFlag  = false;
            try {
                verifyFlag = verifySignData(request, "/http/callback/receiveNotify");
            } catch (IcbcApiException e) {
                log.error("验签出错:",e);
                e.printStackTrace();
            }
            if (verifyFlag) {
                String bizContent = dencrypt(request);
                log.info("解密后参数:" + bizContent);
                ObjectMapper mapper = new ObjectMapper();
                Map transMap = mapper.readValue(bizContent, Map.class);
                log.info("map参数 map={}",transMap);
                TdCommodityUserOrderIcbcResp resp = tdCommodityUserOrderIcbcService.selectByIcbcOrderNo((String) transMap.get("orderId"));
                if ("00000".equals(transMap.get("respCode"))){
                    if ("02".equals(transMap.get("orderStatus"))){
                        payOrderFinish(resp.getOrderNo(), (String) transMap.get("icbcOrderId"),(byte) 2);
                    }else {
                        log.error("订单状态不正确,订单状态为:orderStatus = {}",transMap.get("orderStatus"));
                    }
                }else {
                    log.error("交易响应码失败,交易码为:respCode={}",transMap.get("respCode"));
                }
                String copReturnCode = "0";
                String copReturnMsg = "success";
                returnJson = sign(request, copReturnCode, copReturnMsg);
                return returnJson;
            } else {
                String copReturnCode = "-12345";
                String copReturnMsg = "icbc jft sign not pass.";
                returnJson = sign(request, copReturnCode, copReturnMsg);
            }
            return returnJson;
        }
    
    • 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

    这里的成功和失败返回的代码是固定的,是返回给工行那边的,我们不需要处理,只需要在验签和解密后获取返回的参数对其进行判断,成功执行相关的修改订单的业务逻辑,再去进行判断返回信息,成功/失败设置相关的returnCode和returnMsg返回给工行那边即可.

  • 相关阅读:
    Split to Be Slim: 论文复现
    Python获取MP3文件的ID3标签信息
    网络安全(黑客)自学
    Kindle倒下,iReader接力
    淘天集团联合爱橙科技开源大模型训练框架Megatron-LLaMA
    Qt环境配置VTK
    负载均衡原理
    快速掌握Gulp自动化构建工具
    C#实现观察者模式
    性能测试 —— Tomcat监控与调优:status页监控
  • 原文地址:https://blog.csdn.net/qq_41934990/article/details/126260651