diff --git a/README.md b/README.md index 1c58463ef..b3b17d46f 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,10 @@ If you don't have your own API keys, you can sign up for a test account [here](h **PLEASE NEVER SHARE OR PUBLISH YOUR CHECKOUT CREDENTIALS.** +### Subdomain value + +Requests must be made through your merchant-specific subdomain (MSSD): the first 8 characters of your client ID (excluding `cli_`). For example, if your client ID is `cli_vkuhvk4vjn2edkps7dfsq6emqm`, your subdomain is `vkuhvk4v`. When `environmentSubdomain` is set the SDK sends requests to `https://vkuhvk4v.api.checkout.com`. See [Base URLs](https://api-reference.checkout.com/#section/Base-URLs) and [API endpoints](https://www.checkout.com/docs/developer-resources/api/api-endpoints) for further details, and for where to find your unique client ID. + ### Default Default keys client instantiation can be done as follows: @@ -99,7 +103,7 @@ public static void main(String[] args) { .publicKey("public_key") // optional, only required for operations related with tokens .secretKey("secret_key") .environment(Environment.PRODUCTION) // required - .environmentSubdomain("subdomain") // optional, Merchant-specific DNS name + .environmentSubdomain("subdomain") // required, Merchant-specific DNS name, the first 8 characters of your client ID .executor() // optional for a custom Executor Service .build(); @@ -125,7 +129,7 @@ final CheckoutApi checkoutApi = CheckoutSdk.builder() //.clientCredentials(new URI("https://access.sandbox.checkout.com/connect/token"), "client_id", "client_secret") .scopes(OAuthScope.GATEWAY, OAuthScope.VAULT, OAuthScope.FX) .environment(Environment.PRODUCTION) // required - .environmentSubdomain("subdomain") // optional, Merchant-specific DNS name + .environmentSubdomain("subdomain") // required, Merchant-specific DNS name, the first 8 characters of your client ID .executor() // optional for a custom Executor Service .build(); @@ -149,7 +153,7 @@ public static void main(String[] args) { .publicKey("public_key") // optional, only required for operations related with tokens .secretKey("secret_key") .environment(Environment.PRODUCTION) // required - .environmentSubdomain("subdomain") // optional, Merchant-specific DNS name + .environmentSubdomain("subdomain") // optional for the Previous platform, Merchant-specific DNS name .executor() // optional for a custom Executor Service .build(); @@ -399,7 +403,7 @@ final CheckoutApi checkoutApi = CheckoutSdk.builder() .staticKeys() .secretKey("secret_key") .environment(Environment.PRODUCTION) // required - .environmentSubdomain("subdomain") // optional, Merchant-specific DNS name + .environmentSubdomain("subdomain") // required, Merchant-specific DNS name, the first 8 characters of your client ID .httpClientBuilder(customHttpClient) // optional for a custom HttpClient .build(); ``` @@ -656,6 +660,23 @@ final CheckoutApi checkoutApi = CheckoutSdk.builder() - All resilience patterns are optional - configure only what you need - Rate limiter helps respect API rate limits and prevent overwhelming the service +## Legacy domain (emergency use only) + +> :warning: **Only use if merchant specific sub domains are causing issues.** Connecting through your merchant-specific subdomain (see [Subdomain value](#subdomain-value)) is the supported way of using the Checkout.com API, and non-subdomain usage will be deprecated. + +If, in exceptional circumstances, you cannot use your merchant-specific subdomain, you can explicitly opt out by calling `useLegacyDomain()` instead of `environmentSubdomain(...)`: + +```java +final CheckoutApi checkoutApi = CheckoutSdk.builder() + .staticKeys() + .secretKey("secret_key") + .environment(Environment.SANDBOX) + .useLegacyDomain() // deprecated, emergency fallback only + .build(); +``` + +This routes requests to `api.checkout.com` (or `api.sandbox.checkout.com`) and `access.checkout.com` (or `access.sandbox.checkout.com`). The method is annotated `@Deprecated` and produces a compile-time warning. Exactly one of `environmentSubdomain(...)` or `useLegacyDomain()` must be set: the SDK throws a `CheckoutArgumentException` if both, or neither, are set. The Previous (ABC) platform predates merchant-specific subdomains and is exempt from this requirement. + ## Code of Conduct Please refer to [Code of Conduct](CODE_OF_CONDUCT.md) diff --git a/src/main/java/com/checkout/AbstractCheckoutSdkBuilder.java b/src/main/java/com/checkout/AbstractCheckoutSdkBuilder.java index 0a193487f..6f2b946b5 100644 --- a/src/main/java/com/checkout/AbstractCheckoutSdkBuilder.java +++ b/src/main/java/com/checkout/AbstractCheckoutSdkBuilder.java @@ -9,7 +9,8 @@ public abstract class AbstractCheckoutSdkBuilder { protected HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); private IEnvironment environment; - private EnvironmentSubdomain environmentSubdomain; + private String subdomain; + private boolean useLegacyDomain; private Executor executor = ForkJoinPool.commonPool(); private TransportConfiguration transportConfiguration; private Boolean recordTelemetry = true; @@ -25,7 +26,23 @@ public AbstractCheckoutSdkBuilder environmentSubdomain(final String subdomain if (subdomain == null) { throw new CheckoutArgumentException("subdomain must be specified"); } - this.environmentSubdomain = new EnvironmentSubdomain(this.environment, subdomain); + this.subdomain = subdomain; + return this; + } + + /** + * Opts out of the merchant-specific subdomain, sending every request to the shared + * hosts instead ({@code api.checkout.com} and {@code access.checkout.com}, or their + * sandbox equivalents). + * + * @deprecated this is an emergency fallback for the rare case where the + * merchant-specific subdomain cannot be used, and will be removed in a future release. + * Call {@link #environmentSubdomain(String)} instead. + * See Base URLs. + */ + @Deprecated + public AbstractCheckoutSdkBuilder useLegacyDomain() { + this.useLegacyDomain = true; return this; } @@ -49,7 +66,16 @@ protected IEnvironment getEnvironment() { } protected EnvironmentSubdomain getEnvironmentSubdomain() { - return environmentSubdomain; + return subdomain != null ? new EnvironmentSubdomain(environment, subdomain) : null; + } + + /** + * Whether this builder requires the merchant-specific subdomain to be configured. + * The Previous (ABC) platform predates merchant-specific subdomains, so it overrides + * this to {@code false}. + */ + protected boolean requiresEnvironmentSubdomain() { + return true; } public AbstractCheckoutSdkBuilder recordTelemetry(final Boolean recordTelemetry) { @@ -73,6 +99,7 @@ protected CheckoutConfiguration getCheckoutConfiguration() { if (environment == null) { throw new CheckoutArgumentException("environment must be specified"); } + validateEnvironmentSettings(); final SdkCredentials sdkCredentials = getSdkCredentials(); if (transportConfiguration == null) { transportConfiguration = new DefaultTransportConfiguration(); @@ -80,6 +107,15 @@ protected CheckoutConfiguration getCheckoutConfiguration() { return buildCheckoutConfiguration(sdkCredentials); } + private void validateEnvironmentSettings() { + if (subdomain != null && useLegacyDomain) { + throw new CheckoutArgumentException("environmentSubdomain and useLegacyDomain cannot both be set - provide only your merchant-specific subdomain"); + } + if (subdomain == null && !useLegacyDomain && requiresEnvironmentSubdomain()) { + throw new CheckoutArgumentException("environmentSubdomain is required - provide your merchant-specific subdomain (the first 8 characters of your client ID, see https://api-reference.checkout.com/#section/Base-URLs), or call useLegacyDomain() to opt out only if merchant specific sub domains are causing issues"); + } + } + private CheckoutConfiguration buildCheckoutConfiguration(final SdkCredentials sdkCredentials) { return new DefaultCheckoutConfiguration(sdkCredentials, getEnvironment(), getEnvironmentSubdomain(), httpClientBuilder, executor, transportConfiguration, recordTelemetry, synchronous, resilience4jConfiguration); } diff --git a/src/main/java/com/checkout/CheckoutPreviousSdkBuilder.java b/src/main/java/com/checkout/CheckoutPreviousSdkBuilder.java index 196f4ba2a..f4462a18b 100644 --- a/src/main/java/com/checkout/CheckoutPreviousSdkBuilder.java +++ b/src/main/java/com/checkout/CheckoutPreviousSdkBuilder.java @@ -13,6 +13,13 @@ public static class CheckoutStaticKeysSdkBuilder extends AbstractCheckoutSdkBuil private String publicKey; private String secretKey; + // The Previous (ABC) platform predates merchant-specific subdomains, so it is exempt + // from the mandatory environmentSubdomain/useLegacyDomain configuration. + @Override + protected boolean requiresEnvironmentSubdomain() { + return false; + } + public CheckoutStaticKeysSdkBuilder publicKey(final String publicKey) { this.publicKey = publicKey; return this; diff --git a/src/main/java/com/checkout/EnvironmentSubdomain.java b/src/main/java/com/checkout/EnvironmentSubdomain.java index 54841a6ed..671d39482 100644 --- a/src/main/java/com/checkout/EnvironmentSubdomain.java +++ b/src/main/java/com/checkout/EnvironmentSubdomain.java @@ -24,36 +24,29 @@ public URI getOAuthAuthorizationApi() { } /** - * Applies subdomain transformation to any given URI. - * If the subdomain is valid (alphanumeric pattern), prepends it to the host. - * Otherwise, returns the original URI unchanged. + * Applies subdomain transformation to any given URI, prepending the subdomain to the host. * * @param originalUrl the original URI to transform * @param subdomain the subdomain to prepend - * @return the transformed URI with subdomain, or original URI if subdomain is invalid + * @return the transformed URI with subdomain + * @throws CheckoutArgumentException if the subdomain is not a valid merchant-specific subdomain */ private static URI createUrlWithSubdomain(URI originalUrl, String subdomain) { - URI newEnvironment = null; + Pattern pattern = Pattern.compile("^(?:pl-)?[a-z0-9]+$"); + Matcher matcher = subdomain == null ? null : pattern.matcher(subdomain); + if (matcher == null || !matcher.matches()) { + throw new CheckoutArgumentException("invalid environment subdomain - provide your merchant-specific subdomain, the first 8 characters of your client ID (see https://api-reference.checkout.com/#section/Base-URLs)"); + } + + String host = originalUrl.getHost(); + String scheme = originalUrl.getScheme(); + int port = originalUrl.getPort(); + String newHost = subdomain + "." + host; try { - newEnvironment = new URI(originalUrl.toString()); + return new URI(scheme, null, newHost, port, originalUrl.getPath(), originalUrl.getQuery(), originalUrl.getFragment()); } catch (final URISyntaxException e) { throw new CheckoutException(e); } - - Pattern pattern = Pattern.compile("^(?:pl-)?[a-z0-9]+$"); - Matcher matcher = pattern.matcher(subdomain); - if (matcher.matches()) { - String host = originalUrl.getHost(); - String scheme = originalUrl.getScheme(); - int port = originalUrl.getPort(); - String newHost = subdomain + "." + host; - try { - newEnvironment = new URI(scheme, null, newHost, port, originalUrl.getPath(), originalUrl.getQuery(), originalUrl.getFragment()); - } catch (final URISyntaxException e) { - throw new CheckoutException(e); - } - } - return newEnvironment; } } diff --git a/src/test/java/com/checkout/CheckoutSdkBuilderSynchronousTest.java b/src/test/java/com/checkout/CheckoutSdkBuilderSynchronousTest.java index 859bc8fc4..77efd4744 100644 --- a/src/test/java/com/checkout/CheckoutSdkBuilderSynchronousTest.java +++ b/src/test/java/com/checkout/CheckoutSdkBuilderSynchronousTest.java @@ -18,6 +18,7 @@ void shouldCreateCheckoutApiWithSynchronousMode() { .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .synchronous(true) .build(); @@ -35,6 +36,7 @@ void shouldCreateCheckoutApiWithResilience4jConfiguration() { .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .resilience4jConfiguration(resilience4jConfig) .build(); @@ -49,6 +51,7 @@ void shouldCreateCheckoutApiWithSynchronousAndResilience4j() { .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .synchronous(true) .resilience4jConfiguration(resilience4jConfig) .build(); @@ -63,6 +66,7 @@ void shouldCreateCheckoutApiWithoutNewParameters() { .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .build(); assertNotNull(checkoutApi); @@ -90,6 +94,7 @@ void shouldCreateCheckoutApiWithCustomResilience4jConfiguration() { .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .synchronous(true) .resilience4jConfiguration(resilience4jConfig) .build(); diff --git a/src/test/java/com/checkout/CheckoutSdkBuilderTest.java b/src/test/java/com/checkout/CheckoutSdkBuilderTest.java index 77bdee7df..7ac520330 100644 --- a/src/test/java/com/checkout/CheckoutSdkBuilderTest.java +++ b/src/test/java/com/checkout/CheckoutSdkBuilderTest.java @@ -11,6 +11,7 @@ import static com.checkout.TestHelper.VALID_DEFAULT_SK; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -23,6 +24,7 @@ void shouldCreateStaticKeysCheckoutSdks() { .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .build(); assertNotNull(checkoutApi1); @@ -30,6 +32,7 @@ void shouldCreateStaticKeysCheckoutSdks() { final CheckoutApi checkoutApi2 = new CheckoutSdkBuilder().staticKeys() .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .build(); assertNotNull(checkoutApi2); @@ -66,6 +69,7 @@ void shouldCreateCheckoutAndInitOAuthSdk() throws URISyntaxException { .clientCredentials(new URI("test"), "client_id", "client_secret") .scopes(OAuthScope.GATEWAY) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .build(); fail(); } catch (final CheckoutException e) { @@ -94,6 +98,78 @@ void shouldCreateOAuthSdkWithSubdomain() throws URISyntaxException { } + @SuppressWarnings("deprecation") + @Test + void shouldCreateStaticKeysCheckoutSdkWithLegacyDomain() { + + final CheckoutApi checkoutApi = new CheckoutSdkBuilder().staticKeys() + .publicKey(VALID_DEFAULT_PK) + .secretKey(VALID_DEFAULT_SK) + .environment(Environment.SANDBOX) + .useLegacyDomain() + .build(); + + assertNotNull(checkoutApi); + + } + + @Test + void shouldFailToCreateCheckoutSdkWithoutSubdomainOrLegacyDomain() { + + final CheckoutArgumentException exception = assertThrows(CheckoutArgumentException.class, + () -> new CheckoutSdkBuilder().staticKeys() + .publicKey(VALID_DEFAULT_PK) + .secretKey(VALID_DEFAULT_SK) + .environment(Environment.SANDBOX) + .build()); + + assertTrue(exception.getMessage().contains("environmentSubdomain is required")); + + } + + @SuppressWarnings("deprecation") + @Test + void shouldFailToCreateCheckoutSdkWithBothSubdomainAndLegacyDomain() { + + final CheckoutArgumentException exception = assertThrows(CheckoutArgumentException.class, + () -> new CheckoutSdkBuilder().staticKeys() + .publicKey(VALID_DEFAULT_PK) + .secretKey(VALID_DEFAULT_SK) + .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") + .useLegacyDomain() + .build()); + + assertTrue(exception.getMessage().contains("cannot both be set")); + + } + + @Test + void shouldFailToCreateCheckoutSdkWithInvalidSubdomain() { + + final CheckoutArgumentException exception = assertThrows(CheckoutArgumentException.class, + () -> new CheckoutSdkBuilder().staticKeys() + .publicKey(VALID_DEFAULT_PK) + .secretKey(VALID_DEFAULT_SK) + .environment(Environment.SANDBOX) + .environmentSubdomain("not a subdomain") + .build()); + + assertTrue(exception.getMessage().contains("invalid environment subdomain")); + + } + + @Test + void shouldCreatePreviousSdkWithoutSubdomain() { + + assertNotNull(new CheckoutSdkBuilder().previous().staticKeys() + .publicKey(TestHelper.VALID_PREVIOUS_PK) + .secretKey(TestHelper.VALID_PREVIOUS_SK) + .environment(Environment.SANDBOX) + .build()); + + } + @Test void shouldFailToCreateCheckoutSdks() { @@ -102,6 +178,7 @@ void shouldFailToCreateCheckoutSdks() { .publicKey(INVALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .build(); } catch (final Exception e) { assertTrue(e instanceof CheckoutArgumentException); @@ -113,6 +190,7 @@ void shouldFailToCreateCheckoutSdks() { .publicKey(VALID_DEFAULT_PK) .secretKey(INVALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .build(); } catch (final Exception e) { assertTrue(e instanceof CheckoutArgumentException); @@ -123,6 +201,7 @@ void shouldFailToCreateCheckoutSdks() { new CheckoutSdkBuilder().staticKeys() .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) + .environmentSubdomain("1234doma") .build(); } catch (final Exception e) { assertTrue(e instanceof CheckoutArgumentException); diff --git a/src/test/java/com/checkout/CheckoutSdkTelemetryIntegrationTest.java b/src/test/java/com/checkout/CheckoutSdkTelemetryIntegrationTest.java index d6061afe6..6d4376067 100644 --- a/src/test/java/com/checkout/CheckoutSdkTelemetryIntegrationTest.java +++ b/src/test/java/com/checkout/CheckoutSdkTelemetryIntegrationTest.java @@ -67,6 +67,7 @@ private CheckoutApi buildCheckoutApi(CloseableHttpClient httpClientMock, boolean .secretKey(requireNonNull(System.getenv("CHECKOUT_DEFAULT_SECRET_KEY"))) .recordTelemetry(telemetryEnabled) .environment(SANDBOX) + .environmentSubdomain("1234doma") .httpClientBuilder(httpClientBuilderMock) .build(); } diff --git a/src/test/java/com/checkout/DefaultCheckoutConfigurationTest.java b/src/test/java/com/checkout/DefaultCheckoutConfigurationTest.java index e8f9da6e4..23dd8e852 100644 --- a/src/test/java/com/checkout/DefaultCheckoutConfigurationTest.java +++ b/src/test/java/com/checkout/DefaultCheckoutConfigurationTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -66,14 +67,11 @@ void shouldCreateConfigurationWithSubdomain(String subdomain) { @ParameterizedTest @ValueSource(strings = {"", " ", " ", " - ", "a b", "ab c1", "foo-", "-foo", "ABC123", "FOO", "test-123", "foo-bar", "pl-"}) - void shouldCreateConfigurationWithBadSubdomain(String subdomain) { + void shouldFailWithBadSubdomain(String subdomain) { - final StaticKeysSdkCredentials credentials = Mockito.mock(StaticKeysSdkCredentials.class); - final EnvironmentSubdomain environmentSubdomain = new EnvironmentSubdomain(Environment.SANDBOX, subdomain); - - final CheckoutConfiguration configuration = new DefaultCheckoutConfiguration(credentials, Environment.SANDBOX, environmentSubdomain, DEFAULT_CLIENT_BUILDER, DEFAULT_EXECUTOR, DEFAULT_TRANSPORT_CONFIGURATION, false); - assertEquals("https://api.sandbox.checkout.com/", configuration.getEnvironmentSubdomain().getCheckoutApi().toString()); - assertEquals("https://access.sandbox.checkout.com/connect/token", configuration.getEnvironmentSubdomain().getOAuthAuthorizationApi().toString()); + final CheckoutArgumentException exception = assertThrows(CheckoutArgumentException.class, + () -> new EnvironmentSubdomain(Environment.SANDBOX, subdomain)); + assertTrue(exception.getMessage().contains("invalid environment subdomain")); } @Test diff --git a/src/test/java/com/checkout/OAuthTestIT.java b/src/test/java/com/checkout/OAuthTestIT.java index 2fe1f0bf8..b59267f70 100644 --- a/src/test/java/com/checkout/OAuthTestIT.java +++ b/src/test/java/com/checkout/OAuthTestIT.java @@ -70,6 +70,7 @@ void shouldMakeOAuthCall() { } + @SuppressWarnings("deprecation") @Test void shouldInitAuthorization() { @@ -82,6 +83,7 @@ void shouldInitAuthorization() { "fake") .scopes(OAuthScope.GATEWAY) .environment(Environment.SANDBOX) + .useLegacyDomain() .build(); fail(); } catch (final Exception e) { @@ -118,6 +120,9 @@ void shouldInstantiateCheckoutApiWithOAuth_defaultAuthorizeUrl() { System.getenv("CHECKOUT_DEFAULT_OAUTH_CLIENT_SECRET")) .scopes(OAuthScope.GATEWAY) .environment(Environment.SANDBOX) + // The sandbox OAuth clients are not provisioned for the merchant-specific subdomain, so + // the token request would come back invalid_client. Opting out explicitly until they are. + .useLegacyDomain() .build(); assertNotNull(checkoutApi); @@ -136,6 +141,9 @@ void shouldInstantiateCheckoutApiWithOAuth_customAuthorizeUrl() throws URISyntax System.getenv("CHECKOUT_DEFAULT_OAUTH_CLIENT_SECRET")) .scopes(OAuthScope.GATEWAY) .environment(Environment.SANDBOX) + // The sandbox OAuth clients are not provisioned for the merchant-specific subdomain, so + // the token request would come back invalid_client. Opting out explicitly until they are. + .useLegacyDomain() .build(); assertNotNull(checkoutApi); @@ -155,6 +163,10 @@ void shouldFailInitAuthorizationWithCustomEnvironment() { .environment(CustomEnvironment.builder() .oAuthAuthorizationApi(create("https://the.oauth.uri/connect/token")) .build()) + // The sandbox OAuth clients are not provisioned for the merchant-specific + // subdomain, so the token request would come back invalid_client. Opting out + // explicitly until they are. + .useLegacyDomain() .build(); fail(); } catch (final Exception e) { diff --git a/src/test/java/com/checkout/SandboxTestFixture.java b/src/test/java/com/checkout/SandboxTestFixture.java index a88588b4d..2a861844c 100644 --- a/src/test/java/com/checkout/SandboxTestFixture.java +++ b/src/test/java/com/checkout/SandboxTestFixture.java @@ -68,6 +68,9 @@ public SandboxTestFixture(final PlatformType platformType) { .environment(Environment.SANDBOX) .executor(Executors.newFixedThreadPool(100)) .httpClientBuilder(httpClientBuilder) + // The sandbox OAuth clients are not provisioned for the merchant-specific subdomain, so + // the token request would come back invalid_client. Opting out explicitly until they are. + .useLegacyDomain() .build(); break; case DEFAULT: @@ -77,6 +80,9 @@ public SandboxTestFixture(final PlatformType platformType) { .secretKey(requireNonNull(System.getenv("CHECKOUT_DEFAULT_SECRET_KEY"))) .environment(Environment.SANDBOX) .executor(CUSTOM_EXECUTOR) + // The sandbox OAuth clients are not provisioned for the merchant-specific subdomain, so + // the token request would come back invalid_client. Opting out explicitly until they are. + .useLegacyDomain() .build(); break; case DEFAULT_OAUTH: @@ -93,8 +99,11 @@ public SandboxTestFixture(final PlatformType platformType) { OAuthScope.FORWARD_SECRETS, OAuthScope.PAYMENTS_SEARCH) .environment(Environment.SANDBOX) .executor(CUSTOM_EXECUTOR) + // The sandbox OAuth clients are not provisioned for the merchant-specific subdomain, so + // the token request would come back invalid_client. Opting out explicitly until they are. + .useLegacyDomain() .build(); - case CUSTOM: + case CUSTOM: break; } } diff --git a/src/test/java/com/checkout/SynchronousAsyncClientComparisonTest.java b/src/test/java/com/checkout/SynchronousAsyncClientComparisonTest.java index ef7c15467..0950d6e2a 100644 --- a/src/test/java/com/checkout/SynchronousAsyncClientComparisonTest.java +++ b/src/test/java/com/checkout/SynchronousAsyncClientComparisonTest.java @@ -28,6 +28,7 @@ void shouldUseSameMethodsForSyncAndAsyncClients() throws ExecutionException, Int .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .synchronous(true) // Synchronous mode .build(); @@ -36,6 +37,7 @@ void shouldUseSameMethodsForSyncAndAsyncClients() throws ExecutionException, Int .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .synchronous(false) // Asynchronous mode (default) .build(); @@ -111,6 +113,7 @@ void shouldHaveSameInterfaceForSyncAndAsyncClients() { .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .synchronous(true) .build(); @@ -118,6 +121,7 @@ void shouldHaveSameInterfaceForSyncAndAsyncClients() { .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .synchronous(false) .build(); @@ -142,6 +146,7 @@ void shouldGetSameResponseTypeFromBothClients() throws ExecutionException, Inter .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .synchronous(true) .build(); @@ -149,6 +154,7 @@ void shouldGetSameResponseTypeFromBothClients() throws ExecutionException, Inter .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .synchronous(false) .build(); diff --git a/src/test/java/com/checkout/_external/CheckoutSdkTest.java b/src/test/java/com/checkout/_external/CheckoutSdkTest.java index 84a4b3a3e..9f5bc18f8 100644 --- a/src/test/java/com/checkout/_external/CheckoutSdkTest.java +++ b/src/test/java/com/checkout/_external/CheckoutSdkTest.java @@ -37,6 +37,8 @@ class CheckoutSdkTest { @Test void shouldCreatePreviousSdk() { + // No subdomain here on purpose: the Previous (ABC) platform predates merchant-specific + // subdomains and is exempt, so this also covers that exemption. final CheckoutApi defaultCheckoutApi = CheckoutSdk.builder().previous().staticKeys() .publicKey(VALID_PREVIOUS_PK) .secretKey(VALID_PREVIOUS_SK) @@ -69,6 +71,7 @@ void shouldCreateSdk() { .publicKey(VALID_DEFAULT_PK) .secretKey(VALID_DEFAULT_SK) .environment(Environment.SANDBOX) + .environmentSubdomain("1234doma") .build(); assertNotNull(checkoutApi); diff --git a/src/test/java/com/checkout/accounts/AccountsPayoutSchedulesIT.java b/src/test/java/com/checkout/accounts/AccountsPayoutSchedulesIT.java index 5af2d7004..1fba7be9e 100644 --- a/src/test/java/com/checkout/accounts/AccountsPayoutSchedulesIT.java +++ b/src/test/java/com/checkout/accounts/AccountsPayoutSchedulesIT.java @@ -201,6 +201,9 @@ private CheckoutApi getPayoutSchedulesCheckoutApi() { requireNonNull(System.getenv("CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_SECRET"))) .scopes(OAuthScope.MARKETPLACE) .environment(Environment.SANDBOX) + // The sandbox OAuth clients are not provisioned for the merchant-specific subdomain, so + // the token request would come back invalid_client. Opting out explicitly until they are. + .useLegacyDomain() .build(); } diff --git a/src/test/java/com/checkout/accounts/AccountsTestIT.java b/src/test/java/com/checkout/accounts/AccountsTestIT.java index 61ec0fd1f..8ddb8f1d2 100644 --- a/src/test/java/com/checkout/accounts/AccountsTestIT.java +++ b/src/test/java/com/checkout/accounts/AccountsTestIT.java @@ -801,6 +801,9 @@ private CheckoutApi getAccountsCheckoutApi() { requireNonNull(System.getenv("CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET"))) .scopes(OAuthScope.ACCOUNTS) .environment(Environment.SANDBOX) + // The sandbox OAuth clients are not provisioned for the merchant-specific subdomain, so + // the token request would come back invalid_client. Opting out explicitly until they are. + .useLegacyDomain() .build(); } diff --git a/src/test/java/com/checkout/issuing/BaseIssuingTestIT.java b/src/test/java/com/checkout/issuing/BaseIssuingTestIT.java index 9b4ac598d..892c0e04e 100644 --- a/src/test/java/com/checkout/issuing/BaseIssuingTestIT.java +++ b/src/test/java/com/checkout/issuing/BaseIssuingTestIT.java @@ -43,6 +43,9 @@ private CheckoutApi getIssuingCheckoutApi() { OAuthScope.ISSUING_CONTROLS_READ, OAuthScope.ISSUING_CONTROLS_WRITE, OAuthScope.ISSUING_TRANSACTIONS_READ, OAuthScope.ISSUING_TRANSACTIONS_WRITE) .environment(Environment.SANDBOX) + // The sandbox OAuth clients are not provisioned for the merchant-specific subdomain, so + // the token request would come back invalid_client. Opting out explicitly until they are. + .useLegacyDomain() .build(); } diff --git a/src/test/java/com/checkout/metadata/CardMetadataIT.java b/src/test/java/com/checkout/metadata/CardMetadataIT.java index a780687bf..4ce84a8f9 100644 --- a/src/test/java/com/checkout/metadata/CardMetadataIT.java +++ b/src/test/java/com/checkout/metadata/CardMetadataIT.java @@ -164,6 +164,9 @@ private CheckoutApiImpl createStaticKeyApi() { .publicKey(System.getenv("CHECKOUT_DEFAULT_PUBLIC_KEY")) .secretKey(System.getenv("CHECKOUT_DEFAULT_SECRET_KEY")) .environment(Environment.SANDBOX) + // The sandbox OAuth clients are not provisioned for the merchant-specific subdomain, so + // the token request would come back invalid_client. Opting out explicitly until they are. + .useLegacyDomain() .build(); } diff --git a/src/test/java/com/checkout/payments/RequestApmPaymentsIT.java b/src/test/java/com/checkout/payments/RequestApmPaymentsIT.java index b7507f148..60b2e58d5 100644 --- a/src/test/java/com/checkout/payments/RequestApmPaymentsIT.java +++ b/src/test/java/com/checkout/payments/RequestApmPaymentsIT.java @@ -940,6 +940,9 @@ private CheckoutApi createPreviewApi() { requireNonNull(System.getenv("CHECKOUT_PREVIEW_OAUTH_CLIENT_ID")), requireNonNull(System.getenv("CHECKOUT_PREVIEW_OAUTH_CLIENT_SECRET"))) .environment(Environment.SANDBOX) + // The sandbox OAuth clients are not provisioned for the merchant-specific subdomain, so + // the token request would come back invalid_client. Opting out explicitly until they are. + .useLegacyDomain() .build(); }