支付寶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();
            }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM