碼雲地址:
https://gitee.com/zhang-zhixi/aliyun-pay
前言:
今天在一個公眾號看到了這樣一篇文章,寫的是: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; } }
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); } }
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); }
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 "退款失敗"; } }
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); } }
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>
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>
error_url.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>支付失敗</title> </head> <body> <div> <h2>支付失敗</h2> </div> </body> </html>
三、測試
電腦測試訪問:


手機測試訪問:



