沙箱环境是支付宝开放平台为开发者提供的用于接口开发及主要功能联调的模拟环境。
参考 登录 - 支付宝
在沙箱环境下,已经分配好了用于模拟测试的应用信息、商家信息、买家信息等
小程序文档 - 支付宝文档中心
内网穿透(支付宝付款需要在公网进行检查网站)
这里使用natapp
内网穿透工具,参考 NATAPP-内网穿透 基于ngrok的国内高速内网映射工具
1.创建前端页面
订单列表页 order.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>订单列表</title>
<style>
tr{
text-align: center;
}
</style>
</head>
<body>
<table border="1px" width="700px" height="200px">
<tr>
<th>序号</th>
<th>订单号</th>
<th>订单名称</th>
<th>订单金额</th>
<th>下单时间</th>
<td>操作</td>
</tr>
<tr>
<td>1</td>
<td>202312100001</td>
<td>测试订单1</td>
<td>¥1.0</td>
<td>2023-12-10 10:30:21</td>
<td>
<a href="pay.html">去支付</a>
</td>
</tr>
<tr>
<td>2</td>
<td>202312100002</td>
<td>测试订单2</td>
<td>¥1.0</td>
<td>2023-12-10 10:30:21</td>
<td>
<a href="pay.html">去支付</a>
</td>
</tr>
<tr>
<td>3</td>
<td>202312100003</td>
<td>测试订单3</td>
<td>¥1.0</td>
<td>2023-12-10 10:30:21</td>
<td>
<a href="pay.html">去支付</a>
</td>
</tr>
</table>
</body>
</html>
确认付款页 pay.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>确认付款</title>
</head>
<body>
<h2>确认付款</h2>
<form id="payForm" method="post" action="http://localhost:8080/alipay/pay">
<p>订单号:<input type="text" name="orderNo" value="202312100001"></p>
<p>订单名称:<input type="text" name="subject" value="测试订单1"></p>
<p>付款金额:<input type="text" name="totalAmount" value="1.0"></p>
<p><input type="submit" value="付款"></p>
</form>
</body>
</html>
后端界面:
引入相关的依赖:
processor是为了将配置类中的信息,一次性读取注入到属性中:
<!-- 支付宝 -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.34.0.ALL</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
application.yml
# 支付宝配置信息
alipay:
appId: xxxxxxx
appPrivateKey: xxxxxxx
alipayPublicKey: xxxxxxx
signType: RSA2
charset: utf-8
gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do
notifyUrl: xxxxxxx
returnUrl: http://127.0.0.1:5500/order.html
创建Alipay属性类:
@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayProperties {
// 应用id
private String appId;
// 应用私钥
private String appPrivateKey;
// 支付宝公钥
private String alipayPublicKey;
// 签名方式
private String signType;
// 字符编码
private String charset;
// 支付宝网关
private String gatewayUrl;
// 异步通知地址
private String notifyUrl;
// 同步跳转地址,即支付完成后跳转的地址
private String returnUrl;
}
创建Alipay配置类
@Configuration
public class AlipayConfig {
@Resource
private AlipayProperties alipayProperties;
/**
* 创建支付客户端
*/
@Bean
public AlipayClient alipayClient(){
AlipayClient alipayClient = new DefaultAlipayClient(
alipayProperties.getGatewayUrl(),
alipayProperties.getAppId(),
alipayProperties.getAppPrivateKey(),
"json",
alipayProperties.getCharset(),
alipayProperties.getAlipayPublicKey(),
alipayProperties.getSignType());
return alipayClient;
}
/**
* 创建支付请求对象
*/
@Bean
public AlipayTradePagePayRequest alipayTradePagePayRequest(){
AlipayTradePagePayRequest payRequest = new AlipayTradePagePayRequest();
payRequest.setNotifyUrl(alipayProperties.getNotifyUrl());
payRequest.setReturnUrl(alipayProperties.getReturnUrl());
return payRequest;
}
}
创建Alipay控制类:
package net.wanho.controller;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.wanho.properties.AlipayProperties;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
@RestController
@RequestMapping("/alipay")
@CrossOrigin
@Slf4j
public class AlipayController {
@Resource
private AlipayClient alipayClient;
@Resource
private AlipayTradePagePayRequest payRequest;
@Resource
private AlipayProperties alipayProperties;
/**
* 支付方法
*/
@SneakyThrows
@PostMapping("/pay")
public void pay(String orderNo, String subject, Double totalAmount, HttpServletResponse response) {
//封装业务参数
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", orderNo);//订单号
bizContent.put("subject", subject); //订单名称
bizContent.put("total_amount", totalAmount); //订单总金额
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); // 产品码,固定值
bizContent.put("timeout_express", "15m"); //超时时间,15min
payRequest.setBizContent(bizContent.toString());
//向支付宝发起支付请求
String result = alipayClient.pageExecute(payRequest).getBody();
//将结果返回给前端,进入支付用户登录界面
response.setContentType("text/html;charset=utf-8");
response.getWriter().print(result);
}
/**
* 异步通知
*/
@SneakyThrows
@PostMapping("/notify")
public String notify(@RequestParam Map<String, String> params) {
System.out.println(params);
/**
* 进行验签操作,防止签名被窜改
*/
boolean flag = AlipaySignature.rsaCheckV1(params, alipayProperties.getAlipayPublicKey(), alipayProperties.getCharset(), alipayProperties.getSignType());
if (!flag) {
log.info("验证签名失败!");
return "fail";
}
/**
* 判断返回的订单号、订单总金额是否与发送的一致,防止数据被篡改
*/
String out_trade_no = params.get("out_trade_no");
String total_amount = params.get("total_amount");
/**
* 判断支付宝返回的状态是否正常
*/
String trade_status = params.get("trade_status");
if (!trade_status.equals("TRADE_SUCCESS")) {
log.info("交易失败!");
return "fail";
}
/**
* 更新相应的订单状态,例如将订单标记为已支付、更新库存等
*/
// orderService.updateOrderStatus();
return "success";
}
}
实现效果: