|
@@ -6,8 +6,12 @@ import com.alipay.api.domain.AlipayFundAuthorizeUniApplyModel;
|
|
|
import com.alipay.api.domain.AlipayFundAuthorizeUniQueryModel;
|
|
import com.alipay.api.domain.AlipayFundAuthorizeUniQueryModel;
|
|
|
import com.alipay.api.request.AlipayFundAuthorizeUniApplyRequest;
|
|
import com.alipay.api.request.AlipayFundAuthorizeUniApplyRequest;
|
|
|
import com.alipay.api.request.AlipayFundAuthorizeUniQueryRequest;
|
|
import com.alipay.api.request.AlipayFundAuthorizeUniQueryRequest;
|
|
|
|
|
+import com.alipay.api.request.AlipaySystemOauthTokenRequest;
|
|
|
|
|
+import com.alipay.api.request.AlipayUserInfoShareRequest;
|
|
|
import com.alipay.api.response.AlipayFundAuthorizeUniApplyResponse;
|
|
import com.alipay.api.response.AlipayFundAuthorizeUniApplyResponse;
|
|
|
import com.alipay.api.response.AlipayFundAuthorizeUniQueryResponse;
|
|
import com.alipay.api.response.AlipayFundAuthorizeUniQueryResponse;
|
|
|
|
|
+import com.alipay.api.response.AlipaySystemOauthTokenResponse;
|
|
|
|
|
+import com.alipay.api.response.AlipayUserInfoShareResponse;
|
|
|
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
|
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
|
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
@@ -409,4 +413,190 @@ class BatchSubjectServiceTest {
|
|
|
assertEquals(1, result.getList().size());
|
|
assertEquals(1, result.getList().size());
|
|
|
assertEquals("2088111122223333", result.getList().get(0).getParticipantId());
|
|
assertEquals("2088111122223333", result.getList().get(0).getParticipantId());
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== openid 授权(个人账号获取 OpenID 制单) ====================
|
|
|
|
|
+
|
|
|
|
|
+ /** openid 用例公共: AUTHED + LOGON_ID 本地授权记录(selectById 与 selectOne 均返回) */
|
|
|
|
|
+ private void mockLogonAuthorizedRecord() {
|
|
|
|
|
+ BatchAuthorizeEntity rec = new BatchAuthorizeEntity();
|
|
|
|
|
+ rec.setId(6L);
|
|
|
|
|
+ rec.setOutBizNo("A2");
|
|
|
|
|
+ rec.setServiceProviderId(1L);
|
|
|
|
|
+ rec.setParticipantId("18812345678");
|
|
|
|
|
+ rec.setParticipantIdType("ALIPAY_LOGON_ID");
|
|
|
|
|
+ rec.setStatus("AUTHED");
|
|
|
|
|
+ // lenient: 各用例只触发其一(openIdAuthorizeUrl 用 selectById,openIdCallback 用 selectOne)
|
|
|
|
|
+ lenient().when(batchAuthorizeMapper.selectById(6L)).thenReturn(rec);
|
|
|
|
|
+ lenient().when(batchAuthorizeMapper.selectOne(any())).thenReturn(rec);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void openIdAuthorizeUrl_logonId_returnsOAuthUrl() {
|
|
|
|
|
+ mockLogonAuthorizedRecord();
|
|
|
|
|
+ when(alipayClientFactory.getAppIdByProvider(1L, "BATCH_PAY")).thenReturn("2021000000000001");
|
|
|
|
|
+
|
|
|
|
|
+ // oauthRedirectUri = 支付宝授权回调白名单地址(aplipay/auth 复用),拼 out_biz_no 后整段编码
|
|
|
|
|
+ String url = service.openIdAuthorizeUrl(6L, "https://qcsj88888.com/api/v1/payment/aplipay/auth", "http://localhost:5180");
|
|
|
|
|
+
|
|
|
|
|
+ assertTrue(url.startsWith("https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id=2021000000000001"));
|
|
|
|
|
+ assertTrue(url.contains("scope=auth_user"));
|
|
|
|
|
+ assertTrue(url.contains("redirect_uri=" + java.net.URLEncoder.encode(
|
|
|
|
|
+ "https://qcsj88888.com/api/v1/payment/aplipay/auth?out_biz_no=A2",
|
|
|
|
|
+ java.nio.charset.StandardCharsets.UTF_8)));
|
|
|
|
|
+ verify(alipayClientFactory).getAppIdByProvider(1L, "BATCH_PAY");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void openIdAuthorizeUrl_frontUrlBlank_rejected() {
|
|
|
|
|
+ BusinessException e = assertThrows(BusinessException.class,
|
|
|
|
|
+ () -> service.openIdAuthorizeUrl(6L, "https://qcsj88888.com/api/v1/payment/aplipay/auth", ""));
|
|
|
|
|
+ assertEquals(400, e.getCode());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void openIdAuthorizeUrl_redirectUriBlank_rejected() {
|
|
|
|
|
+ BusinessException e = assertThrows(BusinessException.class,
|
|
|
|
|
+ () -> service.openIdAuthorizeUrl(6L, "", "http://localhost:5180"));
|
|
|
|
|
+ assertEquals(400, e.getCode());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void openIdAuthorizeUrl_nonLogonId_rejected() {
|
|
|
|
|
+ BatchAuthorizeEntity rec = new BatchAuthorizeEntity();
|
|
|
|
|
+ rec.setId(7L);
|
|
|
|
|
+ rec.setParticipantIdType("ALIPAY_USER_ID");
|
|
|
|
|
+ rec.setStatus("AUTHED");
|
|
|
|
|
+ when(batchAuthorizeMapper.selectById(7L)).thenReturn(rec);
|
|
|
|
|
+
|
|
|
|
|
+ BusinessException e = assertThrows(BusinessException.class,
|
|
|
|
|
+ () -> service.openIdAuthorizeUrl(7L, "https://qcsj88888.com/api/v1/payment/aplipay/auth", "http://localhost:5180"));
|
|
|
|
|
+ assertEquals(400, e.getCode());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void openIdAuthorizeUrl_notAuthed_rejected() {
|
|
|
|
|
+ BatchAuthorizeEntity rec = new BatchAuthorizeEntity();
|
|
|
|
|
+ rec.setId(8L);
|
|
|
|
|
+ rec.setParticipantIdType("ALIPAY_LOGON_ID");
|
|
|
|
|
+ rec.setStatus("AUTHING");
|
|
|
|
|
+ when(batchAuthorizeMapper.selectById(8L)).thenReturn(rec);
|
|
|
|
|
+
|
|
|
|
|
+ BusinessException e = assertThrows(BusinessException.class,
|
|
|
|
|
+ () -> service.openIdAuthorizeUrl(8L, "https://qcsj88888.com/api/v1/payment/aplipay/auth", "http://localhost:5180"));
|
|
|
|
|
+ assertEquals(400, e.getCode());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void openIdCallback_tokenAndInfoShare_persistsUserId() throws AlipayApiException {
|
|
|
|
|
+ mockLogonAuthorizedRecord();
|
|
|
|
|
+ AlipaySystemOauthTokenResponse tokenResp = new AlipaySystemOauthTokenResponse();
|
|
|
|
|
+ tokenResp.setAccessToken("at-123");
|
|
|
|
|
+ when(alipayClient.certificateExecute(any(AlipaySystemOauthTokenRequest.class))).thenReturn(tokenResp);
|
|
|
|
|
+ AlipayUserInfoShareResponse shareResp = new AlipayUserInfoShareResponse();
|
|
|
|
|
+ shareResp.setUserId("2088122583917741");
|
|
|
|
|
+ when(alipayClient.certificateExecute(any(AlipayUserInfoShareRequest.class), eq("at-123"))).thenReturn(shareResp);
|
|
|
|
|
+
|
|
|
|
|
+ String userId = service.openIdCallback("auth-code-1", "A2");
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals("2088122583917741", userId);
|
|
|
|
|
+ // oauth.token: auth_code + grant_type=authorization_code 换令牌
|
|
|
|
|
+ ArgumentCaptor<AlipaySystemOauthTokenRequest> cap = ArgumentCaptor.forClass(AlipaySystemOauthTokenRequest.class);
|
|
|
|
|
+ verify(alipayClient).certificateExecute(cap.capture());
|
|
|
|
|
+ assertEquals("auth-code-1", cap.getValue().getCode());
|
|
|
|
|
+ assertEquals("authorization_code", cap.getValue().getGrantType());
|
|
|
|
|
+ // user.info.share: access_token 走 execute 第二参数(非 bizModel)
|
|
|
|
|
+ verify(alipayClient).certificateExecute(any(AlipayUserInfoShareRequest.class), eq("at-123"));
|
|
|
|
|
+ // 支付宝账号ID(2088)落库 — 本应用未开通 open_id 能力,制单身份用 user_id(实证)
|
|
|
|
|
+ ArgumentCaptor<BatchAuthorizeEntity> ent = ArgumentCaptor.forClass(BatchAuthorizeEntity.class);
|
|
|
|
|
+ verify(batchAuthorizeMapper).updateById(ent.capture());
|
|
|
|
|
+ assertEquals("2088122583917741", ent.getValue().getAlipayUserId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void openIdCallback_tokenFailed_rejected() throws AlipayApiException {
|
|
|
|
|
+ mockLogonAuthorizedRecord();
|
|
|
|
|
+ AlipaySystemOauthTokenResponse tokenResp = new AlipaySystemOauthTokenResponse();
|
|
|
|
|
+ tokenResp.setCode("40004");
|
|
|
|
|
+ tokenResp.setMsg("无效授权码");
|
|
|
|
|
+ when(alipayClient.certificateExecute(any(AlipaySystemOauthTokenRequest.class))).thenReturn(tokenResp);
|
|
|
|
|
+
|
|
|
|
|
+ BusinessException e = assertThrows(BusinessException.class,
|
|
|
|
|
+ () -> service.openIdCallback("bad-code", "A2"));
|
|
|
|
|
+ assertEquals(400, e.getCode());
|
|
|
|
|
+ verify(batchAuthorizeMapper, never()).updateById(any());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void openIdCallback_infoShareFailed_rejected() throws AlipayApiException {
|
|
|
|
|
+ mockLogonAuthorizedRecord();
|
|
|
|
|
+ AlipaySystemOauthTokenResponse tokenResp = new AlipaySystemOauthTokenResponse();
|
|
|
|
|
+ tokenResp.setAccessToken("at-123");
|
|
|
|
|
+ when(alipayClient.certificateExecute(any(AlipaySystemOauthTokenRequest.class))).thenReturn(tokenResp);
|
|
|
|
|
+ AlipayUserInfoShareResponse shareResp = new AlipayUserInfoShareResponse();
|
|
|
|
|
+ shareResp.setCode("40004");
|
|
|
|
|
+ shareResp.setMsg("授权令牌失效");
|
|
|
|
|
+ when(alipayClient.certificateExecute(any(AlipayUserInfoShareRequest.class), eq("at-123"))).thenReturn(shareResp);
|
|
|
|
|
+
|
|
|
|
|
+ assertThrows(BusinessException.class, () -> service.openIdCallback("auth-code-1", "A2"));
|
|
|
|
|
+ verify(batchAuthorizeMapper, never()).updateById(any());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void openIdCallback_userIdFallsBackToTokenResponse() throws AlipayApiException {
|
|
|
|
|
+ mockLogonAuthorizedRecord();
|
|
|
|
|
+ AlipaySystemOauthTokenResponse tokenResp = new AlipaySystemOauthTokenResponse();
|
|
|
|
|
+ tokenResp.setAccessToken("at-123");
|
|
|
|
|
+ tokenResp.setUserId("2088-token-uid");
|
|
|
|
|
+ when(alipayClient.certificateExecute(any(AlipaySystemOauthTokenRequest.class))).thenReturn(tokenResp);
|
|
|
|
|
+ // info.share 成功但未返回 user_id → 回退 oauth.token 响应中的 user_id
|
|
|
|
|
+ when(alipayClient.certificateExecute(any(AlipayUserInfoShareRequest.class), eq("at-123"))).thenReturn(new AlipayUserInfoShareResponse());
|
|
|
|
|
+
|
|
|
|
|
+ String userId = service.openIdCallback("auth-code-1", "A2");
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals("2088-token-uid", userId);
|
|
|
|
|
+ ArgumentCaptor<BatchAuthorizeEntity> ent = ArgumentCaptor.forClass(BatchAuthorizeEntity.class);
|
|
|
|
|
+ verify(batchAuthorizeMapper).updateById(ent.capture());
|
|
|
|
|
+ assertEquals("2088-token-uid", ent.getValue().getAlipayUserId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void openIdCallback_userIdFallsBackToTokenAlipayUserId() throws AlipayApiException {
|
|
|
|
|
+ mockLogonAuthorizedRecord();
|
|
|
|
|
+ AlipaySystemOauthTokenResponse tokenResp = new AlipaySystemOauthTokenResponse();
|
|
|
|
|
+ tokenResp.setAccessToken("at-123");
|
|
|
|
|
+ tokenResp.setAlipayUserId("2088-alipay-uid");
|
|
|
|
|
+ when(alipayClient.certificateExecute(any(AlipaySystemOauthTokenRequest.class))).thenReturn(tokenResp);
|
|
|
|
|
+ when(alipayClient.certificateExecute(any(AlipayUserInfoShareRequest.class), eq("at-123"))).thenReturn(new AlipayUserInfoShareResponse());
|
|
|
|
|
+
|
|
|
|
|
+ String userId = service.openIdCallback("auth-code-1", "A2");
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals("2088-alipay-uid", userId);
|
|
|
|
|
+ ArgumentCaptor<BatchAuthorizeEntity> ent = ArgumentCaptor.forClass(BatchAuthorizeEntity.class);
|
|
|
|
|
+ verify(batchAuthorizeMapper).updateById(ent.capture());
|
|
|
|
|
+ assertEquals("2088-alipay-uid", ent.getValue().getAlipayUserId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void openIdCallback_noUserIdAnywhere_rejected() throws AlipayApiException {
|
|
|
|
|
+ mockLogonAuthorizedRecord();
|
|
|
|
|
+ AlipaySystemOauthTokenResponse tokenResp = new AlipaySystemOauthTokenResponse();
|
|
|
|
|
+ tokenResp.setAccessToken("at-123");
|
|
|
|
|
+ when(alipayClient.certificateExecute(any(AlipaySystemOauthTokenRequest.class))).thenReturn(tokenResp);
|
|
|
|
|
+ // info.share 与 oauth.token 均未返回 user_id → 明确报错
|
|
|
|
|
+ when(alipayClient.certificateExecute(any(AlipayUserInfoShareRequest.class), eq("at-123"))).thenReturn(new AlipayUserInfoShareResponse());
|
|
|
|
|
+
|
|
|
|
|
+ BusinessException e = assertThrows(BusinessException.class,
|
|
|
|
|
+ () -> service.openIdCallback("auth-code-1", "A2"));
|
|
|
|
|
+ assertEquals(400, e.getCode());
|
|
|
|
|
+ verify(batchAuthorizeMapper, never()).updateById(any());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void openIdCallback_recordNotFound_rejected() {
|
|
|
|
|
+ when(batchAuthorizeMapper.selectOne(any())).thenReturn(null);
|
|
|
|
|
+
|
|
|
|
|
+ BusinessException e = assertThrows(BusinessException.class,
|
|
|
|
|
+ () -> service.openIdCallback("auth-code-1", "NOPE"));
|
|
|
|
|
+ assertEquals(404, e.getCode());
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|