|
|
@@ -3,18 +3,12 @@ package com.payment.platform.module.payment.batch.service;
|
|
|
import com.alipay.api.AlipayClient;
|
|
|
import com.alipay.api.AlipayApiException;
|
|
|
import com.alipay.api.domain.AccDetailModel;
|
|
|
-import com.alipay.api.domain.AlipayFundAuthorizeUniApplyModel;
|
|
|
-import com.alipay.api.domain.AlipayFundAuthorizeUniQueryModel;
|
|
|
import com.alipay.api.domain.AlipayFundBatchCreateModel;
|
|
|
import com.alipay.api.domain.AlipayFundTransRenderPayModel;
|
|
|
-import com.alipay.api.request.AlipayFundAuthorizeUniApplyRequest;
|
|
|
-import com.alipay.api.request.AlipayFundAuthorizeUniQueryRequest;
|
|
|
import com.alipay.api.request.AlipayFundBatchCloseRequest;
|
|
|
import com.alipay.api.request.AlipayFundBatchCreateRequest;
|
|
|
import com.alipay.api.request.AlipayFundBatchDetailQueryRequest;
|
|
|
import com.alipay.api.request.AlipayFundTransRenderPayRequest;
|
|
|
-import com.alipay.api.response.AlipayFundAuthorizeUniApplyResponse;
|
|
|
-import com.alipay.api.response.AlipayFundAuthorizeUniQueryResponse;
|
|
|
import com.alipay.api.response.AlipayFundBatchCloseResponse;
|
|
|
import com.alipay.api.response.AlipayFundBatchCreateResponse;
|
|
|
import com.alipay.api.response.AlipayFundBatchDetailQueryResponse;
|
|
|
@@ -33,8 +27,6 @@ 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.BatchDetailMapper;
|
|
|
import com.payment.platform.module.payment.batch.mapper.BatchOrderMapper;
|
|
|
-import com.payment.platform.module.payment.enterprise.entity.EnterpriseEntity;
|
|
|
-import com.payment.platform.module.payment.enterprise.mapper.EnterpriseMapper;
|
|
|
import jakarta.validation.ConstraintViolation;
|
|
|
import jakarta.validation.Validation;
|
|
|
import jakarta.validation.Validator;
|
|
|
@@ -65,19 +57,15 @@ class AlipayBatchPayServiceTest {
|
|
|
@Mock private BatchAuthorizeMapper batchAuthorizeMapper;
|
|
|
@Mock private BatchOrderMapper batchOrderMapper;
|
|
|
@Mock private BatchDetailMapper batchDetailMapper;
|
|
|
- @Mock private EnterpriseMapper enterpriseMapper;
|
|
|
private AlipayBatchPayService service;
|
|
|
|
|
|
@BeforeEach
|
|
|
void setUp() {
|
|
|
service = new AlipayBatchPayService(alipayClientFactory, batchAuthorizeMapper, batchOrderMapper,
|
|
|
- batchDetailMapper, enterpriseMapper);
|
|
|
- // lenient: 重复授权预检测试用例在到达 getClient 前即抛异常,该 stub 不会被使用
|
|
|
- lenient().when(alipayClientFactory.getClient("E100", "BATCH_PAY")).thenReturn(alipayClient);
|
|
|
- // 付款方身份解析: E100 企业无 identity → 回退 enterprise_id(createOnboard 惯例);identity 有值用例单独 stub
|
|
|
- EnterpriseEntity ent = new EnterpriseEntity();
|
|
|
- ent.setEnterpriseId("E100");
|
|
|
- lenient().when(enterpriseMapper.selectByEnterpriseIdIgnoreTenant("E100")).thenReturn(ent);
|
|
|
+ batchDetailMapper);
|
|
|
+ // lenient: 预检拦截/本地不存在的用例在到达 getClient 前即抛异常,该 stub 不会被使用
|
|
|
+ // 账号级: client 按订单冗余的 service_provider_id 解析(订单 mock 统一 setServiceProviderId(1L))
|
|
|
+ lenient().when(alipayClientFactory.getClientByProvider(1L, "BATCH_PAY")).thenReturn(alipayClient);
|
|
|
// 初始化 MyBatis-Plus lambda 元数据缓存,使 LambdaQueryWrapper.getSqlSegment() 可在无 Spring 上下文的单测中工作
|
|
|
MybatisConfiguration configuration = new MybatisConfiguration();
|
|
|
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), BatchOrderEntity.class);
|
|
|
@@ -85,96 +73,6 @@ class AlipayBatchPayServiceTest {
|
|
|
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), BatchAuthorizeEntity.class);
|
|
|
}
|
|
|
|
|
|
- @Test
|
|
|
- void authorizeApply_returnsShortLinkAndPersists() throws AlipayApiException {
|
|
|
- AlipayFundAuthorizeUniApplyResponse resp = new AlipayFundAuthorizeUniApplyResponse();
|
|
|
- resp.setAuthorizeLink("https://ur.alipay.com/abc");
|
|
|
- resp.setOutBizNo("A1");
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniApplyRequest.class))).thenReturn(resp);
|
|
|
-
|
|
|
- Map<String, String> result = service.authorizeApply("E100");
|
|
|
-
|
|
|
- assertEquals("https://ur.alipay.com/abc", result.get("authorize_link"));
|
|
|
- assertEquals("AUTHING", result.get("status"));
|
|
|
-
|
|
|
- ArgumentCaptor<AlipayFundAuthorizeUniApplyRequest> cap = ArgumentCaptor.forClass(AlipayFundAuthorizeUniApplyRequest.class);
|
|
|
- verify(alipayClient).certificateExecute(cap.capture());
|
|
|
- AlipayFundAuthorizeUniApplyModel m = (AlipayFundAuthorizeUniApplyModel) cap.getValue().getBizModel();
|
|
|
- assertEquals("TRANSFER_API_STANDARD_AUTHORIZATION", m.getProductCode());
|
|
|
- assertEquals("STANDARD_CREATE_FUND_ORDER", m.getBizScene());
|
|
|
- assertEquals("SHORT_URL", m.getAuthorizeLinkType());
|
|
|
- assertEquals("pc", m.getChannel());
|
|
|
- // Ruling 19/22: 付款方恒为企业自身 UID(enterprise_id),不接受客户端指定
|
|
|
- assertEquals("E100", m.getPrincipalInfo().getParticipantId());
|
|
|
-
|
|
|
- ArgumentCaptor<BatchAuthorizeEntity> ent = ArgumentCaptor.forClass(BatchAuthorizeEntity.class);
|
|
|
- verify(batchAuthorizeMapper).insert(ent.capture());
|
|
|
- assertEquals("E100", ent.getValue().getEnterpriseId());
|
|
|
- assertEquals("AUTHING", ent.getValue().getStatus());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_failure_throwsBusinessException() throws AlipayApiException {
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniApplyRequest.class)))
|
|
|
- .thenThrow(new AlipayApiException("network error"));
|
|
|
-
|
|
|
- assertThrows(BusinessException.class, () -> service.authorizeApply("E100"));
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void queryAuthorize_returnsAgreementNo() throws AlipayApiException {
|
|
|
- AlipayFundAuthorizeUniQueryResponse resp = new AlipayFundAuthorizeUniQueryResponse();
|
|
|
- resp.setAgreementNo("AGMT001");
|
|
|
- resp.setStatus("AUTHED");
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniQueryRequest.class))).thenReturn(resp);
|
|
|
-
|
|
|
- Map<String, String> result = service.queryAuthorize("E100", "A1");
|
|
|
-
|
|
|
- assertEquals("AGMT001", result.get("agreement_no"));
|
|
|
- assertEquals("AUTHED", result.get("status"));
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_existingAuthorization_throwsBusinessException() throws AlipayApiException {
|
|
|
- BatchAuthorizeEntity existing = new BatchAuthorizeEntity();
|
|
|
- existing.setEnterpriseId("E100");
|
|
|
- existing.setParticipantId("2088123412341234");
|
|
|
- existing.setStatus("AUTHED");
|
|
|
- when(batchAuthorizeMapper.selectOne(any())).thenReturn(existing);
|
|
|
-
|
|
|
- assertThrows(BusinessException.class, () -> service.authorizeApply("E100"));
|
|
|
-
|
|
|
- verify(alipayClient, never()).certificateExecute(any(AlipayFundAuthorizeUniApplyRequest.class));
|
|
|
- verify(batchAuthorizeMapper, never()).insert(any());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_nullAuthorizeLink_returnsEmptyString() throws AlipayApiException {
|
|
|
- AlipayFundAuthorizeUniApplyResponse resp = new AlipayFundAuthorizeUniApplyResponse();
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniApplyRequest.class))).thenReturn(resp);
|
|
|
-
|
|
|
- Map<String, String> result = service.authorizeApply("E100");
|
|
|
-
|
|
|
- assertEquals("", result.get("authorize_link"));
|
|
|
- assertEquals("AUTHING", result.get("status"));
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void queryAuthorize_unbind_doesNotOverwriteLocalStatus() throws AlipayApiException {
|
|
|
- AlipayFundAuthorizeUniQueryResponse resp = new AlipayFundAuthorizeUniQueryResponse();
|
|
|
- resp.setAgreementNo("AGMT001");
|
|
|
- resp.setStatus("UNBIND");
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniQueryRequest.class))).thenReturn(resp);
|
|
|
-
|
|
|
- Map<String, String> result = service.queryAuthorize("E100", "A1");
|
|
|
-
|
|
|
- assertEquals("AGMT001", result.get("agreement_no"));
|
|
|
- assertEquals("UNBIND", result.get("status"));
|
|
|
- // UNBIND 下协议号非空也不得回写本地状态
|
|
|
- verify(batchAuthorizeMapper, never()).selectOne(any());
|
|
|
- verify(batchAuthorizeMapper, never()).updateById(any());
|
|
|
- }
|
|
|
-
|
|
|
@Test
|
|
|
void batchCreate_persistsOrderAndDetails() throws AlipayApiException {
|
|
|
AlipayFundBatchCreateResponse resp = new AlipayFundBatchCreateResponse();
|
|
|
@@ -185,11 +83,12 @@ class AlipayBatchPayServiceTest {
|
|
|
// 制单预检: 已有 AUTHED 授权(协议号由系统自动带出,不接受客户端指定)
|
|
|
BatchAuthorizeEntity authed = new BatchAuthorizeEntity();
|
|
|
authed.setStatus("AUTHED");
|
|
|
+ authed.setServiceProviderId(1L);
|
|
|
authed.setAgreementNo("AGMT001");
|
|
|
when(batchAuthorizeMapper.selectOne(any())).thenReturn(authed);
|
|
|
|
|
|
BatchCreateDTO dto = new BatchCreateDTO();
|
|
|
- dto.setEnterpriseId("E100");
|
|
|
+ dto.setParticipantId("2088111122223333");
|
|
|
dto.setOutBatchNo("B1");
|
|
|
dto.setOrderTitle("202608报销");
|
|
|
dto.setTransferSceneName("佣金报酬");
|
|
|
@@ -270,11 +169,12 @@ class AlipayBatchPayServiceTest {
|
|
|
when(batchOrderMapper.selectOne(any())).thenReturn(existing);
|
|
|
BatchAuthorizeEntity authed = new BatchAuthorizeEntity();
|
|
|
authed.setStatus("AUTHED");
|
|
|
+ authed.setServiceProviderId(1L);
|
|
|
authed.setAgreementNo("AGMT001");
|
|
|
when(batchAuthorizeMapper.selectOne(any())).thenReturn(authed);
|
|
|
|
|
|
BatchCreateDTO dto = new BatchCreateDTO();
|
|
|
- dto.setEnterpriseId("E100");
|
|
|
+ dto.setParticipantId("2088111122223333");
|
|
|
dto.setOutBatchNo("B1");
|
|
|
dto.setOrderTitle("t");
|
|
|
BatchCreateDTO.BatchDetailDTO detail = new BatchCreateDTO.BatchDetailDTO();
|
|
|
@@ -297,7 +197,7 @@ class AlipayBatchPayServiceTest {
|
|
|
void batchCreate_duplicateDetailOutBizNo_throwsBusinessException() throws AlipayApiException {
|
|
|
// Ruling 5-2: 每笔 out_biz_no 批内唯一
|
|
|
BatchCreateDTO dto = new BatchCreateDTO();
|
|
|
- dto.setEnterpriseId("E100");
|
|
|
+ dto.setParticipantId("2088111122223333");
|
|
|
dto.setOutBatchNo("B1");
|
|
|
dto.setOrderTitle("t");
|
|
|
BatchCreateDTO.BatchDetailDTO d1 = new BatchCreateDTO.BatchDetailDTO();
|
|
|
@@ -324,7 +224,7 @@ class AlipayBatchPayServiceTest {
|
|
|
@Test
|
|
|
void renderPay_returnsPayUrl() throws AlipayApiException {
|
|
|
BatchOrderEntity order = new BatchOrderEntity();
|
|
|
- order.setEnterpriseId("E100");
|
|
|
+ order.setServiceProviderId(1L);
|
|
|
order.setOutBatchNo("B1");
|
|
|
order.setBatchTransId("BT1");
|
|
|
order.setStatus("INIT");
|
|
|
@@ -334,7 +234,7 @@ class AlipayBatchPayServiceTest {
|
|
|
resp.setInitializeCode("https://p.tb.cn/_2PG4jfHMVvg9vqBUdtx1HZ");
|
|
|
when(alipayClient.certificateExecute(any(AlipayFundTransRenderPayRequest.class))).thenReturn(resp);
|
|
|
|
|
|
- Map<String, String> result = service.renderPay("E100", "B1");
|
|
|
+ Map<String, String> result = service.renderPay("B1");
|
|
|
|
|
|
// render.pay 返回收银台短链接 initialize_code(用户实测成功响应实证),直接作为 pay_url
|
|
|
assertEquals("https://p.tb.cn/_2PG4jfHMVvg9vqBUdtx1HZ", result.get("pay_url"));
|
|
|
@@ -356,7 +256,7 @@ class AlipayBatchPayServiceTest {
|
|
|
// 支付宝业务失败(非 10000)必须抛错且不落库——
|
|
|
// 否则错误响应会被当 pay_url 返回、前端拿错误内容开新窗口(用户实测: 窗口显示整页 INVALID_PARAMETER JSON)
|
|
|
BatchOrderEntity order = new BatchOrderEntity();
|
|
|
- order.setEnterpriseId("E100");
|
|
|
+ order.setServiceProviderId(1L);
|
|
|
order.setOutBatchNo("B1");
|
|
|
order.setBatchTransId("BT1");
|
|
|
order.setStatus("INIT");
|
|
|
@@ -367,7 +267,7 @@ class AlipayBatchPayServiceTest {
|
|
|
resp.setSubMsg("参数[target_terminal_type]错误, 原因: 必须传入参数[target_terminal_type]");
|
|
|
when(alipayClient.certificateExecute(any(AlipayFundTransRenderPayRequest.class))).thenReturn(resp);
|
|
|
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.renderPay("E100", "B1"));
|
|
|
+ BusinessException ex = assertThrows(BusinessException.class, () -> service.renderPay("B1"));
|
|
|
|
|
|
assertEquals(400, ex.getCode());
|
|
|
assertTrue(ex.getMessage().contains("INVALID_PARAMETER"), ex.getMessage());
|
|
|
@@ -378,7 +278,7 @@ class AlipayBatchPayServiceTest {
|
|
|
void renderPay_successWithoutInitializeCode_throws() throws AlipayApiException {
|
|
|
// 成功响应但支付宝未返回跳转链接(防御: 不把 null 当 pay_url 返回)
|
|
|
BatchOrderEntity order = new BatchOrderEntity();
|
|
|
- order.setEnterpriseId("E100");
|
|
|
+ order.setServiceProviderId(1L);
|
|
|
order.setOutBatchNo("B1");
|
|
|
order.setBatchTransId("BT1");
|
|
|
order.setStatus("INIT");
|
|
|
@@ -387,7 +287,7 @@ class AlipayBatchPayServiceTest {
|
|
|
resp.setCode("10000");
|
|
|
when(alipayClient.certificateExecute(any(AlipayFundTransRenderPayRequest.class))).thenReturn(resp);
|
|
|
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.renderPay("E100", "B1"));
|
|
|
+ BusinessException ex = assertThrows(BusinessException.class, () -> service.renderPay("B1"));
|
|
|
|
|
|
assertEquals(400, ex.getCode());
|
|
|
assertTrue(ex.getMessage().contains("跳转链接"), ex.getMessage());
|
|
|
@@ -398,7 +298,7 @@ class AlipayBatchPayServiceTest {
|
|
|
void batchQuery_syncsBatchStatus() throws AlipayApiException {
|
|
|
BatchOrderEntity order = new BatchOrderEntity();
|
|
|
order.setId(1L);
|
|
|
- order.setEnterpriseId("E100");
|
|
|
+ order.setServiceProviderId(1L);
|
|
|
order.setOutBatchNo("B1");
|
|
|
order.setBatchTransId("BT1");
|
|
|
order.setStatus("INIT");
|
|
|
@@ -408,7 +308,7 @@ class AlipayBatchPayServiceTest {
|
|
|
resp.setBatchStatus("SUCCESS");
|
|
|
when(alipayClient.certificateExecute(any(AlipayFundBatchDetailQueryRequest.class))).thenReturn(resp);
|
|
|
|
|
|
- Map<String, Object> result = service.batchQuery("E100", "B1");
|
|
|
+ Map<String, Object> result = service.batchQuery("B1");
|
|
|
|
|
|
assertEquals("SUCCESS", result.get("status"));
|
|
|
assertEquals("SUCCESS", order.getStatus());
|
|
|
@@ -418,7 +318,7 @@ class AlipayBatchPayServiceTest {
|
|
|
@Test
|
|
|
void batchClose_callsCloseApi() throws AlipayApiException {
|
|
|
BatchOrderEntity order = new BatchOrderEntity();
|
|
|
- order.setEnterpriseId("E100");
|
|
|
+ order.setServiceProviderId(1L);
|
|
|
order.setOutBatchNo("B1");
|
|
|
order.setBatchTransId("BT1");
|
|
|
order.setStatus("INIT");
|
|
|
@@ -426,7 +326,7 @@ class AlipayBatchPayServiceTest {
|
|
|
when(alipayClient.certificateExecute(any(AlipayFundBatchCloseRequest.class)))
|
|
|
.thenReturn(new AlipayFundBatchCloseResponse());
|
|
|
|
|
|
- service.batchClose("E100", "B1");
|
|
|
+ service.batchClose("B1");
|
|
|
verify(alipayClient).certificateExecute(any(AlipayFundBatchCloseRequest.class));
|
|
|
}
|
|
|
|
|
|
@@ -434,11 +334,11 @@ class AlipayBatchPayServiceTest {
|
|
|
void batchList_invalidTime_throwsBusinessException() {
|
|
|
// Ruling 5-3: 非法时间输入应抛 400 而非 DateTimeParseException→500
|
|
|
BusinessException ex = assertThrows(BusinessException.class,
|
|
|
- () -> service.batchList("E100", null, "2026-13-45 99:99", null, 1, 20));
|
|
|
+ () -> service.batchList(null, null, "2026-13-45 99:99", null, 1, 20));
|
|
|
assertEquals(400, ex.getCode());
|
|
|
|
|
|
BusinessException ex2 = assertThrows(BusinessException.class,
|
|
|
- () -> service.batchList("E100", null, null, "2026-08-25 25:61", 1, 20));
|
|
|
+ () -> service.batchList(null, null, null, "2026-08-25 25:61", 1, 20));
|
|
|
assertEquals(400, ex2.getCode());
|
|
|
}
|
|
|
|
|
|
@@ -446,26 +346,10 @@ class AlipayBatchPayServiceTest {
|
|
|
void batchExport_invalidTime_throwsBusinessException() {
|
|
|
// Ruling 5-3: 导出 catch-all 需区分 DateTimeParseException 抛 400
|
|
|
BusinessException ex = assertThrows(BusinessException.class,
|
|
|
- () -> service.batchExport("E100", null, "bad-time", null));
|
|
|
+ () -> service.batchExport(null, null, "bad-time", null));
|
|
|
assertEquals(400, ex.getCode());
|
|
|
}
|
|
|
|
|
|
- @Test
|
|
|
- void queryAuthorize_notExist_returnsAUTHINGInsteadOfError() throws AlipayApiException {
|
|
|
- // Task 8 全链路验证: 支付宝 40004 AUTHORIZATION_NOT_EXIST(未授权)是正常业务状态而非查询失败
|
|
|
- AlipayFundAuthorizeUniQueryResponse resp = new AlipayFundAuthorizeUniQueryResponse();
|
|
|
- resp.setCode("40004");
|
|
|
- resp.setSubCode("AUTHORIZATION_NOT_EXIST");
|
|
|
- resp.setMsg("Business Failed");
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniQueryRequest.class))).thenReturn(resp);
|
|
|
-
|
|
|
- Map<String, String> result = service.queryAuthorize("E100", "A1");
|
|
|
-
|
|
|
- assertEquals("", result.get("agreement_no"));
|
|
|
- assertEquals("AUTHING", result.get("status"));
|
|
|
- verify(batchAuthorizeMapper, never()).updateById(any());
|
|
|
- }
|
|
|
-
|
|
|
@Test
|
|
|
void batchCreate_authInfoNotExists_friendlyMessage() throws AlipayApiException {
|
|
|
// Task 8 全链路验证: AUTH_INFO_NOT_EXISTS(付款方未授权)应提示明确动作而非透传支付宝原文案
|
|
|
@@ -477,11 +361,12 @@ class AlipayBatchPayServiceTest {
|
|
|
// 本地预检通过(有 AUTHED 记录)后仍触发支付宝侧 AUTH_INFO_NOT_EXISTS 兜底路径
|
|
|
BatchAuthorizeEntity authed = new BatchAuthorizeEntity();
|
|
|
authed.setStatus("AUTHED");
|
|
|
+ authed.setServiceProviderId(1L);
|
|
|
authed.setAgreementNo("AGMT001");
|
|
|
when(batchAuthorizeMapper.selectOne(any())).thenReturn(authed);
|
|
|
|
|
|
BatchCreateDTO dto = new BatchCreateDTO();
|
|
|
- dto.setEnterpriseId("E100");
|
|
|
+ dto.setParticipantId("2088111122223333");
|
|
|
dto.setOutBatchNo("B1");
|
|
|
dto.setOrderTitle("202608报销");
|
|
|
BatchCreateDTO.BatchDetailDTO detail = new BatchCreateDTO.BatchDetailDTO();
|
|
|
@@ -497,50 +382,50 @@ class AlipayBatchPayServiceTest {
|
|
|
assertTrue(ex.getMessage().contains("制单授权"), "应提示先完成制单授权: " + ex.getMessage());
|
|
|
}
|
|
|
|
|
|
- // ==================== C2: 租户隔离(renderPay/batchQuery/batchClose 缺 enterpriseId 条件) ====================
|
|
|
+ // ==================== C2: 批次不存在(本地查不到 → 404,租户隔离由 TenantInnerInterceptor 自动追加) ====================
|
|
|
|
|
|
@Test
|
|
|
- void renderPay_otherEnterpriseBatch_notFound() {
|
|
|
- // 他人批次: selectOne 带 enterpriseId 条件查不到 → 404
|
|
|
+ void renderPay_batchNotFound_throws404() {
|
|
|
+ // 本地批次不存在(他租户批次或被拦截器过滤)→ 404
|
|
|
when(batchOrderMapper.selectOne(any())).thenReturn(null);
|
|
|
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.renderPay("E999", "B1"));
|
|
|
+ BusinessException ex = assertThrows(BusinessException.class, () -> service.renderPay("B1"));
|
|
|
|
|
|
assertEquals(404, ex.getCode());
|
|
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
|
ArgumentCaptor<LambdaQueryWrapper<BatchOrderEntity>> cap = ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
|
|
verify(batchOrderMapper).selectOne(cap.capture());
|
|
|
- assertTrue(cap.getValue().getSqlSegment().contains("enterprise_id"),
|
|
|
- "renderPay 查询必须带 enterpriseId 条件: " + cap.getValue().getSqlSegment());
|
|
|
+ assertTrue(cap.getValue().getSqlSegment().contains("out_batch_no"),
|
|
|
+ "renderPay 查询必须按 out_batch_no: " + cap.getValue().getSqlSegment());
|
|
|
}
|
|
|
|
|
|
@Test
|
|
|
- void batchQuery_otherEnterpriseBatch_notFound() {
|
|
|
+ void batchQuery_batchNotFound_throws404() {
|
|
|
when(batchOrderMapper.selectOne(any())).thenReturn(null);
|
|
|
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.batchQuery("E999", "B1"));
|
|
|
+ BusinessException ex = assertThrows(BusinessException.class, () -> service.batchQuery("B1"));
|
|
|
|
|
|
assertEquals(404, ex.getCode());
|
|
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
|
ArgumentCaptor<LambdaQueryWrapper<BatchOrderEntity>> cap = ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
|
|
verify(batchOrderMapper).selectOne(cap.capture());
|
|
|
- assertTrue(cap.getValue().getSqlSegment().contains("enterprise_id"),
|
|
|
- "batchQuery 查询必须带 enterpriseId 条件: " + cap.getValue().getSqlSegment());
|
|
|
+ assertTrue(cap.getValue().getSqlSegment().contains("out_batch_no"),
|
|
|
+ "batchQuery 查询必须按 out_batch_no: " + cap.getValue().getSqlSegment());
|
|
|
}
|
|
|
|
|
|
@Test
|
|
|
- void batchClose_otherEnterpriseBatch_notFound() throws AlipayApiException {
|
|
|
+ void batchClose_batchNotFound_throws404() throws AlipayApiException {
|
|
|
when(batchOrderMapper.selectOne(any())).thenReturn(null);
|
|
|
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.batchClose("E999", "B1"));
|
|
|
+ BusinessException ex = assertThrows(BusinessException.class, () -> service.batchClose("B1"));
|
|
|
|
|
|
assertEquals(404, ex.getCode());
|
|
|
verify(alipayClient, never()).certificateExecute(any());
|
|
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
|
ArgumentCaptor<LambdaQueryWrapper<BatchOrderEntity>> cap = ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
|
|
verify(batchOrderMapper).selectOne(cap.capture());
|
|
|
- assertTrue(cap.getValue().getSqlSegment().contains("enterprise_id"),
|
|
|
- "batchClose 查询必须带 enterpriseId 条件: " + cap.getValue().getSqlSegment());
|
|
|
+ assertTrue(cap.getValue().getSqlSegment().contains("out_batch_no"),
|
|
|
+ "batchClose 查询必须按 out_batch_no: " + cap.getValue().getSqlSegment());
|
|
|
}
|
|
|
|
|
|
// ==================== I1: 定时同步兜底(getPendingBatches) ====================
|
|
|
@@ -573,7 +458,7 @@ class AlipayBatchPayServiceTest {
|
|
|
// 两个批次含相同 out_biz_no(out_biz_no 仅批内唯一)→ 不得抛 TooManyResultsException,且只回写本批次明细
|
|
|
BatchOrderEntity order = new BatchOrderEntity();
|
|
|
order.setId(1L);
|
|
|
- order.setEnterpriseId("E100");
|
|
|
+ order.setServiceProviderId(1L);
|
|
|
order.setOutBatchNo("B1");
|
|
|
order.setBatchTransId("BT1");
|
|
|
order.setStatus("INIT");
|
|
|
@@ -597,7 +482,7 @@ class AlipayBatchPayServiceTest {
|
|
|
own.setStatus("INIT");
|
|
|
when(batchDetailMapper.selectList(any())).thenReturn(List.of(own));
|
|
|
|
|
|
- Map<String, Object> result = service.batchQuery("E100", "B1");
|
|
|
+ Map<String, Object> result = service.batchQuery("B1");
|
|
|
|
|
|
assertEquals("SUCCESS", result.get("status"));
|
|
|
assertEquals("SUCCESS", own.getStatus());
|
|
|
@@ -661,7 +546,7 @@ class AlipayBatchPayServiceTest {
|
|
|
void batchList_pureDateRange_parsesToDayBoundaries() {
|
|
|
when(batchOrderMapper.selectPage(any(), any())).thenReturn(new Page<>());
|
|
|
|
|
|
- PageResult<BatchOrderEntity> result = service.batchList("E100", null, "2026-08-01", "2026-08-02", 1, 20);
|
|
|
+ PageResult<BatchOrderEntity> result = service.batchList(null, null, "2026-08-01", "2026-08-02", 1, 20);
|
|
|
|
|
|
assertNotNull(result);
|
|
|
verify(batchOrderMapper).selectPage(any(), any());
|
|
|
@@ -671,7 +556,7 @@ class AlipayBatchPayServiceTest {
|
|
|
void batchExport_pureDateRange_parses() {
|
|
|
when(batchOrderMapper.selectList(any())).thenReturn(List.of());
|
|
|
|
|
|
- byte[] bytes = service.batchExport("E100", null, "2026-08-01", "2026-08-02");
|
|
|
+ byte[] bytes = service.batchExport(null, null, "2026-08-01", "2026-08-02");
|
|
|
|
|
|
assertNotNull(bytes);
|
|
|
assertTrue(bytes.length > 0);
|
|
|
@@ -682,7 +567,7 @@ class AlipayBatchPayServiceTest {
|
|
|
@Test
|
|
|
void renderPay_initializeCode_persistsAndReturns() throws AlipayApiException {
|
|
|
BatchOrderEntity order = new BatchOrderEntity();
|
|
|
- order.setEnterpriseId("E100");
|
|
|
+ order.setServiceProviderId(1L);
|
|
|
order.setOutBatchNo("B1");
|
|
|
order.setBatchTransId("BT1");
|
|
|
order.setStatus("INIT");
|
|
|
@@ -694,280 +579,28 @@ class AlipayBatchPayServiceTest {
|
|
|
resp.setInitializeCode("https://p.tb.cn/_2PG4jfHMVvg9vqBUdtx1HZ");
|
|
|
when(alipayClient.certificateExecute(any(AlipayFundTransRenderPayRequest.class))).thenReturn(resp);
|
|
|
|
|
|
- Map<String, String> result = service.renderPay("E100", "B1");
|
|
|
+ Map<String, String> result = service.renderPay("B1");
|
|
|
|
|
|
assertEquals("https://p.tb.cn/_2PG4jfHMVvg9vqBUdtx1HZ", result.get("pay_url"));
|
|
|
verify(batchOrderMapper).updateById(order);
|
|
|
}
|
|
|
|
|
|
- // ==================== M1: 授权并发 DB 兜底 ====================
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_insertDuplicateKey_throwsFriendlyMessage() throws AlipayApiException {
|
|
|
- AlipayFundAuthorizeUniApplyResponse resp = new AlipayFundAuthorizeUniApplyResponse();
|
|
|
- resp.setAuthorizeLink("https://ur.alipay.com/abc");
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniApplyRequest.class))).thenReturn(resp);
|
|
|
- // 并发双击: 预检通过后 insert 命中 uk_batch_authorize_active 唯一索引
|
|
|
- doThrow(new DuplicateKeyException("duplicate key")).when(batchAuthorizeMapper).insert(any());
|
|
|
-
|
|
|
- BusinessException ex = assertThrows(BusinessException.class,
|
|
|
- () -> service.authorizeApply("E100"));
|
|
|
-
|
|
|
- assertEquals(400, ex.getCode());
|
|
|
- assertTrue(ex.getMessage().contains("请勿重复操作"), ex.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ==================== M2: AUTHING 过期重申请 ====================
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_authingNotExpired_throws() {
|
|
|
- BatchAuthorizeEntity existing = new BatchAuthorizeEntity();
|
|
|
- existing.setEnterpriseId("E100");
|
|
|
- existing.setParticipantId("2088123412341234");
|
|
|
- existing.setStatus("AUTHING");
|
|
|
- existing.setAuthorizeExpireTime(OffsetDateTime.now().plusHours(1));
|
|
|
- when(batchAuthorizeMapper.selectOne(any())).thenReturn(existing);
|
|
|
-
|
|
|
- BusinessException ex = assertThrows(BusinessException.class,
|
|
|
- () -> service.authorizeApply("E100"));
|
|
|
-
|
|
|
- assertEquals(400, ex.getCode());
|
|
|
- assertTrue(ex.getMessage().contains("未完成"), ex.getMessage());
|
|
|
- verify(batchAuthorizeMapper, never()).updateById(any());
|
|
|
- verify(batchAuthorizeMapper, never()).insert(any());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_authingFreshCreatedTime_throws() {
|
|
|
- // authorize_expire_time 为空时按 created_time + 24h 判定: 刚创建的 AUTHING 未过期 → 拒绝
|
|
|
- BatchAuthorizeEntity existing = new BatchAuthorizeEntity();
|
|
|
- existing.setEnterpriseId("E100");
|
|
|
- existing.setParticipantId("2088123412341234");
|
|
|
- existing.setStatus("AUTHING");
|
|
|
- existing.setCreatedTime(OffsetDateTime.now());
|
|
|
- when(batchAuthorizeMapper.selectOne(any())).thenReturn(existing);
|
|
|
-
|
|
|
- assertThrows(BusinessException.class, () -> service.authorizeApply("E100"));
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_authingExpired_rebindWithNewOutBizNo() throws AlipayApiException {
|
|
|
- BatchAuthorizeEntity existing = new BatchAuthorizeEntity();
|
|
|
- existing.setId(1L);
|
|
|
- existing.setEnterpriseId("E100");
|
|
|
- existing.setParticipantId("2088123412341234");
|
|
|
- existing.setOutBizNo("OLD1");
|
|
|
- existing.setStatus("AUTHING");
|
|
|
- existing.setAuthorizeExpireTime(OffsetDateTime.now().minusHours(1));
|
|
|
- when(batchAuthorizeMapper.selectOne(any())).thenReturn(existing);
|
|
|
-
|
|
|
- AlipayFundAuthorizeUniApplyResponse resp = new AlipayFundAuthorizeUniApplyResponse();
|
|
|
- resp.setAuthorizeLink("https://ur.alipay.com/abc");
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniApplyRequest.class))).thenReturn(resp);
|
|
|
-
|
|
|
- Map<String, String> result = service.authorizeApply("E100");
|
|
|
-
|
|
|
- assertEquals("AUTHING", result.get("status"));
|
|
|
- // 旧记录作废置 UNBIND(与 uk_batch_authorize_active 的 WHERE status <> 'UNBIND' 协同)
|
|
|
- verify(batchAuthorizeMapper).updateById(argThat(e -> "UNBIND".equals(e.getStatus())));
|
|
|
- // 新记录插入 + 新 out_biz_no
|
|
|
- ArgumentCaptor<BatchAuthorizeEntity> ent = ArgumentCaptor.forClass(BatchAuthorizeEntity.class);
|
|
|
- verify(batchAuthorizeMapper).insert(ent.capture());
|
|
|
- assertEquals("AUTHING", ent.getValue().getStatus());
|
|
|
- assertFalse(ent.getValue().getOutBizNo().equals("OLD1"), "应换新 out_biz_no 重新申请");
|
|
|
- assertEquals("E100", ent.getValue().getEnterpriseId());
|
|
|
- // 支付宝侧使用新 out_biz_no
|
|
|
- ArgumentCaptor<AlipayFundAuthorizeUniApplyRequest> cap = ArgumentCaptor.forClass(AlipayFundAuthorizeUniApplyRequest.class);
|
|
|
- verify(alipayClient).certificateExecute(cap.capture());
|
|
|
- AlipayFundAuthorizeUniApplyModel m = (AlipayFundAuthorizeUniApplyModel) cap.getValue().getBizModel();
|
|
|
- assertEquals(ent.getValue().getOutBizNo(), m.getOutBizNo());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_authingExpiredByCreatedTimeFallback_allowsReapply() throws AlipayApiException {
|
|
|
- // expire 为空 + created_time 超过 24h → 视为过期,允许重新申请
|
|
|
- BatchAuthorizeEntity existing = new BatchAuthorizeEntity();
|
|
|
- existing.setId(1L);
|
|
|
- existing.setEnterpriseId("E100");
|
|
|
- existing.setParticipantId("2088123412341234");
|
|
|
- existing.setOutBizNo("OLD1");
|
|
|
- existing.setStatus("AUTHING");
|
|
|
- existing.setCreatedTime(OffsetDateTime.now().minusDays(2));
|
|
|
- when(batchAuthorizeMapper.selectOne(any())).thenReturn(existing);
|
|
|
-
|
|
|
- AlipayFundAuthorizeUniApplyResponse resp = new AlipayFundAuthorizeUniApplyResponse();
|
|
|
- resp.setAuthorizeLink("https://ur.alipay.com/abc");
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniApplyRequest.class))).thenReturn(resp);
|
|
|
-
|
|
|
- Map<String, String> result = service.authorizeApply("E100");
|
|
|
-
|
|
|
- assertNotNull(result.get("authorize_link"));
|
|
|
- verify(batchAuthorizeMapper).updateById(argThat(e -> "UNBIND".equals(e.getStatus())));
|
|
|
- verify(batchAuthorizeMapper).insert(any());
|
|
|
- }
|
|
|
-
|
|
|
- // ==================== 用户反馈轮 2: 付款方 UID 自动带出 + 授权可重新生成 ====================
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_alwaysUsesEnterpriseAsPayer() throws AlipayApiException {
|
|
|
- // Ruling 19/22: 付款方恒为企业自身 UID(enterprise_id),方法不接受客户端传入的 participant_id(防篡改指向他人账号)
|
|
|
- AlipayFundAuthorizeUniApplyResponse resp = new AlipayFundAuthorizeUniApplyResponse();
|
|
|
- resp.setAuthorizeLink("https://ur.alipay.com/abc");
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniApplyRequest.class))).thenReturn(resp);
|
|
|
-
|
|
|
- Map<String, String> result = service.authorizeApply("E100");
|
|
|
-
|
|
|
- assertEquals("AUTHING", result.get("status"));
|
|
|
- ArgumentCaptor<AlipayFundAuthorizeUniApplyRequest> cap = ArgumentCaptor.forClass(AlipayFundAuthorizeUniApplyRequest.class);
|
|
|
- verify(alipayClient).certificateExecute(cap.capture());
|
|
|
- AlipayFundAuthorizeUniApplyModel m = (AlipayFundAuthorizeUniApplyModel) cap.getValue().getBizModel();
|
|
|
- assertEquals("E100", m.getPrincipalInfo().getParticipantId());
|
|
|
- ArgumentCaptor<BatchAuthorizeEntity> ent = ArgumentCaptor.forClass(BatchAuthorizeEntity.class);
|
|
|
- verify(batchAuthorizeMapper).insert(ent.capture());
|
|
|
- assertEquals("E100", ent.getValue().getParticipantId());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_usesEnterpriseIdentityWhenPresent() throws AlipayApiException {
|
|
|
- // Ruling 23: 企业入驻身份 identity 优先(配套 identityType),与 AlipayTransferService.createOnboard 惯例一致
|
|
|
- EnterpriseEntity ent = new EnterpriseEntity();
|
|
|
- ent.setEnterpriseId("E100");
|
|
|
- ent.setIdentity("2088IDENTITY");
|
|
|
- ent.setIdentityType("ALIPAY_OPEN_ID");
|
|
|
- when(enterpriseMapper.selectByEnterpriseIdIgnoreTenant("E100")).thenReturn(ent);
|
|
|
-
|
|
|
- AlipayFundAuthorizeUniApplyResponse resp = new AlipayFundAuthorizeUniApplyResponse();
|
|
|
- resp.setAuthorizeLink("https://ur.alipay.com/abc");
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniApplyRequest.class))).thenReturn(resp);
|
|
|
-
|
|
|
- Map<String, String> result = service.authorizeApply("E100");
|
|
|
-
|
|
|
- assertEquals("AUTHING", result.get("status"));
|
|
|
- ArgumentCaptor<AlipayFundAuthorizeUniApplyRequest> cap = ArgumentCaptor.forClass(AlipayFundAuthorizeUniApplyRequest.class);
|
|
|
- verify(alipayClient).certificateExecute(cap.capture());
|
|
|
- AlipayFundAuthorizeUniApplyModel m = (AlipayFundAuthorizeUniApplyModel) cap.getValue().getBizModel();
|
|
|
- assertEquals("2088IDENTITY", m.getPrincipalInfo().getParticipantId());
|
|
|
- assertEquals("ALIPAY_OPEN_ID", m.getPrincipalInfo().getParticipantIdType());
|
|
|
- ArgumentCaptor<BatchAuthorizeEntity> entCap = ArgumentCaptor.forClass(BatchAuthorizeEntity.class);
|
|
|
- verify(batchAuthorizeMapper).insert(entCap.capture());
|
|
|
- assertEquals("2088IDENTITY", entCap.getValue().getParticipantId());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_normalExisting_throws() throws AlipayApiException {
|
|
|
- // NORMAL 为通知归一前落库的支付宝生效状态 — 视同 AUTHED,拒绝重复授权
|
|
|
- BatchAuthorizeEntity existing = new BatchAuthorizeEntity();
|
|
|
- existing.setEnterpriseId("E100");
|
|
|
- existing.setParticipantId("2088123412341234");
|
|
|
- existing.setStatus("NORMAL");
|
|
|
- when(batchAuthorizeMapper.selectOne(any())).thenReturn(existing);
|
|
|
-
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.authorizeApply("E100"));
|
|
|
-
|
|
|
- assertEquals(400, ex.getCode());
|
|
|
- verify(alipayClient, never()).certificateExecute(any(AlipayFundAuthorizeUniApplyRequest.class));
|
|
|
- verify(batchAuthorizeMapper, never()).insert(any());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_enterpriseNotFound_throws() throws AlipayApiException {
|
|
|
- // requireEnterprise 防御: 企业不存在直接拒绝,不发起授权
|
|
|
- when(enterpriseMapper.selectByEnterpriseIdIgnoreTenant("E100")).thenReturn(null);
|
|
|
-
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.authorizeApply("E100"));
|
|
|
-
|
|
|
- assertEquals(400, ex.getCode());
|
|
|
- assertTrue(ex.getMessage().contains("企业不存在"), ex.getMessage());
|
|
|
- verify(alipayClient, never()).certificateExecute(any());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeApply_nullEnterpriseId_throws() {
|
|
|
- // requireEnterpriseId 防御: 企业都不存在时直接拒绝而非默认空串申请
|
|
|
- assertThrows(BusinessException.class, () -> service.authorizeApply(null));
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeRebind_noExisting_appliesFresh() throws AlipayApiException {
|
|
|
- // 无既有授权记录 → 直接申请(付款方 = 企业自己)
|
|
|
- AlipayFundAuthorizeUniApplyResponse resp = new AlipayFundAuthorizeUniApplyResponse();
|
|
|
- resp.setAuthorizeLink("https://ur.alipay.com/abc");
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniApplyRequest.class))).thenReturn(resp);
|
|
|
-
|
|
|
- Map<String, String> result = service.authorizeRebind("E100");
|
|
|
-
|
|
|
- assertEquals("AUTHING", result.get("status"));
|
|
|
- ArgumentCaptor<AlipayFundAuthorizeUniApplyRequest> cap = ArgumentCaptor.forClass(AlipayFundAuthorizeUniApplyRequest.class);
|
|
|
- verify(alipayClient).certificateExecute(cap.capture());
|
|
|
- AlipayFundAuthorizeUniApplyModel m = (AlipayFundAuthorizeUniApplyModel) cap.getValue().getBizModel();
|
|
|
- assertEquals("E100", m.getPrincipalInfo().getParticipantId());
|
|
|
- verify(batchAuthorizeMapper, never()).updateById(any());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeRebind_authingExisting_unbindsAndReapplies() throws AlipayApiException {
|
|
|
- // 最新 AUTHING 记录 → 置 UNBIND(旧链接作废)→ 换新 out_biz_no 重新申请
|
|
|
- BatchAuthorizeEntity existing = new BatchAuthorizeEntity();
|
|
|
- existing.setId(1L);
|
|
|
- existing.setEnterpriseId("E100");
|
|
|
- existing.setParticipantId("E100");
|
|
|
- existing.setOutBizNo("OLD1");
|
|
|
- existing.setStatus("AUTHING");
|
|
|
- // 序列 stub: rebind 预检命中旧记录 → 作废后 doAuthorizeApply 的二次预检(ne UNBIND)应返回 null
|
|
|
- when(batchAuthorizeMapper.selectOne(any())).thenReturn(existing, null);
|
|
|
-
|
|
|
- AlipayFundAuthorizeUniApplyResponse resp = new AlipayFundAuthorizeUniApplyResponse();
|
|
|
- resp.setAuthorizeLink("https://ur.alipay.com/abc");
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundAuthorizeUniApplyRequest.class))).thenReturn(resp);
|
|
|
-
|
|
|
- Map<String, String> result = service.authorizeRebind("E100");
|
|
|
-
|
|
|
- assertEquals("AUTHING", result.get("status"));
|
|
|
- verify(batchAuthorizeMapper).updateById(argThat(e -> "UNBIND".equals(e.getStatus()) && "OLD1".equals(e.getOutBizNo())));
|
|
|
- ArgumentCaptor<BatchAuthorizeEntity> ent = ArgumentCaptor.forClass(BatchAuthorizeEntity.class);
|
|
|
- verify(batchAuthorizeMapper).insert(ent.capture());
|
|
|
- assertFalse(ent.getValue().getOutBizNo().equals("OLD1"), "应换新 out_biz_no 重新申请");
|
|
|
- assertEquals("E100", ent.getValue().getParticipantId());
|
|
|
- }
|
|
|
-
|
|
|
@Test
|
|
|
- void authorizeRebind_authedExisting_throws() throws AlipayApiException {
|
|
|
- // AUTHED 是终态(永久生效授权),重新生成无意义 → 拒绝
|
|
|
- BatchAuthorizeEntity existing = new BatchAuthorizeEntity();
|
|
|
- existing.setId(1L);
|
|
|
- existing.setEnterpriseId("E100");
|
|
|
- existing.setParticipantId("E100");
|
|
|
- existing.setOutBizNo("OLD1");
|
|
|
- existing.setStatus("AUTHED");
|
|
|
- when(batchAuthorizeMapper.selectOne(any())).thenReturn(existing);
|
|
|
-
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.authorizeRebind("E100"));
|
|
|
-
|
|
|
- assertEquals(400, ex.getCode());
|
|
|
- assertTrue(ex.getMessage().contains("生效授权"), ex.getMessage());
|
|
|
- verify(alipayClient, never()).certificateExecute(any());
|
|
|
- verify(batchAuthorizeMapper, never()).updateById(any());
|
|
|
- verify(batchAuthorizeMapper, never()).insert(any());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void authorizeRebind_blankEnterpriseId_throws() {
|
|
|
- assertThrows(BusinessException.class, () -> service.authorizeRebind(""));
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void batchCreate_alwaysUsesEnterpriseAsPayer() throws AlipayApiException {
|
|
|
- // Ruling 19/22: 付款方恒为企业自身 UID(enterprise_id),DTO 已移除 payer_uid 不接受客户端指定
|
|
|
+ void batchCreate_alwaysUsesAuthorizedSubjectAsPayer() throws AlipayApiException {
|
|
|
+ // 账号级: 付款方恒为 DTO 选定的授权主体(participant_id),协议号由授权记录自动带出,不接受客户端指定
|
|
|
AlipayFundBatchCreateResponse resp = new AlipayFundBatchCreateResponse();
|
|
|
resp.setOutBatchNo("B1");
|
|
|
when(alipayClient.certificateExecute(any(AlipayFundBatchCreateRequest.class))).thenReturn(resp);
|
|
|
BatchAuthorizeEntity authed = new BatchAuthorizeEntity();
|
|
|
+ authed.setParticipantId("2088111122223333");
|
|
|
+ authed.setServiceProviderId(1L);
|
|
|
authed.setStatus("AUTHED");
|
|
|
+ authed.setServiceProviderId(1L);
|
|
|
authed.setAgreementNo("AGMT001");
|
|
|
when(batchAuthorizeMapper.selectOne(any())).thenReturn(authed);
|
|
|
|
|
|
BatchCreateDTO dto = new BatchCreateDTO();
|
|
|
- dto.setEnterpriseId("E100");
|
|
|
+ dto.setParticipantId("2088111122223333");
|
|
|
dto.setOutBatchNo("B1");
|
|
|
dto.setOrderTitle("t");
|
|
|
BatchCreateDTO.BatchDetailDTO detail = new BatchCreateDTO.BatchDetailDTO();
|
|
|
@@ -984,11 +617,13 @@ class AlipayBatchPayServiceTest {
|
|
|
ArgumentCaptor<AlipayFundBatchCreateRequest> cap = ArgumentCaptor.forClass(AlipayFundBatchCreateRequest.class);
|
|
|
verify(alipayClient).certificateExecute(cap.capture());
|
|
|
AlipayFundBatchCreateModel m = (AlipayFundBatchCreateModel) cap.getValue().getBizModel();
|
|
|
- assertEquals("E100", m.getPayerInfo().getIdentity());
|
|
|
- // DB 回写 payer_uid 恒为企业自身 UID
|
|
|
+ assertEquals("2088111122223333", m.getPayerInfo().getIdentity());
|
|
|
+ assertEquals("ALIPAY_USER_ID", m.getPayerInfo().getIdentityType());
|
|
|
+ // DB 回写 payer_uid 恒为授权主体 UID + 冗余服务商(client 解析依据)
|
|
|
ArgumentCaptor<BatchOrderEntity> order = ArgumentCaptor.forClass(BatchOrderEntity.class);
|
|
|
verify(batchOrderMapper).insert(order.capture());
|
|
|
- assertEquals("E100", order.getValue().getPayerUid());
|
|
|
+ assertEquals("2088111122223333", order.getValue().getPayerUid());
|
|
|
+ assertEquals(1L, order.getValue().getServiceProviderId());
|
|
|
}
|
|
|
|
|
|
@Test
|
|
|
@@ -997,7 +632,7 @@ class AlipayBatchPayServiceTest {
|
|
|
when(batchAuthorizeMapper.selectOne(any())).thenReturn(null);
|
|
|
|
|
|
BatchCreateDTO dto = new BatchCreateDTO();
|
|
|
- dto.setEnterpriseId("E100");
|
|
|
+ dto.setParticipantId("2088111122223333");
|
|
|
dto.setOutBatchNo("B1");
|
|
|
dto.setOrderTitle("t");
|
|
|
BatchCreateDTO.BatchDetailDTO detail = new BatchCreateDTO.BatchDetailDTO();
|
|
|
@@ -1025,11 +660,12 @@ class AlipayBatchPayServiceTest {
|
|
|
when(alipayClient.certificateExecute(any(AlipayFundBatchCreateRequest.class))).thenReturn(resp);
|
|
|
BatchAuthorizeEntity authed = new BatchAuthorizeEntity();
|
|
|
authed.setStatus("NORMAL");
|
|
|
+ authed.setServiceProviderId(1L);
|
|
|
authed.setAgreementNo("AGMT001");
|
|
|
when(batchAuthorizeMapper.selectOne(any())).thenReturn(authed);
|
|
|
|
|
|
BatchCreateDTO dto = new BatchCreateDTO();
|
|
|
- dto.setEnterpriseId("E100");
|
|
|
+ dto.setParticipantId("2088111122223333");
|
|
|
dto.setOutBatchNo("B1");
|
|
|
dto.setOrderTitle("t");
|
|
|
BatchCreateDTO.BatchDetailDTO detail = new BatchCreateDTO.BatchDetailDTO();
|
|
|
@@ -1046,55 +682,17 @@ class AlipayBatchPayServiceTest {
|
|
|
verify(alipayClient).certificateExecute(any(AlipayFundBatchCreateRequest.class));
|
|
|
}
|
|
|
|
|
|
- @Test
|
|
|
- void batchCreate_authorizedIdentity_proceedsWithIdentityAsPayer() throws AlipayApiException {
|
|
|
- // 反馈轮 4/5 协同: identity 有值时预检与付款方均用 identity(而非 enterprise_id)
|
|
|
- EnterpriseEntity ent = new EnterpriseEntity();
|
|
|
- ent.setEnterpriseId("E100");
|
|
|
- ent.setIdentity("2088IDENTITY");
|
|
|
- ent.setIdentityType("ALIPAY_OPEN_ID");
|
|
|
- when(enterpriseMapper.selectByEnterpriseIdIgnoreTenant("E100")).thenReturn(ent);
|
|
|
- AlipayFundBatchCreateResponse resp = new AlipayFundBatchCreateResponse();
|
|
|
- resp.setOutBatchNo("B1");
|
|
|
- when(alipayClient.certificateExecute(any(AlipayFundBatchCreateRequest.class))).thenReturn(resp);
|
|
|
- BatchAuthorizeEntity authed = new BatchAuthorizeEntity();
|
|
|
- authed.setStatus("AUTHED");
|
|
|
- authed.setAgreementNo("AGMT001");
|
|
|
- when(batchAuthorizeMapper.selectOne(any())).thenReturn(authed);
|
|
|
-
|
|
|
- BatchCreateDTO dto = new BatchCreateDTO();
|
|
|
- dto.setEnterpriseId("E100");
|
|
|
- dto.setOutBatchNo("B1");
|
|
|
- dto.setOrderTitle("t");
|
|
|
- BatchCreateDTO.BatchDetailDTO detail = new BatchCreateDTO.BatchDetailDTO();
|
|
|
- detail.setOutBizNo("D1");
|
|
|
- detail.setAmount(new BigDecimal("10"));
|
|
|
- detail.setPayeeIdentity("a@b.com");
|
|
|
- detail.setPayeeIdentityType("ALIPAY_LOGON_ID");
|
|
|
- detail.setPayeeName("张三");
|
|
|
- dto.setDetails(List.of(detail));
|
|
|
-
|
|
|
- Map<String, Object> result = service.batchCreate(dto);
|
|
|
-
|
|
|
- assertEquals("B1", result.get("out_batch_no"));
|
|
|
- ArgumentCaptor<AlipayFundBatchCreateRequest> cap = ArgumentCaptor.forClass(AlipayFundBatchCreateRequest.class);
|
|
|
- verify(alipayClient).certificateExecute(cap.capture());
|
|
|
- AlipayFundBatchCreateModel m = (AlipayFundBatchCreateModel) cap.getValue().getBizModel();
|
|
|
- assertEquals("2088IDENTITY", m.getPayerInfo().getIdentity());
|
|
|
- assertEquals("ALIPAY_OPEN_ID", m.getPayerInfo().getIdentityType());
|
|
|
- }
|
|
|
-
|
|
|
// ==================== M3: batchClose INIT/WAIT_PAY 守卫 ====================
|
|
|
|
|
|
@Test
|
|
|
void batchClose_nonInitStatus_throwsBusinessException() throws AlipayApiException {
|
|
|
BatchOrderEntity order = new BatchOrderEntity();
|
|
|
- order.setEnterpriseId("E100");
|
|
|
+ order.setServiceProviderId(1L);
|
|
|
order.setOutBatchNo("B1");
|
|
|
order.setStatus("SUCCESS");
|
|
|
when(batchOrderMapper.selectOne(any())).thenReturn(order);
|
|
|
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.batchClose("E100", "B1"));
|
|
|
+ BusinessException ex = assertThrows(BusinessException.class, () -> service.batchClose("B1"));
|
|
|
|
|
|
assertEquals(400, ex.getCode());
|
|
|
assertTrue(ex.getMessage().contains("可关闭"), ex.getMessage());
|
|
|
@@ -1105,7 +703,7 @@ class AlipayBatchPayServiceTest {
|
|
|
void batchClose_waitPayStatus_callsCloseApi() throws AlipayApiException {
|
|
|
// WAIT_PAY=等待支付(render 过链接未支付)应可关闭(用户实测: 本地被同步成 WAIT_PAY 后关闭按钮消失)
|
|
|
BatchOrderEntity order = new BatchOrderEntity();
|
|
|
- order.setEnterpriseId("E100");
|
|
|
+ order.setServiceProviderId(1L);
|
|
|
order.setOutBatchNo("B1");
|
|
|
order.setBatchTransId("BT1");
|
|
|
order.setStatus("WAIT_PAY");
|
|
|
@@ -1113,7 +711,7 @@ class AlipayBatchPayServiceTest {
|
|
|
when(alipayClient.certificateExecute(any(AlipayFundBatchCloseRequest.class)))
|
|
|
.thenReturn(new AlipayFundBatchCloseResponse());
|
|
|
|
|
|
- Map<String, String> result = service.batchClose("E100", "B1");
|
|
|
+ Map<String, String> result = service.batchClose("B1");
|
|
|
|
|
|
assertEquals("DISUSE", result.get("status"));
|
|
|
verify(alipayClient).certificateExecute(any(AlipayFundBatchCloseRequest.class));
|
|
|
@@ -1124,7 +722,7 @@ class AlipayBatchPayServiceTest {
|
|
|
// 关闭失败(支付宝侧批次已不可关闭,用户实测 BATCH_ORDER_STATUS_INVALID)→
|
|
|
// detail.query 回写真实状态到本地,避免残留 INIT 让用户反复点关闭
|
|
|
BatchOrderEntity order = new BatchOrderEntity();
|
|
|
- order.setEnterpriseId("E100");
|
|
|
+ order.setServiceProviderId(1L);
|
|
|
order.setOutBatchNo("B1");
|
|
|
order.setBatchTransId("BT1");
|
|
|
order.setStatus("INIT");
|
|
|
@@ -1139,7 +737,7 @@ class AlipayBatchPayServiceTest {
|
|
|
queryResp.setBatchStatus("INVALID");
|
|
|
when(alipayClient.certificateExecute(any(AlipayFundBatchDetailQueryRequest.class))).thenReturn(queryResp);
|
|
|
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.batchClose("E100", "B1"));
|
|
|
+ BusinessException ex = assertThrows(BusinessException.class, () -> service.batchClose("B1"));
|
|
|
|
|
|
assertEquals(400, ex.getCode());
|
|
|
assertTrue(ex.getMessage().contains("BATCH_ORDER_STATUS_INVALID"), ex.getMessage());
|
|
|
@@ -1152,7 +750,7 @@ class AlipayBatchPayServiceTest {
|
|
|
void renderPay_waitPayStatus_regeneratesPayUrl() throws AlipayApiException {
|
|
|
// WAIT_PAY=等待支付(render 过链接未支付)应可重新生成支付链接(用户实测: 不能二次发起支付)
|
|
|
BatchOrderEntity order = new BatchOrderEntity();
|
|
|
- order.setEnterpriseId("E100");
|
|
|
+ order.setServiceProviderId(1L);
|
|
|
order.setOutBatchNo("B1");
|
|
|
order.setBatchTransId("BT1");
|
|
|
order.setStatus("WAIT_PAY");
|
|
|
@@ -1162,43 +760,12 @@ class AlipayBatchPayServiceTest {
|
|
|
resp.setInitializeCode("https://p.tb.cn/_2PG4jfHMVvg9vqBUdtx1HZ");
|
|
|
when(alipayClient.certificateExecute(any(AlipayFundTransRenderPayRequest.class))).thenReturn(resp);
|
|
|
|
|
|
- Map<String, String> result = service.renderPay("E100", "B1");
|
|
|
+ Map<String, String> result = service.renderPay("B1");
|
|
|
|
|
|
assertEquals("https://p.tb.cn/_2PG4jfHMVvg9vqBUdtx1HZ", result.get("pay_url"));
|
|
|
verify(batchOrderMapper).updateById(order);
|
|
|
}
|
|
|
|
|
|
- // ==================== 二轮复核修复: 空 enterpriseId 拒绝(条件式 eq 的空值漏洞) ====================
|
|
|
-
|
|
|
- @Test
|
|
|
- void renderPay_blankEnterpriseId_throws() {
|
|
|
- // enterprise_id 为空必须拒绝而非静默跳过租户过滤(防御 /pay body 零校验路径)
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.renderPay("", "B1"));
|
|
|
- assertEquals(400, ex.getCode());
|
|
|
- verify(batchOrderMapper, never()).selectOne(any());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void batchQuery_nullEnterpriseId_throws() {
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.batchQuery(null, "B1"));
|
|
|
- assertEquals(400, ex.getCode());
|
|
|
- verify(batchOrderMapper, never()).selectOne(any());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void batchClose_blankEnterpriseId_throws() {
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.batchClose(" ", "B1"));
|
|
|
- assertEquals(400, ex.getCode());
|
|
|
- verify(batchOrderMapper, never()).selectOne(any());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void batchDetail_nullEnterpriseId_throws() {
|
|
|
- BusinessException ex = assertThrows(BusinessException.class, () -> service.batchDetail(null, "B1", 1, 20));
|
|
|
- assertEquals(400, ex.getCode());
|
|
|
- verify(batchOrderMapper, never()).selectOne(any());
|
|
|
- }
|
|
|
-
|
|
|
// ==================== 二轮复核修复: parseTimeFilter 边界精确断言 ====================
|
|
|
|
|
|
@Test
|
|
|
@@ -1221,4 +788,125 @@ class AlipayBatchPayServiceTest {
|
|
|
assertEquals(OffsetDateTime.parse("2026-08-25T10:30:00+08:00"),
|
|
|
AlipayBatchPayService.parseTimeFilter("2026-08-25 10:30", true));
|
|
|
}
|
|
|
+
|
|
|
+ // ==================== 个人账号(LOGON_ID): 制单付款方映射(扫码授权获取 2088 userId) ====================
|
|
|
+
|
|
|
+ private static BatchAuthorizeEntity authed(String idType, String alipayUserId) {
|
|
|
+ BatchAuthorizeEntity e = new BatchAuthorizeEntity();
|
|
|
+ e.setParticipantIdType(idType);
|
|
|
+ e.setAlipayUserId(alipayUserId);
|
|
|
+ return e;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void resolvePayerIdentity_logonId_withUserId_mapsToUserId() {
|
|
|
+ // LOGON_ID(手机号/邮箱)→ 制单时用扫码授权获取的 2088 user_id + ALIPAY_USER_ID
|
|
|
+ // (制单接口不接受 LOGON_ID,用户实证;本应用未开通 open_id 能力,故用 user_id)
|
|
|
+ Map<String, String> r = AlipayBatchPayService.resolvePayerIdentity(authed("ALIPAY_LOGON_ID", "2088122583917741"), "18812345678");
|
|
|
+ assertEquals("2088122583917741", r.get("identity"));
|
|
|
+ assertEquals("ALIPAY_USER_ID", r.get("identity_type"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void resolvePayerIdentity_logonId_withoutUserId_rejected() {
|
|
|
+ // 未获取支付宝ID → 本地拦截(提示先获取),空串存量脏数据同样拦截
|
|
|
+ BusinessException e = assertThrows(BusinessException.class,
|
|
|
+ () -> AlipayBatchPayService.resolvePayerIdentity(authed("ALIPAY_LOGON_ID", null), "18812345678"));
|
|
|
+ assertEquals(400, e.getCode());
|
|
|
+ assertTrue(e.getMessage().contains("支付宝ID"), e.getMessage());
|
|
|
+ assertThrows(BusinessException.class,
|
|
|
+ () -> AlipayBatchPayService.resolvePayerIdentity(authed("ALIPAY_LOGON_ID", " "), "18812345678"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void resolvePayerIdentity_userId_mapsToUid() {
|
|
|
+ Map<String, String> r = AlipayBatchPayService.resolvePayerIdentity(authed("ALIPAY_USER_ID", null), "2088111122223333");
|
|
|
+ assertEquals("2088111122223333", r.get("identity"));
|
|
|
+ assertEquals("ALIPAY_USER_ID", r.get("identity_type"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void resolvePayerIdentity_openIdType_mapsToParticipantId() {
|
|
|
+ Map<String, String> r = AlipayBatchPayService.resolvePayerIdentity(authed("ALIPAY_OPEN_ID", null), "openid-abc");
|
|
|
+ assertEquals("openid-abc", r.get("identity"));
|
|
|
+ assertEquals("ALIPAY_OPEN_ID", r.get("identity_type"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void resolvePayerIdentity_nullIdType_defaultsToUserId() {
|
|
|
+ // 存量记录 participant_id_type 为空 → 按支付宝账号ID处理(与旧行为一致)
|
|
|
+ Map<String, String> r = AlipayBatchPayService.resolvePayerIdentity(authed(null, null), "2088111122223333");
|
|
|
+ assertEquals("2088111122223333", r.get("identity"));
|
|
|
+ assertEquals("ALIPAY_USER_ID", r.get("identity_type"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void batchCreate_logonIdWithoutUserId_throwsFriendlyMessage() throws AlipayApiException {
|
|
|
+ // 全链路: LOGON_ID 主体未获取支付宝ID → 制单被本地拦截,不调支付宝
|
|
|
+ BatchAuthorizeEntity authed = new BatchAuthorizeEntity();
|
|
|
+ authed.setStatus("AUTHED");
|
|
|
+ authed.setParticipantIdType("ALIPAY_LOGON_ID");
|
|
|
+ authed.setServiceProviderId(1L);
|
|
|
+ authed.setAgreementNo("AGMT001");
|
|
|
+ when(batchAuthorizeMapper.selectOne(any())).thenReturn(authed);
|
|
|
+
|
|
|
+ BatchCreateDTO dto = new BatchCreateDTO();
|
|
|
+ dto.setParticipantId("18812345678");
|
|
|
+ dto.setOutBatchNo("B1");
|
|
|
+ dto.setOrderTitle("t");
|
|
|
+ BatchCreateDTO.BatchDetailDTO detail = new BatchCreateDTO.BatchDetailDTO();
|
|
|
+ detail.setOutBizNo("D1");
|
|
|
+ detail.setAmount(new BigDecimal("10"));
|
|
|
+ detail.setPayeeIdentity("a@b.com");
|
|
|
+ detail.setPayeeIdentityType("ALIPAY_LOGON_ID");
|
|
|
+ detail.setPayeeName("张三");
|
|
|
+ dto.setDetails(List.of(detail));
|
|
|
+
|
|
|
+ BusinessException ex = assertThrows(BusinessException.class, () -> service.batchCreate(dto));
|
|
|
+
|
|
|
+ assertEquals(400, ex.getCode());
|
|
|
+ assertTrue(ex.getMessage().contains("支付宝ID"), ex.getMessage());
|
|
|
+ verify(alipayClient, never()).certificateExecute(any());
|
|
|
+ verify(batchOrderMapper, never()).insert(any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void batchCreate_logonIdWithUserId_usesUserIdIdentity() throws AlipayApiException {
|
|
|
+ // 全链路: LOGON_ID 主体已获取支付宝ID → 付款方 identity=2088 user_id + ALIPAY_USER_ID
|
|
|
+ AlipayFundBatchCreateResponse resp = new AlipayFundBatchCreateResponse();
|
|
|
+ resp.setOutBatchNo("B1");
|
|
|
+ when(alipayClient.certificateExecute(any(AlipayFundBatchCreateRequest.class))).thenReturn(resp);
|
|
|
+ BatchAuthorizeEntity authed = new BatchAuthorizeEntity();
|
|
|
+ authed.setStatus("AUTHED");
|
|
|
+ authed.setParticipantIdType("ALIPAY_LOGON_ID");
|
|
|
+ authed.setAlipayUserId("2088122583917741");
|
|
|
+ authed.setServiceProviderId(1L);
|
|
|
+ authed.setAgreementNo("AGMT001");
|
|
|
+ when(batchAuthorizeMapper.selectOne(any())).thenReturn(authed);
|
|
|
+
|
|
|
+ BatchCreateDTO dto = new BatchCreateDTO();
|
|
|
+ dto.setParticipantId("18812345678");
|
|
|
+ dto.setOutBatchNo("B1");
|
|
|
+ dto.setOrderTitle("t");
|
|
|
+ BatchCreateDTO.BatchDetailDTO detail = new BatchCreateDTO.BatchDetailDTO();
|
|
|
+ detail.setOutBizNo("D1");
|
|
|
+ detail.setAmount(new BigDecimal("10"));
|
|
|
+ detail.setPayeeIdentity("a@b.com");
|
|
|
+ detail.setPayeeIdentityType("ALIPAY_LOGON_ID");
|
|
|
+ detail.setPayeeName("张三");
|
|
|
+ dto.setDetails(List.of(detail));
|
|
|
+
|
|
|
+ Map<String, Object> result = service.batchCreate(dto);
|
|
|
+
|
|
|
+ assertEquals("B1", result.get("out_batch_no"));
|
|
|
+ ArgumentCaptor<AlipayFundBatchCreateRequest> cap = ArgumentCaptor.forClass(AlipayFundBatchCreateRequest.class);
|
|
|
+ verify(alipayClient).certificateExecute(cap.capture());
|
|
|
+ AlipayFundBatchCreateModel m = (AlipayFundBatchCreateModel) cap.getValue().getBizModel();
|
|
|
+ assertEquals("2088122583917741", m.getPayerInfo().getIdentity());
|
|
|
+ assertEquals("ALIPAY_USER_ID", m.getPayerInfo().getIdentityType());
|
|
|
+ // DB 回写 payer_uid 恒为表单选择的主体账号(非 2088,与列表筛选口径一致)
|
|
|
+ ArgumentCaptor<BatchOrderEntity> order = ArgumentCaptor.forClass(BatchOrderEntity.class);
|
|
|
+ verify(batchOrderMapper).insert(order.capture());
|
|
|
+ assertEquals("18812345678", order.getValue().getPayerUid());
|
|
|
+ }
|
|
|
}
|