| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- package com.ruoyi.app.omgpay;
- import org.junit.jupiter.api.Test;
- import org.springframework.jdbc.core.JdbcTemplate;
- import org.springframework.jdbc.core.RowMapper;
- import java.sql.ResultSet;
- import java.util.Map;
- import java.util.concurrent.atomic.AtomicInteger;
- import java.time.Clock;
- import java.time.Instant;
- import java.time.ZoneOffset;
- import static org.mockito.Mockito.mock;
- import static org.mockito.Mockito.verify;
- import static org.mockito.Mockito.verifyNoInteractions;
- import static org.mockito.Mockito.doAnswer;
- import static org.mockito.Mockito.when;
- import static org.mockito.Mockito.times;
- import static org.mockito.ArgumentMatchers.any;
- import static org.mockito.ArgumentMatchers.eq;
- import static org.mockito.ArgumentMatchers.startsWith;
- import static org.junit.jupiter.api.Assertions.assertEquals;
- import static org.junit.jupiter.api.Assertions.assertThrows;
- class OmgPaymentRefundServiceTest {
- private final OmgPaymentRefundService service = new OmgPaymentRefundService();
- @Test
- void testEnvironmentRejectsRefundBeforeAnyGatewayOrStateChangeCanExist() {
- OmgPaymentBusinessException error = assertThrows(OmgPaymentBusinessException.class,
- () -> service.refund(5L, "DD-1"));
- assertEquals(OmgPaymentErrorCode.PAYMENT_REFUND_UNAVAILABLE_IN_TEST_ENVIRONMENT,
- error.getCode());
- }
- @Test
- void validatesAuthenticationAndOrderIdBeforeEnvironmentResponse() {
- assertEquals(OmgPaymentErrorCode.AUTH_REQUIRED,
- assertThrows(OmgPaymentBusinessException.class,
- () -> service.refund(null, "DD-1")).getCode());
- assertEquals(OmgPaymentErrorCode.ORDER_REQUIRED,
- assertThrows(OmgPaymentBusinessException.class,
- () -> service.refund(5L, " ")).getCode());
- assertEquals(OmgPaymentErrorCode.ORDER_REQUIRED,
- assertThrows(OmgPaymentBusinessException.class,
- () -> service.refund(5L, "X".repeat(65))).getCode());
- }
- @Test
- void testEnvironmentRecordsExpectedFailureWithoutCallingProductionGateway() {
- JdbcTemplate jdbc = mock(JdbcTemplate.class);
- OmgRefundGateway gateway = mock(OmgRefundGateway.class);
- OmgPaymentRefundService refunds = new OmgPaymentRefundService(new OmgPaymentProperties(),
- null, jdbc, null, gateway, null);
- refunds.processOne(8L);
- verify(jdbc).update(org.mockito.ArgumentMatchers.contains("TEST_ENVIRONMENT"),
- org.mockito.ArgumentMatchers.eq(8L));
- verifyNoInteractions(gateway);
- }
- @Test
- void usesGatewayCreditStateToChooseOfficialAction() {
- assertEquals("N", OmgPaymentRefundService.nextAction("已授權"));
- assertEquals("E", OmgPaymentRefundService.nextAction("要關帳"));
- assertEquals("R", OmgPaymentRefundService.nextAction("已關帳"));
- assertEquals(null, OmgPaymentRefundService.nextAction("關帳中"));
- }
- @Test
- @SuppressWarnings({"unchecked", "rawtypes"})
- void productionQueriesCreditBeforeActionAndDoesNotReplayUnchangedAction() throws Exception {
- OmgPaymentProperties properties = new OmgPaymentProperties();
- properties.setEnvironment("PRODUCTION");
- properties.setCreditCheckCodes(Map.of("1000031", "123456"));
- JdbcTemplate jdbc = mock(JdbcTemplate.class);
- OmgRefundGateway gateway = mock(OmgRefundGateway.class);
- OmgPaymentRefundService refunds = new OmgPaymentRefundService(properties, null, jdbc,
- new OmgCheckMacSigner(), gateway, new OmgQueryResponseParser(),
- Clock.fixed(Instant.parse("2026-10-15T07:00:00Z"), ZoneOffset.UTC));
- when(jdbc.update(startsWith("UPDATE pos_order_omg_refund SET lease_until=DATE_ADD"), eq(8L)))
- .thenReturn(1);
- AtomicInteger reads = new AtomicInteger();
- doAnswer(call -> {
- RowMapper mapper = call.getArgument(1);
- ResultSet rs = mock(ResultSet.class);
- boolean first = reads.getAndIncrement() == 0;
- when(rs.getLong(1)).thenReturn(7L);
- when(rs.getString(2)).thenReturn(first ? "PENDING" : "ACTION_UNKNOWN");
- when(rs.getString(3)).thenReturn(first ? null : "N");
- when(rs.getString(4)).thenReturn(first ? null : "已授權");
- return mapper.mapRow(rs, 0);
- }).when(jdbc).queryForObject(startsWith("SELECT attempt_id"), any(RowMapper.class), eq(8L));
- doAnswer(call -> {
- RowMapper mapper = call.getArgument(1);
- ResultSet rs = mock(ResultSet.class);
- when(rs.getLong("id")).thenReturn(7L);
- when(rs.getString("dd_id")).thenReturn("DD-1");
- when(rs.getString("merchant_trade_no")).thenReturn("OMG123");
- when(rs.getString("merchant_id")).thenReturn("1000031");
- when(rs.getInt("amount")).thenReturn(100);
- when(rs.getString("hash_key_snapshot")).thenReturn("KEY");
- when(rs.getString("hash_iv_snapshot")).thenReturn("IV");
- when(rs.getString("trade_no")).thenReturn("GW123");
- when(rs.getString("credit_refund_id")).thenReturn("123");
- when(rs.getString("gateway_environment")).thenReturn("PRODUCTION");
- return mapper.mapRow(rs, 0);
- }).when(jdbc).queryForObject(startsWith("SELECT id, dd_id"), any(RowMapper.class), eq(7L));
- when(gateway.queryCredit(any())).thenReturn(
- "{\"RtnMsg\":\"\",\"RtnValue\":{\"TradeID\":\"123\",\"amount\":\"100\",\"status\":\"已授權\"}}",
- "{\"RtnMsg\":\"\",\"RtnValue\":{\"TradeID\":\"123\",\"amount\":\"100\",\"status\":\"已授權\"}}",
- "{\"RtnMsg\":\"\",\"RtnValue\":{\"TradeID\":\"123\",\"amount\":\"100\",\"status\":\"已關帳\",\"close_data\":[{\"status\":\"銀行已追回\",\"amount\":\"100\"}]}}");
- when(gateway.doAction(any())).thenReturn("MerchantID=1000031&MerchantTradeNo=OMG123&TradeNo=GW123&RtnCode=1");
- refunds.processOne(8L);
- refunds.processOne(8L);
- refunds.processOne(8L);
- verify(gateway, times(3)).queryCredit(any());
- verify(gateway, times(1)).doAction(org.mockito.ArgumentMatchers.argThat(fields ->
- "N".equals(((Map<?, ?>) fields).get("Action"))));
- verify(jdbc).update(startsWith("UPDATE pos_order_omg_refund SET status='REFUNDED'"), eq(8L));
- }
- }
|