springboot+vue+對接支付寶介面+二維碼掃描支付功能(沙箱環境)
1. 什麼是支付寶介面(沙箱環境)?
記錄時間:2020年10月15日3:55
現如今,手機支付已相當普遍,而作為開發人員應該對手機支付操作有所瞭解。而支付寶介面是支付寶提供的一個介面,用來對接軟體應用程式在進行金錢交易使用。然後對於程式設計愛好者而言,想學習這一點就有點難,因為要想使用支付寶介面,必須前提是使用軟體應用程式,軟體應用程式需要向支付寶申請,提交一系列資料,這一點是實現不了的。這就對開發者增加了一定的難度,因為產品沒有上線,然後需要對接支付寶介面就是很大的問題,所以出現了沙箱環境,具有虛擬的使用者和管理員賬戶,進行實驗測試是否對接成功。接下來就根據我的經驗,簡單的介紹一下我的使用和學習過程。
使用技術+程式設計軟體:
springboot(idea)vue + elementui(HBuilderX)+ vue-qr(vue生成二維碼框架)NATAPP(連線外網,實現支付寶回撥)websocket(實現前端響應)
步驟:
準備沙箱環境JAVA + springboot 中使用 SDK 連線支付寶介面配置前端使用vue+elementui頁面設計需要注意點結果測試
首先建立應用,這裡是沙箱環境,先準備沙箱環境:
百度搜索**支付寶螞蟻金服**,登入支付包賬號,出現如下顯示:
這裡是建立應用的地方,也就是說有專案要上線時,在這裡申請。使用沙箱環境的話,點選左上角開放平臺,然後往下拉,會出現沙箱二字,點選進入即可:
然後就可以看到支付寶官網介紹的沙箱環境的使用,其實官網已經介紹的非常詳細了,如有小夥伴們看著嫌麻煩,可以根據我學習的步驟作為參考。在如何使用沙箱環境下點選沙箱應用連結,去配置自己的沙箱環境。
點選 i 符號,上面有提示連結,如何生成RSA2金鑰,這裡就不詳細介紹了,官網寫的非常明白,我這麼笨的人也可以學會,相信小夥伴們也可以學會的。按照步驟會生成兩個檔案:應用公鑰和應用私鑰。記住,是應用公鑰,而不是支付寶公鑰。這裡學習過程中沒有使用證書模式。
然後點選RSA2的設定/檢視。將產生的應用公鑰複製進去就可以了,而下面會生成支付寶公鑰,在後續連線過程中非常重要,需要區分使用的是支付寶公鑰還是應用公鑰
JAVA + springboot 中使用 SDK 連線支付寶介面配置
這裡是官網使用SDK的方法,這裡使用舊版的方式,新版的方式我使用過程中出現某些問題不會解決。
使用之前,先封裝幾個配置,第一個是連線支付寶的配置:
import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.internal.util.AlipaySignature; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; /** * 支付寶介面配置類 */ @Configuration public class PayConfig { // 請填寫您的AppId,例如:2019091767145019(必填) private static final String appID = "2016102500758313"; //應用私鑰,這裡修改生成的私鑰即可(必填) private static final String privateKey = ""; //支付寶公鑰,而非應用公鑰(必填) public static final String publicKey = ""; //預設即可(必填) public static final String charset = "utf-8"; //預設即可(必填) public static final String signType = "RSA2"; @Bean public AlipayClient alipayClient(){ //沙箱環境使用https://openapi.alipaydev.com/gateway.do,線上環境使用https://openapi.alipay.com/gateway.do return new DefaultAlipayClient("https://openapi.alipaydev.com/gateway.do",appID,privateKey,"json",charset,publicKey,signType); } /** * 驗籤,是否正確 */ public static boolean checkSign(HttpServletRequest request){ Map<String,String[]> requestMap = request.getParameterMap(); Map<String,String> paramsMap = new HashMap<>(); requestMap.forEach((key,values) -> { String strs = ""; for(String value : values) { strs = strs + value; } System.out.println(key +"===>"+strs); paramsMap.put(key,strs); }); System.out.println(); //呼叫SDK驗證簽名 try { return AlipaySignature.rsaCheckV1(paramsMap,PayConfig.publicKey,PayConfig.charset,PayConfig.signType); } catch (AlipayApiException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println("*********************驗籤失敗********************"); return false; } } }
然後封裝一個支付寶回撥的引數物件,這裡就不需要自己手動去獲取引數了
import java.io.Serializable; /** * 支付寶回撥引數 */ public class AliReturnPayBean implements Serializable { /** * */ private static final long serialVersionUID = 1L; /** * 開發者的app_id */ private String app_id; /** * 商戶訂單號 */ private String out_trade_no; /** * 簽名 */ private String sign; /** * 交易狀態 */ private String trade_status; /** * 支付寶交易號 */ private String trade_no; /** * 交易的金額 */ private String total_amount; public String getTotal_amount() { return total_amount; } public void setTotal_amount(String total_amount) { this.total_amount = total_amount; } public String getApp_id() { return app_id; } public void setApp_id(String app_id) { this.app_id = app_id; } public String getOut_trade_no() { return out_trade_no; } public void setOut_trade_no(String out_trade_no) { this.out_trade_no = out_trade_no; } public String getSign() { return sign; } public void setSign(String sign) { this.sign = sign; } public String getTrade_status() { return trade_status; } public void setTrade_status(String trade_status) { this.trade_status = trade_status; } public String getTrade_no() { return trade_no; } public void setTrade_no(String trade_no) { this.trade_no = trade_no; } @Override public String toString() { return "AliReturnPayBean [app_id=" + app_id + ",out_trade_no=" + out_trade_no + ",sign=" + sign + ",trade_status=" + trade_status + ",trade_no=" + trade_no + "]"; } }
然後寫一個控制層去連線支付寶,控制層必須是@Controller修飾,而不是@RestController修飾,因為支付寶的回撥函式裡面返回的是請求。具體事例如下:
前提:在pom.xml 中匯入SDK依賴:
<dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.10.145.ALL</version> </dependency>
package com.example.zhifubaozhifu.controller; import com.alibaba.fastjson.JSON; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.request.AlipayTradePrecreateRequest; import com.alipay.api.response.AlipayTradePrecreateResponse; import com.example.zhifubaozhifu.config.PayConfig; import com.example.zhifubaozhifu.util.AliReturnPayBean; import com.example.zhifubaozhifu.util.Shop; import com.example.zhifubaozhifu.util.WebSocket; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.math.BigDecimal; @Controller @Slf4j public class Test { @Autowired private AlipayClient alipayClient; @Autowired private WebSocket webSocket; @RequestMapping("/createQR") @ResponseBody public String send() throws AlipayApiException { AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); //建立API對應的request類 // 在下面會介紹notifyUrl怎麼來的 request.setNotifyUrl("http://cv95x3.natappfree.cc/call"); //同步回撥地址 // request.setReturnUrl(""); request.setBizContent(" {" + " \"primary_industry_name\":\"IT科技/IT軟體與服務\"," + " \"primary_industry_code\":\"10001/20102\"," + " \"secondary_industry_code\":\"10001/20102\"," + " \"secondary_industry_name\":\"IT科技/IT軟體與服務\"" + " }");; AlipayTradePrecreateResponse response = alipayClient.execute(request); String path = "zhifu.jpg"; if (response.isSuccess()) { System.out.println("呼叫成功"); return response.getQrCode(); } else { System.out.println("呼叫失敗"); } return ""; } /** * 支付寶回撥函式 * @param request * @param response * @param returnPay * @throws IOException */ @RequestMapping("/call") public void call(HttpServletRequest request,HttpServletResponse response,AliReturnPayBean returnPay) throws IOException { response.setContentType("type=text/html;charset=UTF-8"); log.info("支付寶的的回撥函式被呼叫"); if (!PayConfig.checkSign(request)) { log.info("驗籤失敗"); response.getWriter().write("failture"); return; } if (returnPay == null) { log.info("支付寶的returnPay返回為空"); response.getWriter().write("success"); return; } log.info("支付寶的returnPay" + returnPay.toString()); //表示支付成功狀態下的操作 if (returnPay.getTrade_status().equals("TRADE_SUCCESS")) { log.info("支付寶的支付狀態為TRADE_SUCCESS"); //業務邏輯處理,webSocket在下面會有介紹配置 webSocket.sendMessage("true"); } response.getWriter().write("success"); } }
前端使用vue+elementui頁面設計:
vue專案的搭建這裡就不介紹,首先準備環境,新增 vue-qr 外掛:
npm install vue-qr --save
前端程式碼:
<template> <div> <!-- 支付按鈕,模擬支付操作 --> <van-button type="primary" @click="pay">支付</van-button> <el-dialog :title="paySucc?'支付成功':'掃碼支付'" :visible.sync="dialogVisible" width="16%" center> <!-- 生成二維碼圖片 --> <vueQr :text="text" :size="200" v-if="!paySucc"></vueQr> <!-- 使用websocket監控是否掃描,掃描成功顯示成功並退出介面 --> <span class="iconfont icon-success" style="position: relative;font-size: 100px;color:#42B983;margin-left: 50px;top:-10px;" v-else></span> </el-dialog> </div> </template> <script> import vueQr from 'vue-qr' export default { data() { return { dialogVisible: false,text: "",paySucc: false } },components: { vueQr },methods: { pay() { let _this = this; _this.paySucc = false; _this.dialogVisible = true; this.axios.request("http://localhost:8081/createQR") .then((response) => { _this.text = response.data; _this.dialogVisible = true; //使用webSocket傳送請求,下面會簡單介紹websocket使用 if ("WebSocket" in window) { // 開啟一個 web socket var ws = new WebSocket("ws://localhost:8081/bindingRecord"); ws.onopen = function() { // Web Socket 已連線上,使用 send() 方法傳送資料 // ws.send("data"); // alert("資料傳送中..."); }; ws.onmessage = function(evt) { var received_msg = evt.data; // alert("資料已接收..." + evt.data); if (Boolean(evt.data)) { _this.paySucc = true; setTimeout(() => { _this.dialogVisible = false; },3 * 1000); } ws.close(); }; ws.onclose = function() { // // 關閉 websocket console.log("連線已關閉..."); }; } else { // 瀏覽器不支援 WebSocket alert("您的瀏覽器不支援 WebSocket!"); } }).catch((err) => { console.log(err) }) },back(dataUrl,id) { console.log(dataUrl,id) } } } </script> <style> .btn { margin-left: 100px; } </style>
這樣前後端程式碼就準備就緒了,再上面的程式碼中有兩個需要注意的: 第一個問題是:notifyUrl使用的url是外網地址,並不是IP地址,否則會無法回撥。但是在學習環境下使用不了外網地址。這就需要使用**NATAPP**。百度搜索NATAPp官網,點進去下載,它是一個natapp.exe 應用,這有這個。在官網註冊賬號,點選購買免費隧道(免費隧道只能購買兩個,要珍惜哦。隧道協議寫web,其他的就按要本地環境配置就可以了)。購買之後就會有下面一條自己的隧道
雙擊natapp.exe,進入控制檯,輸入以下命令,開啟隧道:
# 這裡的值是上面的authtoken的值 natapp --authtoken=值
之後如下顯示:
這裡的外網連線地址,就是notifyUrl的地址,然後再加上方法mapping路徑即可,如我的是:http://cv95x3.natappfree.cc/call
第二個問題是:支付成功後,回撥函式執行了,如何告訴前端我已經支付成功,同時關閉掃描二維碼按鈕,這裡就用到了websocket,這裡不詳細介紹websocket是什麼,只要知道一點,它是可以監聽到後端是否傳送資訊。首先在pom.xml中匯入依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> <version>2.3.4.RELEASE</version> </dependency>
然後建立一個webconfig的配置類:
package com.example.zhifubaozhifu.util; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.socket.server.standard.ServerEndpointExporter; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; /** * @desc: WebSocket服務 * **/ //連線webSocket服務的URL @ServerEndpoint("/bindingRecord") @Component @Slf4j public class WebSocket { private Session session; private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>(); /** * 新建webSocket配置類 * @return */ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } /** * 建立連線 * @param session */ @OnOpen public void onOpen(Session session) { this.session = session; webSockets.add(this); log.info("【新建連線】,連線總數:{}",webSockets.size()); } /** * 斷開連線 */ @OnClose public void onClose(){ webSockets.remove(this); log.info("【斷開連線】,連線總數:{}",webSockets.size()); } /** * 接收到資訊 * @param message */ @OnMessage public void onMessage(String message){ log.info("【收到】,客戶端的資訊:{},連線總數:{}",message,webSockets.size()); } /** * 傳送訊息 * @param message */ public void sendMessage(String message){ log.info("【廣播發送】,資訊:{},總連線數:{}",webSockets.size()); for (WebSocket webSocket : webSockets) { try { webSocket.session.getBasicRemote().sendText(message); } catch (IOException e) { log.info("【廣播發送】,資訊異常:{}",e.fillInStackTrace()); } } } }
然後使用的時候呼叫方法onMessage即可接收訊息,使用onMessage即可廣發訊息。
前端使用步驟:
<script type="text/javascript"> function WebSocketTest() { if ("WebSocket" in window) { alert("您的瀏覽器支援 WebSocket!"); // 開啟一個 web socket var ws = new WebSocket("ws://localhost:9998/echo"); ws.onopen = function() { // Web Socket 已連線上,使用 send() 方法傳送資料 ws.send("傳送資料"); alert("資料傳送中..."); }; ws.onmessage = function (evt) { var received_msg = evt.data; alert("資料已接收..."); }; ws.onclose = function() { // 關閉 websocket alert("連線已關閉..."); }; } else { // 瀏覽器不支援 WebSocket alert("您的瀏覽器不支援 WebSocket!"); } } </script>
想詳細瞭解的話,可以去菜鳥教程學習。
使用思路: 前端先建立websocket,連線到後端websocket ,這樣才能將websocket通道連線。當支付成功之後,後端向前端反饋支付成功資訊,前端監控接收到訊息後做處理,即關閉二維碼對話方塊。
測試結果:
然後下載支付寶沙箱版手機即可掃描模擬支付,在螞蟻金服的沙箱環境中就有二維碼下載,如下圖:
這裡指記錄了支付到回撥處理的操作,再這之上還可進行進一步的封裝。
搞定,記錄結束。
結束時間:2020年10月15日6:12
到此這篇關於springboot+vue+對接支付寶介面+二維碼掃描支付(沙箱環境)的文章就介紹到這了,更多相關springboot對接支付寶支付介面內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!