文章目錄
支付寶網站支付官方文檔:https://docs.open.alipay.com/270
一、時序圖
這裏沒有使用第7步,只使用的第8步
二、請求與響應參數
全部的參數:
這裏只介紹一下使用到的必填的參數:
1. 過程1.1的請求參數與響應參數
公共請求參數:
- app_id:必填,支付寶分配給開發者的應用ID
- method:接口名稱
- return_url:HTTP/HTTPS開頭字符串,時序圖中第六步回調的地址
- charset:請求使用的編碼格式,如utf-8,gbk,gb2312等
- sign_type:商戶生成簽名字符串所使用的簽名算法類型,目前支持RSA2和RSA,推薦使用RSA2,這裏使用的是RSA2
- sign:商戶請求參數的簽名串
- timestamp:時間戳,發送請求的時間,格式"yyyy-MM-dd HH:mm:ss"
- version:調用的接口版本,固定爲:1.0
- notify_url:支付寶服務器主動通知商戶服務器裏指定的頁面http/https路徑。要求可以在公網上訪問的地址
業務請求參數:
- out_trade_no:商戶訂單號,64個字符以內、可包含字母、數字、下劃線;需保證在商戶端不重複
- product_code:僅支持"FAST_INSTANT_TRADE_PAY"
- total_amount:訂單總金額,單位爲元,精確到小數點後兩位,取值範圍[0.01,100000000]。
- subject:訂單標題,
響應參數(過程6的同步返回請求參數):
- code:通信標識,10000表示通信成功
- out_trade_no:商戶訂單號
- total_amount:交易金額
2. 過程8中的請求參數與響應參數
公共請求參數同上,業務請求參數有:
- out_trade_no:訂單支付時傳入的商戶訂單號,和支付寶交易號不能同時爲空。
- trade_no:支付寶交易號,和商戶訂單號不能同時爲空
響應參數:
- code:通信標識,10000表示通信成功
- trade_status:交易狀態,有四種情況:
- WAIT_BUYER_PAY(交易創建,等待買家付款)
- TRADE_CLOSED(未付款交易超時關閉,或支付完成後全額退款)
- TRADE_SUCCESS(交易支付成功)
- TRADE_FINISHED(交易結束,不可退款)
三、配置類
1. 引入依賴
<!--日誌依賴-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
<!--支付寶開發軟件包依賴-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
</dependency>
2. 配置一個配置類
配置類用來指定公共的請求參數
要改的參數:
- 私鑰與公鑰的生成方法:https://docs.open.alipay.com/291/105971
- appid:註冊支付寶開放平臺就有
- notify_url:異步通知頁面路徑,也可以不指定
- return_url:同步通知頁面路徑,也可以不指定,這裏採用的方式是收到同步通知的請求後,調用支付寶的統一收單線下交易查詢接口,確認訂單是否完成
- gatewayUrl :支付寶網關,開發的接口需要在alipay後面加上dev,即
alipaydev
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓請在這裏配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 應用ID,您的APPID,收款賬號既是您的APPID對應支付寶賬號
public static String app_id = "111111111";
// 商戶私鑰,您的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://localhost:9090/notify_url.jsp";
// 頁面跳轉同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常訪問
public static String return_url = "http://localhost:9090/p2p/loan/back";
// 簽名方式
public static String sign_type = "RSA2";
// 字符編碼格式
public static String charset = "utf-8";
// 支付寶網關
public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
// 支付寶網關
public static String log_path = "C:\\";
//↑↑↑↑↑↑↑↑↑↑請在這裏配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
/**
* 寫日誌,方便測試(看網站需求,也可以改成把記錄存入數據庫)
* @param sWord 要寫入日誌裏的文本內容
*/
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();
}
}
}
}
}
四、實現
由於支付這個功能是公共的,所以把這個功能單獨提出來做爲一個pay模塊,而頁面是在web模塊,從web訪問pay的兩種方式:
- 直接在後臺重定向到pay中的地址,缺點:只能發送get請求,在瀏覽器地址欄中中會看到參數一閃而過
- 先請求轉發到一個web中的頁面,該頁面在加載完成後,自動訪問pay模塊中的地址(使用form表單發送post請求,隱藏域存放參數,js實現頁面加載完畢後自動訪問)
1. web模塊
後臺接收到充值請求後,訪問pay工程的Alipay方法,Alipay方法中訪問了支付寶的統一收單下單並支付頁面接口
/**
* @param rechargeMoney:充值金額
* @return:跳轉到一個頁面
*/
@RequestMapping("/loan/toAlipayRecharge")
public String toAlipayRecharge(HttpServletRequest request,
Double rechargeMoney,
Model model) {
model.addAttribute("out_trade_no",rechargeNo);
model.addAttribute("total_amount", rechargeMoney);
model.addAttribute("subject", rechargeRecord.getRechargeDesc());
return "p2pToPay";
}
p2pToPay.html:在頁面加載完成後,自動調用pay模塊的方法
<form method="post" action="http://localhost:9094/pay/api/alipay">
<input type="hidden" name="out_trade_no" th:value="${out_trade_no}">
<input type="hidden" name="total_amount" th:value="${total_amount}">
<input type="hidden" name="subject" th:value="${subject}">
</form>
<script>document.forms[0].submit()</script>
2. pay模塊
pay模塊的alipay方法,該模塊的負責向支付寶統一收單下單並支付頁面接口發起請求,獲取到一個類似上面 p2pToPay.html 頁面的字符串,將該字符串放到一個頁面中,即可跳轉到支付寶的付款頁面
@RequestMapping("/api/alipay")
public String alipay(Model model,
String out_trade_no,
String total_amount,
String subject) throws AlipayApiException {
//獲得初始化的AlipayClient,從AlipayConfig中獲取公共請求參數
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); 有需要的設,這裏就不設置了
//拼接請求的參數
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//請求
String result = alipayClient.pageExecute(alipayRequest).getBody();
//輸出
model.addAttribute("result", result);
return "/payToAlipay";
}
這裏的使用的是jsp頁面,payToAlipay.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${result}
</body>
</html>
3. 同步返回
@RequestMapping("/loan/back")
public String alipayBack(HttpServletRequest request,
Model model,
String out_trade_no) throws Exception {
//轉換後的請求參數
Map<String,String> params = new HashMap<String,String>();
//請求參數
Map<String,String[]> requestParams = request.getParameterMap();
//這段代碼將請求參數的String[]轉爲使用逗號隔開的String字符串
for (Iterator<String> 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] + ",";
}
//亂碼解決,這段代碼在出現亂碼時使用
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
//驗證簽名
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); //調用SDK驗證簽名
if(signVerified){
Map<String ,Object > map = new HashMap<>();
map.put("out_trade_no", out_trade_no);
/*
在這裏調用通過httpClint調用支付寶統一收單線下交易查詢接口,查詢交易是否成功
*/
// 創建httpClient對象
CloseableHttpClient httpClient = HttpClients.createDefault();
// 創建http對象
HttpPost httpPost = new HttpPost(url);
/**
* setConnectTimeout:設置連接超時時間,單位毫秒。
* setConnectionRequestTimeout:設置從connect Manager(連接池)獲取Connection
* 超時時間,單位毫秒。這個屬性是新加的屬性,因爲目前版本是可以共享連接池的。
* setSocketTimeout:請求獲取數據的超時時間(即響應時間),單位毫秒。 如果訪問一個接口,多少時間內無法返回數據,就直接放棄此次調用。
*/
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
httpPost.setConfig(requestConfig);
// 封裝請求參數
packageParam(params, map);
// 創建httpResponse對象
CloseableHttpResponse httpResponse = null;
String result = "";
try {
// 執行請求
httpResponse = httpClient.execute(httpPost);
// 獲取返回結果
if (httpResponse != null && httpResponse.getStatusLine() != null) {
if (httpResponse.getEntity() != null) {
result = EntityUtils.toString(httpResponse.getEntity(), ENCODING);
}
}
} finally {
// 釋放資源
release(httpResponse, httpClient);
}
//result爲響應回來的json字符串,根據查詢結果做出相應的業務處理
}
五、沙箱環境
官方文檔:使用沙箱環境
使用沙箱環境可以在支付寶開發環境下完成一些主要功能和主要邏輯,比如使用沙箱環境進行支付,收款等