支付寶:APP支付接口2.0(alipay.trade.app.pay)

本章是跟支付寶進行簽約對接商戶服務端(也就是自行開發的JAVA後端),做此記錄。

文獻基本都來源於支付寶,詳情請看支付寶官方文檔:APP支付

目錄

系統交互圖

服務端demo

配置參數

獲取APPID

獲取公密鑰

使用demo

獲取簽名後的訂單信息

驗籤


系統交互圖

先來查看下主要系統交互圖,方便後面的流程梳理。

文檔位置:https://docs.open.alipay.com/204/105297/ 的快速接入,第四步:調用接口

看到這裏,可以清楚的知道,java後端並不需要進行支付操作,只負責生成簽名後的訂單信息最終驗籤,並最後異步接收支付通知

服務端demo

下載 服務端mode 依賴包

文檔地址:https://docs.open.alipay.com/54/106370/

我這JAVA MAVEN項目 , 直接引入依賴即可

<dependencies>
    <dependency>
        <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java</artifactId>
        <version>4.8.10.ALL</version>
    </dependency>
</dependencies>

服務端demo 文檔地址:https://docs.open.alipay.com/54/106370/

代碼塊文檔內有展示,這裏就不顯示了,(簡易固定參數不做說明)主要展示咱們需要自助獲取的參數:

  • app_id :應用Id
  • sign_type :簽名類型,demo採用了(證書)版本,這裏選用推薦的 RSA2,詳情可看:RSA 和 RSA2 簽名算法區別
  • private_key :應用私鑰
  • app_cert_path :應用公鑰證書路徑
  • alipay_cert_path:支付寶公鑰證書路徑
  • alipay_root_cert_path:支付寶根證書路徑

配置參數

先登錄到支付寶開放平臺,這裏的實名認證,綁定手機號等操作這裏就不講解了。

獲取APPID

如下圖進行3步進入創建應用界面

創建完成之後,就獲取到咱們要用的應用APPID

獲取公密鑰

阿里文檔鏈接參考:

第一步:生成 RSA 密鑰(這裏選擇公鑰證書方式)

第二步:上傳應用公鑰並獲取支付寶公鑰

 上傳完成後,可在設置頁面下載到 應用公鑰證書,支付寶公鑰證書,支付寶根證書

使用證書的方式,直接查看demo就可以。

使用demo

查看demo其中的一句

//SDK已經封裝掉了公共參數,這裏只需要傳入業務參數。
//以下方法爲sdk的model入參方式(model和biz_content同時存在的情況下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();

該業務對象已經幫我們封裝好了,也夠用了,demo展示的set只是其中幾個參數,其他可以繼續賦值。

使用demo提供的AlipayTradeAppPayRequest好處是,自行幫我們做好了sign簽名處理,我們無需再自己簽名。

麻煩的問題在於證書路徑的讀取。如下標識:

//設置應用公鑰證書路徑
certAlipayRequest.setCertPath(app_cert_path);
//設置支付寶公鑰證書路徑
certAlipayRequest.setAlipayPublicCertPath(alipay_cert_path);
//設置支付寶根證書路徑
certAlipayRequest.setRootCertPath(alipay_root_cert_path);

如果項目是war,那還好,這三個參數直接搜索項目根路徑比較簡單,但如果是springboot打包成jar項目的啓動方式的話,就比較困難。jar包如果想讀取內部配置文件,只能通過流來讀取,網上搜索的讀取配置文件大多是InputStream,可我們要的是文件的路徑而不是內容。在開發測試的時候,Linux環境只能讀取絕對路徑,Window可以讀取相對路徑。所以做了兩個版本的路徑切換。下面展示下效果。

// Linux文件絕對路徑 --- ${jar包所在“根”路徑}/crt/
// String outpath = System.getProperty("user.dir") + File.separator + "crt" + File.separator;
// window文件相對路徑
String outpath = this.getClass().getResource("/").getPath()
// 設置應用公鑰證書路徑
// linux getPath: ${jar包所在“根”路徑}/crt/appCertPublicKey_2019102168481752.crt,其他兩個相同
// window getPath: G:\git\leopard-console\target\classes\appCertPublicKey_2019102168481752.crt
File appCertfile = new File(outpath + "appCertPublicKey_2019102168481752.crt");
if (appCertfile == null || appCertfile.getPath() == null) {
	logger.error("------/crt/------找不到應用公鑰證書");
	return null;
}
// 設置應用公鑰證書路徑
certAlipayRequest.setCertPath(appCertfile.getPath());

本地window測試還方便,如果是linux記得切換路徑,實例裏面有個 “crt“ 文件夾,是我創建後將文件放進去的。

如果是通過dockerfile來構建docker讀取配置。相應配置如下:

