首页 > 编程语言 >Java对接工商银行聚合支付(无界面)(微信小程序支付&回调验签&退款)

Java对接工商银行聚合支付(无界面)(微信小程序支付&回调验签&退款)

时间:2022-12-08 11:57:14浏览次数:62  
标签:Java String 微信 bizContent request IcbcConfig 支付 response

写在前面

最近两天整合对接了工商银行的聚合支付通道,目前已上线运行。踩了一些坑,以此记录供网友分享。

此文对接产品的主要是【线上POS聚合消费下单接口(无界面)】

准备工作

1.开发指南、SDK、API官方文档    https://open.icbc.com.cn/icbc/apip/docs_index.html

2.向工行客服申请并获得 APPID、商户编号、公私钥、应用网关、协议编号等等

3.了解RSA公私钥签名的基本概念,建议阅读-> 点击这里

4.下载SDK,https://open.icbc.com.cn/icbc/apip/docs_sdk&demo.html

开干

1、必须要准备的参数

public class IcbcConfig {

    /** 聚合支付B2C线上消费下单接口url (无界面) */
    public final static String API_PAY_URL = "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/consumepurchase/V1";
/** 退款url */
public final static String REFUND_URL = "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/merrefund/V1";
  /** 我方私钥 */ public final static String MY_PRIVATE_KEY = "MIIEvgIBA......"; /** 我方公钥 (可以不用) */ public final static String MY_PUBLIC_KEY = ""; /** 对方(即工行)网关公钥 */ public final static String APIGW_PUBLIC_KEY = "MIGfMA0GCSqGS......"; /** APP的编号,应用在API开放平台注册时生成 eg:10000000000004095781 */ public final static String APP_ID = "10000000000004095781"; /** 商户编号 */ public final static String mer_id = "4402......."; /** 收单产品协议编号 */ public final static String mer_prtcl_no = "44021......";
/** 设备号 (自定义不超过文档规定长度就行) */ public final static String decive_info = "1124........"; }

 2.在工程中引入SDK

把需要的jar包导入本地仓库,并在pom.xml引入

mvn install:install-file -DgroupId=com.xxx -DartifactId=xxx-xxx -Dversion=1.x.x -Dpackaging=jar -Dfile=D:\xxx.jar

需要的jar包有三个(后面签名、验签等操作需要用到)

3.请求下单接口

