| | |
| | | import com.tourguide.modules.order.domain.RefundAmountCalculator; |
| | | import com.tourguide.modules.order.domain.RefundStatus; |
| | | import com.tourguide.modules.order.entity.TourOrder; |
| | | import com.tourguide.modules.order.entity.TourOrderRefund; |
| | | import com.tourguide.modules.order.mapper.TourOrderMapper; |
| | | import com.tourguide.modules.order.mapper.TourOrderRefundMapper; |
| | | import com.tourguide.modules.finance.service.GuideBalanceSettlementService; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | |
| | | |
| | | private final HuiFuProperties properties; |
| | | private final TourOrderMapper orderMapper; |
| | | private final TourOrderRefundMapper refundMapper; |
| | | private final GuideBalanceSettlementService balanceSettlementService; |
| | | private final GuideSessionTransitionService sessionTransitionService; |
| | | |
| | | public HuifuRefundCallbackService(HuiFuProperties properties, TourOrderMapper orderMapper) { |
| | | this(properties, orderMapper, null, null, null); |
| | | } |
| | | |
| | | public HuifuRefundCallbackService(HuiFuProperties properties, TourOrderMapper orderMapper, |
| | | TourOrderRefundMapper refundMapper) { |
| | | this(properties, orderMapper, refundMapper, null, null); |
| | | } |
| | | |
| | | public HuifuRefundCallbackService(HuiFuProperties properties, TourOrderMapper orderMapper, |
| | | TourOrderRefundMapper refundMapper, |
| | | GuideBalanceSettlementService balanceSettlementService) { |
| | | this(properties, orderMapper, refundMapper, balanceSettlementService, null); |
| | | } |
| | | |
| | | @Autowired |
| | | public HuifuRefundCallbackService(HuiFuProperties properties, TourOrderMapper orderMapper, |
| | | TourOrderRefundMapper refundMapper, |
| | | GuideBalanceSettlementService balanceSettlementService, |
| | | GuideSessionTransitionService sessionTransitionService) { |
| | | this.properties = properties; |
| | | this.orderMapper = orderMapper; |
| | | this.refundMapper = refundMapper; |
| | | this.balanceSettlementService = balanceSettlementService; |
| | | this.sessionTransitionService = sessionTransitionService; |
| | | } |
| | | |
| | | @Transactional |
| | |
| | | throw new BusinessException(400, "Huifu refund callback request sequence is missing"); |
| | | } |
| | | validateMerchant(first(body, "huifu_id", "out_huifu_id")); |
| | | TourOrder order = orderMapper.selectByRefundOrderNoForUpdate(refundOrderNo); |
| | | TourOrderRefund refundRecord = refundMapper == null |
| | | ? null : refundMapper.selectByRefundOrderNoForUpdate(refundOrderNo); |
| | | TourOrder order = refundRecord == null |
| | | ? orderMapper.selectByRefundOrderNoForUpdate(refundOrderNo) |
| | | : orderMapper.selectForUpdate(refundRecord.getOrderId()); |
| | | if (order == null) throw new BusinessException(404, "Refund order does not exist"); |
| | | |
| | | String status = first(body, "refund_stat", "trans_stat"); |
| | | String result = StringUtils.hasText(status) ? status : first(body, "resp_code", "code"); |
| | | if (!isSuccess(result)) { |
| | | if (RefundStatus.REFUNDING.matches(order.getRefundStatus())) { |
| | | if (isRefunding(order, refundRecord)) { |
| | | order.setRefundStatus(RefundStatus.REFUND_FAILED.getCode()); |
| | | updateRefundRecord(refundRecord, RefundStatus.REFUND_FAILED.getCode(), null); |
| | | orderMapper.updateById(order); |
| | | } |
| | | return false; |
| | | } |
| | | if (!RefundStatus.REFUNDING.matches(order.getRefundStatus())) { |
| | | if (!isRefunding(order, refundRecord)) { |
| | | return false; |
| | | } |
| | | boolean restoreSession = OrderStatus.PENDING_MEETING.matches(order.getStatus()) |
| | | && order.getGuideId() != null && order.getReceivingSessionId() != null; |
| | | BigDecimal callbackAmount = amount(body); |
| | | if (order.getRefundAmount() == null || order.getRefundAmount().compareTo(callbackAmount) != 0) { |
| | | BigDecimal expectedAmount = refundRecord == null |
| | | ? order.getRefundAmount() : refundRecord.getRefundAmount(); |
| | | if (expectedAmount == null || expectedAmount.compareTo(callbackAmount) != 0) { |
| | | throw new BusinessException(409, "Refund amount does not match order"); |
| | | } |
| | | BigDecimal refundedAmount = RefundAmountCalculator.refundedAmount(order).add(callbackAmount); |
| | | BigDecimal previousRefundedAmount = RefundAmountCalculator.refundedAmount(order); |
| | | BigDecimal refundedAmount = previousRefundedAmount.add(callbackAmount); |
| | | BigDecimal paidAmount = RefundAmountCalculator.paidAmount(order); |
| | | if (refundedAmount.compareTo(paidAmount) > 0) { |
| | | throw new BusinessException(409, "Refund amount exceeds order payment"); |
| | | } |
| | | order.setRefundedAmount(refundedAmount); |
| | | order.setRefundAmount(callbackAmount); |
| | | if (refundedAmount.compareTo(paidAmount) >= 0) { |
| | | order.setRefundStatus(RefundStatus.REFUNDED.getCode()); |
| | | order.setStatus(OrderStatus.REFUNDED.getCode()); |
| | |
| | | LocalDateTime refundCompletedAt = callbackTime(body); |
| | | order.setRefundTime(refundCompletedAt); |
| | | if (order.getCancelledAt() == null) order.setCancelledAt(refundCompletedAt); |
| | | if (balanceSettlementService != null) { |
| | | balanceSettlementService.reverseRefund(order, previousRefundedAmount, refundedAmount); |
| | | } |
| | | // 回调确认的是当前退款流水,明细必须单独标记为已完成;订单状态仍表示累计退款进度。 |
| | | updateRefundRecord(refundRecord, RefundStatus.REFUNDED.getCode(), refundCompletedAt); |
| | | orderMapper.updateById(order); |
| | | if (restoreSession && sessionTransitionService != null) { |
| | | sessionTransitionService.restoreAfterOrderCancellation(order); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private boolean isRefunding(TourOrder order, TourOrderRefund refund) { |
| | | return refund == null |
| | | ? RefundStatus.REFUNDING.matches(order.getRefundStatus()) |
| | | : RefundStatus.REFUNDING.matches(refund.getRefundStatus()); |
| | | } |
| | | |
| | | private void updateRefundRecord(TourOrderRefund refund, String status, LocalDateTime refundTime) { |
| | | if (refund == null) return; |
| | | refund.setRefundStatus(status); |
| | | refund.setRefundTime(refundTime); |
| | | refundMapper.updateById(refund); |
| | | } |
| | | |
| | | private void validateMerchant(String callbackHuifuId) { |
| | | if (!StringUtils.hasText(callbackHuifuId) |
| | | || !callbackHuifuId.equals(properties.getHuifuId())) { |