FROM java:7
VOLUME /tmp
ADD leopard-console*.jar /leopard-console.jar
ADD classes/alipayCertPublicKey_RSA2.crt /crt/alipayCertPublicKey_RSA2.crt
ADD classes/alipayRootCert.crt /crt/alipayRootCert.crt
ADD classes/appCertPublicKey_2019102168481752.crt /crt/appCertPublicKey_2019102168481752.crt
RUN bash -c 'touch /leopard-console.jar'
EXPOSE 8080
CMD ["java", "-jar","leopard-console.jar"]

將jar編譯的配置文件,創建到 /crt 目錄下,代碼不變,就可以引用到。

自建的 JAR 項目地址:https://github.com/leopardF/alipay , 有需要參考的可自行下載。

該JAR以嵌入依賴爲主,等下測試時候會看到引用。

獲取簽名後的訂單信息

開始測試請求,引入自建項目依賴

<dependency>
	<groupId>com.pay</groupId>
	<artifactId>alipay</artifactId>
	<version>0.0.1</version>
</dependency>

編寫測試代碼:

/**
 * 請求生成簽名信息
 * 
 * @return
 */
@RequestMapping(value = "/getOrderInfoSign")
public Message getOrderInfoSign() {
	AliAppPayResponseCode appPayRequest = new AliAppPayRequest().appPayRequest(30, "測試-model", "預約3天,詳情如下。。。",
			"0.01", "201910221548751212", "paramUrl;paramUrl2", "http://cwfpx6.natappfree.cc/verifyOrderInfoSign");
	return new Message("200", "aa", appPayRequest.alias());
}

瀏覽器輸入: localhost:8080/getOrderInfoSign , 成功獲取簽名驗證信息。

data就是移動端需要的簽名信息。

我們再重新看下系統流程圖:

第2、3步我們已經完成。

接下來開始我們的驗籤。

驗籤

直接上代碼:


import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;
import com.alipay.AliAppPayRequest;
import com.alipay.config.AlipayConfig;
import com.alipay.enums.AliAppPayResponseCode;
import com.alipay.enums.AliPayPublicCode;
import com.leopard.util.bean.Message;
import com.leopard.util.enums.SystemCodeAndMsg;

/**
 * ali支付
 */
@RestController
@RequestMapping(value = "/alipay")
public class AliPayAction {

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

	/**
	 * 請求生成訂單簽名信息
	 * 
	 * @param subject
	 *            標題
	 * @param body
	 *            內容
	 * @param totalAmount
	 *            金額
	 * @param passbackParams
	 *            附加參數
	 * @return
	 */
	@RequestMapping(value = "/getModelOrderInfoSign", method = RequestMethod.POST)
	public Message getModelOrderInfoSign(@RequestParam(value = "subject") String subject,
			@RequestParam(value = "body") String body, @RequestParam(value = "totalAmount") String totalAmount,
			@RequestParam(value = "passbackParams") String passbackParams) {

		AliAppPayResponseCode appPayRequest = new AliAppPayRequest().appPayRequest(30, subject, body, totalAmount,
				"201910221548751212", passbackParams, "http://cwfpx6.natappfree.cc/verifyOrderInfoSign");
		// AliAppPayResponseCode appPayRequest = new
		// AliAppPayRequest().appPayRequest(30, "測試-model", "預約3天,詳情如下。。。",
		// "0.01", "201910221548751212", "paramUrl;paramUrl2",
		// "http://cwfpx6.natappfree.cc/verifyOrderInfoSign");
		return new Message("200", "aa", appPayRequest.alias());
	}

	/**
	 * 同步獲取前端成功返回結果
	 * 
	 * @param result
	 *            支付成功後的result的json串
	 * @return
	 */
	@RequestMapping(value = "/syncNotifyValidate", method = RequestMethod.POST)
	public Message syncNotifyValidate(@RequestParam(value = "result") String result) {

		JSONObject resultObject = JSONObject.parseObject(result);
		String alipayTradeAppPayResponse = resultObject.getString("alipay_trade_app_pay_response");
		if (StringUtils.isBlank(alipayTradeAppPayResponse)) {
			return new Message(SystemCodeAndMsg.FAIL.code(), "響應參數不存在");
		}
		String sign = resultObject.getString("sign");
		if (StringUtils.isBlank(sign)) {
			return new Message(SystemCodeAndMsg.FAIL.code(), "驗籤信息不能爲空");
		}
		String signType = resultObject.getString("sign_type");
		if (!AlipayConfig.sign_type.equals(signType)) {
			return new Message(SystemCodeAndMsg.FAIL.code(), "簽名類型不匹配");
		}

		JSONObject responseObject = JSONObject.parseObject(alipayTradeAppPayResponse);
		if (!AliPayPublicCode.Success.code().equals(responseObject.getString("code"))) {
			logger.info("---syncNotifyValidate-----responseObject:" + responseObject.toString());
			return new Message(responseObject.getString("code"), responseObject.getString("msg"));
		}

		// 以下驗證業務邏輯需補充
		Message publicValidateBaseInfo = publicValidateBaseInfo(responseObject);
		if(!SystemCodeAndMsg.SUCCESS.code().equals(publicValidateBaseInfo.getCode())){
			//驗證失敗
			return publicValidateBaseInfo;
		}

		// 驗證通過,買家付款成功,將信息記錄到數據庫
		// 業務代碼

		// 反饋給前端
		return new Message(SystemCodeAndMsg.SUCCESS.code(), SystemCodeAndMsg.SUCCESS.msg());
	}

	/**
	 * 異步獲取支付寶POST通知。 此地址是一開始生成訂單傳入的異步地址,請保留好路徑
	 * 
	 * @param request
	 * @return
	 */
	@RequestMapping(value = "/asynNotifyValidate", method = RequestMethod.POST)
	public String asynNotifyValidate(HttpServletRequest request) {

		// 支付寶提供的驗籤方法,已經封裝到依賴包,請求調用就行
		AliAppPayResponseCode verifyOrderInfoSign = new AliAppPayRequest().verifyOrderInfoSign(request);
		if (!AliAppPayResponseCode.SUCCESS.code().equals(verifyOrderInfoSign.code())) {
			return "failure";
		}
		
		// 以下驗證業務邏輯需補充
		// 已經將驗證過程中的訂單信息和響應信息放入到alias內回傳回來
		JSONObject verifyOrderInfoJson = JSONObject.parseObject(verifyOrderInfoSign.alias());
		Message publicValidateBaseInfo = publicValidateBaseInfo(verifyOrderInfoJson);
		if(!SystemCodeAndMsg.SUCCESS.code().equals(publicValidateBaseInfo.getCode())){
			//驗證失敗
			return "failure";
		}
		
		//驗證通過,開始業務代碼,並標記訂單支付成功,該信息可覆蓋同步回調信息,較爲準確
		//最好將回調信息都保存在數據庫做記錄
		//verifyOrderInfoJson已經保存好回調數據,直接放入數據庫用保存即可

		return "success";
	}

	/**
	 * 支付寶公用回調信息驗籤
	 * 
	 * @param jsonObject
	 * @return
	 */
	private Message publicValidateBaseInfo(JSONObject jsonObject) {
		
		// 1、商戶需要驗證該通知數據中的 out_trade_no 是否爲商戶系統中創建的訂單號;
		String outTradeNo = jsonObject.getString("out_trade_no");
		// 業務查找訂單號對應的信息,並進行匹配驗證

		// 2、判斷 total_amount 是否確實爲該訂單的實際金額(即商戶訂單創建時的金額);
		String totalAmount = jsonObject.getString("total_amount");
		// 根據1獲取到的信息,進行匹配驗證

		// 3、校驗通知中的 seller_id(或者 seller_email) 是否爲 out_trade_no
		// 這筆單據對應的操作方(有的時候,一個商戶可能有多個 seller_id/seller_email);
		String sellerId = jsonObject.getString("seller_id");
		if (!AliAppPayRequest.verifySellerId(sellerId)) {
			logger.info("---publicValidateBaseInfo-----verifySellerId:" + jsonObject.toString());
			return new Message(SystemCodeAndMsg.FAIL.code(), "驗證信息有誤");
		}

		// 4、驗證 app_id
		// 是否爲該商戶本身。上述1、2、3、4有任何一個驗證不通過,則表明同步校驗結果是無效的,只有全部驗證通過後,纔可以認定買家付款成功。
		String appId = jsonObject.getString("app_id");
		if (!AliAppPayRequest.verifyAppId(appId)) {
			logger.info("---publicValidateBaseInfo-----verifyAppId:" + jsonObject.toString());
			return new Message(SystemCodeAndMsg.FAIL.code(), "驗證信息有誤");
		}
		
		return new Message(SystemCodeAndMsg.SUCCESS.code(), SystemCodeAndMsg.SUCCESS.msg());
	}
}

這是自己寫的驗籤demo,可直接更改使用到自己的業務內,其中的Message是自己封裝的相應格式,這裏就不羅列了。

驗籤請求可參考支付寶技術文檔:

第四步:使用支付寶公鑰驗籤

App支付服務端 DEMO & SDK

服務端:通知參數說明

 代碼已經將系統流程圖的,9、10、12、13步完成。

聯調測試,請自行跟移動端聯調對接。

調用支付寶的聯調日誌查詢地址爲:https://openmonitor.alipay.com/acceptance/cloudparse.htm

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章