|
@@ -0,0 +1,136 @@
|
|
|
|
|
+package com.payment.platform.module.payment.notification.handler;
|
|
|
|
|
+
|
|
|
|
|
+import com.payment.platform.module.payment.batch.entity.BatchAuthorizeEntity;
|
|
|
|
|
+import com.payment.platform.module.payment.batch.entity.BatchOrderEntity;
|
|
|
|
|
+import com.payment.platform.module.payment.batch.mapper.BatchAuthorizeMapper;
|
|
|
|
|
+import com.payment.platform.module.payment.batch.mapper.BatchOrderMapper;
|
|
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
|
|
+import org.junit.jupiter.api.extension.ExtendWith;
|
|
|
|
|
+import org.mockito.Mock;
|
|
|
|
|
+import org.mockito.junit.jupiter.MockitoExtension;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.HashMap;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.*;
|
|
|
|
|
+import static org.mockito.ArgumentMatchers.any;
|
|
|
|
|
+import static org.mockito.Mockito.*;
|
|
|
|
|
+
|
|
|
|
|
+@ExtendWith(MockitoExtension.class)
|
|
|
|
|
+class BatchPayHandlerTest {
|
|
|
|
|
+
|
|
|
|
|
+ @Mock private BatchAuthorizeMapper batchAuthorizeMapper;
|
|
|
|
|
+ @Mock private BatchOrderMapper batchOrderMapper;
|
|
|
|
|
+ private BatchPayHandler handler;
|
|
|
|
|
+
|
|
|
|
|
+ @BeforeEach
|
|
|
|
|
+ void setUp() {
|
|
|
|
|
+ // handler 不持有 mapper —— mapper 通过 NotifyContext 传入(BaseNotifyHandler 的 ctx 模式)
|
|
|
|
|
+ handler = new BatchPayHandler();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private NotifyContext ctx() {
|
|
|
|
|
+ return new NotifyContext()
|
|
|
|
|
+ .setBatchAuthorizeMapper(batchAuthorizeMapper)
|
|
|
|
|
+ .setBatchOrderMapper(batchOrderMapper);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void acceptsBothNotifyMethods() {
|
|
|
|
|
+ assertTrue(handler.accept("alipay.fund.authorize.status.notify"));
|
|
|
|
|
+ assertTrue(handler.accept("alipay.fund.batch.order.changed"));
|
|
|
|
|
+ assertFalse(handler.accept("alipay.commerce.ec.enterprise.change.notify"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void authorizeNotify_updatesAgreementNo() {
|
|
|
|
|
+ BatchAuthorizeEntity entity = new BatchAuthorizeEntity();
|
|
|
|
|
+ entity.setId(1L);
|
|
|
|
|
+ when(batchAuthorizeMapper.selectOne(any())).thenReturn(entity);
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, String> params = new HashMap<>();
|
|
|
|
|
+ params.put("out_biz_no", "A1");
|
|
|
|
|
+ params.put("agreement_no", "AGMT001");
|
|
|
|
|
+ params.put("status", "AUTHED");
|
|
|
|
|
+ handler.dispatch("alipay.fund.authorize.status.notify", params, ctx());
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals("AGMT001", entity.getAgreementNo());
|
|
|
|
|
+ assertEquals("AUTHED", entity.getStatus());
|
|
|
|
|
+ verify(batchAuthorizeMapper).updateById(entity);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void batchNotify_updatesOrderStatus() {
|
|
|
|
|
+ BatchOrderEntity order = new BatchOrderEntity();
|
|
|
|
|
+ order.setId(1L);
|
|
|
|
|
+ order.setOutBatchNo("B1");
|
|
|
|
|
+ order.setStatus("INIT");
|
|
|
|
|
+ when(batchOrderMapper.selectOne(any())).thenReturn(order);
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, String> params = new HashMap<>();
|
|
|
|
|
+ params.put("out_batch_no", "B1");
|
|
|
|
|
+ params.put("status", "SUCCESS");
|
|
|
|
|
+ handler.dispatch("alipay.fund.batch.order.changed", params, ctx());
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals("SUCCESS", order.getStatus());
|
|
|
|
|
+ verify(batchOrderMapper).updateById(order);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void batchNotify_skipsWhenOrderAlreadyTerminal() {
|
|
|
|
|
+ // Ruling 7.3: 本地状态 ∈ {SUCCESS, DISUSE, FAIL} 为终态不可变,通知不再回写
|
|
|
|
|
+ for (String terminal : List.of("SUCCESS", "DISUSE", "FAIL")) {
|
|
|
|
|
+ BatchOrderEntity order = new BatchOrderEntity();
|
|
|
|
|
+ order.setId(1L);
|
|
|
|
|
+ order.setOutBatchNo("B1");
|
|
|
|
|
+ order.setStatus(terminal);
|
|
|
|
|
+ when(batchOrderMapper.selectOne(any())).thenReturn(order);
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, String> params = new HashMap<>();
|
|
|
|
|
+ params.put("out_batch_no", "B1");
|
|
|
|
|
+ params.put("status", "SUCCESS");
|
|
|
|
|
+ handler.dispatch("alipay.fund.batch.order.changed", params, ctx());
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(terminal, order.getStatus());
|
|
|
|
|
+ }
|
|
|
|
|
+ verify(batchOrderMapper, never()).updateById(any());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void authorizeNotify_skipsWhenAlreadyUnbind() {
|
|
|
|
|
+ // Ruling 7.3: UNBIND 后不接受回退(AUTHED/UNBIND 之后不再回写)
|
|
|
|
|
+ BatchAuthorizeEntity entity = new BatchAuthorizeEntity();
|
|
|
|
|
+ entity.setId(1L);
|
|
|
|
|
+ entity.setStatus("UNBIND");
|
|
|
|
|
+ when(batchAuthorizeMapper.selectOne(any())).thenReturn(entity);
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, String> params = new HashMap<>();
|
|
|
|
|
+ params.put("out_biz_no", "A1");
|
|
|
|
|
+ params.put("agreement_no", "AGMT001");
|
|
|
|
|
+ params.put("status", "AUTHED");
|
|
|
|
|
+ handler.dispatch("alipay.fund.authorize.status.notify", params, ctx());
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals("UNBIND", entity.getStatus());
|
|
|
|
|
+ assertNull(entity.getAgreementNo());
|
|
|
|
|
+ verify(batchAuthorizeMapper, never()).updateById(any());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void authorizeNotify_allowsUnbindTransitionFromAuthed() {
|
|
|
|
|
+ // Ruling 7.3: AUTHED→UNBIND 解约转换合法,不被终态判重拦截
|
|
|
|
|
+ BatchAuthorizeEntity entity = new BatchAuthorizeEntity();
|
|
|
|
|
+ entity.setId(1L);
|
|
|
|
|
+ entity.setStatus("AUTHED");
|
|
|
|
|
+ when(batchAuthorizeMapper.selectOne(any())).thenReturn(entity);
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, String> params = new HashMap<>();
|
|
|
|
|
+ params.put("out_biz_no", "A1");
|
|
|
|
|
+ params.put("status", "UNBIND");
|
|
|
|
|
+ handler.dispatch("alipay.fund.authorize.status.notify", params, ctx());
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals("UNBIND", entity.getStatus());
|
|
|
|
|
+ verify(batchAuthorizeMapper).updateById(entity);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|