SpringBoot接入支付寶教程
阿新 • • 發佈:2021-08-19
碼雲地址:
前言:
今天在一個公眾號看到了這樣一篇文章,寫的是:SpringBoot接入支付寶,看了那篇文章發現寫的比較一般,本著求知的心態,今天想著接觸下接入支付寶,然後寫個部落格~
參考部落格:
springboot接入支付寶支付-新版SDK(2020/11/03)
使用到的支付寶開發網址:
下面需要使用到的連結或者祕鑰我都會用紅色標註出來
一、環境搭建
1、應用公鑰/私鑰的獲取
進入到支付寶開發助手-->MiniU研發工作臺,以前的話應該是讓下載軟體什麼的獲取祕鑰,現在可以直接通過網頁進行獲取了
複製下來生成的公鑰跟私鑰,記到小本子上面~
2、APPID、支付寶公鑰的獲取
進入到支付寶開放平臺-沙箱環境,複製我們剛剛生成的公鑰,放在對應的框框中即可生成"支付寶公鑰"
複製你的APPID
複製生成的支付寶公鑰~
記得複製下來支付寶提供的測試賬號,下面測試要用:
3、內網穿透設定
我使用的是Windows環境,這裡我就下載Windows版本的ngrok,這是國外的網站有的小夥伴下載比較慢,可以使用藍奏雲下載:https://wws.lanzoui.com/izXZZsuw9wb
下載完成之後,使用命令列進入到解壓後的目錄,複製內網穿透的連結:注意先別把視窗關閉了。
1、進入目錄:滑鼠放到導航欄上面,輸入cmd,即可快速進入 2、啟動應用:ngrok http 埠
二、程式碼整理
新建SpringBoot專案(只需新增SpringBootWeb依賴即可),專案結構如下:
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-easysdk</artifactId> <version>2.1.0</version> </dependency>
application.properties
## 埠 server.port=8080 ## 支付寶配置 # 應用ID alipay.appId=把你的應用id填到這裡 # 應用私鑰 alipay.privateKey=把你的應用私鑰填到這裡 # 支付寶公鑰,注意不是生成的應用公鑰 alipay.publicKey=把你的支付寶公鑰填到這裡 #支付閘道器配置,這一項是寫死的,正式環境是openapi.alipay.com alipay.gateway=openapi.alipaydev.com # 支付寶前臺跳轉地址 alipay.returnUrl=生成的外網穿透連結/return_url.html # 支付寶後臺通知地址 alipay.notifyUrl=生成的外網穿透連結/api/alipay/notify_url # 支付寶前臺手機網頁支付中途取消跳轉地址 alipay.errorUrl=生成的外網穿透連結/error_url.html
config/AlipayConfig.java
/** * @author zhangzhixi * @version 1.0 * @date 2021-8-19 15:02 */ import com.alipay.easysdk.factory.Factory; import com.alipay.easysdk.kernel.Config; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class AlipayConfig implements ApplicationRunner { // 應用id @Value("${alipay.appId}") private String appId; // 私鑰 @Value("${alipay.privateKey}") private String privateKey; // 公鑰 @Value("${alipay.publicKey}") private String publicKey; // 支付寶閘道器 @Value("${alipay.gateway}") private String gateway; // 支付成功後的介面回撥地址,不是回撥的友好頁面,不要弄混了 @Value("${alipay.notifyUrl}") private String notifyUrl; /** * 專案初始化事件 * */ @Override public void run(ApplicationArguments args) throws Exception { //初始化支付寶SDK Factory.setOptions(getOptions()); System.out.println("**********支付寶SDK初始化完成**********"); } private Config getOptions() { //這裡省略了一些不必要的配置,可參考文件的說明 Config config = new Config(); config.protocol = "https"; config.gatewayHost = this.gateway; config.signType = "RSA2"; config.appId = this.appId; // 為避免私鑰隨原始碼洩露,推薦從檔案中讀取私鑰字串而不是寫入原始碼中 config.merchantPrivateKey = this.privateKey; // 注:如果採用非證書模式,則無需賦值上面的三個證書路徑,改為賦值如下的支付寶公鑰字串即可 config.alipayPublicKey = this.publicKey; // 可設定非同步通知接收服務地址(可選) config.notifyUrl = notifyUrl; return config; } }View Code
controller/AlipayController.java
/** * @author zhangzhixi * @version 1.0 * @date 2021-8-19 15:06 */ import com.alipay.easysdk.factory.Factory; import com.zhixi.aliyunpay.service.AlipayService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/api/alipay") public class AlipayController { private final AlipayService alipayService; @Autowired public AlipayController(AlipayService alipayService) { this.alipayService = alipayService; } /** * 支付寶電腦網頁支付 * @param subject: 訂單名稱 * @param total: 金額 * @return java.lang.String */ @PostMapping("/page") public String page(String subject, String total) { subject = "測試支付"; total = "1000"; return alipayService.page(subject, total); } /** * 支付寶手機網頁支付 * @param subject: 訂單名稱 * @param total: 金額 * @return java.lang.String */ @PostMapping("/wap") public String wap(String subject, String total) { subject = "測試支付"; total = "1000"; return alipayService.wap(subject, total); } /** * @param request: 請求 * @return java.lang.String * @description: 支付寶非同步回撥 * @date: 2020/11/3 */ @PostMapping("/notify_url") public String notify_url(HttpServletRequest request) throws Exception { if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) { System.out.println("=========支付寶非同步回撥========"); Map<String, String> params = new HashMap<>(); Map<String, String[]> requestParams = request.getParameterMap(); for (String name : requestParams.keySet()) { params.put(name, request.getParameter(name)); // System.out.println(name + " = " + request.getParameter(name)); } // 支付寶驗籤 if (Factory.Payment.Common().verifyNotify(params)) { // 驗籤通過 System.out.println("交易名稱: " + params.get("subject")); System.out.println("交易狀態: " + params.get("trade_status")); System.out.println("支付寶交易憑證號: " + params.get("trade_no")); System.out.println("商戶訂單號: " + params.get("out_trade_no")); System.out.println("交易金額: " + params.get("total_amount")); System.out.println("買家在支付寶唯一id: " + params.get("buyer_id")); System.out.println("買家付款時間: " + params.get("gmt_payment")); System.out.println("買家付款金額: " + params.get("buyer_pay_amount")); } } return "success"; } /** * @param outTradeNo: 商家訂單號 * @param refundAmount: 退款金額(不能大於交易金額) * @return java.lang.String * @description: 支付寶退款 * @date: 2020/11/3 */ @PostMapping("/refund") public String refund(String outTradeNo, String refundAmount) { return alipayService.refund(outTradeNo, refundAmount); } }View Code
service/AlipayService.java
/** * @author zhangzhixi * @version 1.0 * @date 2021-8-19 15:04 */ public interface AlipayService { /** * @description: 支付寶電腦網頁支付 * @param subject: 訂單名稱 * @param total: 金額 * @date: 2020/11/3 * @return java.lang.String */ String page(String subject, String total); /** * @description: 支付寶手機網頁支付 * @param subject: 訂單名稱 * @param total: 金額 * @date: 2020/11/3 * @return java.lang.String */ String wap(String subject, String total); /** * @description: 支付寶退款 * @param outTradeNo: 商家訂單號 * @param refundAmount: 退款金額(不能大於交易金額) * @date: 2020/11/3 * @return java.lang.String */ String refund(String outTradeNo, String refundAmount); }View Code
service/impl/AlipayService.java
/** * @author zhangzhixi * @version 1.0 * @date 2021-8-19 15:04 */ import com.alipay.easysdk.factory.Factory; import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse; import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse; import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse; import com.zhixi.aliyunpay.service.AlipayService; import com.zhixi.aliyunpay.util.OrderUtil; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service public class AlipayServiceImpl implements AlipayService { // 支付成功後要跳轉的頁面 @Value("${alipay.returnUrl}") private String returnUrl; // 支付寶前臺手機網頁支付中途取消跳轉地址 @Value("${alipay.errorUrl}") private String errorUrl; @Override public String page(String subject, String total) { try { AlipayTradePagePayResponse response = Factory.Payment // 選擇電腦網站 .Page() // 呼叫支付方法(訂單名稱, 商家訂單號, 金額, 成功頁面) .pay(subject, OrderUtil.getOrderNo(), total, returnUrl); return response.body; } catch (Exception e) { e.printStackTrace(); } return null; } @Override public String wap(String subject, String total) { try { AlipayTradeWapPayResponse response = Factory.Payment //選擇手機網站 .Wap() // 呼叫支付方法(訂單名稱, 商家訂單號, 金額, 中途退出頁面, 成功頁面) .pay(subject, OrderUtil.getOrderNo(), total, errorUrl, returnUrl); return response.body; } catch (Exception e) { e.printStackTrace(); } return null; } @Override public String refund(String outTradeNo, String refundAmount) { try { AlipayTradeRefundResponse response = Factory.Payment .Common() // 呼叫交易退款(商家訂單號, 退款金額) .refund(outTradeNo, refundAmount); if (response.getMsg().equals("Success")) { return "退款成功"; } } catch (Exception e) { e.printStackTrace(); } return "退款失敗"; } }View Code
util/OrderUtil.java
/** * @author zhangzhixi * @version 1.0 * @date 2021-8-19 15:03 */ import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; public class OrderUtil { /** * 根據時間戳生成訂單號 */ public static String getOrderNo() { DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"); LocalDateTime localDateTime = Instant.ofEpochMilli(System.currentTimeMillis()).atZone(ZoneOffset.ofHours(8)).toLocalDateTime(); return df.format(localDateTime); } }View Code
html頁面:
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>測試支付</title> </head> <body> <div> <h2>測試支付</h2> <form enctype="multipart/form-data" action="/api/alipay/page" method="post"> <button type="submit">電腦確認支付</button> </form> <form enctype="multipart/form-data" action="/api/alipay/wap" method="post"> <button type="submit">手機確認支付</button> </form> </div> </body> </html>View Code
return_url.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>支付成功</title> </head> <body> <div> <h2>支付成功</h2> <a href="http://78ff7c27cdb6.ngrok.io/return_url.html">主頁</a> <a href="index.html">主頁</a> </div> </body> </html>View Code
error_url.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>支付失敗</title> </head> <body> <div> <h2>支付失敗</h2> </div> </body> </html>View Code