天天看點

支付寶支付接口的調用

應公司業務要求,需要調用支付寶的支付接口進行支付的操作,于是将整個調用過程用部落格形式記錄下來,以供以後使用。

本次調用支付寶采用的是電腦支付,官方文檔頁面如下:

電腦端調用支付寶,流程很簡單,在頁面有一個立即支付的按鈕,點選進入商戶的背景,商戶的背景将支付所需的參數傳給支付寶,支付寶傳回給商戶一個字元串形式的form表單,商戶将這個form表單傳給前台,前台對表單進行送出即可跳轉到支付包頁面,使用者在支付寶頁面支付完成後,支付寶會先調取我們的通知接口進行支付結果通知。然後跳轉到我們傳給支付包的回調頁面。這就是電腦端調用支付寶的整個流程。

要調用支付寶的接口,首先需要下載下傳支付寶的sdk。這裡給出官方的下載下傳位址: 

支付寶sdk下載下傳位址。下載下傳下來的是一個zip包,裡面檔案如下:

這四個jar封包件如下: 

alipay-sdk-java-3.0.0.jar┈┈┈┈┈┈┈支付寶SDK編譯檔案jar 

alipay-sdk-java-3.0.0-source.jar┈┈┈ 支付寶SDK源碼檔案jar 

commons-logging-1.1.1.jar┈┈┈┈┈┈SDK依賴的日志jar 

commons-logging-1.1.1-sources.jar┈┈SDK依賴的日志源碼jar 

項目中需要引入的是alipay-sdk-java-3.0.0.jar和commons-logging-1.1.1.jar。對于commons-logging-1.1.1.jar,可以直接在pom檔案中添加依賴:

<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->

<dependency>

    <groupId>commons-logging</groupId>

    <artifactId>commons-logging</artifactId>

    <version>1.1.1</version>

</dependency>

1

2

3

4

5

6

7

對于alipay-sdk-java-3.0.0.jar,目前maven倉庫沒有該依賴,隻能手動添加。首先将alipay-sdk-java-3.0.0.jar放入D盤的根目錄下,然後運作mvn指令:

 mvn install:install-file -DgroupId=com.alipay -DartifactId=sdk-java -Dversion=3.0.0 -Dpackaging=jar -Dfile=alipay-sdk-java-3.0.0.jar

1

此指令會把支付寶的jar包打包到maven本地倉庫下,然後在pom檔案中引用:

<dependency>

          <groupId>com.alipay</groupId>

          <artifactId>sdk-java</artifactId>

          <version>3.0.0</version>

    </dependency>

1

2

3

4

5

即可完成支付寶jar包的導入。

前面準備工作已經做好,現在來看看代碼,我把對支付寶的調用都封裝到了一個工具類中:

package com.avie.ltd.utils;

import java.util.HashMap;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import com.alipay.api.AlipayApiException;

import com.alipay.api.AlipayClient;

import com.alipay.api.DefaultAlipayClient;

import com.alipay.api.internal.util.AlipaySignature;

import com.alipay.api.request.AlipayTradePagePayRequest;

import com.alipay.api.request.AlipayTradeRefundRequest;

import com.avie.ltd.config.AlipayConfig;

public class PayUtil {

