以下是 Spring Boot 对接支付宝支付的详细流程和步骤,以电脑网站支付(即时到账)场景为例,涵盖从环境准备到生产环境部署的全流程。
一、前期准备
1. 注册支付宝开放平台账号
- 访问 支付宝开放平台,使用支付宝企业/个人账号登录(企业账号需完成实名认证)。
- 进入「开发者中心」→「应用管理」→「创建应用」,选择「电脑网站支付」场景,填写应用信息(如应用名称、官网链接等),提交审核(一般1-3个工作日)。
2. 配置应用密钥
支付宝使用 RSA/RSA2 加密验证请求合法性,需为应用生成密钥对:
-
生成密钥工具 :使用支付宝提供的 密钥生成工具 或 OpenSSL 命令。
csharp# 生成 RSA2 私钥(2048位) openssl genrsa -out app_private_key.pem 2048 # 生成公钥(用于上传到支付宝) openssl rsa -in app_private_key.pem -pubout -out app_public_key.pem
-
上传公钥到支付宝 :在开放平台应用的「开发设置」→「应用公钥」中,粘贴生成的
app_public_key.pem
内容并保存。 -
获取支付宝公钥:在应用「开发设置」→「支付宝公钥」中查看(支付宝会为你生成对应的公钥,用于验证其回调)。
3. 配置应用网关(回调地址)
在应用「开发设置」→「网页&移动应用」→「电脑网站支付」中,配置「返回 URL」(支付成功后用户跳转的页面)和「异步通知地址」(支付宝服务器主动推送支付结果的 URL,需为公网可访问的地址,如 https://your-domain.com/api/alipay/notify
)。
二、Spring Boot 项目搭建
1. 创建 Spring Boot 项目
使用 IDEA 或 Spring Initializr 创建项目,添加以下依赖(pom.xml
):
xml
<dependencies>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 支付宝 SDK -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.24.1.ALL</version> <!-- 最新版本 -->
</dependency>
<!-- 工具类(可选,如 JSON 解析) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.31</version>
</dependency>
</dependencies>
2. 配置支付宝参数
在 application.yml
中配置支付宝相关参数(替换为你的实际值):
yaml
alipay:
appId: 202100xxxxxxxxxx # 开放平台审核通过的应用 APPID
gatewayUrl: https://openapi.alipay.com/gateway.do # 生产环境网关;沙箱环境为 https://openapi.alipaydev.com/gateway.do
privateKey: | # 应用的私钥(格式需为 PKCS8,且去掉头尾的 -----BEGIN PRIVATE KEY----- 等注释)
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... # 支付宝公钥(从开放平台获取)
charset: UTF-8
signType: RSA2 # 推荐 RSA2
notifyUrl: https://your-domain.com/api/alipay/notify # 异步通知地址(需与开放平台配置一致)
returnUrl: https://your-domain.com/pay/success # 同步返回地址(用户支付后跳转)
三、核心代码实现
1. 支付宝配置类
创建 AlipayConfig
类,加载配置并初始化支付宝客户端:
kotlin
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AlipayConfig {
@Value("${alipay.appId}")
private String appId;
@Value("${alipay.gatewayUrl}")
private String gatewayUrl;
@Value("${alipay.privateKey}")
private String privateKey;
@Value("${alipay.alipayPublicKey}")
private String alipayPublicKey;
@Value("${alipay.charset}")
private String charset;
@Value("${alipay.signType}")
private String signType;
@Bean
public AlipayClient alipayClient() {
return new DefaultAlipayClient(
gatewayUrl,
appId,
privateKey,
"json",
charset,
alipayPublicKey,
signType
);
}
}
2. 支付服务类(生成支付链接)
创建 AlipayService
类,封装支付请求逻辑:
typescript
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class AlipayService {
@Autowired
private AlipayClient alipayClient;
@Value("${alipay.returnUrl}")
private String returnUrl;
@Value("${alipay.notifyUrl}")
private String notifyUrl;
/**
* 生成支付宝支付链接(电脑网站支付)
*/
public String createPayLink(String orderId, String subject, String totalAmount) throws AlipayApiException {
// 构造请求模型
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setOutTradeNo(orderId); // 商户订单号(需唯一)
model.setSubject(subject); // 商品标题
model.setTotalAmount(totalAmount); // 支付金额(单位:元,保留两位小数)
model.setProductCode("FAST_INSTANT_TRADE_PAY"); // 销售产品码(固定值)
// 构造请求对象
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setReturnUrl(returnUrl); // 同步返回地址
request.setNotifyUrl(notifyUrl); // 异步通知地址
request.setBizModel(model);
// 发送请求并获取响应
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
if (response.isSuccess()) {
// 成功则返回支付宝支付页面的 URL(前端需要跳转到此 URL 完成支付)
return response.getBody();
} else {
throw new RuntimeException("支付宝支付请求失败:" + response.getMsg());
}
}
}
3. 支付控制器(接收前端请求)
创建 AlipayController
类,提供支付入口接口:
less
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AlipayController {
@Autowired
private AlipayService alipayService;
/**
* 前端调用此接口生成支付链接,跳转到支付宝页面
*/
@GetMapping("/api/pay/create")
public String createPay(
@RequestParam("orderId") String orderId,
@RequestParam("subject") String subject,
@RequestParam("amount") String amount
) {
try {
String payUrl = alipayService.createPayLink(orderId, subject, amount);
// 重定向到支付宝支付页面(或返回 URL 给前端跳转)
return "redirect:" + payUrl;
} catch (Exception e) {
e.printStackTrace();
return "支付异常:" + e.getMessage();
}
}
}
4. 处理异步通知(关键)
支付宝支付完成后,会向 notifyUrl
发送 POST 请求通知支付结果(需验证签名以确保真实性)。创建 AlipayNotifyController
处理:
kotlin
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/alipay")
public class AlipayNotifyController {
@Value("${alipay.alipayPublicKey}")
private String alipayPublicKey;
@Value("${alipay.charset}")
private String charset;
@Value("${alipay.signType}")
private String signType;
/**
* 处理支付宝异步通知
*/
@PostMapping("/notify")
public String handleNotify(HttpServletRequest request) {
// 1. 获取支付宝回调的参数(包括签名)
Map<String, String> params = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
params.put(name, valueStr);
}
// 2. 验证签名(防止伪造请求)
boolean signVerified = false;
try {
signVerified = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
charset,
signType
);
} catch (AlipayApiException e) {
e.printStackTrace();
return "failure"; // 签名验证失败,返回 failure 给支付宝
}
// 3. 验证业务参数(如交易状态、金额等)
if (signVerified) {
String tradeStatus = params.get("trade_status");
String outTradeNo = params.get("out_trade_no"); // 商户订单号
String tradeNo = params.get("trade_no"); // 支付宝交易号
String totalAmount = params.get("total_amount"); // 实际支付金额
// 业务逻辑:更新订单状态(需保证幂等性,避免重复处理)
if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
// 调用你的订单服务,标记订单为已支付
boolean updateSuccess = yourOrderService.updateOrderStatus(outTradeNo, tradeNo, totalAmount);
if (updateSuccess) {
return "success"; // 通知支付宝处理成功,不再重复通知
} else {
return "failure"; // 业务处理失败,支付宝会重试(最多8次)
}
}
}
return "failure";
}
}
5. 处理同步返回(可选)
用户支付完成后,支付宝会将用户重定向到 returnUrl
,可在该页面显示支付结果(需再次验证签名,避免伪造):
typescript
// 在 Controller 中添加同步返回处理接口
@GetMapping("/pay/success")
public String paySuccess(HttpServletRequest request) {
// 验证同步返回的签名(逻辑类似异步通知)
Map<String, String> params = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
params.put(name, request.getParameter(name));
}
boolean signVerified = false;
try {
signVerified = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
charset,
signType
);
} catch (AlipayApiException e) {
e.printStackTrace();
return "支付结果验证失败";
}
if (signVerified) {
String outTradeNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");
// 查询订单状态(可选,确保同步返回的准确性)
String orderStatus = yourOrderService.getOrderStatus(outTradeNo);
if ("PAID".equals(orderStatus)) {
return "支付成功!订单号:" + outTradeNo + ",支付宝交易号:" + tradeNo;
} else {
return "支付结果异常,请联系客服";
}
} else {
return "非法请求,签名验证失败";
}
}
四、测试与上线
1. 沙箱环境测试
- 支付宝提供 沙箱环境 用于测试,需在沙箱开放平台重新创建应用并获取沙箱 APPID 和网关(
https://openapi.alipaydev.com/gateway.do
)。 - 使用沙箱买家账号(可在沙箱「账户中心」→「测试账号」中查看,如
13800138000
,密码111111
)完成支付流程测试。 - 重点测试:支付成功、支付失败、异步通知延迟、重复通知等场景。
2. 生产环境部署
- 替换
application.yml
中的沙箱参数为生产环境参数(APPID、网关、密钥等)。 - 确保
notifyUrl
和returnUrl
为生产环境的公网地址,且服务器防火墙开放对应端口(如80/443)。 - 监控异步通知日志,处理可能的异常(如网络延迟导致的重复通知)。
五、常见问题
- 签名错误 :检查私钥是否为 PKCS8 格式(支付宝 SDK 要求),公钥是否正确上传到支付宝,
signType
是否与配置一致(RSA2)。 - 异步通知未收到 :检查
notifyUrl
是否可公网访问(可通过curl
测试),支付宝是否将你的 IP 加入白名单(开放平台「IP 白名单」设置)。 - 订单状态未更新:确保异步通知处理逻辑的幂等性(如通过数据库唯一索引或状态字段判断是否已处理)。
- 交易金额单位错误 :支付宝金额单位为元(如
100.00
表示100元),注意前端传递的金额是否已转换为元。
通过以上步骤,即可完成 Spring Boot 与支付宝支付的对接。实际开发中需根据业务需求调整参数验证、订单状态管理等逻辑,并做好异常处理和日志记录。