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 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 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 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 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); } }