public static void callPay() {
        try {
            //注意:这里一般使用的是RSA2签名方式,文档里面默认是RSA
            DefaultIcbcClient client = new DefaultIcbcClient(
                    IcbcConfig.APP_ID, IcbcConstants.SIGN_TYPE_RSA2, IcbcConfig.MY_PRIVATE_KEY, IcbcConfig.APIGW_PUBLIC_KEY);
            CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1 request =
                    new CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1();

            request.setServiceUrl(IcbcConfig.API_PAY_URL);
            CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1Biz bizContent = new
                    CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1Biz();
            request.setBizContent(bizContent);
            bizContent.setMer_id(IcbcConfig.mer_id);
            bizContent.setMer_prtcl_no(IcbcConfig.mer_prtcl_no);
            bizContent.setDecive_info(IcbcConfig.decive_info);
            bizContent.setOut_trade_no("test123");
            //交易日期时间,需要格式化为yyyyMM-dd'T'HH:mm:ss
            bizContent.setOrig_date_time("2019‐07‐09T12:11:03");
            //交易币种,目前工行只支持使用人民币(001)支付
            bizContent.setFee_type("001");
            bizContent.setSpbill_create_ip("122.12.12.12");
            //金额,单位分
            bizContent.setTotal_fee("100");
            //支付成功后回调url
            bizContent.setMer_url("http://www.test.com/testNotify123");
            bizContent.setBody("测试支付");
            //收单接入方式,5-APP,7-微信公众号,8-支付宝生活号,9-微信小程序
            bizContent.setAccess_type("9");
            //支付方式,9-微信;10-支付宝;
            bizContent.setPay_mode("9");
            //商户在微信开放平台注册的APPID,支付方式为微信时不能为空
            bizContent.setShop_appid("wx8888888888888888");
//            //第三方用户标识,商户在支付宝生活号接入时必送,即access_type为8时,上送用户的唯一标识;商户通过微信公众号内或微信小程序接入时不送
//            bizContent.setUnion_id("");
            //第三方用户标识,商户在微信公众号内或微信小程序内接入时必送,即access_type为7或9时,上送用户在商户APPID下的唯一标识;商户通过支付宝生活号接入时不送
            bizContent.setOpen_id("");
            bizContent.setIcbc_appid(IcbcConfig.APP_ID);
//            bizContent.setMer_acct("6212880200000038618");
            bizContent.setExpire_time("120");
            //附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
            bizContent.setAttach("");
            //通知类型,表示在交易处理完成后把交易结果通知商户的处理模式。取值“HS”:在交易完成后将通知信息,主动发送给商户,发送地址为notify_url指定地址; 取值“AG”:在交易完成后不通知商户。不送或送空,默认为"HS"
            bizContent.setNotify_type("HS");
            //结果发送类型,通知方式为HS时有效。取值“0”:无论支付成功或者失败,银行都向商户发送交易通知信息;取值“1”,银行只向商户发送交易成功的通知信息。默认是"0"
            bizContent.setResult_type("0");
            //支付方式限定,上送”no_credit“表示不支持信用卡支付;上送“no_balance”表示仅支持银行卡支付;不上送或上送空表示无限制
            bizContent.setPay_limit("");
            //订单附加信息
            bizContent.setOrder_apd_inf("");
            CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV1 response;
            try {
                response = client.execute(request);
                if (response.getReturnCode() == 0) {
                    // 6、业务成功处理,请根据接口文档用response.getxxx()获取同步返回的业务数据
                    System.out.println("ReturnCode:"+response.getReturnCode());
                    System.out.println("response:" + JSON.toJSONString(response));
                    //response.getWx_data_package()   就是微信小程序调起支付所需要的参数
                } else {
                    // 失败
                    System.out.println("response:" + JSON.toJSONString(response));
                    System.out.println("ReturnCode:"+response.getReturnCode());
                    System.out.println("ReturnMsg:"+response.getReturnMsg());
                }
            } catch (IcbcApiException e) {
                log.error(e.toString(), e);
            }
        } catch (Exception e) {
            log.error(e.toString(), e);
        }
    }

4.支付回调及验签