    public static String alipay(String outTradeNo,String totalAmount,String subject,String body) {

        //獲得初始化的AlipayClient

                AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

                //設定請求參數

                AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();

                alipayRequest.setReturnUrl(AlipayConfig.return_url);

                alipayRequest.setNotifyUrl(AlipayConfig.notify_url);

                try {               

                alipayRequest.setBizContent("{\"out_trade_no\":\""+ outTradeNo +"\"," 

                        + "\"total_amount\":\""+ totalAmount +"\"," 

                        + "\"subject\":\""+ subject +"\"," 

                        + "\"timeout_express\":\""+ Constants.TIMEOUT_EXPRESS +"\"," 

                        + "\"body\":\""+ body +"\"," 

                        + "\"qr_pay_mode\":\""+ Constants.QR_PAY_MODE +"\"," 

                        + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");                                                                                                                                                               

                //請求

                String result;

                result = alipayClient.pageExecute(alipayRequest).getBody();

                System.out.println("*********************\n傳回結果為:"+result);

                return result;

                } catch (Exception e) {

                    // TODO Auto-generated catch block

                    e.printStackTrace();

                    return null;

                }

    }

    public static String aliRefund(String outTradeNo,String tradeNo,String refundAmount,String refundReason,String out_request_no) {

        //獲得初始化的AlipayClient

                AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

                //設定請求參數

                AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();

                alipayRequest.setReturnUrl(AlipayConfig.return_url);

                alipayRequest.setNotifyUrl(AlipayConfig.notify_url);

                try {               

                    alipayRequest.setBizContent("{\"out_trade_no\":\""+ outTradeNo +"\"," 

                            + "\"trade_no\":\""+ tradeNo +"\"," 

                            + "\"refund_amount\":\""+ refundAmount +"\"," 

                            + "\"refund_reason\":\""+ refundReason +"\"," 

                            + "\"out_request_no\":\""+ out_request_no +"\"}");                                                                                                                                                               

                //請求

                String result;

                //請求

                result = alipayClient.execute(alipayRequest).getBody();

                System.out.println("*********************\n傳回結果為:"+result);

                return result;

                } catch (Exception e) {

                    // TODO Auto-generated catch block

                    e.printStackTrace();

                    return null;

                }

    }

    public static boolean checkSign(HttpServletRequest req) {

        Map<String, String[]> requestMap = req.getParameterMap();

        Map<String, String> paramsMap = new HashMap<>();

        requestMap.forEach((key, values) -> {

              String strs = "";

              for(String value : values) {

              strs = strs + value;

              }

              System.out.println(("key值為"+key+"value為:"+strs));

              paramsMap.put(key, strs);

            });

        //調用SDK驗證簽名

        try {

            return  AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);

        } catch (AlipayApiException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

            System.out.println("*********************驗簽失敗********************");

            return false;

        }

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

這個工具類一共有三個靜态方法。 

alipay:支付寶的下單接口 

aliRefund:支付寶的退款方法 

checkSign:支付寶的驗簽方法

先來講alipay這個方法,當我們需要下單的時候,需要在業務邏輯代碼中調用該工具類的該方法。該方法需要傳入的參數如下:

outTradeNo: 商戶訂單号,商戶網站訂單系統中唯一訂單号,必填 。需要保證商戶端唯一。 

totalAmount :付款金額,必填 

subject:主題 

body:商品描述,可空

都是一些商品屬性的基本參數,一般都是根據我們具體的業務邏輯去設定。進入方法,第一句話:

AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

1

這句話主要是初始化一個DefaultAlipayClient,這個類的初始化構造器需要傳很多參數,從左往右依次為:支付寶的網關,商戶的appid,商戶的私鑰,傳參的格式,傳參的字元集,商戶的公鑰,商戶的簽名類型。這裡都把這些參數封裝進了AlipayConfig這個類裡面,來看看AlipayConfig這個類:

package com.avie.ltd.config;

import java.io.FileWriter;

import java.io.IOException;

public class AlipayConfig {

//↓↓↓↓↓↓↓↓↓↓請在這裡配置您的基本資訊↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    // 應用ID,您的APPID,收款賬号既是您的APPID對應支付寶賬号

    public static String app_id = "你的APPID";

    // 商戶私鑰,您的PKCS8格式RSA2私鑰

    public static String merchant_private_key = "你的商戶私鑰";

    // 支付寶公鑰,檢視位址:https://openhome.alipay.com/platform/keyManage.htm 對應APPID下的支付寶公鑰。

    public static String alipay_public_key = "你的支付寶公鑰";

    // 伺服器異步通知頁面路徑  需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常通路

    public static String notify_url = "你的異步通知頁面";

    // 頁面跳轉同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常通路

    public static String return_url = "你的回調頁面";

    // 簽名方式

    public static String sign_type = "RSA2";

    // 字元編碼格式

    public static String charset = "utf-8";

    // 支付寶網關

    public static String gatewayUrl = "https://openapi.alipay.com/gateway.do";

    // 支付寶網關

    public static String log_path = "C:\\";

//↑↑↑↑↑↑↑↑↑↑請在這裡配置您的基本資訊↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

    public static void logResult(String sWord) {

        FileWriter writer = null;

        try {

            writer = new FileWriter(log_path + "alipay_log_" + System.currentTimeMillis()+".txt");

            writer.write(sWord);

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            if (writer != null) {

                try {

                    writer.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

        }

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

這個類其實很簡單,就是存儲了一些支付所需的公鑰啊,私鑰啊等參數,友善同意管理和使用。代碼往下走AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();就是初始化一個阿裡封裝的request,後面幾句就是設定這個request要傳入的參數,把回調函數位址,外部訂單好,金額都設定到request裡面。然後執行alipayClient.pageExecute(alipayRequest).getBody();這個方法,即可向支付寶發起一個下單請求,支付寶傳回給我們的是一個字元串,這個字元串實際上是一個form表單。現在,我們寫個簡單的方法來測試以下,代碼如下:

package com.avie.itd;

import com.avie.ltd.utils.PayUtil;

public class Test {

    public static void main(String[] args) {

        String str = PayUtil.alipay("2313131", "0.01", "hehe", "haha");

        System.out.println(str);

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

測試方式非常簡單,不需要弄什麼運作環境或者一大堆依賴,就寫個main方法,調用我的PayUtil的alipay方法,傳入自己随便編的參數,右鍵運作即可。如果基本的參數配置都沒有錯,那麼你的控制台會列印出如下結果:

看到了嗎,傳回的結果實際上是一個表單加上一個javascript腳本,腳本的目的在于送出表單。是以在實際開發中,你隻需要拿到這個傳回結果,把結果傳給前端,前端把用jquery把這段代碼放入一個div即可。表單自動送出,然後你就會跳轉到支付寶的支付頁面了。

那麼到這裡就完了嗎?肯定還沒有,支付完成後,支付寶會将支付結果推送到我們自己的業務系統中,是以我們需要為支付寶寫個異步通知的接口。先來看代碼:

package com.avie.ltd.controller;

import java.io.IOException;

import java.util.SortedMap;

import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.util.StringUtils;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import com.avie.ltd.entity.AliReturnPayBean;

import com.avie.ltd.service.TbPaymentRecordsService;

import com.avie.ltd.utils.JaxbUtil;

import com.avie.ltd.utils.PayUtil;

import com.avie.ltd.utils.wxUtil.UnifiedOrderRespose;

import com.thoughtworks.xstream.XStream;

import net.sf.json.JSONObject;

@Controller

@RequestMapping(value = "/returnPay")

public class ReturnController {

    private static Logger logger = LoggerFactory.getLogger(ReturnController.class);

    @Autowired

    private TbPaymentRecordsService tbPaymentRecordsService;

    @RequestMapping(value = "/aliReturnPay", method = RequestMethod.POST)

    public void returnPay(HttpServletResponse response, AliReturnPayBean returnPay, HttpServletRequest req)

            throws IOException {

        response.setContentType("type=text/html;charset=UTF-8");

        logger.info("****************************************支付寶的的回調函數被調用******************************");

        if (!PayUtil.checkSign(req)) {

            logger.info("****************************************驗簽失敗*******************************************");

            response.getWriter().write("failture");

            return;

        }

        if (returnPay == null) {

            logger.info("支付寶的returnPay傳回為空");

            response.getWriter().write("success");

            return;

        }

        logger.info("支付寶的returnPay" + returnPay.toString());

        if (returnPay.getTrade_status().equals("TRADE_SUCCESS")) {

            logger.info("支付寶的支付狀态為TRADE_SUCCESS");

            tbPaymentRecordsService.aliPaySuccess(returnPay);

        }

        response.getWriter().write("success");

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

首先需要注意的是,這個類隻能被@Controller注釋修飾,不能用@RestController,因為@RestController會将該類中所有方法的傳回值自動轉為json格式,而此處支付寶的傳回值不需要json格式。這裡因為線上環境不能debug,我列印了大量的日志以監控該方法的運作情況。在支付的回調方法中,首先要做的就是驗證簽名,是以剛進入方法首先調用了PayUtil.checkSign(req)這個驗證簽名的方法,我們來看看這個checkSign(req)裡面是怎麼寫的:

    public static boolean checkSign(HttpServletRequest req) {

        Map<String, String[]> requestMap = req.getParameterMap();

        Map<String, String> paramsMap = new HashMap<>();

        requestMap.forEach((key, values) -> {

              String strs = "";

              for(String value : values) {

              strs = strs + value;

              }

              System.out.println(("key值為"+key+"value為:"+strs));

              paramsMap.put(key, strs);

            });

        //調用SDK驗證簽名

        try {

            return  AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);

        } catch (AlipayApiException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

            System.out.println("*********************驗簽失敗********************");

            return false;

        }

    }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

首先我們先看最下面: AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);這句話是調用支付寶給我們封裝的驗證簽名的方法,右鍵指上去,會發現該方法的描述是這個樣子的:

boolean com.alipay.api.internal.util.AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String signType) throws AlipayApiException

1

看到Map

package com.avie.ltd.entity;

import java.io.Serializable;

public class AliReturnPayBean  implements Serializable{

    private static final long serialVersionUID = 1L;

    private String app_id;

    private String out_trade_no;

    private String sign;

    private String trade_status;

    private String trade_no;

    private String total_amount;

    public String getTotal_amount() {

        return total_amount;

    }

    public void setTotal_amount(String total_amount) {

        this.total_amount = total_amount;

    }

    public String getApp_id() {

        return app_id;

    }

    public void setApp_id(String app_id) {

        this.app_id = app_id;

    }

    public String getOut_trade_no() {

        return out_trade_no;

    }

    public void setOut_trade_no(String out_trade_no) {

        this.out_trade_no = out_trade_no;

    }

    public String getSign() {

        return sign;

    }

    public void setSign(String sign) {

        this.sign = sign;

    }

    public String getTrade_status() {

        return trade_status;

    }

    public void setTrade_status(String trade_status) {

        this.trade_status = trade_status;

    }

    public String getTrade_no() {

        return trade_no;

    }

    public void setTrade_no(String trade_no) {

        this.trade_no = trade_no;

    }

    @Override

    public String toString() {

        return "AliReturnPayBean [app_id=" + app_id + ", out_trade_no=" + out_trade_no + ", sign=" + sign

                + ", trade_status=" + trade_status + ", trade_no=" + trade_no + "]";

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

将傳回參數封裝在實體bean中,這樣springmvc會利用反射機制自動将相關參數映射進實體bean裡面,省去了我們自己再去解析參數的麻煩。後面那句returnPay.getTrade_status().equals(“TRADE_SUCCESS”)是得到支付寶中trade_status這個參數,在支付寶的接口文檔中,若這個參數等于TRADE_SUCCESS,則說明支付是成功的,然後我就會調用tbPaymentRecordsService.aliPaySuccess(returnPay);。這句代碼就是具體的業務邏輯代碼了,對成功傳回的參數進行處理,由于每個人業務邏輯都不一樣,我就不再贅述了。你們隻需将這局代碼替換成你們的業務邏輯代碼即可。

這樣,支付下單的流程就走通了。當然我還有一個退款的接口沒講。這個接口其實和下單接口大同小異,我也寫了相關注釋。就請各位自行閱讀,我就不做解釋了。

那這次支付寶的調用就先寫到這裡,下次我還會寫一些關于微信的支付接口,喜歡的小夥伴可以點波關注哦。

--------------------- 

原文:https://blog.csdn.net/zoroduyu/article/details/79825880 

繼續閱讀