支付寶支付、查詢訂單、退款

1. 前言

   對接了好長一段時間的支付,期間涉及到支付寶相關工做,這裏將支付寶相關部分整理一下。php

   環境配置,主要是在pom文件中添加以下依賴:html

<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>3.1.0</version>
</dependency>

   支付寶的支付api文檔比較完整,對接起來也比較順利,下面就開始吧‘(*>﹏<*)′ 前端

2. 電腦網站支付

   實際上是統一收單下單並支付頁面接口(alipay.trade.page.pay),PC場景下單並支付,接口調用成功後會返回一個form表單,直接提交就能夠了,跳過去的頁面是二維碼,掃碼支付,成功後會回跳到指定的頁面(這裏是一個重定向,重定向地址是在發起調用時以接口參數的形式指定的)。java

  接口發起示例代碼,詳見統一收單下單並支付頁面接口express

AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", app_id, private_key, "json", "UTF-8", alipay_public_key, "RSA2");
//設置請求參數
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(returnUrl); // 支付成功後回跳地址
alipayRequest.setNotifyUrl(notifyUrl); // 支付後的異步通知地址json

//商戶訂單號,商戶網站訂單系統中惟一訂單號,必填
String out_trade_no = "";
//付款金額,必填
String total_amount = "";
//訂單名稱,必填
String subject = "";
//商品描述,可空
String body = "";
// 銷售產品碼 必填
String product_code="FAST_INSTANT_TRADE_PAY";api

alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ body +"\","
+ "\"timeout_express\":\"10m\","
+ "\"product_code\":\""+product_code+"\"}");瀏覽器

//請求
String form = "";
try{
form = alipayClient.pageExecute(alipayRequest).getBody();//調用SDK生成表單
}catch (AlipayApiException e){
// TODO Auto-generated catch block
e.printStackTrace();
}
return form ;服務器

  這裏拿到的是一個字符串,實際上是一個form表單,前端收到這個以後直接跳過去就能夠了,剩下的就交給支付寶了,厲害吧!示例以下:微信

-<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>Document</title>
</head>

<body>

<form ...> // 這裏就是返回的form字符串
</form>

<script>document.forms[0].submit();</script>


</body>
<script>
</script>
</html>

  關於異步回調,後臺須要提供一個外網可訪問的接口,供支付寶回調,示例代碼:

    //獲取支付寶POST過來反饋信息
        logger.debug("----------------------------------->pay notify start--------------^_^");
        Map<String, String> params = new HashMap<String, String>();
        Map requestParams = request.getParameterMap();
        for (Iterator 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);
        }

        // 驗籤
        boolean result = AlipaySignature.rsaCheckV1(params, aliPayPublicKey, "utf-8" ,"RSA2");
        if (result) {
            // 驗籤成功
            if (trade_status.equals("TRADE_FINISHED")) {
                response.getWriter().print("success");
            } else if (trade_status.equals("TRADE_SUCCESS")) {
                // 支付成功
                // 業務邏輯
            }
        } else {//驗證失敗
            response.getWriter().print("failure");
        }
    }

 

3. 手機網站支付

   這個是用於手機端,在手機瀏覽器上發起的,接口名稱(alipay.trade.wap.pay),這個接口和電腦網站支付接口返回的結果相似,也是一個form表單,相關操做相似,跳轉過去以後會檢測手機上是否安裝支付寶客戶端,若是有則打開客戶端支付,沒有則在網頁上登錄進行支付,示例代碼,詳見手機網站支付接口2.0

     String out_trade_no = "";       // 商戶訂單號,商戶網站訂單系統中惟一訂單號,必填
        String subject = "";            // 訂單名稱,必填
        String total_amount="";         // 付款金額,必填
        String body = "";               // 商品描述,可空
        String timeout_express="30m";   // 超時時間 可空

        // SDK 公共請求類,包含公共請求參數,以及封裝了簽名與驗籤,開發者無需關注簽名與驗籤
        //調用RSA簽名方式
        AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",appId, privateKey, "json", "UTF-8", aliPayPublicKey, "RSA2"); 

        // 移動端封裝請求支付信息
        String product_code="QUICK_WAP_PAY";   //銷售產品碼 必填
        AlipayTradeWapPayRequest alipay_request=new AlipayTradeWapPayRequest();
        AlipayTradeWapPayModel model=new AlipayTradeWapPayModel();
        model.setOutTradeNo(out_trade_no);
        model.setSubject(subject);
        model.setTotalAmount(total_amount);
        model.setBody(body);
        model.setTimeoutExpress(timeout_express);
        model.setProductCode(product_code);
        alipay_request.setBizModel(model);
        alipay_request.setNotifyUrl(notifyUrl);// 設置異步通知地址,支付成功後支付寶回掉通知地址
        alipay_request.setReturnUrl(returnUrl);// 設置同步地址,支付後頁面跳轉的地址
        // form表單生產
        String form = "";
        try {
            // 調用SDK生成表單
            form = alipayClient.pageExecute(alipay_request).getBody();
        } catch (AlipayApiException e) {
            
        }
        return form;

 

