支付宝网页支付流程与实现
文章目錄
- 一、運行官方Demo
- 二、時序圖
- 三、請求與響應(yīng)參數(shù)
- 1. 過程1.1的請求參數(shù)與響應(yīng)參數(shù)
- 2. 過程8中的請求參數(shù)與響應(yīng)參數(shù)
- 四、配置類
- 1. 引入依賴
- 2. 配置一個配置類
- 五、實現(xiàn)
- 1. web模塊
- 2. pay模塊
- 3. 同步返回
- 六、沙箱環(huán)境
- 七、收單
- 八、封裝的Template與Vo
- 1. AlipayTemplate
- 2. PayVo
支付寶網(wǎng)站支付官方文檔: https://docs.open.alipay.com/270
一、運行官方Demo
官方文檔:https://opendocs.alipay.com/open/270/106291
首先要在控制臺創(chuàng)建應(yīng)用,創(chuàng)建完成后,可以得到 appId,后面需要在代碼中配置:
創(chuàng)建應(yīng)用流程官方文檔:https://opendocs.alipay.com/open/200/105310
B站雷神的視頻:https://www.bilibili.com/video/BV1np4y1C7Yf?p=301
二、時序圖
這里沒有使用第7步,只使用的第8步
三、請求與響應(yīng)參數(shù)
全部的參數(shù):
- 統(tǒng)一收單下單并支付頁面接口
- 統(tǒng)一收單線下交易查詢
這里只介紹一下使用到的必填的參數(shù):
1. 過程1.1的請求參數(shù)與響應(yīng)參數(shù)
公共請求參數(shù):
- app_id:必填,支付寶分配給開發(fā)者的應(yīng)用ID
- method:接口名稱
- return_url:HTTP/HTTPS開頭字符串,時序圖中第六步回調(diào)的地址
- charset:請求使用的編碼格式,如utf-8,gbk,gb2312等
- sign_type:商戶生成簽名字符串所使用的簽名算法類型,目前支持RSA2和RSA,推薦使用RSA2,這里使用的是RSA2
- sign:商戶請求參數(shù)的簽名串
- timestamp:時間戳,發(fā)送請求的時間,格式"yyyy-MM-dd HH:mm:ss"
- version:調(diào)用的接口版本,固定為:1.0
- notify_url:支付寶服務(wù)器主動通知商戶服務(wù)器里指定的頁面http/https路徑。要求可以在公網(wǎng)上訪問的地址
業(yè)務(wù)請求參數(shù):
- out_trade_no:商戶訂單號,64個字符以內(nèi)、可包含字母、數(shù)字、下劃線;需保證在商戶端不重復(fù)
- product_code:僅支持"FAST_INSTANT_TRADE_PAY"
- total_amount:訂單總金額,單位為元,精確到小數(shù)點后兩位,取值范圍[0.01,100000000]。
- subject:訂單標(biāo)題,
響應(yīng)參數(shù)(過程6的同步返回請求參數(shù)):
- code:通信標(biāo)識,10000表示通信成功
- out_trade_no:商戶訂單號
- total_amount:交易金額
2. 過程8中的請求參數(shù)與響應(yīng)參數(shù)
公共請求參數(shù)同上,業(yè)務(wù)請求參數(shù)有:
- out_trade_no:訂單支付時傳入的商戶訂單號,和支付寶交易號不能同時為空。
- trade_no:支付寶交易號,和商戶訂單號不能同時為空
響應(yīng)參數(shù):
- code:通信標(biāo)識,10000表示通信成功
- trade_status:交易狀態(tài),有四種情況:
- WAIT_BUYER_PAY(交易創(chuàng)建,等待買家付款)
- TRADE_CLOSED(未付款交易超時關(guān)閉,或支付完成后全額退款)
- TRADE_SUCCESS(交易支付成功)
- TRADE_FINISHED(交易結(jié)束,不可退款)
四、配置類
1. 引入依賴
<!--日志依賴--> <dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId> </dependency> <!--支付寶開發(fā)軟件包依賴--> <!--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. 配置一個配置類
配置類用來指定公共的請求參數(shù)
要改的參數(shù):
- 私鑰與公鑰的生成方法:https://docs.open.alipay.com/291/105971
- appid:注冊支付寶開放平臺就有(https://opendocs.alipay.com/open/200/105310)
- notify_url:異步通知頁面路徑,也可以不指定
- return_url:同步通知頁面路徑,也可以不指定,這里采用的方式是收到同步通知的請求后,調(diào)用支付寶的統(tǒng)一收單線下交易查詢接口,確認(rèn)訂單是否完成
- gatewayUrl :支付寶網(wǎng)關(guān),開發(fā)的接口需要在alipay后面加上dev,即alipaydev
五、實現(xiàn)
由于支付這個功能是公共的,所以把這個功能單獨提出來做為一個pay模塊,而頁面是在web模塊,從web訪問pay的兩種方式:
- 直接在后臺重定向到pay中的地址,缺點:只能發(fā)送get請求,在瀏覽器地址欄中中會看到參數(shù)一閃而過
- 先請求轉(zhuǎn)發(fā)到一個web中的頁面,該頁面在加載完成后,自動訪問pay模塊中的地址(使用form表單發(fā)送post請求,隱藏域存放參數(shù),js實現(xiàn)頁面加載完畢后自動訪問)
1. web模塊
后臺接收到充值請求后,訪問pay工程的Alipay方法,Alipay方法中訪問了支付寶的統(tǒng)一收單下單并支付頁面接口
/** * @param rechargeMoney:充值金額 * @return:跳轉(zhuǎn)到一個頁面 */ @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:在頁面加載完成后,自動調(diào)用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方法,該模塊的負(fù)責(zé)向支付寶統(tǒng)一收單下單并支付頁面接口發(fā)起請求,獲取到一個類似上面 p2pToPay.html 頁面的字符串,將該字符串放到一個頁面中,即可跳轉(zhuǎn)到支付寶的付款頁面
@RequestMapping("/api/alipay") public String alipay(Model model,String out_trade_no,String total_amount,String subject) throws AlipayApiException {//獲得初始化的AlipayClient,從AlipayConfig中獲取公共請求參數(shù)AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);//設(shè)置同步響應(yīng)請求參數(shù)AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();alipayRequest.setReturnUrl(AlipayConfig.return_url);//alipayRequest.setNotifyUrl(AlipayConfig.notify_url); 有需要的設(shè),這里就不設(shè)置了//拼接請求的參數(shù)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 {//轉(zhuǎn)換后的請求參數(shù)Map<String,String> params = new HashMap<String,String>();//請求參數(shù)Map<String,String[]> requestParams = request.getParameterMap();//這段代碼將請求參數(shù)的String[]轉(zhuǎn)為使用逗號隔開的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] + ",";}//亂碼解決,這段代碼在出現(xiàn)亂碼時使用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); //調(diào)用SDK驗證簽名if(signVerified){ Map<String ,Object > map = new HashMap<>();map.put("out_trade_no", out_trade_no);/*在這里調(diào)用通過httpClint調(diào)用支付寶統(tǒng)一收單線下交易查詢接口,查詢交易是否成功*/// 創(chuàng)建httpClient對象CloseableHttpClient httpClient = HttpClients.createDefault();// 創(chuàng)建http對象HttpPost httpPost = new HttpPost(url);/*** setConnectTimeout:設(shè)置連接超時時間,單位毫秒。* setConnectionRequestTimeout:設(shè)置從connect Manager(連接池)獲取Connection* 超時時間,單位毫秒。這個屬性是新加的屬性,因為目前版本是可以共享連接池的。* setSocketTimeout:請求獲取數(shù)據(jù)的超時時間(即響應(yīng)時間),單位毫秒。 如果訪問一個接口,多少時間內(nèi)無法返回數(shù)據(jù),就直接放棄此次調(diào)用。*/RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();httpPost.setConfig(requestConfig);// 封裝請求參數(shù)packageParam(params, map);// 創(chuàng)建httpResponse對象CloseableHttpResponse httpResponse = null;String result = "";try {// 執(zhí)行請求httpResponse = httpClient.execute(httpPost);// 獲取返回結(jié)果if (httpResponse != null && httpResponse.getStatusLine() != null) {if (httpResponse.getEntity() != null) {result = EntityUtils.toString(httpResponse.getEntity(), ENCODING);}}} finally {// 釋放資源release(httpResponse, httpClient);}//result為響應(yīng)回來的json字符串,根據(jù)查詢結(jié)果做出相應(yīng)的業(yè)務(wù)處理}六、沙箱環(huán)境
官方文檔:使用沙箱環(huán)境
使用沙箱環(huán)境可以在支付寶開發(fā)環(huán)境下完成一些主要功能和主要邏輯,比如使用沙箱環(huán)境進(jìn)行支付,收款等
七、收單
訂單在支付頁,不支付,一直刷新,訂單過期了才支付,訂單狀態(tài)改為已支付了,但是庫存解鎖了
- 使用支付寶自動收單功能解決。只要一段時間不支付,就不能支付了
由于時延等問題。訂單解鎖完成,正在解鎖庫存的時候,異步通知才到
- 訂單解鎖,手動調(diào)用收單
網(wǎng)絡(luò)阻塞問題,訂單支付成功的異步通知一直不到達(dá)
- 查詢訂單列表時,ajax 獲取當(dāng)前未支付的訂單狀態(tài),查詢訂單狀態(tài)時,再獲取一下支付寶此訂單的狀態(tài)
其它各種問題
- 每天晚上閑時下載支付寶對賬單,一一進(jìn)行對賬。
八、封裝的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 {// 應(yīng)用ID,您的APPID,收款賬號既是您的APPID對應(yīng)支付寶賬號public String app_id;// 商戶私鑰,您的PKCS8格式RSA2私鑰public String merchant_private_key;// 支付寶公鑰,查看地址:https://openhome.alipay.com/platform/keyManage.htm 對應(yīng)APPID下的支付寶公鑰。public String alipay_public_key;// 服務(wù)器[異步通知]頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數(shù),必須外網(wǎng)可以正常訪問// 支付寶會悄悄的給我們發(fā)送一個請求,告訴我們支付成功的信息public String notify_url;// 頁面跳轉(zhuǎn)同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數(shù),必須外網(wǎng)可以正常訪問//同步通知,支付成功,一般跳轉(zhuǎn)到成功頁public String return_url;// 簽名方式private String sign_type;// 字符編碼格式private String charset;//訂單超時時間private String timeout = "1m";// 支付寶網(wǎng)關(guān); 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、根據(jù)支付寶的配置生成一個支付客戶端AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl,app_id, merchant_private_key, "json",charset, alipay_public_key, sign_type);//2、創(chuàng)建一個支付請求 //設(shè)置請求參數(shù)AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();alipayRequest.setReturnUrl(return_url);alipayRequest.setNotifyUrl(notify_url);//商戶訂單號,商戶網(wǎng)站訂單系統(tǒng)中唯一訂單號,必填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();//會收到支付寶的響應(yīng),響應(yīng)的是一個頁面,只要瀏覽器顯示這個頁面,就會自動來到支付寶的收銀臺頁面System.out.println("支付寶的響應(yīng):"+result);return result;} }AlipayTemplate 對象中的屬性可以直接在 application 配置文件中進(jìn)行配置。
2. PayVo
@Data public class PayVo {private String out_trade_no; // 商戶訂單號 必填private String subject; // 訂單名稱 必填private String total_amount; // 付款金額 必填private String body; // 商品描述 可空 }總結(jié)
以上是生活随笔為你收集整理的支付宝网页支付流程与实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 表单美化设置
- 下一篇: 2015 2020 r4烧录卡 区别_2