支付寶支付-當面付之掃碼支付「掃碼支付」


前言

支付寶支付—沙箱環境使用
支付寶支付-支付寶PC端掃碼支付
支付寶支付-手機瀏覽器H5支付
支付寶支付-當面付之掃碼支付本文

當面付包含兩種支付方式:商品條形碼支付 + 掃碼支付

經過前面兩篇PC端掃碼支付手機H5支付,我們可以看到一個共同的特點就是接口返回的都是一個Form表單,然后交給前端提交執行,然后調起支付。其中PC端提交Form表單后跳轉至新的窗口進行支付,而手機端H5支付則是喚起支付寶APP支付。

但是現在有這么個場景,我不希望在PC端支付時跳轉支付寶網頁支付,而是直接將二維碼嵌入到系統當中,用戶直觀看到的就是一個彈窗二維碼,然后用戶拿支付寶掃碼支付…這就是本文的當面付之掃碼支付了。

廢話不多說,直接進入主題。

Java支付項目實戰教程,包括支付寶,微信等支付方式,不看虧!

本文開發環境:IDEA + Tomcat8.5 + 支付寶沙箱環境 + SpringBoot

補充:調用沙箱環境接口,需要安裝沙箱環境下的支付寶APP,不了解的小伙伴可以參考上方 支付寶支付—沙箱環境使用

一、pom引入依賴 + 參數准備

引入支付寶支付依賴文件,本文基於4.9.153.ALL版本,及供參考, 最新版本可去官方文檔查閱。

<!--alipay-->
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.9.153.ALL</version>
</dependency>

關於支付寶公鑰、私鑰、回調地址啥的就不再重復了,不清楚的去看支付寶支付—沙箱環境使用

配置可以單獨創建一個類,靜態初始化參數::AlipayConfig.java

public class AlipayConfig {
    /** 商戶appid **/
    public static String APPID = "201610170070";
    /** 私鑰 pkcs8格式的 **/
    public static String RSA_PRIVATE_KEY = "";
    /** 服務器異步通知頁面路徑 需http://或者https://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常訪問 **/
    public static String notify_url = "http://ngrok.sscai.club/alipay_trade_wap_pay_java_utf_8_war_exploded/notify_url.jsp";
    /** 頁面跳轉同步通知頁面路徑 需http://或者https://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常訪問 商戶可以自定義同步跳轉地址 **/
    public static String return_url = "http://ngrok.sscai.club/alipay_trade_wap_pay_java_utf_8_war_exploded/return_url.jsp";
    /** 請求網關地址 **/
    public static String URL = "https://openapi.alipaydev.com/gateway.do";
    /** 編碼 **/
    public static String CHARSET = "UTF-8";
    /** 返回格式 **/
    public static String FORMAT = "json";
    /** 支付寶公鑰 **/
    public static String ALIPAY_PUBLIC_KEY = "";
    /** 日志記錄目錄 **/
    public static String log_path = "/log";
    /** RSA2 **/
    public static String SIGNTYPE = "RSA2";
    /** 商戶門店編號「需要申請當面付」 **/
    public static String STORE_ID = "122211242";
}

幾個主要的參數:

  1. APPID :商戶appid
  2. RSA_PRIVATE_KEY:應用私鑰
  3. ALIPAY_PUBLIC_KEY:支付寶公鑰「注意不是應用公鑰」

這幾個參數不清楚的,可以看一下 沙箱環境使用,或者看一下官方文檔參數說明

二、后端代碼

還是之前的代碼,一個controller接口方法,一個具體的實現方法「沒有使用開源SDK」:

Controller

