zoukankan      html  css  js  c++  java
  • 支付宝H5支付---证书模式

    支付宝H5支付---证书模式

    在文章之前,简单吐槽一下支付宝的官网文档,官网文档提供的demo跟例子都是基于普通公钥模式,按照文档来对接支付宝H5开发会一直提示验签错误,但是相比较与微信支付的文档已经友好太多了

    本文档内容如下:

    1.支付宝参数说明
    2.初始化支付客户端
    3.调用支付宝H5支付
    4.支付成功回调验签
    5.根据商户订单号查询是否支付
    6.根据支付宝交易号进行退款

    附上官网的一张流程图,我会标记出本文档的6条内容对应流程图的具体那一步

    avatar

    必须要的配置

    如果你跟我一样,只是个简单的开发,并没有商户支付宝账号,你的领导会给你如下配置,让你去开发支付宝支付功能。有了如下配置,你就可以动工了。如果是从头开发,请阅读官方文档设置签名,生成一对RAS密钥(应用公钥、应用私钥),获得证书,并开通H5支付。

    fuxiao:
      ali:
        pay:
          app-id: 20210011***
          format: json
          charset: UTF-8
          sign-type: RSA2
          app-cert-path: certappCertPublicKey_20210***.crt
          ali-pay-cert-path: certalipayCertPublicKey_RSA2.crt
          ali-pay-root-cert-path: certalipayRootCert.crt
          private_key: MIIEvwIBADANBgk******
          public_key: MIIBIjANBgkqhkiG9w0BAQE******
          server-url: https://openapi.alipay.com/gateway.do
          domain: http://puzpa1xe.xiaomy.net:56794/member/ali/payNotifyWithExamination
          front-notify-url: https://pre.*****.com/qyf/report?orderId=
    
    1.支付宝参数说明
    @Data
    @Component
    @ConfigurationProperties(prefix = "fuxiao.ali.pay")
    public class AliPayBean {
        //APPID 即创建小程序后生成。
        public String appId;
        //开发者私钥,由开发者自己生成。用户消息加签之后,将消息和签名传递给支付宝
        public String privateKey;
        //开发者公钥,由开发者自己生成。支付成功后,对支付回调进行验签
        public String publicKey;
        //应用公钥证书文件---证书模式使用
        public String appCertPath;
        //支付宝公钥证书文件---证书模式使用
        public String aliPayCertPath;
        //支付宝根证书文件---证书模式使用
        public String aliPayRootCertPath;
        //支付宝网关(固定)。
        public String serverUrl;
        /**
         * h5支付成功后,后端回调的地址
         */
        public String domain;
        /**
         * 字符编码格式
         */
        public String charset;
    
        /**
         * 格式
         */
        public String format;
    
        /**
         * 签名方式
         */
        public String signType;
    
        /**
         * h5支付成功后,前段回调地址
         */
        public String frontNotifyUrl;
    
    
    }
    
    2.初始化证书模式支付客户端
    @Configuration
    @Slf4j
    public class AliClient {
    
        @Resource
        private AliPayBean aliPayBean;
    
        @Bean
        public DefaultAlipayClient alipayClient() {
            CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
            //设置appid
            certAlipayRequest.setAppId(aliPayBean.getAppId());
            // 设置网关地址
            certAlipayRequest.setServerUrl(aliPayBean.getServerUrl());
            // 设置字符集
            certAlipayRequest.setCharset(aliPayBean.getCharset());
            // 设置签名类型
            certAlipayRequest.setSignType(aliPayBean.getSignType());
            // 设置请求格式,默认json
            certAlipayRequest.setFormat(aliPayBean.getFormat());
            // 设置应用私钥
            certAlipayRequest.setPrivateKey(aliPayBean.getPrivateKey());
            // 设置应该公钥证书路径 app-cert-path  appCertPublicKey_2021001159659046.crt
            certAlipayRequest.setCertPath(aliPayBean.getAppCertPath());
            // 设置支付宝公钥证书路径 /  ali-pay-cert-path alipayCertPublicKey_RSA2.crt
            certAlipayRequest.setAlipayPublicCertPath(aliPayBean.getAliPayCertPath());
            // 设置支付宝跟证书路径   ali-pay-root-cert-path   alipayRootCert.crt
            certAlipayRequest.setRootCertPath(aliPayBean.getAliPayRootCertPath());
            try {
                return new DefaultAlipayClient(certAlipayRequest);
            } catch (AlipayApiException e) {
                e.printStackTrace();
                log.error("初始化AlipayClient失败," + e);
            }
            return null;
        }
    }
    
    
    3.调用支付宝H5支付,对应流程图的1.1--->1.2--->1.3--->1.4
        @Resource
        private DefaultAlipayClient defaultAlipayClient; 
    /**
         * 支付宝H5支付官网例子
         * 已改造,官网的例子这块用的是公钥模式调用,如果用官网的例子会一直提示验签错误
         * @param response 响应
         * @param amount   金额
         * @param orderNo  商户订单号
         * @param desc     订单描述
         */
        private void aliPayH5WithTest(HttpServletResponse response, BigDecimal amount, String orderNo,
                                      String desc) {
            try {
    
                AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
                // 商户订单号,商户网站订单系统中唯一订单号,必填
                String outTradeNo = new String(orderNo.getBytes(aliPayBean.getCharset()));
    
                // 订单名称,必填T
                String subject = new String(desc.getBytes(aliPayBean.getCharset()));
                // 封装请求支付信息
                AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
                // 商户订单号,商户网站订单系统中唯一订单号,必填
                model.setOutTradeNo(outTradeNo);
                // 订单名称,必填T
                model.setSubject(subject);
                // 付款金额,必填
    
                model.setTotalAmount(amount.toString());
    
                //放弃支付的地址
                //model.setQuitUrl("https://www.baidu.com/");
    
    
                // 超时时间 可空
                //model.setTimeoutExpress(PayConfig.TIMEOUT_EXPRESS);
                // 销售产品码 必填
                model.setProductCode("QUICK_WAP_WAY");
                alipayRequest.setBizModel(model);
                // 设置异步通知地址
                alipayRequest.setNotifyUrl(aliPayBean.getSignNotifyUrl());
                // 设置同步地址
               // alipayRequest.setReturnUrl("http://www.taobao.com/product/113714.html");
                // form表单生产
                String form;
    
                // 调用SDK生成表单
                AlipayTradeWapPayResponse alipayTradeWapPayResponse = defaultAlipayClient.pageExecute(alipayRequest);
                String code = alipayTradeWapPayResponse.getCode();
                log.info("调用状态:{}", code);
                form = alipayTradeWapPayResponse.getBody();
                response.setContentType("text/html;charset=" + aliPayBean.getCharset());
                log.info("完整相应:{}", form);
                //直接将完整的表单html输出到页面
                response.getWriter().write(form);
                response.getWriter().flush();
                response.getWriter().close();
            } catch (AlipayApiException | IOException e) {
                log.error("aliPayWithH5 error", e);
            }
        }
    
    4.支付成功回调验签 对应流程图的1.8--->1.9
        /** 支付成功回调验签
         * 已改造,官网的例子这块用的是公钥模式验签,如果用官网的例子会一直提示验签错误
         * @param request
         * @return
         */
    @RequestMapping(value = "/payNotifyWithExamination", method = {RequestMethod.POST, RequestMethod.GET})
        @ApiOperation("支付宝支付回调")
        public String payCallBack(HttpServletRequest request) {
    
            try {
                // 获取支付宝POST过来反馈信息
                Map<String, String> params = toMap(request);
    
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    log.info("key是:{},value是:{}", entry.getKey(), entry.getValue());
                }
                //notify_url 验证
                boolean verifyResult = AlipaySignature.rsaCertCheckV1(params, aliPayBean.getAliPayCertPath(),
                        aliPayBean.getCharset(), "RSA2");
    
                log.info("验签结果:{}", verifyResult);
                if (verifyResult) {
                    //回调触发类型.交易关闭,交易完结,交易成功
                    if ("TRADE_SUCCESS".equals(params.get("trade_status"))) {
                        //商户交易号
                        String orderNo = params.get("out_trade_no");
                        //支付宝交易号,必须保存,后面退款要用到
                        String tradeNo = params.get("trade_no");
                        //支付金额
                        String amount = params.get("total_amount");
                        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        //支付时间
                        Date dt2 = df.parse(params.get("gmt_payment"));
                        LocalDateTime payTime = DateUtil.toLocalDateTime(dt2);
    					//处理业务逻辑,处理业务逻辑的时候一定要先幂等性校验.因为网络存在波动,支付宝回调会有补偿机制
                      
                    }
    
    
                    return "success";
                } else {
                    log.info("支付宝notify_url验证失败");
                    return "failure";
                }
            } catch (Exception e) {
                e.printStackTrace();
                return "failure";
            }
        }
    
        public static Map<String, String> toMap(HttpServletRequest request) {
            Map<String, String> params = new HashMap<>(24);
            Map<String, String[]> requestParams = request.getParameterMap();
            for (String name : requestParams.keySet()) {
                String[] values = 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);
            }
            return params;
        }
    
    5.根据商户订单号查询是否支付 对应流程图的3.1--->3.2
        @Resource
        private DefaultAlipayClient defaultAlipayClient; 
    	/** 根据商户订单号查询是是否支付
         * @param request
         * @return
    	*/
    	public void tradeQuery(String orderNo){
            try {
    
                AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest();
    
                AlipayTradeQueryModel model = new AlipayTradeQueryModel();
                model.setOutTradeNo(orderNo);
                alipayRequest.setBizModel(model);
    
                AlipayTradeQueryResponse alipayResponse = defaultAlipayClient.certificateExecute(alipayRequest);
                String body = alipayResponse.getBody();
                log.info("查询的结果:{}", body);
                AlipayTradeQueryResponse alipayTradeQueryResponse = JSONUtil.toBean(body, AlipayTradeQueryResponse.class);
                //支付成功不做任何修改
                if ("10000".equals(alipayTradeQueryResponse.getCode())
                        && "Success".equals(alipayTradeQueryResponse.getMsg())) {
                    log.info("调用支付宝查询订单已支付,商户号id:{}", orderNo);
                    return;
                }
    
            } catch (AlipayApiException e) {
                log.error("查询支付宝是否支付失败:{},商户号id:{}", e, orderNo);
            }
        }
    
    6.根据支付宝交易号进行退款 对应流程图的4.1--->4.2--->4.3

    支付宝交易号:支付成功回调之后里面的有个参数的key是tradeNo,需要保存下来 ,这里退款使用

    public void tradeRefund(String tradeNo){
                    AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
    
                AlipayTradeRefundModel model = new AlipayTradeRefundModel();
                model.setTradeNo(limitOne.getTransactionId());
                model.setRefundAmount(limitOne.getAmount().toString());
             //   model.setOutRequestNo("");
                model.setRefundReason("正常退款");
                alipayRequest.setBizModel(model);
    
                try {
                    AlipayTradeRefundResponse alipayResponse = defaultAlipayClient.pageExecute(alipayRequest);
                    log.info("支付宝退款结果查看:{}",alipayResponse.getBody());
                } catch (AlipayApiException e) {
                    e.printStackTrace();
                }
    }
    
  • 相关阅读:
    github release 文件下载贼慢,干脆失败的解决方法
    windows 下sublime text 3 配置python 环境详解
    Ubuntu下安装并使用sublime text 3(建议:先安装Package controls 后在看本教程,否则可能会安装不了)
    将博客搬至CSDN
    signalr core客户端通过ssl连接服务的方式
    解决html导出pdf中文乱码问题的正确姿势
    记一次asp.net core 在iis上运行抛出502.5错误
    Elasticsearch 集群搭建
    bower私服部署
    体验Code::Blocks下的Windows GUI编程(32 bit and 64 bit)
  • 原文地址:https://www.cnblogs.com/shuxiaosheng/p/15029173.html
Copyright © 2011-2022 走看看