|
@@ -705,9 +705,13 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
RefundNotification refundNotification = ((NotificationParser) notifyRes[1]).parse((RequestParam) notifyRes[0], RefundNotification.class);
|
|
RefundNotification refundNotification = ((NotificationParser) notifyRes[1]).parse((RequestParam) notifyRes[0], RefundNotification.class);
|
|
|
LOGGER.info("微信退款回调{}:验签解密完毕,数据:\n{}", notifyRes[2], refundNotification);
|
|
LOGGER.info("微信退款回调{}:验签解密完毕,数据:\n{}", notifyRes[2], refundNotification);
|
|
|
|
|
|
|
|
- //退款日志在申请时插入,接收通知时更新
|
|
|
|
|
|
|
+ // 退款日志在申请时插入,接收通知时更新
|
|
|
var refundLog = refundLogService.lambdaQuery().eq(RefundLog::getOutRefundNo, refundNotification.getOutRefundNo()).one();
|
|
var refundLog = refundLogService.lambdaQuery().eq(RefundLog::getOutRefundNo, refundNotification.getOutRefundNo()).one();
|
|
|
- // 防止重复处理消息
|
|
|
|
|
|
|
+ if (refundLog == null) {
|
|
|
|
|
+ LOGGER.error("微信退款回调:未找到退款记录,outRefundNo={}", refundNotification.getOutRefundNo());
|
|
|
|
|
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of("code", HttpStatus.INTERNAL_SERVER_ERROR, "message", "退款记录不存在"));
|
|
|
|
|
+ }
|
|
|
|
|
+ // 幂等快路径:已处理成功直接返回
|
|
|
if (RefundLog.STATUS_退款成功.equals(refundLog.getStatus())) {
|
|
if (RefundLog.STATUS_退款成功.equals(refundLog.getStatus())) {
|
|
|
return ResponseEntity.status(HttpStatus.OK).build();
|
|
return ResponseEntity.status(HttpStatus.OK).build();
|
|
|
}
|
|
}
|
|
@@ -715,28 +719,41 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
DateTime dt = DateUtil.parse(refundNotification.getSuccessTime());
|
|
DateTime dt = DateUtil.parse(refundNotification.getSuccessTime());
|
|
|
LocalDateTime successTime = LocalDateTimeUtil.of(dt);
|
|
LocalDateTime successTime = LocalDateTimeUtil.of(dt);
|
|
|
|
|
|
|
|
- refundLogService.lambdaUpdate()
|
|
|
|
|
- .set(RefundLog::getRefundId, refundNotification.getRefundId())
|
|
|
|
|
- .set(RefundLog::getTransactionId, refundNotification.getTransactionId())
|
|
|
|
|
- .set(RefundLog::getUserReceivedAccount, refundNotification.getUserReceivedAccount())
|
|
|
|
|
- .set(RefundLog::getSuccessTime, successTime)
|
|
|
|
|
- .set(RefundLog::getStatus, refundNotification.getRefundStatus().name())
|
|
|
|
|
- .set(RefundLog::getTotal, refundNotification.getAmount().getTotal().intValue())
|
|
|
|
|
- .set(RefundLog::getRefund, refundNotification.getAmount().getRefund().intValue())
|
|
|
|
|
- .eq(RefundLog::getId, refundLog.getId()).update();
|
|
|
|
|
-
|
|
|
|
|
if (RefundLog.STATUS_退款成功.equals(refundNotification.getRefundStatus().name())) {
|
|
if (RefundLog.STATUS_退款成功.equals(refundNotification.getRefundStatus().name())) {
|
|
|
|
|
+ // 资金流水必须在申请退款时已存在;检查放在门闩之前,避免门闩已置成功但后续失败造成不一致
|
|
|
|
|
+ var walletDetail = walletDetailService.getWalletDetailByOrderNo(refundNotification.getOutRefundNo(), WalletDetail.TYPE_退款);
|
|
|
|
|
+ if (walletDetail == null) {
|
|
|
|
|
+ LOGGER.error("微信退款回调:未找到退款资金流水,outRefundNo={}", refundNotification.getOutRefundNo());
|
|
|
|
|
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of("code", HttpStatus.INTERNAL_SERVER_ERROR, "message", "退款流水不存在"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 幂等门闩:条件更新(status <> 已成功),并发重复回调第二个事务更新 0 行直接跳过
|
|
|
|
|
+ boolean claimed = refundLogService.lambdaUpdate()
|
|
|
|
|
+ .set(RefundLog::getRefundId, refundNotification.getRefundId())
|
|
|
|
|
+ .set(RefundLog::getTransactionId, refundNotification.getTransactionId())
|
|
|
|
|
+ .set(RefundLog::getUserReceivedAccount, refundNotification.getUserReceivedAccount())
|
|
|
|
|
+ .set(RefundLog::getSuccessTime, successTime)
|
|
|
|
|
+ .set(RefundLog::getStatus, refundNotification.getRefundStatus().name())
|
|
|
|
|
+ .set(RefundLog::getTotal, refundNotification.getAmount().getTotal().intValue())
|
|
|
|
|
+ .set(RefundLog::getRefund, refundNotification.getAmount().getRefund().intValue())
|
|
|
|
|
+ .eq(RefundLog::getId, refundLog.getId())
|
|
|
|
|
+ .ne(RefundLog::getStatus, RefundLog.STATUS_退款成功)
|
|
|
|
|
+ .update();
|
|
|
|
|
+ if (!claimed) {
|
|
|
|
|
+ return ResponseEntity.status(HttpStatus.OK).build();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 冻结金额扣减此次(退款金额+优惠金额),优惠金额字段减去申请退款时的优惠金额
|
|
// 冻结金额扣减此次(退款金额+优惠金额),优惠金额字段减去申请退款时的优惠金额
|
|
|
var account = accountService.getAccountByUserId(refundLog.getUserId());
|
|
var account = accountService.getAccountByUserId(refundLog.getUserId());
|
|
|
- accountService.lambdaUpdate().setSql("frozen_amount = (frozen_amount - %d) , discount_amount = (discount_amount - %d)"
|
|
|
|
|
- .formatted(refundNotification.getAmount().getRefund().intValue(), refundLog.getDiscountAmount()))
|
|
|
|
|
|
|
+ var refundAmount = refundNotification.getAmount().getRefund().intValue();
|
|
|
|
|
+ accountService.lambdaUpdate()
|
|
|
|
|
+ .setSql("frozen_amount = (frozen_amount - {0}), discount_amount = (discount_amount - {1})",
|
|
|
|
|
+ refundAmount, refundLog.getDiscountAmount())
|
|
|
.eq(Account::getUserId, refundLog.getUserId()).update();
|
|
.eq(Account::getUserId, refundLog.getUserId()).update();
|
|
|
|
|
|
|
|
// 更新资金流水
|
|
// 更新资金流水
|
|
|
// 注意:此时 balance 已在 applyWxRefund 阶段清零,
|
|
// 注意:此时 balance 已在 applyWxRefund 阶段清零,
|
|
|
// beforeBalance 应反映退款前余额(即退款金额),afterBalance 为 0
|
|
// beforeBalance 应反映退款前余额(即退款金额),afterBalance 为 0
|
|
|
- var walletDetail = walletDetailService.getWalletDetailByOrderNo(refundNotification.getOutRefundNo(), WalletDetail.TYPE_退款);
|
|
|
|
|
- var refundAmount = refundNotification.getAmount().getRefund().intValue();
|
|
|
|
|
walletDetailService.lambdaUpdate()
|
|
walletDetailService.lambdaUpdate()
|
|
|
.set(WalletDetail::getStatus, WalletDetail.STATUS_已确认)
|
|
.set(WalletDetail::getStatus, WalletDetail.STATUS_已确认)
|
|
|
.set(WalletDetail::getTransactionId, refundNotification.getTransactionId())
|
|
.set(WalletDetail::getTransactionId, refundNotification.getTransactionId())
|
|
@@ -744,7 +761,6 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
.set(WalletDetail::getAmount, refundAmount)
|
|
.set(WalletDetail::getAmount, refundAmount)
|
|
|
.set(WalletDetail::getBeforeBalance, account.getBalance() + refundAmount)
|
|
.set(WalletDetail::getBeforeBalance, account.getBalance() + refundAmount)
|
|
|
.set(WalletDetail::getAfterBalance, account.getBalance())
|
|
.set(WalletDetail::getAfterBalance, account.getBalance())
|
|
|
- .set(WalletDetail::getTransactionTime, successTime)
|
|
|
|
|
.eq(WalletDetail::getId, walletDetail.getId()).update();
|
|
.eq(WalletDetail::getId, walletDetail.getId()).update();
|
|
|
LOGGER.info("微信退款回调{}:业务处理结束", notifyRes[2]);
|
|
LOGGER.info("微信退款回调{}:业务处理结束", notifyRes[2]);
|
|
|
|
|
|
|
@@ -754,7 +770,7 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
.setFromStationId(stationId)
|
|
.setFromStationId(stationId)
|
|
|
.setToStationId(stationId)
|
|
.setToStationId(stationId)
|
|
|
.setTradeNo(refundNotification.getTransactionId())
|
|
.setTradeNo(refundNotification.getTransactionId())
|
|
|
- .setAmount(refundNotification.getAmount().getRefund().intValue())
|
|
|
|
|
|
|
+ .setAmount(refundAmount)
|
|
|
.setType(SplitRecord.TYPE_REFUND);
|
|
.setType(SplitRecord.TYPE_REFUND);
|
|
|
splitRecordService.save(refundSplit);
|
|
splitRecordService.save(refundSplit);
|
|
|
|
|
|