@ApiOperation(value = "獲取支付寶支付二維碼")
public Result<AlipayResponse> getAliPayQrCode(@CurrentUser UserModel user, @RequestBody AlipayOrderRequest alipayOrderRequest ) {
    /** 返回給前端的二維碼內容 **/
    String sHtmlText = null;
    try {
        /** 判斷是否已經存在訂單,根據自己業務的不同自行判斷**/
        if(!StringUtils.isNotBlank(alipayOrderRequest.getWidOutTradeNo())){
            Orders orders=ordersService.createOrder(user,alipayOrderRequest.getWidTotalFee(),alipayOrderRequest.getAppType());
            alipayOrderRequest.setWidOutTradeNo(orders.getOrderId());
        }else{
            OrderResponse orders=ordersService.searchOrderDetail(user,alipayOrderRequest.getWidOutTradeNo());
            alipayOrderRequest.setWidTotalFee(orders.getAmount().toString());
        }
        /** 這是主要的方法 **/
        sHtmlText = alipayService.alipayOrder(alipayOrderRequest);
    } catch (AlipayApiException e) {
        e.printStackTrace();
    }
    AlipayResponse alipayResponse = new AlipayResponse();
    alipayResponse.setSHtmlText(sHtmlText);
    alipayResponse.setOrderId(alipayOrderRequest.getWidOutTradeNo());
    return ResultUtil.success(alipayResponse);
}

ServerImpl

@Transactional
public String alipayOrder(AlipayOrderRequest alipayOrderRequest) throws AlipayApiException {

    /**獲得初始化的AlipayClient**/
    AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl,
            AlipayConfig.app_id,
            AlipayConfig.merchant_private_key,
            "json",
            AlipayConfig.charset,
            AlipayConfig.alipay_public_key,
            AlipayConfig.sign_type);

    /**當面付之「掃碼支付」**/
    AlipayTradePrecreateRequest alipayTradePrecreateRequest = new AlipayTradePrecreateRequest();
    /**回調地址+異步通知**/
    alipayTradePrecreateRequest.setNotifyUrl(AlipayConfig.notify_url);
    alipayTradePrecreateRequest.setReturnUrl(AlipayConfig.return_url);

    /**商戶訂單號,商戶網站訂單系統中唯一訂單號,必填**/
    String out_trade_no = alipayOrderRequest.getWidOutTradeNo();
    /**付款金額,必填**/
    String total_amount = alipayOrderRequest.getWidTotalFee();
    /**訂單名稱,必填**/
    String subject = alipayOrderRequest.getWidSubject();
    /**商戶門店編號,必填**/
    String store_id = AlipayConfig.STORE_ID;
    /**交易超時時間,訂單允許的最晚付款時間,必填**/
    String timeout_express = "120m";

    /**拼接參數**/
    alipayTradePrecreateRequest.setBizContent(
        "{\"out_trade_no\":\""+ out_trade_no +"\","
        + "\"total_amount\":\""+ total_amount +"\","
        + "\"subject\":\""+ subject +"\","
        + "\"store_id\":\""+ store_id +"\","
        + "\"timeout_express\":\""+timeout_express+"\"}");

    return alipayClient.execute(alipayTradePrecreateRequest).getBody();
}

三、返回結果+測試

如上代碼調用execute之后的返回結果:

{
    "alipay_trade_precreate_response":{
        "code":"10000",
        "msg":"Success",
        "out_trade_no":"20200508160037744742",
        "qr_code":"https://qr.alipay.com/bax06173nktjyrrwupss00bf"
    },
    "sign":"sdk3Q6idlQC+SRuuxc6xXv3g4BAkxEgpA9WYJoiE8oYH5mA6K8+GMwAETNKOhOPh/SoYS4CECzswk/H7qw9A=="
}

我們可以看到qr_code參數了,剩下的就是交給前端生成二維碼了,然后用戶掃碼支付,在這我就不展示了,直接拿鏈接去草料二維碼直接生成個二維碼試試。

四、掃碼支付回調地址

支付成功后支付寶會回調前邊設置的回調地址,由后端的的 return_url 參數控制。

再看看支付成功后的回調接口,由后端的 notify_url 參數控制「沒有使用開源的SDK演示」:

