支付宝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: \cert\appCertPublicKey_20210***.crt
      ali-pay-cert-path: \cert\alipayCertPublicKey_RSA2.crt
      ali-pay-root-cert-path: \cert\alipayRootCert.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();
            }
}

posted on 2021-07-19 10:49  疏小胜  阅读(1054)  评论(0编辑  收藏  举报

导航