package com.ruoyi.payment.application.service;
|
|
import com.alibaba.fastjson.JSON;
|
import com.ruoyi.payment.domain.model.NotifyLog;
|
import com.ruoyi.payment.domain.model.PaymentOrder;
|
import com.ruoyi.payment.domain.model.PaymentTransaction;
|
import com.ruoyi.payment.infrastructure.persistence.mapper.NotifyLogMapper;
|
import com.ruoyi.payment.infrastructure.persistence.mapper.PaymentOrderMapper;
|
import com.ruoyi.payment.infrastructure.persistence.mapper.PaymentTransactionMapper;
|
import com.ruoyi.payment.infrastructure.util.SnowflakeIdGenerator;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.time.LocalDateTime;
|
import java.util.Map;
|
|
/**
|
* 支付回调处理服务
|
*
|
* @author ruoyi
|
*/
|
@Slf4j
|
@Service
|
public class PaymentNotifyService {
|
|
@Autowired
|
private PaymentOrderMapper paymentOrderMapper;
|
|
@Autowired
|
private PaymentTransactionMapper paymentTransactionMapper;
|
|
@Autowired
|
private NotifyLogMapper notifyLogMapper;
|
|
@Autowired
|
private BizCallbackService bizCallbackService;
|
|
/**
|
* 处理微信支付回调
|
*/
|
@Transactional(rollbackFor = Exception.class)
|
public void processWechatNotify(Map<String, String> params, String rawXml) {
|
String outTradeNo = params.get("out_trade_no"); // 商户订单号
|
String transactionId = params.get("transaction_id"); // 微信交易号
|
String resultCode = params.get("result_code");
|
String returnCode = params.get("return_code");
|
|
log.info("处理微信回调,商户订单号: {}, 微信交易号: {}, 结果: {}", outTradeNo, transactionId, resultCode);
|
|
// 检查幂等性
|
if (isNotifyProcessed("WECHAT", transactionId)) {
|
log.info("微信回调已处理过,交易号: {}", transactionId);
|
return;
|
}
|
|
// 记录回调日志
|
NotifyLog notifyLog = saveNotifyLog("WECHAT", transactionId, rawXml, true);
|
|
try {
|
// 通信成功且业务成功
|
if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(resultCode)) {
|
// 查询订单和交易(使用业务订单号查询)
|
PaymentOrder order = paymentOrderMapper.selectById(Long.parseLong(outTradeNo));
|
if (order == null) {
|
log.error("订单不存在: {}", outTradeNo);
|
updateNotifyLog(notifyLog.getId(), false, "订单不存在");
|
return;
|
}
|
|
PaymentTransaction transaction = paymentTransactionMapper.selectPendingByOrderId(order.getId());
|
if (transaction == null) {
|
log.error("待支付交易不存在,订单ID: {}", order.getId());
|
updateNotifyLog(notifyLog.getId(), false, "交易不存在");
|
return;
|
}
|
|
// 更新交易状态
|
transaction.setStatus("SUCCEEDED");
|
transaction.setChannelTradeNo(transactionId);
|
transaction.setPaidAt(LocalDateTime.now());
|
transaction.setResponseSnapshot(JSON.toJSONString(params));
|
paymentTransactionMapper.update(transaction);
|
|
// 更新订单状态
|
order.setStatus("SUCCEEDED");
|
order.setChannelTradeNo(transactionId);
|
order.setPaidAt(LocalDateTime.now());
|
order.setLatestTransactionId(transaction.getId());
|
paymentOrderMapper.update(order);
|
|
// 更新通知日志
|
notifyLog.setOrderId(order.getId());
|
notifyLog.setTransactionId(transaction.getId());
|
updateNotifyLog(notifyLog.getId(), true, "处理成功");
|
|
// 触发业务回调
|
bizCallbackService.triggerCallback(order, transaction);
|
|
log.info("微信支付回调处理成功,订单ID: {}, 交易ID: {}", order.getId(), transaction.getId());
|
} else {
|
log.warn("微信支付失败,returnCode: {}, resultCode: {}", returnCode, resultCode);
|
updateNotifyLog(notifyLog.getId(), true, "支付失败");
|
}
|
} catch (Exception e) {
|
log.error("处理微信回调异常", e);
|
updateNotifyLog(notifyLog.getId(), false, "处理异常: " + e.getMessage());
|
throw e;
|
}
|
}
|
|
/**
|
* 处理支付宝回调
|
*/
|
@Transactional(rollbackFor = Exception.class)
|
public void processAlipayNotify(Map<String, String> params) {
|
String outTradeNo = params.get("out_trade_no"); // 商户订单号
|
String tradeNo = params.get("trade_no"); // 支付宝交易号
|
String tradeStatus = params.get("trade_status");
|
|
log.info("处理支付宝回调,商户订单号: {}, 支付宝交易号: {}, 状态: {}", outTradeNo, tradeNo, tradeStatus);
|
|
// 检查幂等性
|
if (isNotifyProcessed("ALIPAY", tradeNo)) {
|
log.info("支付宝回调已处理过,交易号: {}", tradeNo);
|
return;
|
}
|
|
// 记录回调日志
|
NotifyLog notifyLog = saveNotifyLog("ALIPAY", tradeNo, JSON.toJSONString(params), true);
|
|
try {
|
// 交易成功
|
if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
|
// 查询订单和交易(使用业务订单号查询)
|
PaymentOrder order = paymentOrderMapper.selectById(Long.parseLong(outTradeNo));
|
if (order == null) {
|
log.error("订单不存在: {}", outTradeNo);
|
updateNotifyLog(notifyLog.getId(), false, "订单不存在");
|
return;
|
}
|
|
PaymentTransaction transaction = paymentTransactionMapper.selectPendingByOrderId(order.getId());
|
if (transaction == null) {
|
log.error("待支付交易不存在,订单ID: {}", order.getId());
|
updateNotifyLog(notifyLog.getId(), false, "交易不存在");
|
return;
|
}
|
|
// 更新交易状态
|
transaction.setStatus("SUCCEEDED");
|
transaction.setChannelTradeNo(tradeNo);
|
transaction.setPaidAt(LocalDateTime.now());
|
transaction.setResponseSnapshot(JSON.toJSONString(params));
|
paymentTransactionMapper.update(transaction);
|
|
// 更新订单状态
|
order.setStatus("SUCCEEDED");
|
order.setChannelTradeNo(tradeNo);
|
order.setPaidAt(LocalDateTime.now());
|
order.setLatestTransactionId(transaction.getId());
|
paymentOrderMapper.update(order);
|
|
// 更新通知日志
|
notifyLog.setOrderId(order.getId());
|
notifyLog.setTransactionId(transaction.getId());
|
updateNotifyLog(notifyLog.getId(), true, "处理成功");
|
|
// 触发业务回调
|
bizCallbackService.triggerCallback(order, transaction);
|
|
log.info("支付宝回调处理成功,订单ID: {}, 交易ID: {}", order.getId(), transaction.getId());
|
} else {
|
log.warn("支付宝交易状态异常: {}", tradeStatus);
|
updateNotifyLog(notifyLog.getId(), true, "状态: " + tradeStatus);
|
}
|
} catch (Exception e) {
|
log.error("处理支付宝回调异常", e);
|
updateNotifyLog(notifyLog.getId(), false, "处理异常: " + e.getMessage());
|
throw e;
|
}
|
}
|
|
/**
|
* 处理第三方支付宝回调
|
* 第三方支付宝通过GET方式回调,参数通过URL传递
|
*
|
* 参数格式:
|
* method: 方法名
|
* APPID: 应用ID
|
* PaidMoneyType: 支付类型
|
* PaidMoney: 支付金额(分)
|
* PaidRemarks: 支付备注(包含transaction_id)
|
* UnixTime: 时间戳
|
* Sign: 签名
|
*/
|
@Transactional(rollbackFor = Exception.class)
|
public void processAlipayThirdPartyNotify(Map<String, String> params) {
|
String method = params.get("method"); // 方法名
|
String appId = params.get("APPID"); // 应用ID
|
String paidMoneyType = params.get("PaidMoneyType"); // 支付类型
|
String paidMoney = params.get("PaidMoney"); // 支付金额(分)
|
String paidRemarks = params.get("PaidRemarks"); // 支付备注(可能包含订单号和交易号)
|
String unixTime = params.get("UnixTime"); // 时间戳
|
String sign = params.get("Sign"); // 签名
|
|
log.info("处理第三方支付宝回调,method: {}, APPID: {}, PaidMoneyType: {}, PaidMoney: {}, PaidRemarks: {}, UnixTime: {}, Sign: {}",
|
method, appId, paidMoneyType, paidMoney, paidRemarks, unixTime, sign);
|
log.info("第三方支付宝回调完整参数: {}", params);
|
|
// 从PaidRemarks中提取订单号(out_trade_no)
|
// PaidRemarks格式可能是: "订单号+交易号" 或其他格式,需要解析
|
String outTradeNo = extractOutTradeNo(paidRemarks);
|
String transactionId = extractTransactionId(paidRemarks);
|
|
if (outTradeNo == null || outTradeNo.isEmpty()) {
|
log.error("无法从PaidRemarks中提取订单号: {}", paidRemarks);
|
return;
|
}
|
|
log.info("解析出订单号: {}, 交易号: {}", outTradeNo, transactionId);
|
|
// 检查幂等性(使用订单号或签名)
|
String notifyId = transactionId != null ? transactionId : sign;
|
if (isNotifyProcessed("ALIPAY_THIRD_PARTY", notifyId)) {
|
log.info("第三方支付宝回调已处理过,标识: {}", notifyId);
|
return;
|
}
|
|
// 记录回调日志
|
NotifyLog notifyLog = saveNotifyLog("ALIPAY_THIRD_PARTY", notifyId, JSON.toJSONString(params), true);
|
|
try {
|
// 第三方支付宝成功的判断条件(根据实际情况调整)
|
// 通常只要能收到回调就表示支付成功
|
if (paidMoney != null && Integer.parseInt(paidMoney) > 0) {
|
// 查询订单(使用商户订单号,这里的outTradeNo实际是我们的订单ID)
|
PaymentOrder order = paymentOrderMapper.selectById(Long.valueOf(outTradeNo));
|
if (order == null) {
|
log.error("订单不存在: {}", outTradeNo);
|
updateNotifyLog(notifyLog.getId(), false, "订单不存在");
|
return;
|
}
|
|
// 验证金额是否匹配
|
if (!order.getAmount().equals(Integer.parseInt(paidMoney))) {
|
log.error("订单金额不匹配,订单金额: {}, 回调金额: {}", order.getAmount(), paidMoney);
|
updateNotifyLog(notifyLog.getId(), false, "金额不匹配");
|
return;
|
}
|
|
// 查询待支付交易
|
PaymentTransaction transaction = paymentTransactionMapper.selectPendingByOrderId(order.getId());
|
if (transaction == null) {
|
// 如果没有待支付交易,尝试查询最新交易
|
transaction = paymentTransactionMapper.selectLatestByOrderId(order.getId());
|
if (transaction == null || "SUCCEEDED".equals(transaction.getStatus())) {
|
log.warn("交易已完成或不存在,订单ID: {}", order.getId());
|
updateNotifyLog(notifyLog.getId(), true, "交易已完成");
|
return;
|
}
|
}
|
|
// 更新交易状态
|
transaction.setStatus("SUCCEEDED");
|
if (transactionId != null) {
|
transaction.setChannelTradeNo(transactionId);
|
}
|
transaction.setPaidAt(LocalDateTime.now());
|
transaction.setResponseSnapshot(JSON.toJSONString(params));
|
paymentTransactionMapper.update(transaction);
|
|
// 更新订单状态
|
order.setStatus("SUCCEEDED");
|
if (transactionId != null) {
|
order.setChannelTradeNo(transactionId);
|
}
|
order.setPaidAt(LocalDateTime.now());
|
order.setLatestTransactionId(transaction.getId());
|
paymentOrderMapper.update(order);
|
|
// 更新通知日志
|
notifyLog.setOrderId(order.getId());
|
notifyLog.setTransactionId(transaction.getId());
|
updateNotifyLog(notifyLog.getId(), true, "处理成功");
|
|
// 触发业务回调
|
bizCallbackService.triggerCallback(order, transaction);
|
|
log.info("第三方支付宝回调处理成功,订单ID: {}, 交易ID: {}", order.getId(), transaction.getId());
|
} else {
|
log.warn("第三方支付宝支付金额异常: {}", paidMoney);
|
updateNotifyLog(notifyLog.getId(), true, "金额异常: " + paidMoney);
|
}
|
} catch (Exception e) {
|
log.error("处理第三方支付宝回调异常", e);
|
updateNotifyLog(notifyLog.getId(), false, "处理异常: " + e.getMessage());
|
throw e;
|
}
|
}
|
|
/**
|
* 从PaidRemarks中提取订单号
|
* PaidRemarks可能包含订单号和交易号,需要解析
|
*/
|
private String extractOutTradeNo(String paidRemarks) {
|
if (paidRemarks == null || paidRemarks.isEmpty()) {
|
return null;
|
}
|
|
// 假设PaidRemarks格式: "订单号+交易号" 或 "订单号"
|
// 根据实际格式调整解析逻辑
|
// 这里假设订单号在最前面,可能用某个分隔符分隔
|
|
// 尝试提取纯数字作为订单ID
|
String[] parts = paidRemarks.split("[^0-9]+");
|
for (String part : parts) {
|
if (part.length() > 10) { // 订单ID通常是长整型,长度较长
|
return part;
|
}
|
}
|
|
// 如果没有找到,返回整个字符串的数字部分
|
return paidRemarks.replaceAll("[^0-9]", "");
|
}
|
|
/**
|
* 从PaidRemarks中提取交易号
|
*/
|
private String extractTransactionId(String paidRemarks) {
|
if (paidRemarks == null || paidRemarks.isEmpty()) {
|
return null;
|
}
|
|
// 如果PaidRemarks包含支付宝交易号(通常以2开头的28位数字)
|
// 这里简单返回null,可以根据实际格式调整
|
return null;
|
}
|
|
/**
|
* 处理第三方微信回调
|
* 第三方微信通过GET方式回调,参数通过URL传递
|
*
|
* 参数格式:
|
* method: 方法名
|
* APPID: 应用ID
|
* PaidMoneyType: 支付类型
|
* PaidMoney: 支付金额(分)
|
* PaidRemarks: 支付备注(包含transaction_id)
|
* UnixTime: 时间戳
|
*/
|
@Transactional(rollbackFor = Exception.class)
|
public void processWechatThirdPartyNotify(Map<String, String> params) {
|
String method = params.get("method"); // 方法名
|
String appId = params.get("APPID"); // 应用ID
|
String paidMoneyType = params.get("PaidMoneyType"); // 支付类型
|
String paidMoney = params.get("PaidMoney"); // 支付金额(分)
|
String paidRemarks = params.get("PaidRemarks"); // 支付备注(可能包含订单号和交易号)
|
String unixTime = params.get("UnixTime"); // 时间戳
|
|
log.info("处理第三方微信回调,method: {}, APPID: {}, PaidMoneyType: {}, PaidMoney: {}, PaidRemarks: {}, UnixTime: {}",
|
method, appId, paidMoneyType, paidMoney, paidRemarks, unixTime);
|
log.info("第三方微信回调完整参数: {}", params);
|
|
// 从PaidRemarks中提取订单号(out_trade_no)
|
String outTradeNo = extractOutTradeNo(paidRemarks);
|
String transactionId = extractTransactionId(paidRemarks);
|
|
if (outTradeNo == null || outTradeNo.isEmpty()) {
|
log.error("无法从PaidRemarks中提取订单号: {}", paidRemarks);
|
return;
|
}
|
|
log.info("解析出订单号: {}, 交易号: {}", outTradeNo, transactionId);
|
|
// 检查幂等性(使用订单号或时间戳)
|
String notifyId = transactionId != null ? transactionId : (outTradeNo + "_" + unixTime);
|
if (isNotifyProcessed("WECHAT_THIRD_PARTY", notifyId)) {
|
log.info("第三方微信回调已处理过,标识: {}", notifyId);
|
return;
|
}
|
|
// 记录回调日志
|
NotifyLog notifyLog = saveNotifyLog("WECHAT_THIRD_PARTY", notifyId, JSON.toJSONString(params), true);
|
|
try {
|
// 第三方微信成功的判断条件(通常只要能收到回调就表示支付成功)
|
if (paidMoney != null && Integer.parseInt(paidMoney) > 0) {
|
// 查询订单(使用商户订单号,这里的outTradeNo实际是我们的订单ID)
|
PaymentOrder order = paymentOrderMapper.selectById(Long.valueOf(outTradeNo));
|
if (order == null) {
|
log.error("订单不存在: {}", outTradeNo);
|
updateNotifyLog(notifyLog.getId(), false, "订单不存在");
|
return;
|
}
|
|
// 验证金额是否匹配
|
if (!order.getAmount().equals(Integer.parseInt(paidMoney))) {
|
log.error("订单金额不匹配,订单金额: {}, 回调金额: {}", order.getAmount(), paidMoney);
|
updateNotifyLog(notifyLog.getId(), false, "金额不匹配");
|
return;
|
}
|
|
// 查询待支付交易
|
PaymentTransaction transaction = paymentTransactionMapper.selectPendingByOrderId(order.getId());
|
if (transaction == null) {
|
// 如果没有待支付交易,尝试查询最新交易
|
transaction = paymentTransactionMapper.selectLatestByOrderId(order.getId());
|
if (transaction == null || "SUCCEEDED".equals(transaction.getStatus())) {
|
log.warn("交易已完成或不存在,订单ID: {}", order.getId());
|
updateNotifyLog(notifyLog.getId(), true, "交易已完成");
|
return;
|
}
|
}
|
|
// 更新交易状态
|
transaction.setStatus("SUCCEEDED");
|
if (transactionId != null) {
|
transaction.setChannelTradeNo(transactionId);
|
}
|
transaction.setPaidAt(LocalDateTime.now());
|
transaction.setResponseSnapshot(JSON.toJSONString(params));
|
paymentTransactionMapper.update(transaction);
|
|
// 更新订单状态
|
order.setStatus("SUCCEEDED");
|
if (transactionId != null) {
|
order.setChannelTradeNo(transactionId);
|
}
|
order.setPaidAt(LocalDateTime.now());
|
order.setLatestTransactionId(transaction.getId());
|
paymentOrderMapper.update(order);
|
|
// 更新通知日志
|
notifyLog.setOrderId(order.getId());
|
notifyLog.setTransactionId(transaction.getId());
|
updateNotifyLog(notifyLog.getId(), true, "处理成功");
|
|
// 触发业务回调
|
bizCallbackService.triggerCallback(order, transaction);
|
|
log.info("第三方微信回调处理成功,订单ID: {}, 交易ID: {}", order.getId(), transaction.getId());
|
} else {
|
log.warn("第三方微信支付金额异常: {}", paidMoney);
|
updateNotifyLog(notifyLog.getId(), true, "金额异常: " + paidMoney);
|
}
|
} catch (Exception e) {
|
log.error("处理第三方微信回调异常", e);
|
updateNotifyLog(notifyLog.getId(), false, "处理异常: " + e.getMessage());
|
throw e;
|
}
|
}
|
|
/**
|
* 检查通知是否已处理(幂等性)
|
*/
|
private boolean isNotifyProcessed(String channel, String notifyId) {
|
return notifyLogMapper.selectProcessedCount(channel, notifyId) > 0;
|
}
|
|
/**
|
* 保存通知日志
|
*/
|
private NotifyLog saveNotifyLog(String channel, String notifyId, String payload, boolean verified) {
|
NotifyLog log = new NotifyLog();
|
log.setId(SnowflakeIdGenerator.generateId());
|
log.setChannel(channel);
|
log.setNotifyIdOrSerial(notifyId);
|
log.setPayload(payload);
|
log.setVerified(verified);
|
log.setProcessed(false);
|
log.setCreatedAt(LocalDateTime.now());
|
notifyLogMapper.insert(log);
|
return log;
|
}
|
|
/**
|
* 更新通知日志
|
*/
|
private void updateNotifyLog(Long id, boolean processed, String result) {
|
NotifyLog log = new NotifyLog();
|
log.setId(id);
|
log.setProcessed(processed);
|
log.setResult(result);
|
notifyLogMapper.update(log);
|
}
|
}
|