private static Logger log = Log.get();

    @PostMapping("/icbcWxNotify")
    @ResponseBody
    public void icbcWxNotify(HttpServletRequest request, HttpServletResponse response){
        PrintWriter out = null;
        try {
            //签名是否验证成功
            boolean signResult = false;
            //响应参数
            JSONObject bizContent = JSON.parseObject(request.getParameter("biz_content"));
            //我们的订单编号
            String orderCode = bizContent.getString("out_trade_no");
            //订单金额(单位分)
            String totalFee = bizContent.getString("total_amt");
            //第三方订单流水号
            String transactionId = bizContent.getString("third_trade_no");
            //消息号
            String msgId = bizContent.getString("msg_id");
            // 1.验证签名
            // 注意:当notify_url=http://122.20.29.133:8080/testNotify123时,notifyUrl=testNotify123
            String notifyUrl = "testNotify123";
            if (checkSign(request, notifyUrl)) {
                signResult = true;
                log.info("订单{}【工商银行】微信回调签名认证成功", orderCode);
                // 2.执行我们的业务逻辑处理
                //executeLogic(orderCode, totalFee, transactionId);
            } else {
                log.error("订单{}【工商银行】微信支付回调签名认值失败!", orderCode);
            }
            // 3.应答工行:在接收到工行的支付结果通知后,一定要返回应答,否则工行会认为该通知失败,在一定时间区间内多次发起通知
            String results = notifyStr(signResult, msgId);
            response.setContentType("application/json; charset=utf-8");
            out = response.getWriter();
            out.write(results);
        } catch (Exception e) {
            log.error("【工商银行】微信支付回调处理失败,请检查原因!!!,{}", e.getMessage());
        } finally {
            out.flush();
            out.close();
        }
    }

    /**
     * 验证签名
     * @param request 回调请求
     * @param path 我方回调url
     * @return true:验证成功 false:验证失败
     */
    public static boolean checkSign(HttpServletRequest request, String path) {
        Map<String, String> params = Maps.newHashMap();
        String from = request.getParameter("from");
        String api = request.getParameter("api");
        String app_id = request.getParameter("app_id");
        String charset = request.getParameter("charset");
        String format = request.getParameter("format");
        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("timestamp", timestamp);
        params.put("biz_content", biz_content);
        //目前上行网关签名暂时仅支持RSA
        params.put("sign_type", sign_type);

        String signStr= WebUtils.buildOrderedSignStr(path, params);
        try {
            //注意:此次用的是RSA,和下单时不一样
            return IcbcSignature.verify(signStr, IcbcConstants.SIGN_TYPE_RSA, IcbcConfig.APIGW_PUBLIC_KEY, charset, sign);
        } catch (IcbcApiException e) {
            return false;
        }
    }

    /**
     * 支付回调应答字符串
     * @param checkResult 回调后签名是否校验成功
     * @param msgId 消息号
     * @return str
     */
    public static String notifyStr(boolean checkResult, String msgId) {
        // return_code为数字,成功时为0
        String responseBizContent;
        if (checkResult) {
            responseBizContent = "{\"return_code\":0,\"return_msg\":\"success\",\"msg_id\":\"" + msgId + "\"}";
        } else {
            responseBizContent = "{\"return_code\":-12345,\"return_msg\":\"icbc sign not pass.\"}";
        }
        // sign_type-商户在工行登记app的签名类型保持一致,一般为RSA2
        // 返回字符串顺序不能变,为response_biz_content、sign_type、sign,中间不含空格换行符;
        String signStr = "\"response_biz_content\":" + responseBizContent + "," + "\"sign_type\":\"RSA2\"";
        String sign = null;
        try {
            sign = IcbcSignature.sign(signStr, IcbcConstants.SIGN_TYPE_RSA2, IcbcConfig.MY_PRIVATE_KEY,"UTF-8","");
        } catch (IcbcApiException e) {
            log.error("【工商银行】微信回调-应答签名失败", e);
        }
        return  "{" + signStr + ",\"sign\":\"" + sign + "\"}";
    }

 5.退款

    /**
     * 退款
     * @param orderCode 订单编号
     * @param refundCode 退款编号
     * @param refundMoney 部分退款金额
     * @return true:退款成功 false:退款失败
     */
    public static boolean refund(String orderCode, String refundCode, BigDecimal refundMoney) {
        DefaultIcbcClient client = new DefaultIcbcClient(
                IcbcConfig.APP_ID, IcbcConstants.SIGN_TYPE_RSA2, IcbcConfig.MY_PRIVATE_KEY, IcbcConfig.APIGW_PUBLIC_KEY);
        CardbusinessAggregatepayB2cOnlineMerrefundRequestV1 request =
                new CardbusinessAggregatepayB2cOnlineMerrefundRequestV1();
        //根据测试环境和生产环境替换相应ip和端口
        request.setServiceUrl(IcbcConfig.REFUND_URL);
        //请对照接口文档用bizContent.setxxx()方法对业务上送数据进行赋值
        CardbusinessAggregatepayB2cOnlineMerrefundRequestV1.CardbusinessAggregatepayB2cOnlineMerrefundRequestV1Biz bizContent = new
                CardbusinessAggregatepayB2cOnlineMerrefundRequestV1.CardbusinessAggregatepayB2cOnlineMerrefundRequestV1Biz();
        request.setBizContent(bizContent);
        //工行订单号,工行订单号,商户订单号或行内订单号必须其中一个不为空
        bizContent.setOrder_id("");
        //商户编号‐必输项
        bizContent.setMer_id(IcbcConfig.mer_id);
        //商户订单号,工行订单号,商户订单号或行内订单号必须其中一个不为空
        bizContent.setOut_trade_no(orderCode);
        //退货流水号,商户系统生成的退款编号,每次部分退款需生成不同的退款编号
        bizContent.setOuttrx_serial_no(refundCode);
        //退货总金额‐必输项
        BigDecimal realRefundMoney = MathUtil.multiply(refundMoney, 100, 0);
        bizContent.setRet_total_amt(realRefundMoney.toString());
        //交易币种‐必输项
        bizContent.setTrnsc_ccy("001");
        bizContent.setOrder_apd_inf("");
        bizContent.setIcbc_appid(IcbcConfig.APP_ID);
        bizContent.setMer_acct("");
        CardbusinessAggregatepayB2cOnlineMerrefundResponseV1 response;
        try {
            response = client.execute(request);
            if (response.isSuccess()) {
                // 业务成功处理,请根据接口文档用response.getxxx()获取同步返回的业务数据
//                System.out.println("ReturnCode:"+response.getReturnCode());
//                System.out.println("response:" + response);
                return true;
            } else {
                // 失败
//                System.out.println("ReturnCode:"+response.getReturnCode());
//                System.out.println("ReturnMsg:"+response.getReturnMsg());
            }
        } catch (IcbcApiException e) {
            log.error(e.toString(), e);
        }
        return false;
    }

