聚合支付PC端-银联扫码支付

聚合支付

第四章 银联扫码支付



前言

本章主要介绍银联扫码支付接口:ChinaPay 新一代商户支付接口接入为 ChinaPay 商户会员提供完善的网上支付解决方案,让商户更快捷、方便和安全的开展网上交易,Chinapay为商户提供了多种支付场景用的支付接口,本文只介绍其中一种-扫码支付。


提示:以下是本篇文章正文内容,下面案例可供参考

一、开发前的准备

首先商户需要申请在 ChinaPay 测试环境开户,这时商户会获得商户号、公钥、私钥、支付工具类以及一些ChinaPay的logo图片和开发技术人员需要的“商户接入手册”开发文档。
在开发文档中我们可找到“扫码支付”的流程图,根据流程图理解整个支付流程的支付实现逻辑。流程图如下:在这里插入图片描述

二、java实现

1.pom中引入jar包

jar包是需要商户申请成为Chinapay的商户后,银联支付平台会提供这样一个工具类的离线jar包(主要是一些http请求方法的封装和签名和验签方法)以及支付实现的demo。

有两种方式使用这个加包:第一种是将离线jar包引入到本地maven仓库,可参考这位大佬的方法https://blog.csdn.net/kkagr/article/details/84502062 不过这种方式容易收到jdk版本的影响,可能jdk7及以下的不能用这种方法。第二种是将jar包解压将里面的工具类提取出来放到本地程序中。

由于本次支付接口是对接的“扫码支付”方式,最重要的是将获取到的二维码做成图片展示给客户进行支付,即流程图中的“根据返回的二维码信息生成二维码并展示给持卡人”,所以需要引入hutool工具将二维码做成图片兵役base64流的方式传至页面前端。

2.支付相关参数

代码如下(示例):

2.1 商户秘钥配置方法:

使用此方法需要将 security.properties 放在类路径下,并配置如下:
#交易证书路径
sign.file=D:/cert_cp/000000000000001.pfx
#交易证书密码
sign.file.password=XXXXXX
#交易证书的密钥容器格式
sign.cert.type=PKCS12
#报文中不参与签名的字段名称,多个字段用逗号进行分隔
sign.invalid.fields=Signature,CertId
#验签证书路径
verify.file=D:/cert_cp/cp_test.cer
#签名值字段名称
signature.field=Signature

2.2支付平台参数

public class chinaPayConstants {
    public static final String VERSION = "20***8";
    public static final String MERCHANT_ID = "8299***01";
    public static final String PAGE_RET_URL = "http://localhost:8080/***pageretback";
    public static final String BG_RET_URL = "http://localhost:8080/***pageretback";
    public static final String PAY_SEND_URL = "https://******";
    public static final String ORDER_QUERY = "https:******";
    public static final String ORDER_DATE_QUERY = "https:*****";
}

3.支付实现

