支付宝网页支付流程与实现
文章目錄
- 一、運行官方Demo
- 二、時序圖
- 三、請求與響應參數
- 1. 過程1.1的請求參數與響應參數
- 2. 過程8中的請求參數與響應參數
- 四、配置類
- 1. 引入依賴
- 2. 配置一個配置類
- 五、實現
- 1. web模塊
- 2. pay模塊
- 3. 同步返回
- 六、沙箱環境
- 七、收單
- 八、封裝的Template與Vo
- 1. AlipayTemplate
- 2. PayVo
支付寶網站支付官方文檔: https://docs.open.alipay.com/270
一、運行官方Demo
官方文檔:https://opendocs.alipay.com/open/270/106291
首先要在控制臺創建應用,創建完成后,可以得到 appId,后面需要在代碼中配置:
創建應用流程官方文檔:https://opendocs.alipay.com/open/200/105310
B站雷神的視頻:https://www.bilibili.com/video/BV1np4y1C7Yf?p=301
二、時序圖
這里沒有使用第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> <!--支付寶開發軟件包依賴--> <!--https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java--> <dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.16.44.ALL</version> </dependency>2. 配置一個配置類
配置類用來指定公共的請求參數
要改的參數:
- 私鑰與公鑰的生成方法:https://docs.open.alipay.com/291/105971
- appid:注冊支付寶開放平臺就有(https://opendocs.alipay.com/open/200/105310)
- notify_url:異步通知頁面路徑,也可以不指定
- return_url:同步通知頁面路徑,也可以不指定,這里采用的方式是收到同步通知的請求后,調用支付寶的統一收單線下交易查詢接口,確認訂單是否完成
- gatewayUrl :支付寶網關,開發的接口需要在alipay后面加上dev,即alipaydev
五、實現
由于支付這個功能是公共的,所以把這個功能單獨提出來做為一個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字符串,根據查詢結果做出相應的業務處理}六、沙箱環境
官方文檔:使用沙箱環境
使用沙箱環境可以在支付寶開發環境下完成一些主要功能和主要邏輯,比如使用沙箱環境進行支付,收款等
七、收單
訂單在支付頁,不支付,一直刷新,訂單過期了才支付,訂單狀態改為已支付了,但是庫存解鎖了
- 使用支付寶自動收單功能解決。只要一段時間不支付,就不能支付了
由于時延等問題。訂單解鎖完成,正在解鎖庫存的時候,異步通知才到
- 訂單解鎖,手動調用收單
網絡阻塞問題,訂單支付成功的異步通知一直不到達
- 查詢訂單列表時,ajax 獲取當前未支付的訂單狀態,查詢訂單狀態時,再獲取一下支付寶此訂單的狀態
其它各種問題
- 每天晚上閑時下載支付寶對賬單,一一進行對賬。
八、封裝的Template與Vo
1. AlipayTemplate
import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.request.AlipayTradePagePayRequest; import com.atguigu.gulimall.order.vo.PayVo; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;@ConfigurationProperties(prefix = "alipay") @Component @Data public class AlipayTemplate {// 應用ID,您的APPID,收款賬號既是您的APPID對應支付寶賬號public String app_id;// 商戶私鑰,您的PKCS8格式RSA2私鑰public String merchant_private_key;// 支付寶公鑰,查看地址:https://openhome.alipay.com/platform/keyManage.htm 對應APPID下的支付寶公鑰。public String alipay_public_key;// 服務器[異步通知]頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常訪問// 支付寶會悄悄的給我們發送一個請求,告訴我們支付成功的信息public String notify_url;// 頁面跳轉同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常訪問//同步通知,支付成功,一般跳轉到成功頁public String return_url;// 簽名方式private String sign_type;// 字符編碼格式private String charset;//訂單超時時間private String timeout = "1m";// 支付寶網關; https://openapi.alipaydev.com/gateway.dopublic String gatewayUrl;public String pay(PayVo vo) throws AlipayApiException {//AlipayClient alipayClient = new DefaultAlipayClient(AlipayTemplate.gatewayUrl, AlipayTemplate.app_id, AlipayTemplate.merchant_private_key, "json", AlipayTemplate.charset, AlipayTemplate.alipay_public_key, AlipayTemplate.sign_type);//1、根據支付寶的配置生成一個支付客戶端AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl,app_id, merchant_private_key, "json",charset, alipay_public_key, sign_type);//2、創建一個支付請求 //設置請求參數AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();alipayRequest.setReturnUrl(return_url);alipayRequest.setNotifyUrl(notify_url);//商戶訂單號,商戶網站訂單系統中唯一訂單號,必填String out_trade_no = vo.getOut_trade_no();//付款金額,必填String total_amount = vo.getTotal_amount();//訂單名稱,必填String subject = vo.getSubject();//商品描述,可空String body = vo.getBody();alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","+ "\"total_amount\":\""+ total_amount +"\","+ "\"subject\":\""+ subject +"\","+ "\"body\":\""+ body +"\","+ "\"timeout_express\":\""+timeout+"\","+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");String result = alipayClient.pageExecute(alipayRequest).getBody();//會收到支付寶的響應,響應的是一個頁面,只要瀏覽器顯示這個頁面,就會自動來到支付寶的收銀臺頁面System.out.println("支付寶的響應:"+result);return result;} }AlipayTemplate 對象中的屬性可以直接在 application 配置文件中進行配置。
2. PayVo
@Data public class PayVo {private String out_trade_no; // 商戶訂單號 必填private String subject; // 訂單名稱 必填private String total_amount; // 付款金額 必填private String body; // 商品描述 可空 }總結
以上是生活随笔為你收集整理的支付宝网页支付流程与实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 表单美化设置
- 下一篇: 2015 2020 r4烧录卡 区别_2