工程搭建
創建youxin_pay_interface的jar工程,依賴pojo
創建youxin_pay_service的war工程,除了項目依賴,需要加入支付的依賴
<!-- 支付寶支付所需類庫包 -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>3.4.27.ALL</version>
</dependency>
配置sring-pay-service.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.youxin"/>
<!-- 創建AlipayClient -->
<bean id="payClient" class="com.alipay.api.DefaultAlipayClient">
<constructor-arg name="serverUrl" value="${serverUrl}"/>
<constructor-arg name="appId" value="${appId}"/>
<constructor-arg name="privateKey" value="${privateKey}"/>
<constructor-arg name="alipayPublicKey" value="${alipayPulicKey}"/>
<constructor-arg name="format" value="${format}"/>
<constructor-arg name="charset" value="${charset}"/>
<constructor-arg name="signType" value="${signType}"/>
</bean>
</beans>
配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<!-- spring監聽 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- spring配置文件初始化 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-*.xml</param-value>
</context-param>
</web-app>
配置alipay.properties
serverUrl=https://openapi.alipay.com/gateway.do
appId=*******
privateKey=生成的應用私鑰
alipayPulicKey=支付寶公鑰
format=json
charset=utf-8
signType=RSA2
注意
在spring的配置文件中加載alipay.properties(我們已經設置依賴了common層導入所有properties)
向支付寶發起預支付,前端接收預支付返回信息生成付款二維碼
Map用來存儲預支付響應信息,如果發起預支付成功,將訂單號和支付總金額以及二維碼碼串返回給前端頁面
響應結果示例
service層代碼
@Autowired
private AlipayClient alipayClient;
public Map createNative(String out_trade_no, String total_fee) {
Map<String, String> map = new HashMap<String, String>();
// 創建預下單請求對象
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
// 設置業務參數
request.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\","
+ "\"total_amount\":\"" + total_fee + "\","
+ "\"subject\":\"在線支付\","
+ "\"timeout_express\":\"90m\"}");
// 發出預下單業務請求
try {
AlipayTradePrecreateResponse response = alipayClient.execute(request);
// 從相應對象讀取相應結果
String code = response.getCode();
System.out.println("支付寶接口響應碼:" + code);
// 全部的響應結果
String body = response.getBody();
System.out.println("支付寶返回結果:" + body);
if (code.equals("10000")) {
map.put("qrcode", response.getQrCode());
map.put("out_trade_no", response.getOutTradeNo());
map.put("total_fee", total_fee);
System.out.println("返回qrcode:" + response.getQrCode());
System.out.println("返回out_trade_no:" + response.getOutTradeNo());
System.out.println("返回total_fee:" + total_fee);
} else {
System.out.println("預下單接口調用失敗:" + body);
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
return map;
}
檢測支付狀態
實現思路:
通過AlipayClient實現對交易查詢接口(alipay.trade.query)的調用。阿里支付api
交易查詢接口請求關鍵參數:
響應關鍵參數
service層
public Map queryPayStatus(String out_trade_no) {
Map<String, String> map = new HashMap<String, String>();
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
// 設置業務參數
request.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\","
+ "\"trade_no\":\"\"}");
// 發出請求
try {
AlipayTradeQueryResponse response = alipayClient.execute(request);
String code = response.getCode();
System.out.println("查詢交易狀態--返回值1:" + code);
System.out.println("查詢交易狀態--返回值2:" + response.getBody());
if (code.equals("10000")) {
map.put("out_trade_no", out_trade_no);
map.put("tradestatus", response.getTradeStatus());
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
return map;
}
controller層
先調用service層的方法去查詢支付狀態,service層會將查詢出來的結果封裝進map集合返回,controller拿到集合中交易狀態,懟它進行判斷
支付狀態的查詢如果是等待買家付款就需要一直查詢支付狀態,通過循環的方式實現
/*
查詢支付狀態
*/
public Result queryPayStatus(String out_trade_no){
Result result = null;
int x = 0;
while (true) {
Map map = new HashMap();
try {
map = payService.queryPayStatus(out_trade_no);
} catch (Exception e) {
e.printStackTrace();
}
String tradestatus = (String) map.get("tradestatus");
if (tradestatus!=null && tradestatus.equals("TRADE_CLOSED") ){
result = new Result(false,"未付款交易超時關閉,或支付完成後全額退款");
break;
}
if (tradestatus!=null && tradestatus.equals("TRADE_SUCCESS")){
result = new Result(true,"支付成功");
break;
}
if (tradestatus!=null && tradestatus.equals("TRADE_FINISHED")){
result = new Result(false,"交易結束,不可退款");
break;
}
//時間間隔三秒再去循環校驗支付狀態
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 定義一個過期時間,可以參考登錄信息的過期時間
// 爲了測試,可以設置30秒的過期時間,即 x 約 10次,實際時可以設置和用戶登錄過期時間
x++;
if(x>10){
result = new Result(false,"二維碼已失效!");
break;
}
}
return result;
}
支付日誌
(1)系統中無法查詢到支付記錄
(2)支付後訂單狀態沒有改變
實現思路:
(1)在用戶下訂單時就向支付日誌表添加一條記錄,信息包括支付總金額、訂單ID(多個)、用戶ID、下單時間等信息,支付狀態爲0(未支付)
(2)生成的支付日誌對象放入redis中,以用戶ID作爲key,這樣在生成支付二維碼時就可以從redis中提取支付日誌對象中的金額和訂單號。
(3)當用戶支付成功後,修改支付日誌的支付狀態爲1(已支付),並記錄支付寶傳遞給我們的交易流水號。根據訂單ID(多個)修改訂單的狀態爲2(已付款)。
在創建的訂單同時創建支付日誌
支付控制層從redis中獲取訂單號和總金額生成支付二維碼
監聽支付狀態的同時,如果支付成功,更新支付日誌和訂單狀態