@RequestMapping(value = {"/chinaPay/payUrlChinaPay"})
    public void payUrlChinaPay(HttpServletRequest request, HttpServletResponse response,
                       @RequestParam(value = "totalFee") int totalFee,
                       @RequestParam(value = "outTradeNo") String outTradeNo,
                       @RequestParam(value = "productId") String productId) throws Exception {
        //模拟测试订单信息
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
        SimpleDateFormat timeFormat = new SimpleDateFormat("HHmmss");
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
        Date current = new Date();
        Map<String, String> orderReserveMap = new HashMap<>();
        orderReserveMap.put("OrderType","0001");
        orderReserveMap.put("OrderValidTime",format.format(current));
        orderReserveMap.put("qrPattern","link");
        String orderReserve = JSON.toJSONString(orderReserveMap);

        Map<String, Object> submitFromData = new HashMap();
        submitFromData.put("Version", chinaPayConstants.VERSION);
        submitFromData.put("AccessType","0");
        submitFromData.put("MerId",chinaPayConstants.MERCHANT_ID);
        submitFromData.put("MerOrderNo",outTradeNo);
        submitFromData.put("TranDate",dateFormat.format(new Date()));
        submitFromData.put("TranTime",timeFormat.format(new Date()));
        submitFromData.put("OrderAmt",String.valueOf(totalFee));
        submitFromData.put("TranType","0009");
        submitFromData.put("BusiType","0001");
        submitFromData.put("CurryNo","CNY");

        submitFromData.put("MerPageUrl",chinaPayConstants.PAGE_RET_URL);
        submitFromData.put("MerBgUrl",chinaPayConstants.BG_RET_URL);
        submitFromData.put("MerResv",productId);
        submitFromData.put("PayTimeOut","30");
        submitFromData.put("OrderReserved",orderReserve);
        submitFromData.put("CommodityMsg", "测试支付");//商品名称
        //String MerKeyFile = ResourceUtils.getFile("classpath:security.properties").getParentFile().getAbsolutePath();
        String MerKeyFile = NetpayChinaPayController.class.getClassLoader().getResource("/").getPath();
        SecssUtil secssUtil = new SecssUtil();
        secssUtil.init(MerKeyFile);
        System.out.println("weee"+secssUtil.getErrCode()+secssUtil.getErrMsg());
        secssUtil.sign(submitFromData);
        String signature = secssUtil.getSign();
        submitFromData.put("Signature", signature);
        String params = CommonUtil.getURLParam(submitFromData, true);
        HttpResponse httpRes =  HttpHelper.doHttp(chinaPayConstants.PAY_SEND_URL, "POST", "UTF-8",params, "60000");
        //获取二维码链接
        String codeUrl = "";
        if ((httpRes == null) || (httpRes.getResponseCode() != 200)){
            request.setAttribute("msg", "调用接口失败,响应码[" + Objects.requireNonNull(httpRes).getResponseCode() + "]");
        }else {
            String result = httpRes.getBody();
            Map resultMap = CommonUtil.strToMap(result);
            String respCode = (String)resultMap.get("respCode");
            if ("0000".equals(respCode)){
                secssUtil.verify(resultMap);
                if("00".equals(secssUtil.getErrCode())){
                    String payReserved = (String)resultMap.get("PayReserved");
                    Map payReservedMap = JSON.parseObject(payReserved, Map.class);
                    codeUrl = (String)payReservedMap.get("QrCode");
                    codeUrl = URLDecoder.decode(codeUrl,"UTF-8");
                }
            }
        }
        if (!StringUtils.isNotBlank(codeUrl)) {
            System.out.println("----生成二维码失败----");
        } else {
            //根据链接生成二维码
           BufferedImage bufferedImage = QrCodeUtil.generate(qrCode, 300, 300);
           ByteArrayOutputStream outStream = new ByteArrayOutputStream();
           try {
               ImageIO.write(bufferedImage, "png", outStream);
               byte[] bytes = outStream.toByteArray();
               imageBase64 = cn.hutool.core.codec.Base64.encode(bytes);
              } catch (IOException e) {
                e.printStackTrace();
             }
          request.setAttribute("imageBase64","data:image/png;base64,"+imageBase64);
        }
    }

4.查询实现

4.1二维码订单支付日期查询接口
因为订单查询接口需要上传交易日期,对于扫码付的商户,交易日期仅在消费类交易接口应答报文中返回,考虑到各种因素可能导致商户接收不到应答报文,估提供二维码订单支付日期查询接口,供商户查询使用。

@RequestMapping(value = {"/chinaPay/orderQueryChinaPay"})
    public String orderQueryChinaPay(@RequestParam(value = "outTradeNo") String outTradeNo) throws Exception {
        JSONObject responseObject = new JSONObject();
        Order order = WxConfig.getOrderMap("order");
        Date sendDate = order.getSendDate();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
        SimpleDateFormat timeFormat = new SimpleDateFormat("HHmmss");
        Map<String, Object> submitFromData = new HashMap();
        submitFromData.put("Version",chinaPayConstants.VERSION);
        submitFromData.put("AccessType","0");
        submitFromData.put("MerId",chinaPayConstants.MERCHANT_ID);
        submitFromData.put("MerOrderNo",order.getOrderNo());
        submitFromData.put("TranDate",dateFormat.format(sendDate));
        submitFromData.put("TranTime",timeFormat.format(sendDate));
        //String MerKeyFile = ResourceUtils.getFile("classpath:security.properties").getParentFile().getAbsolutePath();
        String MerKeyFile = NetpayChinaPayController.class.getClassLoader().getResource("/").getPath();
        SecssUtil secssUtil = new SecssUtil();
        secssUtil.init(MerKeyFile);
        secssUtil.sign(submitFromData);
        String signature = secssUtil.getSign();
        submitFromData.put("Signature",signature);
        String paramsDate = CommonUtil.getURLParam(submitFromData, true);
        HttpResponse httpRes =  HttpHelper.doHttp(chinaPayConstants.ORDER_DATE_QUERY, "POST", "UTF-8",paramsDate, "60000");
        if ((httpRes == null) || (httpRes.getResponseCode() != 200)){
            responseObject.put("msg", "调用二维码订单交易日期查询接口失败,响应码[" + httpRes.getResponseCode() + "]");
        }else {
            String result = httpRes.getBody();
            Map payDateMap = CommonUtil.strToMap(result);
            String respCode = (String)payDateMap.get("respCode");
            if ("0000".equals(respCode)){
                secssUtil.verify(payDateMap);
                if("00".equals(secssUtil.getErrCode())){
                    String payDate = (String)payDateMap.get("PayDate");
                }
            }
        }
        return responseObject.toString();
    }

