本文目录导读:

- 依赖配置 (pom.xml)
- 配置类 (AlipayConfig.java)
- application.yml 配置
- 回调DTO类
- 回调服务接口
- 回调服务实现
- 控制器
- 业务处理示例(订单服务)
- 测试控制器
- 注意事项
- 回调验证流程
- 回调测试
我来为您提供一个完整的Java实现支付宝回调的案例,包括验签、处理业务逻辑和返回响应。
依赖配置 (pom.xml)
<dependencies>
<!-- 支付宝SDK -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.38.0.ALL</version>
</dependency>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
配置类 (AlipayConfig.java)
package com.example.alipay.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig {
/**
* 应用ID
*/
private String appId;
/**
* 应用私钥
*/
private String privateKey;
/**
* 支付宝公钥
*/
private String alipayPublicKey;
/**
* 签名方式
*/
private String signType = "RSA2";
/**
* 字符编码
*/
private String charset = "UTF-8";
/**
* 支付宝网关
*/
private String gatewayUrl = "https://openapi.alipay.com/gateway.do";
/**
* 异步通知地址
*/
private String notifyUrl;
/**
* 同步通知地址
*/
private String returnUrl;
}
application.yml 配置
alipay: app-id: your-app-id private-key: your-private-key alipay-public-key: your-alipay-public-key sign-type: RSA2 charset: UTF-8 gateway-url: https://openapi.alipay.com/gateway.do notify-url: http://your-domain.com/api/alipay/notify return-url: http://your-domain.com/api/alipay/return server: port: 8080
回调DTO类
package com.example.alipay.dto;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class AlipayNotifyDTO {
/** 通知时间 */
private String notifyTime;
/** 通知类型 */
private String notifyType;
/** 通知校验ID */
private String notifyId;
/** 编码格式 */
private String charset;
/** 签名类型 */
private String signType;
/** 签名 */
private String sign;
/** 支付宝交易号 */
private String tradeNo;
/** 商户订单号 */
private String outTradeNo;
/** 商家业务号 */
private String outBizNo;
/** 买家支付宝用户号 */
private String buyerId;
/** 买家支付宝账号 */
private String buyerLogonId;
/** 卖家支付宝用户号 */
private String sellerId;
/** 卖家支付宝账号 */
private String sellerEmail;
/** 交易状态 */
private String tradeStatus;
/** 订单金额 */
private BigDecimal totalAmount;
/** 实收金额 */
private BigDecimal receiptAmount;
/** 开票金额 */
private BigDecimal invoiceAmount;
/** 付款金额 */
private BigDecimal buyerPayAmount;
/** 集分宝金额 */
private BigDecimal pointAmount;
/** 总退款金额 */
private BigDecimal refundFee;
/** 退款状态 */
private String refundStatus;
/** 商品名称 */
private String subject;
/** 商品描述 */
private String body;
/** 交易创建时间 */
private String gmtCreate;
/** 交易付款时间 */
private String gmtPayment;
/** 交易退款时间 */
private String gmtRefund;
/** 交易结束时间 */
private String gmtClose;
/** 支付金额信息 */
private String fundBillList;
/** 回传参数 */
private String passbackParams;
}
回调服务接口
package com.example.alipay.service;
import com.example.alipay.dto.AlipayNotifyDTO;
import javax.servlet.http.HttpServletRequest;
public interface AlipayNotifyService {
/**
* 处理异步通知
* @param request HTTP请求
* @return 响应字符串
*/
String handleNotify(HttpServletRequest request);
/**
* 处理同步通知
* @param request HTTP请求
* @return 重定向地址
*/
String handleReturn(HttpServletRequest request);
/**
* 验签
* @param params 参数Map
* @return 是否通过
*/
boolean verifySign(java.util.Map<String, String> params);
/**
* 处理支付成功的业务逻辑
* @param notifyDTO 通知DTO
*/
void processPaymentSuccess(AlipayNotifyDTO notifyDTO);
}
回调服务实现
package com.example.alipay.service.impl;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.internal.util.WebUtils;
import com.example.alipay.config.AlipayConfig;
import com.example.alipay.dto.AlipayNotifyDTO;
import com.example.alipay.service.AlipayNotifyService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service
@RequiredArgsConstructor
public class AlipayNotifyServiceImpl implements AlipayNotifyService {
private final AlipayConfig alipayConfig;
/**
* 处理异步通知
* 支付宝会以POST方式请求我们提供的notify_url
*/
@Override
public String handleNotify(HttpServletRequest request) {
log.info("收到支付宝异步通知");
try {
// 1. 获取请求参数
Map<String, String> params = getParamsFromRequest(request);
// 2. 记录完整的通知参数(便于调试)
log.info("支付宝异步通知参数: {}", params);
// 3. 验签
boolean signVerified = verifySign(params);
if (!signVerified) {
log.error("验签失败");
return "failure";
}
// 4. 转换参数为DTO
AlipayNotifyDTO notifyDTO = convertToDTO(params);
// 5. 根据交易状态处理业务
String tradeStatus = notifyDTO.getTradeStatus();
if ("TRADE_SUCCESS".equals(tradeStatus)) {
log.info("交易成功,订单号: {}, 支付宝交易号: {}",
notifyDTO.getOutTradeNo(), notifyDTO.getTradeNo());
// 处理支付成功的业务逻辑
processPaymentSuccess(notifyDTO);
} else if ("TRADE_FINISHED".equals(tradeStatus)) {
log.info("交易完成,订单号: {}", notifyDTO.getOutTradeNo());
// 处理交易完成的业务逻辑
processTradeFinished(notifyDTO);
} else if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
log.info("交易创建,等待买家付款,订单号: {}", notifyDTO.getOutTradeNo());
} else if ("TRADE_CLOSED".equals(tradeStatus)) {
log.info("交易关闭,订单号: {}", notifyDTO.getOutTradeNo());
// 处理交易关闭的业务逻辑
processTradeClosed(notifyDTO);
}
// 6. 返回成功,支付宝将不再发送通知
return "success";
} catch (Exception e) {
log.error("处理支付宝异步通知异常", e);
return "failure";
}
}
/**
* 处理同步通知(页面跳转)
*/
@Override
public String handleReturn(HttpServletRequest request) {
log.info("收到支付宝同步通知(页面跳转)");
try {
// 1. 获取请求参数
Map<String, String> params = getParamsFromRequest(request);
// 2. 验签
boolean signVerified = verifySign(params);
if (!signVerified) {
log.error("页面跳转验签失败");
return "redirect:http://localhost:8080/error";
}
// 3. 获取关键信息
String outTradeNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");
String totalAmount = params.get("total_amount");
log.info("页面跳转成功,订单号: {}, 交易号: {}, 金额: {}",
outTradeNo, tradeNo, totalAmount);
// 4. 重定向到前端页面,并携带订单信息
return "redirect:http://localhost:8080/payment/success?orderNo=" + outTradeNo;
} catch (Exception e) {
log.error("处理页面跳转异常", e);
return "redirect:http://localhost:8080/error";
}
}
/**
* 验签
*/
@Override
public boolean verifySign(Map<String, String> params) {
try {
if (params.isEmpty()) {
return false;
}
// 注意:使用支付宝公钥验签,不是应用公钥
return AlipaySignature.rsaCheckV1(
params,
alipayConfig.getAlipayPublicKey(),
alipayConfig.getCharset(),
alipayConfig.getSignType()
);
} catch (AlipayApiException e) {
log.error("验签异常", e);
return false;
}
}
/**
* 处理支付成功业务
*/
@Override
public void processPaymentSuccess(AlipayNotifyDTO notifyDTO) {
// TODO: 实现具体的业务逻辑
// 1. 根据outTradeNo查询本地订单
// 2. 校验订单金额是否一致
// 3. 更新订单状态为已支付
// 4. 更新支付流水号
// 5. 处理其他业务(如发货、发送通知等)
log.info("处理支付成功业务: 订单号={}", notifyDTO.getOutTradeNo());
// 示例:校验订单金额
BigDecimal totalAmount = notifyDTO.getTotalAmount();
log.info("订单金额校验: 支付金额={}", totalAmount);
// TODO: 在这里编写您的业务逻辑
}
/**
* 处理交易完成业务
*/
private void processTradeFinished(AlipayNotifyDTO notifyDTO) {
log.info("处理交易完成业务: 订单号={}", notifyDTO.getOutTradeNo());
// TODO: 处理交易完成后的业务逻辑
}
/**
* 处理交易关闭业务
*/
private void processTradeClosed(AlipayNotifyDTO notifyDTO) {
log.info("处理交易关闭业务: 订单号={}, 原因: {}",
notifyDTO.getOutTradeNo(), notifyDTO.getTradeStatus());
// TODO: 处理交易关闭后的业务逻辑
}
/**
* 获取请求参数
*/
private Map<String, String> getParamsFromRequest(HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
String[] values = requestParams.get(name);
String valueStr = "";
if (values != null && values.length > 0) {
for (int i = 0; i < values.length; i++) {
valueStr += (i == values.length - 1) ? values[i] : values[i] + ",";
}
}
params.put(name, valueStr);
}
return params;
}
/**
* 转换为DTO
*/
private AlipayNotifyDTO convertToDTO(Map<String, String> params) {
AlipayNotifyDTO dto = new AlipayNotifyDTO();
dto.setNotifyTime(params.get("notify_time"));
dto.setNotifyType(params.get("notify_type"));
dto.setNotifyId(params.get("notify_id"));
dto.setCharset(params.get("charset"));
dto.setSignType(params.get("sign_type"));
dto.setSign(params.get("sign"));
dto.setTradeNo(params.get("trade_no"));
dto.setOutTradeNo(params.get("out_trade_no"));
dto.setOutBizNo(params.get("out_biz_no"));
dto.setBuyerId(params.get("buyer_id"));
dto.setBuyerLogonId(params.get("buyer_logon_id"));
dto.setSellerId(params.get("seller_id"));
dto.setSellerEmail(params.get("seller_email"));
dto.setTradeStatus(params.get("trade_status"));
if (StringUtils.hasText(params.get("total_amount"))) {
dto.setTotalAmount(new BigDecimal(params.get("total_amount")));
}
if (StringUtils.hasText(params.get("receipt_amount"))) {
dto.setReceiptAmount(new BigDecimal(params.get("receipt_amount")));
}
if (StringUtils.hasText(params.get("invoice_amount"))) {
dto.setInvoiceAmount(new BigDecimal(params.get("invoice_amount")));
}
if (StringUtils.hasText(params.get("buyer_pay_amount"))) {
dto.setBuyerPayAmount(new BigDecimal(params.get("buyer_pay_amount")));
}
if (StringUtils.hasText(params.get("point_amount"))) {
dto.setPointAmount(new BigDecimal(params.get("point_amount")));
}
if (StringUtils.hasText(params.get("refund_fee"))) {
dto.setRefundFee(new BigDecimal(params.get("refund_fee")));
}
dto.setRefundStatus(params.get("refund_status"));
dto.setSubject(params.get("subject"));
dto.setBody(params.get("body"));
dto.setGmtCreate(params.get("gmt_create"));
dto.setGmtPayment(params.get("gmt_payment"));
dto.setGmtRefund(params.get("gmt_refund"));
dto.setGmtClose(params.get("gmt_close"));
dto.setFundBillList(params.get("fund_bill_list"));
dto.setPassbackParams(params.get("passback_params"));
return dto;
}
}
控制器
package com.example.alipay.controller;
import com.example.alipay.service.AlipayNotifyService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.view.RedirectView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@RestController
@RequestMapping("/api/alipay")
@RequiredArgsConstructor
public class AlipayNotifyController {
private final AlipayNotifyService alipayNotifyService;
/**
* 异步通知接口
* 支付宝服务器会调用此接口,必须返回"success"或"failure"
*/
@PostMapping("/notify")
public String notify(HttpServletRequest request) {
return alipayNotifyService.handleNotify(request);
}
/**
* 同步通知接口(页面跳转)
*/
@RequestMapping("/return")
public void returnNotify(HttpServletRequest request, HttpServletResponse response)
throws IOException {
String redirectUrl = alipayNotifyService.handleReturn(request);
response.sendRedirect(redirectUrl);
}
}
业务处理示例(订单服务)
package com.example.alipay.service;
import com.example.alipay.dto.AlipayNotifyDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {
/**
* 处理支付成功后的业务逻辑
*/
@Transactional(rollbackFor = Exception.class)
public void handlePaymentSuccess(AlipayNotifyDTO notifyDTO) {
String outTradeNo = notifyDTO.getOutTradeNo();
String tradeNo = notifyDTO.getTradeNo();
BigDecimal totalAmount = notifyDTO.getTotalAmount();
// 1. 查询订单
// OrderEntity order = orderMapper.selectByOrderNo(outTradeNo);
// if (order == null) {
// throw new RuntimeException("订单不存在: " + outTradeNo);
// }
// 2. 检查订单状态,防止重复处理
// if ("PAID".equals(order.getStatus())) {
// log.info("订单已处理,跳过: {}", outTradeNo);
// return;
// }
// 3. 校验金额是否一致
// BigDecimal orderAmount = order.getTotalAmount();
// if (totalAmount.compareTo(orderAmount) != 0) {
// throw new RuntimeException("订单金额不一致: 订单金额=" + orderAmount + ", 支付金额=" + totalAmount);
// }
// 4. 更新订单状态为已支付
// order.setStatus("PAID");
// order.setTradeNo(tradeNo);
// order.setPayTime(new Date());
// orderMapper.updateById(order);
// 5. 记录支付日志
// PaymentLog paymentLog = new PaymentLog();
// paymentLog.setOrderNo(outTradeNo);
// paymentLog.setTradeNo(tradeNo);
// paymentLog.setAmount(totalAmount);
// paymentLog.setPayTime(new Date());
// paymentLogMapper.insert(paymentLog);
log.info("订单支付成功处理完成: 订单号={}, 交易号={}, 金额={}",
outTradeNo, tradeNo, totalAmount);
}
}
测试控制器
package com.example.alipay.controller;
import com.example.alipay.config.AlipayConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequiredArgsConstructor
public class TestController {
private final AlipayConfig alipayConfig;
/**
* 测试接口 - 模拟接收通知
*/
@PostMapping("/test/notify")
public String testNotify(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = request.getReader()) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (Exception e) {
log.error("读取请求失败", e);
}
log.info("测试通知内容: {}", sb.toString());
return "success";
}
/**
* 获取配置信息(仅测试用)
*/
@GetMapping("/test/config")
public Map<String, String> getConfig() {
Map<String, String> config = new HashMap<>();
config.put("appId", alipayConfig.getAppId());
config.put("gatewayUrl", alipayConfig.getGatewayUrl());
config.put("notifyUrl", alipayConfig.getNotifyUrl());
config.put("returnUrl", alipayConfig.getReturnUrl());
return config;
}
}
注意事项
1 安全建议
// 在正式环境中,建议: // 1. 使用HTTPS协议 // 2. 限制IP白名单(支付宝通知服务器的IP) // 3. 对敏感信息进行加密存储 // 4. 添加幂等性处理(防止重复通知)
2 日志记录
// 建议记录完整的通知参数用于排查问题
log.info("支付宝通知参数: {}", JSON.toJSONString(params));
3 异常处理
// 处理失败时返回"failure",支付宝会重试 // 处理成功时返回"success",支付宝不会重试 // 建议:处理失败抛出异常,返回"failure"
回调验证流程
- 验签:使用支付宝公钥对通知参数进行签名验证
- 订单匹配:根据
out_trade_no查询本地订单 - 金额校验:核对通知中的
total_amount是否与订单金额一致 - 状态检查:防止重复通知导致重复处理
- 业务处理:更新订单状态、处理其他业务逻辑
- 返回响应:返回
success或failure
回调测试
在沙箱环境测试时,可以使用支付宝提供的模拟工具,或者使用Postman模拟回调请求。
这个案例提供了一个完整的支付宝回调处理流程,您可以根据实际业务需求进行修改和扩展。