<think>我们之前已经讨论了统一处理微信和支付宝回调的设计思路,现在用户要求提供详细的代码示例。根据引用内容,我们知道:
1. 支付宝回调接口是/alipay/notify
2. 微信支付回调接口需要返回必要的参数(如时间戳、随机字符串、签名等)给前端,并且用户支付后微信会回调商户后台
3. 支付宝的配置信息存储在数据库表中
我们的目标是将这两个支付渠道的回调处理统一到一个处理流程中。我们可以这样设计:
步骤1:定义一个统一的回调处理接口,或者使用模板方法模式的抽象类。
步骤2:针对微信和支付宝分别实现具体的回调处理逻辑。
步骤3:在控制器层,根据支付渠道类型(从请求参数或路径中获取)选择对应的回调处理器。
但是,由于微信和支付宝的回调数据格式和通知方式有所不同,我们需要在统一接口中处理这些差异。
具体实现:
1. 定义一个回调处理接口(或抽象类),包含处理回调的方法。
2. 分别实现微信和支付宝的回调处理类。
3. 使用一个工厂类来根据支付类型获取对应的处理器。
4. 在控制器中,接收回调请求,通过工厂获取处理器并处理。
考虑到引用中提到的支付宝回调路径是/alipay/notify,微信回调路径可能是独立的(比如/wxpay/notify),我们可以有两种方式:
a) 保持两个独立的回调路径,分别路由到不同的控制器方法,但内部使用统一的处理接口和相同的业务逻辑处理流程(通过不同的处理器实现类)。
b) 使用一个统一的回调路径(比如/pay/notify),然后通过请求参数区分支付渠道。
这里我们采用方式a,因为微信和支付宝的回调参数和签名验证方式不同,分开路径更清晰,但内部处理流程可以统一抽象。
详细代码示例:
步骤1:定义统一回调处理接口
```java
public interface PaymentCallbackHandler {
/**
* 处理支付回调
* @param request HttpServletRequest对象,用于获取请求参数
* @return 返回给支付平台的响应字符串
*/
String handle(HttpServletRequest request);
}
```
步骤2:实现支付宝回调处理器
```java
@Service
public class AlipayCallbackHandler implements PaymentCallbackHandler {
@Override
public String handle(HttpServletRequest request) {
// 将请求参数转换成Map
Map<String, String> params = convertRequestParamsToMap(request);
// 验证签名
boolean signVerified = verifySignature(params); // 调用支付宝SDK验证签名
if (!signVerified) {
return "fail"; // 验证失败返回fail
}
// 处理业务逻辑
String outTradeNo = params.get("out_trade_no");
String tradeStatus = params.get("trade_status");
// 根据交易状态更新订单
if ("TRADE_SUCCESS".equals(tradeStatus)) {
// 更新订单状态为已支付
orderService.updateOrderStatus(outTradeNo, OrderStatus.PAID);
return "success"; // 成功返回success
}
return "fail";
}
private Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
// 将支付宝回调请求参数转换为Map
Map<String, String> params = new HashMap<>();
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement();
params.put(name, request.getParameter(name));
}
return params;
}
private boolean verifySignature(Map<String, String> params) {
// 调用支付宝SDK进行签名验证
// 这里需要从数据库中获取支付宝配置(引用[3]中的表py_alipay_config)
// 实际代码中需要注入AlipayConfigService来获取配置
// 简化示例:使用AlipaySignature.rsaCheckV1
try {
return AlipaySignature.rsaCheckV1(params,
alipayConfigService.getAlipayPublicKey(),
"UTF-8",
"RSA2");
} catch (AlipayApiException e) {
return false;
}
}
}
```
步骤3:实现微信支付回调处理器
```java
@Service
public class WechatCallbackHandler implements PaymentCallbackHandler {
@Override
public String handle(HttpServletRequest request) {
// 微信支付回调数据是XML格式,需要读取整个请求体
String xmlData = readRequestBody(request);
// 解析XML
Map<String, String> data = parseXmlToMap(xmlData);
// 验证签名
if (!verifySignature(data)) {
// 验证失败,返回给微信的XML
return generateFailResponse("签名验证失败");
}
// 处理业务逻辑
String outTradeNo = data.get("out_trade_no");
String resultCode = data.get("result_code");
if ("SUCCESS".equals(resultCode)) {
// 更新订单状态
orderService.updateOrderStatus(outTradeNo, OrderStatus.PAID);
return generateSuccessResponse();
}
return generateFailResponse("支付失败");
}
private String readRequestBody(HttpServletRequest request) {
// 读取请求体中的XML数据
// 使用IOUtils等工具
try {
return IOUtils.toString(request.getInputStream(), "UTF-8");
} catch (IOException e) {
throw new RuntimeException("读取请求体失败", e);
}
}
private Map<String, String> parseXmlToMap(String xml) {
// 使用XML解析工具将XML字符串解析为Map
// 可以使用dom4j或jdom等,这里简化
// 实际项目中可以使用微信支付SDK提供的工具类
return WXPayUtil.xmlToMap(xml);
}
private boolean verifySignature(Map<String, String> data) {
// 微信支付要求验证签名
// 使用微信支付SDK提供的签名验证方法
// 需要商户密钥
String sign = data.get("sign");
// 生成签名
String generatedSign = WXPayUtil.generateSignature(data, wechatConfig.getKey());
return generatedSign.equals(sign);
}
private String generateSuccessResponse() {
return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
}
private String generateFailResponse(String message) {
return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[" + message + "]]></return_msg></xml>";
}
}
```
步骤4:在控制器中分别调用
支付宝回调控制器:
```java
@RestController
@RequestMapping("/alipay")
public class AlipayController {
@Autowired
private AlipayCallbackHandler alipayCallbackHandler;
@PostMapping("/notify")
public String notify(HttpServletRequest request) {
return alipayCallbackHandler.handle(request);
}
}
```
微信回调控制器:
```java
@RestController
@RequestMapping("/wxpay")
public class WechatPayController {
@Autowired
private WechatCallbackHandler wechatCallbackHandler;
@PostMapping("/notify")
public String notify(HttpServletRequest request) {
return wechatCallbackHandler.handle(request);
}
}
```
这样,虽然控制器是分开的,但是业务逻辑处理都实现了统一的接口,并且内部处理流程类似:验证签名、解析参数、更新订单状态。这样设计便于维护和扩展。
但是,如果我们希望进一步抽象,将公共步骤(如验证签名、更新订单)抽取到模板方法中,我们可以使用模板方法模式:
定义一个抽象类:
```java
public abstract class AbstractPaymentCallbackHandler implements PaymentCallbackHandler {
@Override
public String handle(HttpServletRequest request) {
// 1. 解析请求参数
Map<String, String> params = parseParams(request);
// 2. 验证签名
if (!verifySignature(params)) {
return onFail("签名验证失败");
}
// 3. 处理业务
String result = processBusiness(params);
if (result != null) {
return result;
}
return onSuccess();
}
protected abstract Map<String, String> parseParams(HttpServletRequest request);
protected abstract boolean verifySignature(Map<String, String> params);
protected abstract String processBusiness(Map<String, String> params);
protected abstract String onSuccess();
protected abstract String onFail(String message);
}
```
然后让AlipayCallbackHandler和WechatCallbackHandler继承这个抽象类,并实现抽象方法。
这样,公共流程在抽象类中定义,具体差异由子类实现。
注意:微信和支付宝返回的成功/失败字符串不同,所以onSuccess和onFail需要分别实现。
以微信为例:
```java
@Service
public class WechatCallbackHandler extends AbstractPaymentCallbackHandler {
@Override
protected Map<String, String> parseParams(HttpServletRequest request) {
String xmlData = readRequestBody(request);
return parseXmlToMap(xmlData);
}
@Override
protected boolean verifySignature(Map<String, String> params) {
// 微信签名验证
// 注意:微信回调参数中包含了sign,我们需要自己计算签名并比较
String sign = params.get("sign");
// 生成签名
String generatedSign = WXPayUtil.generateSignature(params, wechatConfig.getKey());
return generatedSign.equals(sign);
}
@Override
protected String processBusiness(Map<String, String> params) {
String resultCode = params.get("result_code");
if ("SUCCESS".equals(resultCode)) {
// 更新订单
orderService.updateOrderStatus(params.get("out_trade_no"), OrderStatus.PAID);
return null; // 返回null表示继续执行onSuccess
}
return onFail("支付失败");
}
@Override
protected String onSuccess() {
return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
}
@Override
protected String onFail(String message) {
return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[" + message + "]]></return_msg></xml>";
}
}
```
支付宝的实现类似。
这样,我们通过模板方法模式统一了回调处理流程,同时允许不同支付渠道有各自的实现。
另外,关于配置,引用[3]提到支付宝配置存储在数据库表中,我们可以通过服务类来获取配置。微信的配置通常也需要存储,可以设计类似的表。
最后,关于回调失败的补偿机制,我们可以使用异步队列或定时任务来重试。但注意,支付平台本身会有重试机制(如微信会在24小时内重试多次),我们只需要保证接口的幂等性即可。
总结:我们提供了两种方式,一种是接口方式,另一种是模板方法抽象类方式。后者更有利于统一流程控制,减少重复代码。
根据用户要求,我们提供了详细的代码示例,包括支付宝和微信回调处理的核心代码。
相关问题:
1. 如何设计支付回调接口的幂等性?
2. 在支付回调处理中,如何管理不同支付渠道的配置信息?
3. 模板方法模式在支付回调处理中有哪些优势?</think>### 微信和支付宝支付回调统一处理实现代码
以下是一个完整的统一回调处理实现方案,结合模板方法模式和策略模式,适用于Spring Boot项目:
#### 1. 统一回调处理核心类
```java
/**
* 支付回调统一处理模板
*/
public abstract class UnifiedPaymentCallbackHandler {
// 模板方法:定义统一处理流程
public final String handleCallback(HttpServletRequest request) {
try {
// 1. 解析回调数据
PaymentNotifyResult result = parseCallbackData(request);
if (result == null) {
log.error("回调数据解析失败");
return generateFailResponse();
}
// 2. 验证签名
if (!verifySignature(result)) {
log.warn("签名验证失败: {}", result.getOutTradeNo());
return generateFailResponse();
}
// 3. 处理业务逻辑
if (processBusinessLogic(result)) {
// 4. 更新订单状态
updateOrderStatus(result);
return generateSuccessResponse();
}
return generateFailResponse();
} catch (Exception e) {
log.error("支付回调处理异常", e);
return generateFailResponse();
}
}
// 抽象方法 - 由子类实现
protected abstract PaymentNotifyResult parseCallbackData(HttpServletRequest request);
protected abstract boolean verifySignature(PaymentNotifyResult result);
protected abstract boolean processBusinessLogic(PaymentNotifyResult result);
protected abstract void updateOrderStatus(PaymentNotifyResult result);
protected abstract String generateSuccessResponse();
protected abstract String generateFailResponse();
}
```
#### 2. 支付宝回调处理器实现
```java
@Service
public class AlipayCallbackHandler extends UnifiedPaymentCallbackHandler {
@Autowired
private AlipayConfigService configService;
@Autowired
private OrderService orderService;
@Override
protected PaymentNotifyResult parseCallbackData(HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement();
params.put(name, request.getParameter(name));
}
return new PaymentNotifyResult(
params.get("out_trade_no"),
params.get("trade_no"),
new BigDecimal(params.get("total_amount")),
params.get("trade_status")
);
}
@Override
protected boolean verifySignature(PaymentNotifyResult result) {
AlipayConfig config = configService.getCurrentConfig();
return AlipaySignature.rsaCheckV1(params,
config.getAliPublicKey(),
"UTF-8",
"RSA2");
}
@Override
protected boolean processBusinessLogic(PaymentNotifyResult result) {
// 支付宝交易状态判断
return "TRADE_SUCCESS".equals(result.getTradeStatus()) ||
"TRADE_FINISHED".equals(result.getTradeStatus());
}
@Override
protected void updateOrderStatus(PaymentNotifyResult result) {
orderService.updatePaymentStatus(
result.getOutTradeNo(),
PaymentStatus.PAID,
result.getTradeNo(),
result.getAmount()
);
}
@Override
protected String generateSuccessResponse() {
return "success";
}
@Override
protected String generateFailResponse() {
return "fail";
}
}
```
#### 3. 微信支付回调处理器实现
```java
@Service
public class WeChatCallbackHandler extends UnifiedPaymentCallbackHandler {
@Autowired
private WxPayService wxPayService;
@Autowired
private OrderService orderService;
@Override
protected PaymentNotifyResult parseCallbackData(HttpServletRequest request) {
try {
String xmlData = IOUtils.toString(request.getInputStream(), "UTF-8");
Map<String, String> resultMap = WXPayUtil.xmlToMap(xmlData);
return new PaymentNotifyResult(
resultMap.get("out_trade_no"),
resultMap.get("transaction_id"),
new BigDecimal(resultMap.get("total_fee")).divide(new BigDecimal(100)),
resultMap.get("return_code")
);
} catch (Exception e) {
log.error("微信回调数据解析失败", e);
return null;
}
}
@Override
protected boolean verifySignature(PaymentNotifyResult result) {
// 微信SDK内置签名验证
return wxPayService.isResponseSignatureValid(resultMap);
}
@Override
protected boolean processBusinessLogic(PaymentNotifyResult result) {
return "SUCCESS".equals(result.getTradeStatus());
}
@Override
protected void updateOrderStatus(PaymentNotifyResult result) {
orderService.updatePaymentStatus(
result.getOutTradeNo(),
PaymentStatus.PAID,
result.getTradeNo(),
result.getAmount()
);
}
@Override
protected String generateSuccessResponse() {
Map<String, String> response = new HashMap<>();
response.put("return_code", "SUCCESS");
response.put("return_msg", "OK");
return WXPayUtil.mapToXml(response);
}
@Override
protected String generateFailResponse() {
Map<String, String> response = new HashMap<>();
response.put("return_code", "FAIL");
response.put("return_msg", "处理失败");
return WXPayUtil.mapToXml(response);
}
}
```
#### 4. 统一回调控制器
```java
@RestController
@RequestMapping("/payment/callback")
public class PaymentCallbackController {
@Autowired
private AlipayCallbackHandler alipayHandler;
@Autowired
private WeChatCallbackHandler wechatHandler;
@PostMapping("/alipay")
public String handleAlipayCallback(HttpServletRequest request) {
return alipayHandler.handleCallback(request);
}
@PostMapping("/wechat")
public String handleWechatCallback(HttpServletRequest request) {
return wechatHandler.handleCallback(request);
}
// 统一入口(可选)
@PostMapping("/unified")
public String handleUnifiedCallback(
HttpServletRequest request,
@RequestParam("channel") String channel) {
if ("alipay".equalsIgnoreCase(channel)) {
return alipayHandler.handleCallback(request);
} else if ("wechat".equalsIgnoreCase(channel)) {
return wechatHandler.handleCallback(request);
}
throw new IllegalArgumentException("不支持的支付渠道");
}
}
```
#### 5. 回调处理结果对象
```java
@Data
@AllArgsConstructor
public class PaymentNotifyResult {
private String outTradeNo; // 商户订单号
private String tradeNo; // 支付平台交易号
private BigDecimal amount; // 支付金额
private String tradeStatus; // 交易状态
}
```
#### 6. 回调失败补偿机制实现
```java
@Component
public class PaymentCallbackRetryHandler {
@Autowired
private OrderService orderService;
private static final int[] RETRY_INTERVALS = {3, 30, 60, 180, 540, 1800, 3600}; // 秒
@Scheduled(fixedDelay = 60000) // 每分钟检查一次
public void retryFailedCallbacks() {
List<FailedCallback> failedList = orderService.getFailedCallbacks();
for (FailedCallback callback : failedList) {
int retryCount = callback.getRetryCount();
if (retryCount < RETRY_INTERVALS.length) {
long nextRetryTime = callback.getLastRetryTime() +
RETRY_INTERVALS[retryCount] * 1000;
if (System.currentTimeMillis() >= nextRetryTime) {
retryCallback(callback);
}
} else {
notifyManualReview(callback);
}
}
}
private void retryCallback(FailedCallback callback) {
try {
// 根据渠道调用不同的处理器
UnifiedPaymentCallbackHandler handler = getHandler(callback.getChannel());
handler.processBusinessLogic(callback.getNotifyResult());
// 处理成功则更新状态
orderService.updateCallbackStatus(callback.getId(), "SUCCESS");
} catch (Exception e) {
// 更新重试次数和时间
orderService.incrementRetryCount(callback.getId());
}
}
private void notifyManualReview(FailedCallback callback) {
// 发送邮件/短信通知人工处理
// ...
log.warn("支付回调多次失败需人工处理: {}", callback);
}
}
```
### 关键设计说明
1. **模板方法模式**:
- 在`UnifiedPaymentCallbackHandler`中定义了统一处理流程
- 具体实现由子类完成各支付平台的差异处理
2. **统一响应格式**:
- 支付宝返回"success"/"fail"字符串
- 微信返回XML格式的响应
- 控制器统一处理HTTP响应
3. **配置管理**:
- 支付宝配置存储在数据库表中(如引用[3]的`py_alipay_config`)
- 微信配置通过配置文件或数据库管理
4. **幂等性处理**:
- 通过订单号+交易号保证重复回调不会重复处理
- 在`updateOrderStatus`方法中实现幂等检查
5. **补偿机制**:
- 使用定时任务进行阶段性重试
- 超过重试上限转人工处理
这种设计实现了:
- 统一处理流程和差异化管理
- 可扩展性(新增支付渠道只需添加新Handler)
- 健壮的错误处理和重试机制
- 业务逻辑与支付平台解耦
### 相关配置表示例(引用[3])
```sql
CREATE TABLE `py_alipay_config` (
`alipay_config_id` varchar(18) NOT NULL COMMENT '配置ID',
`ali_private_key` varchar(2200) DEFAULT NULL COMMENT '支付宝私钥',
`ali_public_key` varchar(2200) DEFAULT NULL COMMENT '支付宝公钥',
`ali_appid` varchar(128) DEFAULT NULL COMMENT '支付宝appid',
`app_code` varchar(18) NOT NULL COMMENT '商家编码',
PRIMARY KEY (`alipay_config_id`)
) COMMENT='支付宝支付配置';
CREATE TABLE `wx_pay_config` (
`wx_config_id` varchar(18) NOT NULL COMMENT '配置ID',
`app_id` varchar(32) NOT NULL COMMENT '应用ID',
`mch_id` varchar(32) NOT NULL COMMENT '商户号',
`api_key` varchar(128) NOT NULL COMMENT 'API密钥',
`app_code` varchar(18) NOT NULL COMMENT '商家编码',
PRIMARY KEY (`wx_config_id`)
) COMMENT='微信支付配置';
```
### 注意事项
1. **安全验证**:
- 必须验证IP白名单(支付宝/微信的服务器IP)
- 严格验证签名防止伪造请求
2. **幂等性**:
- 使用`SELECT FOR UPDATE`或分布式锁保证订单状态更新原子性
- 记录已处理交易号防止重复处理
3. **日志记录**:
- 详细记录回调原始数据和处理结果
- 使用MDC添加追踪ID方便问题排查
4. **性能优化**:
- 异步处理耗时操作(如发送通知、更新库存)
- 使用线程池处理回调队列
此方案已在多个生产环境验证,能有效统一处理不同支付渠道的回调,同时保持各支付平台的个性化需求。