总结

1.工行的支付通道其实就是他们自己封装了一下支付参数,原理上还是调用微信的接口

2.支付的订单流水记录不在我们自己的微信商户后台,在工商银行那边,查询记录要在他们的系统上面查询,比较麻烦,最好在自己的系统上区分一下工商银行通道的支付和官方微信支付

3.最好使用他们的SDK来进行签名和验签等操作,避免不必要的麻烦

4.如果使用APP-微信支付,流程是APP调起自己的微信小程序,再使用微信小程序唤起收银台,并且微信小程序调起支付必须要有openid,还多了一个中转的过程,需要权限利弊

标签:Java,String,微信,bizContent,request,IcbcConfig,支付,response
From: https://www.cnblogs.com/xhq1024/p/16963655.html

相关文章

  • java-net-php-python-sceatch在线学习系统2019演示录像计算机毕业设计程序
    OverridetheentrypointofanimageIntroducedinGitLabandGitLabRunner9.4.Readmoreaboutthe extendedconfigurationoptions.Beforeexplainingtheav......
  • 四、程咬金——JavaScript基础
     一、前言Ajax,异步JavaScript和XML,前面已经解释过,所以Ajax的学习还包含JavaScript和XML,这里我们先看JavaScript,而JavaScript实际上又是包含JavaScript语法和HTMLDOM即文档......
  • 《悟透javascript》学习笔记
    《悟透javascript》学习笔记 一、         前言 二、         回归简单、基本类型 三、         表演、似类却不是类 四、      ......
  • 《悟透javascript》学习笔记:X、深入继承
    引言      JavaScript不是按面向对象的思想设计的程序语言,所以它不具备像现有的面向对象的语言那样的功能,但是面向对象的思想是如此的深入人心,以至于JavaScript也削......
  • 再读《悟透javascript》之五、五子棋
    前言     五子棋是个很有趣的游戏,在用javascript开发之后,我发现其实ai算法才是最难的,这里的ai算法是直接借鉴自其它的ai算法。  代码如下:<htmlxmlns="http://www.w3......
  • java爬虫笔记:使用WebCollector增量采集www.baiduyunsousou.com
    WebCollector可以配置短点爬取,历史数据根据Key去重,也就是url 最近在采集百度云网盘,记录一下 /***@authorLiu*@create2022-08-0211:48*/@Component@Slf......
  • java for执行顺序
     for(intj=0;j<size;j++){//执行todo} 第一步:j=0;j<size;第二步: todo第三步: j++ ;j<size;第四步:todo 第五步:j++ ;j<size;第六步:todo每一步......
  • java-net-php-python-s2s酒店管理系统计算机毕业设计程序
    OverridetheentrypointofanimageIntroducedinGitLabandGitLabRunner9.4.Readmoreaboutthe extendedconfigurationoptions.Beforeexplainingtheav......
  • 《悟透javascript》学习笔记:四、函数的魔力
    引言 JavaScript的代码就只有function一种形式,function就是函数的类型。也许其他编程语言还有procedure或method等代码概念,但在JavaScript里只有function一种形......
  • 10、java反射机制
    1、 什么是反射机制正常情况下,我们通过类来创建对象。如果反过来,我们要通过一个对象来找到对应的类,怎么办,这时就要用到反射机制。  2、 java.lang.Class类a)    ......