public String alipaynotify(Model model, HttpServletRequest request{

    log.info("支付寶異步回調 ------------beg-----------");
    String result = "fail";
    /**獲取支付寶POST過來反饋信息**/
    /* *
     * 功能:支付寶服務器異步通知頁面
     * 說明:
     * 以下代碼只是為了方便商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,並非一定要使用該代碼。
     * 該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。
     */

    Map<String, String> params=this.getAlipayRequest(request);
    if(params == null || params.size()==0){
        BufferedReader bufferReader = null;
        StringBuilder sb = new StringBuilder();
        try {
            bufferReader = new BufferedReader(request.getReader());

            String line = null;
            while ((line = bufferReader.readLine()) != null) {
                sb.append(new String(line.getBytes("ISO-8859-1"), "utf-8"));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        String body= null;
        try {
            body = URLDecoder.decode(sb.toString(),"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        params=UriComponentsBuilder.newInstance().query(body).build().getQueryParams().toSingleValueMap();
    }
    boolean signVerified =false;
    try {
        signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
    } catch (AlipayApiException e1) {
        log.error("由於"+e1.getErrMsg()+"返回給支付寶系統的結果result:fail");
        model.addAttribute("result""fail");
        return result;
    }

    /**——請在這里編寫您的程序(以下代碼僅作參考)——**/

    /* 實際驗證過程建議商戶務必添加以下校驗:
    1、需要驗證該通知數據中的out_trade_no是否為商戶系統中創建的訂單號,
    2、判斷total_amount是否確實為該訂單的實際金額(即商戶訂單創建時的金額),
    3、校驗通知中的seller_id(或者seller_email) 是否為out_trade_no這筆單據的對應的操作方(有的時候,一個商戶可能有多個seller_id/seller_email)
    4、驗證app_id是否為該商戶本身。
    */

    log.error("支付寶驗證簽名:---------------------------------"+signVerified);
    if(signVerified) {/**驗證成功**/
        /**商戶訂單號**/
        /**交易狀態**/
        log.info("支付寶異步回調驗簽成功!");
        String trade_status = params.get("trade_status");

        if("TRADE_FINISHED".equals(trade_status)){
            /**判斷該筆訂單是否在商戶網站中已經做過處理**/
            /**如果沒有做過處理,根據訂單號(out_trade_no)在商戶網站的訂單系統中查到該筆訂單的詳細,並執行商戶的業務程序**/
            /**如果有做過處理,不執行商戶的業務程序**/

            /**注意:**/
            /**退款日期超過可退款期限后(如三個月可退款),支付寶系統發送該交易狀態通知**/
            try {
                /** 在這里處理支付成功后的操作,比如修改訂單狀態等等**/
                coding...
                result = "success";
            } catch (Exception e) {
                log.error(e.getMessage());
                result = "fail";
            }
        }else if ("TRADE_SUCCESS".equals(trade_status)){
            /**判斷該筆訂單是否在商戶網站中已經做過處理**/
            /**如果沒有做過處理,根據訂單號(out_trade_no)在商戶網站的訂單系統中查到該筆訂單的詳細,並執行商戶的業務程序**/
            /**如果有做過處理,不執行商戶的業務程序**/

            /**注意:**/
            /**付款完成后,支付寶系統發送該交易狀態通知**/
            try {
                /** 在這里處理支付成功后的操作,比如修改訂單狀態等等**/
                coding...
                result = "success";
            } catch (Exception e) {
                log.error(e.getMessage());
                result = "fail";
            }
        }else{
            result = "fail";
        }
    }else {/**驗證失敗**/
        result = "fail";
        /**調試用,寫文本函數記錄程序運行情況是否正常**/
        /**String sWord = AlipaySignature.getSignCheckContentV1(params);**/
        /**AlipayConfig.logResult(sWord);**/
        log.debug("支付寶異步回調驗簽失敗");
    }
    log.debug("異步回調返回給支付寶系統的結果result:"+result);

    model.addAttribute("result", result);
    log.info("支付寶異步回調  -------------end ------------");
    return result;
}

該方法返回給支付寶的 result 就 successfail 兩個結果。
從以上看來,其實不難發現支付寶支付是非常簡單的,盡管我上邊貼了大量的代碼,其實采用開源SDK的話可以更加縮減、美化一些。

ok,這篇文章就到這結束了,上邊並沒有詳細介紹接口調用、參數說明等,詳細介紹請查看官方文檔:

https://opendocs.alipay.com/open/194/106078/
https://opendocs.alipay.com/open/194/103296

文章最后

博客地址:https://www.cnblogs.com/niceyoo

如果覺得這篇文章有丶東西,不妨關注一下我,關注是對我最大的鼓勵~


免責聲明!

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



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