4. 交易查詢

  經過調用alipay.trade.query(統一收單線下交易查詢)接口來實現,該接口提供全部支付寶支付訂單的查詢,商戶能夠經過該接口主動查詢訂單狀態,完成下一步的業務邏輯。

  須要調用查詢接口的狀況:

  • 當商戶後臺、網絡、服務器等出現異常,商戶系統最終未接收到支付通知;
  • 調用支付接口後,返回系統錯誤或未知交易狀態狀況;
  • 調用alipay.trade.pay,返回INPROCESS的狀態;
  • 調用alipay.trade.cancel以前,需確認支付狀態;

  示例代碼,詳見支付寶文檔

AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do","app_id","your private_key","json","utf8","alipay_public_key","RSA2");
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent("{" +
"\"out_trade_no\":\"20150320010101001\"," +
"\"trade_no\":\"2014112611001004680073956707\"," +
"\"org_pid\":\"2088101117952222\"" +
"  }");
AlipayTradeQueryResponse response = alipayClient.execute(request);
if(response.isSuccess()){
  System.out.println("調用成功");
  // 業務邏輯
} else {   
  System.out.println("調用失敗");
}

 

5. 退款

  經過調用alipay.trade.refund(統一收單交易退款接口)接口實現,當交易發生以後一段時間內,因爲買家或者賣家的緣由須要退款時,賣家能夠經過退款接口將支付款退還給買家,支付寶將在收到退款請求而且驗證成功以後,按照退款規則將支付款按原路退到買家賬號上。 交易超過約定時間(簽約時設置的可退款時間)的訂單沒法進行退款 支付寶退款支持單筆交易分屢次退款,屢次退款須要提交原支付訂單的商戶訂單號和設置不一樣的退款單號。一筆退款失敗後從新提交,要採用原來的退款單號。總退款金額不能超過用戶實際支付金額。

  關於退款的異步通知,這和微信是不同的,微信的異步通知地址是在發起退款接口時指定的,而支付寶呢,比較厲害,分兩種狀況:

  •   全額退款,交易狀態變爲交易關閉(TRADE_CLOSED),具體是否會觸發異步通知根據接口中的通知觸發條件判斷,個人測試是沒有回調通知;
  •   部分退款都會觸發異步通知,異步通知的地址是支付時指定的異步回調地址;

  那既然退款的回調地址和支付時的回調地址是一致的,我如何區分一次回調是支付仍是退款呢?退款的異步通知參數中必定會返回out_biz_no(商戶業務號)、refund_fee(總退款金額)、gmt_refund(交易退款時間)。

  關於退款期限,支付寶通常是3個月,這個能夠在開通的服務簽約信息中查看。

  示例代碼,詳見退款文檔

AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do","app_id","your private_key","json","GBK","alipay_public_key","RSA2");
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
request.setBizContent("{" +
"\"out_trade_no\":\"20150320010101001\"," +
"\"trade_no\":\"2014112611001004680073956707\"," +
"\"refund_amount\":200.12," +
"\"refund_currency\":\"USD\"," +
"\"refund_reason\":\"正常退款\"," +
"\"out_request_no\":\"HZ01RF001\"," +
"\"operator_id\":\"OP001\"," +
"\"store_id\":\"NJ_S_001\"," +
"\"terminal_id\":\"NJ_T_001\"," +
"      \"goods_detail\":[{" +
"        \"goods_id\":\"apple-01\"," +
"\"alipay_goods_id\":\"20010001\"," +
"\"goods_name\":\"ipad\"," +
"\"quantity\":1," +
"\"price\":2000," +
"\"goods_category\":\"34543238\"," +
"\"categories_tree\":\"124868003|126232002|126252004\"," +
"\"body\":\"特價手機\"," +
"\"show_url\":\"http://www.alipay.com/xxx.jpg\"" +
"        }]," +
"      \"refund_royalty_parameters\":[{" +
"        \"royalty_type\":\"transfer\"," +
"\"trans_out\":\"2088101126765726\"," +
"\"trans_out_type\":\"userId\"," +
"\"trans_in_type\":\"userId\"," +
"\"trans_in\":\"2088101126708402\"," +
"\"amount\":0.1," +
"\"amount_percentage\":100," +
"\"desc\":\"分帳給2088101126708402\"" +
"        }]," +
"\"org_pid\":\"2088101117952222\"" +
"  }");
AlipayTradeRefundResponse response = alipayClient.execute(request);
if(response.isSuccess()){
  System.out.println("調用成功");
  // 業務邏輯 }
else {   System.out.println("調用失敗"); }

  最後退款成功後能夠在手機支付寶--朋友--服務提醒中查看,以下所示:

6. 退款查詢

  經過調用alipay.trade.fastpay.refund.query接口完成退款查詢功能,該接口的返回碼10000,僅表明本次查詢操做成功,不表明退款成功。若是該接口返回了查詢數據,則表明退款成功,若是沒有查詢到則表明未退款成功,能夠調用退款接口進行重試。

  關於如何肯定退款是否成功,能夠經過主動調用查詢接口來肯定,也可經過如上的回調接口等待支付寶的異步通知,可是若是未收到支付寶的異步通知而且肯定不是參數以及回調地址的問題,這時應該主動調用查詢接口來同步退款狀態。

  示例代碼,詳細見統一收單交易退款查詢

AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do","app_id","your private_key","json","GBK","alipay_public_key","RSA2");
AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
request.setBizContent("{" +
"\"trade_no\":\"20150320010101001\"," +
"\"out_trade_no\":\"2014112611001004680073956707\"," +
"\"out_request_no\":\"2014112611001004680073956707\"," +
"\"org_pid\":\"2088101117952222\"" +
"  }");
AlipayTradeFastpayRefundQueryResponse response = alipayClient.execute(request);
if(response.isSuccess()){
System.out.println("調用成功");
} else {
System.out.println("調用失敗");
}

 

7. 遇到的問題

1. 訂單查詢時報:com.alipay.api.AlipayApiException: sign check fail: check Sign and Data Fail

緣由:報這個錯誤是由於支付寶公鑰(alipay_public_key)使用錯誤致使的!不少開發者容易把本身生成的應用公鑰和支付寶公鑰搞混淆,從而配置錯誤致使這個錯誤,本身生成的是應用公鑰和應用私鑰!

  • RSA支付寶公鑰對於全部商戶都是惟一的相同值
  • RSA2對於全部商戶都是單獨一對一的,而且只支持開發平臺密鑰管理和沙箱RSA2支付寶公鑰,只能您的appid下面商戶公鑰上傳纔會顯示,而且獲取只能從這個位置獲取, 全部商戶一個帳號下的RSA2支付寶公鑰是相同的。

  具體查看可參考下圖,螞蟻金服開放平臺登錄--開發者中心--網頁&移動應用--應用列表--查看詳情--應用信息。

  如何設置公私鑰,可參考:上傳應用公鑰並獲取支付寶公鑰

解決辦法

  確認使用的支付寶公鑰是否正確,不一樣的環境使用的支付寶公鑰不一樣,如沙箱環境、線上openapi網關和mapi網關對應的支付寶公鑰是不同的。查看地址以下:

  查看到正確的公鑰後,更換正確的支付寶公鑰後便可成功。

參考文獻:

支付API

支付寶公鑰詳解

相關文章
相關標籤/搜索