0.前言
文章需求:
對于學生來說,目前網上确實沒有比較統一而且品質好的支付教程。因為支付對個人開發者尤其是學生來說不太友好。是以,自己折騰兩天,算是整理了一篇關于支付寶沙箱支付的文章。
那麼為什麼不用微信沙箱支付呢?
微信支付我在另一篇文章中寫過,用的是别人的公衆号和開放平台賬号,而微信的沙箱測試我自己也搞了好幾天了也沒弄好,确實沒有支付寶沙箱測試容易。因為支付寶提供了一套pc用戶端和手機端的測試軟體(比如沙箱版支付寶)!
微信和支付寶官方為了開發者測試和學習友善,都提供了沙箱測試環境下模拟支付業務。這篇文章我們就來動手搭建一個支付寶沙箱環境支付的demo,如果想額外了解微信支付可以看下我的另一篇文章:Springboot整合微信登入與微信支付(附源碼)
個人覺得,在沙箱測試支付方面,支付寶會相較于微信來說更簡單一些,文檔相對而言比較詳細,有附帶沙箱支付寶APP,測試遠比微信沙箱友善!
1.效果展示
說明:沙箱測試并不是真正的支付,是以付款金額也都是模拟的!
2.技術棧介紹
- 前端demo :
- 後端demo:SpringBoot2.x + (LomBook插件)
- 第三方支付:AliPay-SDK
3.前期準備
第一步:申請一個沙箱測試賬号
支付寶沙箱測試賬号申請進傳入連結接後掃碼登入!
第二步:電腦下載下傳一個支付寶提供的用戶端用于生成RSA2
用戶端工具下載下傳(密鑰生成操作文檔)下載下傳安裝完成後,開始使用,使用參考文檔:
幫助文檔第三步:手機下載下傳 【沙箱版支付寶】
可以自行掃碼下載下傳,也可以手機點傳入連結接下載下傳,
沙箱支付寶下載下傳連結下載下傳後打開沙箱支付寶如下圖:
踩坑:沙箱支付寶登入所用的賬号和密碼是以開放平台提供的沙箱賬号為準,而不是我們的手機号或者淘寶賬号!如圖:
如果萌新沒有注意到這一點,直接用自己的手機号登入,是會出現逾時或者操作頻繁的提示而登入不了的!(沒錯,我就是這個萌新…)
哈哈,沙箱支付寶裡的錢要能轉到支付寶中就好了!
4.後端搭建
項目目錄結構
pom.xml
<dependencies>
<!--
springboot新版本踩坑:
不加這個依賴的話,當在配置類中
使用@ConfigurationProperties(prefix = "xxx")注解時,
我這個版本的spring boot會提示有問題
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artif
<optional>true</optional>
</dependency>
<!-- 支付寶支付jar包 -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>3.1.0</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!-- 熱部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
application.yml
由于隻是demo 示範 是以可以不加資料庫和mybatis相關的配置,隻留server相關配置即可
#=====================================server相關配置=====================================
server:
port: 8080
#=====================================資料庫相關配置=====================================
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/alipay?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
password: root
# 使用Alibaba的druid資料源,預設使用自帶的
type: com.alibaba.druid.pool.DruidDataSource
#=====================================mybatis相關配置=====================================
# 開啟控制台列印sql日志
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# mybatis 下劃線轉駝峰配置
map-underscore-to-camel-case: true
# 配置mapper檔案掃描
mapper-locations: com.haust.alipaydemoserver.mapper/*.xml
# 配置實體類掃描
type-aliases-package: com.haust.alipaydemoserver.pojo
application-alipay.proerties
# APPID
alipay.appId:2016110100784885
# 商戶私鑰, 即PKCS8格式RSA2私鑰
alipay.privateKey:MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCQiliwqcldz8Kb6HALM35rFNif0Tvg7qiywMzC5F8ySt5HgqN5VWIDC/ABTsUb6MlWhdGbC15r7uIwFB7mkDutG6vIZahhfWnCzisZuMebUkMGO9vJJiJDird20nOGyVuvxHYwQ+E777tZKnJM239psXL+o/vfWEtkVu1A6zEoseGc26gBp7Z6czAFGcOvorrWsj6Z93+ZE4LNAb21Hdc0KgILniYwKev38xACRjNn3a3lpRb+e4QQ24vPqIoLTGmw5DcpsEv3uKMRZwQBK8UB7b8ZwJP+yVSJdCVx+6LHP9VQd+RT5BlD8U6328VITsTr3WteHuPxP+t2a17/RWD1AgMBAAECggEATDHiFx8qG94OBQo/Jmh62BAhMf6mxiiJndGtH4Ar/uMg0im365prFJgSaV4Q4mmQ2Z+po0YW/GbtrdKth3W5P8Q6hmWwodPvENaGOgUClIqE8qBTeHI11c0mcej3JbK4NqwmccMW1PXHmXWa05FSVXFJ4ZqoiFCPTdHVOEfDnmN54DFgoEKYPk6q/xxQmIpwqHQH+s3S2eFSU0rt0aqiKBg2mqKSMYzLE+8vB7FWJS61Cy0mzYVugfgWx2KTQy8y1SC4i4mjQ50DQQ2AgbNr+k8l5G7p7Cja+KDQ6JbGv4nR8DC+sPNhxECDHrSpYDn6BYqw8aRwvN6nmaCdoU3GcQKBgQDheWn3kpJz8r8wdy15WaWzItZD+c8Qwou997bn3iBR7RdFi5INNXmlr5TRc3Lr3d5uidzl4ues2dLnqRYh7tYMQudB6HoweWoPlig6gXblamn6am5MvpFRK9Lsl1zNbN82DDvVH6oaDPvqD+KLPvZ1MIWRQiTz7eRvwo9NYTqKYwKBgQCkG+Q2N2U5g0t6baZ1/TlWpFKnb1t17JTHfwfmbwotnLcKQRVWzqayOdPsKtFHqyhmnhH7BlLt1a1QqK5d/a2KH80i7Y0jmv8Pglpt+gfxEQt87gD8+uZrvx33EpU8KCIwhFNshw4FUr5yB5BcLNQo92NsO0ujHhFC6Q/LxX9axwKBgH3WJS3mv5W2hL2nxdlUDwZLCwolAUt5SERdW9dMQP14NOS7YGe+0IWH2KaMqDa7PMi0aHRkjqgJaYug8pk9kniFXkuKU6d6G5dXVlxQpOqk2UDI5YYvVSrYKn+gekqr2GdxrHLlmSmw1WdsNiNAoIwG6ISJRdZdjoBRNWkaOnHBAoGADpa0KOWvx/cWBKIuxBpouH0PI/dQSCFp8Hood6GzY+6kjvLONNNWGk3tuvbrd9WNV+IBczFSufXe3GbCaXSdssO09r/rZhjnR7es1k392r5LKSX3TIX5aeapgUdToO9oaqu4xtMSugJrD7QAb1FE4wdq/TogNTX9DtetIc5Czg0CgYBGmAHwxJmvokrFmk8ZPkj544tZ9emY1aaUY0jqW21wWCHcRID2+56jsDXXb6IH/Hn9DgHndkl3v9ql6Ha2Y/j2t2Q9XA31QoUmKHU+6QA1aw+GP3VSaLTwlCMgGeWjA9imiFc3pEkS3JrNm3ASdwp+1TPIjcWcxa/ZU+JWrdM3bA==
# 支付寶公鑰, 即對應APPID下的支付寶公鑰
alipay.publicKey:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlREBzwWQaJd6ZzSrYlnucsvlX3jV1swYdnymIRsbrObxftBFoKcvuMnqzCgRhvqZN4NEJvW+cQZkbk4otPvk+g+uIK1ZNjF+Cln/gePwHq3WRCrhWFwwBVWASJwPg/XISuaahjXicLb9z2AxvKcNZsLDL46HHO442mniGGOlD8PqK6LvvrhRUvhYgvY5SQ0RfRT0LUnxNl2evsdVOl3dlS6EuQVe4DrqpKS2WseVuXsA8nbXzBKwmR3vcqoxnDI/Nu8ldSLS+KEMEFh6MUL9OcTwhHr4/F8MdKtMSSWru+5m4q8BGNxW4PAP5GM3e1cBlxi/8FnBamL0jIlnhH/8qwIDAQAB
# 伺服器異步通知頁面路徑, 需http://格式的完整路徑
# 踩坑:不能加?type=abc這類自定義參數
alipay.notifyUrl:
# 頁面跳轉同步通知頁面路徑, 需http://格式的完整路徑
# 踩坑:不能加?type=abc這類自定義參數
alipay.returnUrl:http://192.168.0.106:8084/#/paySuccess
# 簽名方式
alipay.signType:RSA2
# 字元編碼格式
alipay.charset:utf-8
# 支付寶網關
alipay.gatewayUrl:https://openapi.alipaydev.com/gateway.do
# 日志列印位址
alipay.logPath:"F:\\"
Order訂單實體類
這裡我使用了lombok插件,是以以注解的方式節省了setter/getter 以及構造函數
package com.haust.alipaydemoserver.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @Auther: csp1999
* @Date: 2020/11/13/21:49
* @Description: 商品訂單實體類(支付實體對象)
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Order {
/**
* 商戶訂單号,必填
*/
private String out_trade_no;
/**
* 訂單名稱,必填
*/
private String subject;
/**
* 付款金額,必填
* 根據支付寶接口協定,必須使用下劃線
*/
private String total_amount;
/**
* 商品描述,可空
*/
private String description;
/**
* 逾時時間參數
*/
private String timeout_express = "10m";
/**
* 産品編号
*/
private String product_code = "FAST_INSTANT_TRADE_PAY";
}
Service層
/**
* @Auther: csp1999
* @Date: 2020/11/13/21:55
* @Description: 支付 service
*/
public interface AliPayService {
/**
* 支付寶支付接口
* @param order
* @return
* @throws AlipayApiException
*/
String aliPay(Order order) throws AlipayApiException;
}
/**
* @Auther: csp1999
* @Date: 2020/11/13/21:56
* @Description: 支付service 實作類
*/
@Service
public class AliPayServiceImpl implements AliPayService {
@Autowired
private Alipay alipay;
@Override
public String aliPay(Order order) throws AlipayApiException {
return alipay.pay(order);
}
}
Controller層
/**
* @Auther: csp1999
* @Date: 2020/11/13/21:47
* @Description: 支付寶沙箱測試 controller
*/
@RestController
public class PayController {
@Autowired
private AliPayService aliPayService;
/**
* 支付寶支付 api
*
* @param outTradeNo
* @param subject
* @param totalAmount
* @param description
* @return
* @throws AlipayApiException
*/
@PostMapping(value = "/order/alipay")
public String alipay(String outTradeNo, String subject,
String totalAmount, String description) throws AlipayApiException {
Order order = new Order();
order.setOut_trade_no(outTradeNo);
order.setSubject(subject);
order.setTotal_amount(totalAmount);
order.setDescription(description);
System.out.println(order);
return aliPayService.aliPay(order);
}
}
配置類
支付寶支付配置類以及支付寶支付元件注入Spring容器
AliPayConfig.java
/**
* @Auther: csp1999
* @Date: 2020/11/13/19:19
* @Description: 支付寶配置類(讀取配置檔案)
*/
@Configuration
@PropertySource("classpath:application-alipay.properties")
@ConfigurationProperties(prefix = "alipay")
@Data
public class AliPayConfig {
/**
* APPID
*/
private String appId;
/**
* 商戶私鑰, 即PKCS8格式RSA2私鑰
*/
private String privateKey;
/**
* 支付寶公鑰
*/
private String publicKey;
/**
* 伺服器異步通知頁面路徑,需http://格式的完整路徑
* 踩坑:不能加?type=abc這類自定義參數
*/
private String notifyUrl;
/**
* 頁面跳轉同步通知頁面路徑,需http://格式的完整路徑
* 踩坑:不能加?type=abc這類自定義參數
*/
private String returnUrl;
/**
* 簽名方式
*/
private String signType;
/**
* 字元編碼格式
*/
private String charset;
/**
* 支付寶網關
*/
private String gatewayUrl;
/**
* 日志列印位址
*/
private String logPath;
}
Alipay.java
/**
* @Auther: csp1999
* @Date: 2020/11/13/21:57
* @Description: 調用支付寶支付的元件
*/
@Component
public class Alipay {
@Autowired
private AliPayConfig alipayConfig;
/**
* 支付接口
*
* @param order
* @return
* @throws AlipayApiException
*/
public String pay(Order order) throws AlipayApiException {
// 支付寶網關
String serverUrl = alipayConfig.getGatewayUrl();
// APPID
String appId = alipayConfig.getAppId();
// 商戶私鑰, 即PKCS8格式RSA2私鑰
String privateKey = alipayConfig.getPrivateKey();
// 格式化為 json 格式
String format = "json";
// 字元編碼格式
String charset = alipayConfig.getCharset();
// 支付寶公鑰, 即對應APPID下的支付寶公鑰
String alipayPublicKey = alipayConfig.getPublicKey();
// 簽名方式
String signType = alipayConfig.getSignType();
// 頁面跳轉同步通知頁面路徑
String returnUrl = alipayConfig.getReturnUrl();
// 伺服器異步通知頁面路徑
String notifyUrl = alipayConfig.getNotifyUrl();
// 1、獲得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(
serverUrl, appId, privateKey, format, charset, alipayPublicKey, signType);
// 2、設定請求參數
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
// 頁面跳轉同步通知頁面路徑
alipayRequest.setReturnUrl(returnUrl);
// 伺服器異步通知頁面路徑
alipayRequest.setNotifyUrl(notifyUrl);
// 封裝參數(以json格式封裝)
alipayRequest.setBizContent(JSON.toJSONString(order));
// 3、請求支付寶進行付款,并擷取支付結果
String result = alipayClient.pageExecute(alipayRequest).getBody();
// 傳回付款資訊
return result;
}
}
跨域攔截器配置以及注冊
CorsInterceptor.java
/**
* @Auther: csp1999
* @Date: 2020/10/21/12:06
* @Description: 跨域攔截器
*/
public class CorsInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//表示接受任意域名的請求,也可以指定域名
response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));
//該字段可選,是個布爾值,表示是否可以攜帶cookie
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "*");
// 預檢處理,方行所有非簡單請求方法
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
return true;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
}
}
InterceptorConfig.java
/**
* @Auther: csp1999
* @Date: 2020/10/20/13:26
* @Description: 攔截器配置
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
/*
* 将跨域攔截器交給spring容器托管
* @return: com.haust.online_class.interceptor.CorsInterceptor
* @create: 2020/10/21 12:20
* @author: csp1999
*/
@Bean
public CorsInterceptor corsInterceptor() {
return new CorsInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 跨域攔截器注冊(注意:跨域攔截器注冊要放在最上方)
registry.addInterceptor(corsInterceptor()).addPathPatterns("/**");
WebMvcConfigurer.super.addInterceptors(registry);
}
}
啟動spirngboot項目
5.前端搭建
前端代碼就不展示了,demo源碼會提供給大家,來看下前端的樣子把:
支付操作的頁面:
啟動前端項目:
端口自定義就行!
6. 支付測試
測試支付:
進入支付寶支付頁面:
沙箱支付寶掃碼支付:(支付密碼預設111111)
背景檢視列印訂單情況:
如果出現點選支付按鈕後進入支付釣魚風險的頁面,請先檢查自己的應用公匙是否填寫錯誤,可以參考下這篇文章:
https://blog.csdn.net/MacWx/article/details/107410685如果還是有問題,可以換個浏覽器,這也是沙箱支付存在的小問題!
7. 總結
源碼位址Gitee代碼倉庫位址
再給大家分享另外一篇支付寶沙箱測試文章,可以對比着學一下:
https://blog.csdn.net/qq_30385099/article/details/109089450如果對大家有幫助,請三連支援一下!
有問題歡迎評論區留言,及時幫大家解決!