4.2交易查询接口

@RequestMapping(value = {"/chinaPay/orderQueryChinaPay"})
    public String orderQueryChinaPay(@RequestParam(value = "payDate") String payDate) throws Exception {
        JSONObject responseObject = new JSONObject();
        Order order = WxConfig.getOrderMap("order");
        Date sendDate = order.getSendDate();
        Map<String, Object> queryFromData = new HashMap();
        queryFromData.put("Version",chinaPayConstants.VERSION);
        queryFromData.put("AccessType","0");
        queryFromData.put("MerId",chinaPayConstants.MERCHANT_ID);
        queryFromData.put("MerOrderNo",order.getOrderNo());
        queryFromData.put("TranDate",payDate);
        queryFromData.put("TranType","0502");
        queryFromData.put("BusiType","0001");
        //String MerKeyFile = ResourceUtils.getFile("classpath:security.properties").getParentFile().getAbsolutePath();
        String MerKeyFile = NetpayChinaPayController.class.getClassLoader().getResource("/").getPath();
        SecssUtil secssUtil = new SecssUtil();
        secssUtil.init(MerKeyFile);
        secssUtil.sign(queryFromData);
        queryFromData.put("Signature",secssUtil.getSign());
        String paramsQuery = CommonUtil.getURLParam(queryFromData, true);
        HttpResponse httpResQuery =  HttpHelper.doHttp(chinaPayConstants.ORDER_QUERY, "POST", "UTF-8",paramsQuery, "60000");
        if ((httpResQuery == null) || (httpResQuery.getResponseCode() != 200)){
            responseObject.put("msg", "调用交易查询接口失败,响应码[" + httpResQuery.getResponseCode() + "]");
        }else {
            String resultQuery = httpResQuery.getBody();
            Map queryDataMap = CommonUtil.strToMap(resultQuery);
            String queryRespCode = (String)queryDataMap.get("respCode");
            if ("0000".equals(queryRespCode)){
                secssUtil.verify(queryDataMap);
                if("00".equals(secssUtil.getErrCode())){
                    String orderStatus = (String)queryDataMap.get("OrderStatus");
                    if ("0000".equals(orderStatus)){
                        responseObject.put("status","1");
                        System.out.println("支付成功");
                    }else {
                        responseObject.put("status","0");
                    }
                }
            }
        }
        return responseObject.toString();
    }

5.支付成功回调

当 ChinaPay 交易平台处理完成时,ChinaPay 会将订单信息发送给商户,对于商户后台地址的异步通知,ChinaPay 通知商户交易结果后,如果收到商户服务器返回的 http 200 应答码,就认为是通知商户成功,否则当天会再次通知,当天再次通知最多 10 次。

@RequestMapping(value = {"/chinaPay/unifiedorderNotify"})
    public void unifiedorderNotifyChinaPay(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, String> params = new HashMap<String, String>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }
        //String MerKeyFile = ResourceUtils.getFile("classpath:security.properties").getParentFile().getAbsolutePath();
        String MerKeyFile = NetpayRetController.class.getClassLoader().getResource("/").getPath();
        SecssUtil secssUtil = new SecssUtil();
        secssUtil.init(MerKeyFile);
        secssUtil.verify(params);
        if("00".equals(secssUtil.getErrCode())){
            String orderStatus = params.get("OrderStatus");
            if ("0000".equals(orderStatus)){
                System.out.println("支付成功");
                //回复支付公司
                try {
                    response.getWriter().print("http 200");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }else {
                System.out.println("支付失败");
            }
        }

    }

6、工具类

/**
     * 获取最终路径
     */
    private static String getFinalPath(String rootPath,String path){
        rootPath = rootPath.replace('/', '\\');
        rootPath = rootPath.replace("file:", "");
        rootPath = rootPath.replace("classes\\", "");
        rootPath = rootPath.substring(1);
        rootPath = rootPath+path;
        rootPath = rootPath.replace('\\', '/');
        rootPath = "/"+rootPath;
        return rootPath;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值