package com.tourguide.modules.order.service; import com.alibaba.fastjson.JSONObject; import com.tourguide.common.huifu.properties.HuiFuProperties; import com.tourguide.modules.order.domain.OrderStatus; import com.tourguide.modules.order.domain.RefundStatus; import com.tourguide.modules.order.entity.TourOrder; import com.tourguide.modules.order.mapper.TourOrderMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.math.BigDecimal; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class HuifuRefundCallbackServiceTest { private TourOrderMapper orderMapper; private HuifuRefundCallbackService callbackService; @BeforeEach void setUp() { HuiFuProperties properties = new HuiFuProperties(); properties.setHuifuId("merchant-1"); orderMapper = mock(TourOrderMapper.class); callbackService = new HuifuRefundCallbackService(properties, orderMapper); } @Test void successCallbackShouldCompleteRefundOnce() { TourOrder order = refundingOrder(); when(orderMapper.selectByRefundOrderNoForUpdate("RF001")).thenReturn(order); boolean first = callbackService.handle(successBody()); boolean duplicate = callbackService.handle(successBody()); assertThat(first).isTrue(); assertThat(duplicate).isFalse(); assertThat(order.getRefundStatus()).isEqualTo(RefundStatus.REFUNDED.getCode()); assertThat(order.getStatus()).isEqualTo(OrderStatus.REFUNDED.getCode()); assertThat(order.getRefundTime()).isNotNull(); verify(orderMapper).updateById(order); } @Test void failedCallbackShouldRecordFailureWithoutCompletingRefund() { TourOrder order = refundingOrder(); when(orderMapper.selectByRefundOrderNoForUpdate("RF001")).thenReturn(order); JSONObject body = successBody(); body.put("refund_stat", "F"); boolean completed = callbackService.handle(body); assertThat(completed).isFalse(); assertThat(order.getRefundStatus()).isEqualTo(RefundStatus.REFUND_FAILED.getCode()); verify(orderMapper).updateById(order); } @Test void duplicateSuccessCallbackShouldNotUpdateAgain() { TourOrder order = refundingOrder(); order.setRefundStatus(RefundStatus.REFUNDED.getCode()); when(orderMapper.selectByRefundOrderNoForUpdate("RF001")).thenReturn(order); boolean completed = callbackService.handle(successBody()); assertThat(completed).isFalse(); verify(orderMapper, never()).updateById(order); } @Test void nestedSuccessCallbackShouldCompleteOrder() { TourOrder order = refundingOrder(); when(orderMapper.selectByRefundOrderNoForUpdate("RF001")).thenReturn(order); JSONObject data = successBody(); JSONObject body = new JSONObject(); body.put("data", data); assertThat(callbackService.handle(body)).isTrue(); assertThat(order.getStatus()).isEqualTo(OrderStatus.REFUNDED.getCode()); } @Test void partialSuccessCallbackShouldKeepRefundingBalance() { TourOrder order = refundingOrder(); order.setRefundAmount(new BigDecimal("20.00")); when(orderMapper.selectByRefundOrderNoForUpdate("RF001")).thenReturn(order); JSONObject body = successBody(); body.put("ord_amt", "20.00"); assertThat(callbackService.handle(body)).isTrue(); assertThat(order.getRefundedAmount()).isEqualByComparingTo("20.00"); assertThat(order.getRefundStatus()).isEqualTo(RefundStatus.PARTIAL.getCode()); assertThat(order.getStatus()).isEqualTo(OrderStatus.CANCELLED.getCode()); } @Test void followUpSuccessCallbackShouldCompleteRemainingRefund() { TourOrder order = refundingOrder(); order.setRefundAmount(new BigDecimal("16.00")); order.setRefundedAmount(new BigDecimal("20.00")); when(orderMapper.selectByRefundOrderNoForUpdate("RF001")).thenReturn(order); JSONObject body = successBody(); body.put("ord_amt", "16.00"); assertThat(callbackService.handle(body)).isTrue(); assertThat(order.getRefundedAmount()).isEqualByComparingTo("36.00"); assertThat(order.getRefundStatus()).isEqualTo(RefundStatus.REFUNDED.getCode()); assertThat(order.getStatus()).isEqualTo(OrderStatus.REFUNDED.getCode()); } private TourOrder refundingOrder() { TourOrder order = new TourOrder(); order.setRefundOrderNo("RF001"); order.setRefundStatus(RefundStatus.REFUNDING.getCode()); order.setRefundAmount(new BigDecimal("36.00")); order.setPaymentAmount(new BigDecimal("36.00")); return order; } private JSONObject successBody() { JSONObject body = new JSONObject(); body.put("req_seq_id", "RF001"); body.put("huifu_id", "merchant-1"); body.put("refund_stat", "S"); body.put("ord_amt", "36.00"); body.put("end_time", "20260803180000"); return body; } }