文章目录
前期准备
- 进入支付宝沙箱控制台: 沙箱控制台
- 界面如图

我们主要注意公钥模式 我们的密钥都要在里面查看
导入依赖
<!-- 支付宝SDK -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.40.26.ALL</version>
</dependency>
配置文件
alipay:
app-id: 你的appid
private-key: 你公钥模式中的应用私钥(JAVA语言)
alipay-public-key: 支付宝公钥
server-url: https://openapi-sandbox.dl.alipaydev.com/gateway.do
charset: UTF-8
format: json
sign-type: RSA2
notify-url: notify请求的ip地址端口/owner/property/fee/alipay/notify 公网能访问
return-url: return请求的ip地址端口/owner/property/fee/alipay/return
配置类
/**
* 支付宝配置工具类(封装配置,提供AlipayClient实例)
*/
@Data
@Component
public class AlipayConfigUtil {
// 从配置文件注入参数
@Value("${alipay.app-id}")
private String appId;
@Value("${alipay.private-key}")
private String privateKey;
@Value("${alipay.alipay-public-key}")
private String alipayPublicKey;
@Value("${alipay.server-url}")
private String serverUrl;
@Value("${alipay.charset}")
private String charset;
@Value("${alipay.format}")
private String format;
@Value("${alipay.sign-type}")
private String signType;
/**
* 获取AlipayClient实例(支付宝SDK核心客户端,用于调用支付接口)
* 单例复用,避免重复创建
*/
public AlipayClient getAlipayClient() throws AlipayApiException {
// 构建AlipayConfig(对应main方法中的getAlipayConfig)
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl(serverUrl);
alipayConfig.setAppId(appId);
alipayConfig.setPrivateKey(privateKey);
alipayConfig.setFormat(format);
alipayConfig.setAlipayPublicKey(alipayPublicKey);
alipayConfig.setCharset(charset);
alipayConfig.setSignType(signType);
// 返回DefaultAlipayClient实例(核心客户端)
return new DefaultAlipayClient(alipayConfig);
}
}
开始接入
notify示例代码
controller
/**
* 物业费支付宝支付Controller
*/
@Slf4j
@RestController
@RequestMapping("/owner/property/fee/alipay")
public class AlipayPayController {
@Autowired
private AlipayPayService alipayPayService;
@Autowired
AlipayConfigUtil alipayConfigUtil;
/**
* 生成支付表单(前端点击「立即缴费」调用此接口)
*/
@GetMapping("/createPayForm/{feeId}")
public Result<String> createPayForm(@PathVariable Long feeId) {
return alipayPayService.createPropertyFeePayForm(feeId);
}
/**
* 处理支付宝支付回调(支付宝主动调用,无需前端干预)
*/
@PostMapping("/notify")
public String handleAlipayNotify(HttpServletRequest request) {
try {
// 解析支付宝回调的参数(封装为Map)
Map<String, String> params = new HashMap<>();
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement();
String value = request.getParameter(name);
params.put(name, value);
}
log.info("支付宝回调参数:{}", params);
// 调用服务层处理回调
return alipayPayService.handleAlipayNotify(params);
} catch (Exception e) {
log.error("处理支付宝回调异常", e);
return "fail";
}
}
}
在下面代码中实现handleAlipayNotify
impl
本方法具体的数据库更新由消息队列实现,不想使用消息队列可以在本方法中进行更新
示例方法代码
/**
* 支付宝支付服务实现类
*/
@Service
@Slf4j
public class AlipayPayServiceImpl implements AlipayPayService {
// 注入支付宝配置工具类
@Autowired
private AlipayConfigUtil alipayConfigUtil;
// 注入账单Mapper
@Autowired
private PropertyFeeMapper propertyFeeMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
// 注入支付宝回调地址
@Value("${alipay.notify-url}")
private String notifyUrl;
@Value("${alipay.return-url}")
private String returnUrl;
@Override
public Result<String> createPropertyFeePayForm(Long feeId) {
try {
// 1. 查询待支付的物业费账单(校验账单有效性)
PropertyFee propertyFee = propertyFeeMapper.selectById(feeId);
if (propertyFee == null) {
return Result.error("账单不存在");
}
if (propertyFee.getStatus() == 1) {
return Result.error("该账单已支付,无需重复下单");
}
if (propertyFee.getDeleteFlag() == 1) {
return Result.error("该账单已被删除");
}
// 2. 获取AlipayClient实例
AlipayClient alipayClient = alipayConfigUtil.getAlipayClient();
// 3. 构建支付请求 - 使用电脑网站支付请求类
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
// 设置异步回调地址(支付宝服务器主动通知)
request.setNotifyUrl(notifyUrl);
// 设置同步回调地址(支付完成后页面跳转)- 电脑网站支付需要这个
request.setReturnUrl(returnUrl); // 需要定义returnUrl变量
// 4. 构建业务参数
String outTradeNo = "PROPERTY_FEE_" + feeId + "_" + System.currentTimeMillis();
String subject = propertyFee.getFeeYear() + "年" + propertyFee.getFeeMonth() + "月费用";
// 格式化金额
DecimalFormat df = new DecimalFormat("0.00");
String totalAmount = df.format(propertyFee.getActualAmount());
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", outTradeNo);
bizContent.put("total_amount", totalAmount);
bizContent.put("subject", subject);
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
bizContent.put("body", "费用");
bizContent.put("timeout_express", "30m");
request.setBizContent(bizContent.toJSONString());
// 5. 调用支付宝接口
AlipayTradePagePayResponse response = alipayClient.pageExecute(request, "GET");
// 6. 处理响应结果
if (response.isSuccess()) {
String payForm = response.getBody();
// 将商户订单号存入账单
updatePropertyFeeOutTradeNo(feeId, outTradeNo);
return Result.success(payForm, "支付表单生成成功");
} else {
return Result.error("支付表单生成失败:" + response.getMsg());
}
} catch (AlipayApiException e) {
return Result.error("支付宝接口异常:" + e.getMessage());
} catch (Exception e) {
return Result.error("系统异常,生成支付表单失败");
}
}
/**
* 处理支付宝支付回调(解耦后:仅验签+发送消息,快速返回结果)
*/
@Override
// 注:回调服务无需事务(仅发送消息,消息持久化由RabbitMQ保证)
public String handleAlipayNotify(Map<String, String> params) {
String alipayPublicKey = alipayConfigUtil.getAlipayPublicKey();
try {
// 1. 日志记录接收到的参数
log.info("=== 开始处理支付宝回调(解耦版)===");
log.info("回调参数数量:{}", params.size());
log.info("sign参数:{}", params.get("sign"));
log.info("sign_type参数:{}", params.get("sign_type"));
// 2. 验签(核心:仅保留必要的校验,确保消息合法性)
boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayPublicKey, "UTF-8", "RSA2");
log.info("验签结果:{}", signVerified ? "成功" : "失败");
if (!signVerified) {
log.error("支付宝回调签名验证失败");
return "fail";
}
// 3. 解析并校验核心回调参数(仅做合法性校验,不操作数据库)
String outTradeNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");
String tradeStatus = params.get("trade_status");
String totalAmount = params.get("total_amount");
String appId = params.get("app_id");
String sellerId = params.get("seller_id");
log.info("解析参数:outTradeNo={}, tradeNo={}, tradeStatus={}, totalAmount={}",
outTradeNo, tradeNo, tradeStatus, totalAmount);
// 4. 校验核心条件(交易状态+appId+必要参数非空)
if (!"TRADE_SUCCESS".equals(tradeStatus)) {
log.info("交易状态不是TRADE_SUCCESS:{}", tradeStatus);
return "success"; // 非支付成功状态,返回success避免重复通知
}
String configAppId = alipayConfigUtil.getAppId();
if (!configAppId.equals(appId) || StringUtils.isEmpty(outTradeNo) || StringUtils.isEmpty(totalAmount)) {
log.error("参数不合法:appId不匹配或核心参数为空");
return "fail";
}
// 5. 构建支付成功消息(封装需要的所有数据,避免后续查询支付宝)
AlipayPaySuccessMessage payMessage = new AlipayPaySuccessMessage();
payMessage.setOutTradeNo(outTradeNo);
payMessage.setTradeNo(tradeNo);
payMessage.setTotalAmount(totalAmount);
payMessage.setPayTime(LocalDateTime.now());
payMessage.setPayMethod("支付宝支付");
// 6. 发送消息到RabbitMQ(同步发送,快速返回;开启消息持久化)
rabbitTemplate.convertAndSend(
"alipay.pay.success.exchange", // 交换机名称
"alipay.pay.success.key", // 路由键
payMessage, // 消息体
message -> {
// 消息持久化配置(确保RabbitMQ重启后消息不丢失)
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return message;
}
);
log.info("支付宝支付成功消息发送至RabbitMQ:outTradeNo={}", outTradeNo);
// 7. 快速返回success给支付宝,避免超时重试
return "success";
} catch (AlipayApiException e) {
log.error("支付宝验签异常:{}", e.getMessage(), e);
return "fail";
} catch (Exception e) {
log.error("处理支付宝回调并发送消息异常:{}", e.getMessage(), e);
return "fail";
}
}
注意notify返回的结果必须是"success"或者"fail"字符串
return示例代码
controller层
/**
* 支付宝同步回调接口(处理重定向跳转)
* 注解使用 @Controller,支持返回 RedirectView 实现页面跳转
*/
@Controller // 核心修正:替换 @RestController 为 @Controller
public class AlipayReturnController {
@Autowired
PropertyFeeService propertyFeeService;
/**
* 接收支付宝同步回调,处理逻辑后重定向回业主页面
*/
@GetMapping("/owner/property/fee/alipay/return")
public RedirectView handleAlipayReturn(HttpServletRequest request) {
try {
// 1. 解析支付宝同步回调参数(验证订单、更新订单状态等核心逻辑)
Map<String, String> params = new HashMap<>();
request.getParameterMap().forEach((key, values) -> {
if (values != null && values.length > 0) {
params.put(key, values[0]);
}
});
System.out.println("支付宝同步回调接收参数:" + params);
String targetOwnerPage = "你支付完成后重定向跳转的网址";
/
// 4. 构建重定向视图(302 重定向)
RedirectView redirectView = new RedirectView(targetOwnerPage);
redirectView.setStatusCode(HttpStatus.FOUND); // 明确设置 302 重定向
redirectView.setExposeModelAttributes(false); // 不暴露模型属性,避免拼接多余参数
return redirectView;
} catch (Exception e) {
e.printStackTrace();
// 异常时,重定向到支付失败页面
return new RedirectView("你支付失败重定向跳转的网址");
}
}
}
可能出现的问题
数据库表的问题
你的相关表中要携带 out_trade_no,trade_node等字段
生成支付宝表单出错
检查你的createPropertyFeePayForm方法
移动端不能生成表单?
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
这里的value是电脑网站支付,如果你想使用其他支付方式请使用其他产品码作为value
异步同调失败(notify接口响应失败)
是否能被公网访问?
支付发起的异调是支付宝服务器发起的,所以你要使用能被公网访问的网址
使用公网网址还是不行?
因为异调支付对于SSL证书有要求,检查你的公网地址是否具有相关资格(推荐ngrok 免费可用 不推荐花生壳 因为发送不了notify)