diff --git a/README.md b/README.md index 05601f2..0141c56 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,10 @@ account [here](https://www.checkout.com/get-test-account). **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 `with_environment_subdomain` 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: @@ -79,6 +83,7 @@ api = CheckoutSdk.builder .with_secret_key('secret_key') .with_public_key('public_key') # optional, only required for operations related with tokens .with_environment(CheckoutSdk::Environment.sandbox) + .with_environment_subdomain('subdomain') # required, the first 8 characters of your client ID .build ``` @@ -93,6 +98,7 @@ api = CheckoutSdk.builder .with_client_credentials("client_id", "client_secret") .with_scopes([CheckoutSdk::OAuthScopes::VAULT, CheckoutSdk::OAuthScopes::GATEWAY]) # array of scopes .with_environment(CheckoutSdk::Environment.sandbox) + .with_environment_subdomain('subdomain') # required, the first 8 characters of your client ID .build ``` @@ -107,6 +113,7 @@ api = CheckoutSdk.builder .with_secret_key('secret_key') .with_public_key('public_key') # optional, only required for operations related with tokens .with_environment(CheckoutSdk::Environment.sandbox) + .with_environment_subdomain('subdomain') # optional for the Previous platform .build ``` @@ -267,6 +274,23 @@ If you absolutely need to skip the pre-commit hooks: OVERCOMMIT_DISABLE=1 git commit -m "your message" ``` +## 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 `with_legacy_domain` instead of `with_environment_subdomain`: + +```ruby +api = CheckoutSdk.builder + .static_keys + .with_secret_key('secret_key') + .with_environment(CheckoutSdk::Environment.sandbox) + .with_legacy_domain # 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 prints a deprecation warning. Exactly one of `with_environment_subdomain` or `with_legacy_domain` must be set: the SDK raises a `CheckoutSdk::CheckoutArgumentException` if both, or neither, are. 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/lib/checkout_sdk/abstract_checkout_sdk_builder.rb b/lib/checkout_sdk/abstract_checkout_sdk_builder.rb index 12e26c6..dd230f8 100644 --- a/lib/checkout_sdk/abstract_checkout_sdk_builder.rb +++ b/lib/checkout_sdk/abstract_checkout_sdk_builder.rb @@ -8,9 +8,9 @@ class AbstractCheckoutSdkBuilder # @return [Faraday::Connection] # @!attribute multipart_http_client # @return [Faraday::Connection] - # @!attribute environment_subdomain - # @return [EnvironmentSubdomain, nil] - attr_accessor :environment, :http_client, :multipart_http_client, :logger, :environment_subdomain + # @!attribute subdomain + # @return [String, nil] + attr_accessor :environment, :http_client, :multipart_http_client, :logger, :subdomain # @param [Environment] environment def with_environment(environment) @@ -37,10 +37,39 @@ def with_logger(logger) # @param [String, nil] subdomain def with_environment_subdomain(subdomain) - @environment_subdomain = EnvironmentSubdomain.new(@environment, subdomain) + @subdomain = subdomain self end + # Opts out of the merchant-specific subdomain, sending every request to the shared hosts + # instead (api.checkout.com and 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 + # {#with_environment_subdomain} instead. + # See https://api-reference.checkout.com/#section/Base-URLs + def with_legacy_domain + warn '[DEPRECATION] with_legacy_domain is deprecated and will be removed in a future ' \ + 'release. It is intended only as an emergency fallback when the merchant-specific ' \ + 'subdomain cannot be used. Call with_environment_subdomain instead. ' \ + 'See https://api-reference.checkout.com/#section/Base-URLs' + @use_legacy_domain = true + self + end + + # @return [EnvironmentSubdomain, nil] + def environment_subdomain + return nil if subdomain.nil? + + EnvironmentSubdomain.new(environment, subdomain) + end + + # Whether this builder requires the merchant-specific subdomain to be configured. The + # Previous (ABC) platform predates merchant-specific subdomains, so it overrides this. + def requires_environment_subdomain? + true + end + def build with_environment(Environment.sandbox) if environment.nil? if http_client.nil? @@ -58,6 +87,24 @@ def build end end @logger = SimpleLogger.new.logger if @logger.nil? + validate_environment_settings + end + + private + + def validate_environment_settings + if !subdomain.nil? && @use_legacy_domain + raise CheckoutArgumentException, + 'with_environment_subdomain and with_legacy_domain cannot both be set - provide ' \ + 'only your merchant-specific subdomain' + end + return unless subdomain.nil? && !@use_legacy_domain && requires_environment_subdomain? + + raise CheckoutArgumentException, + 'environment subdomain 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 with_legacy_domain ' \ + 'to opt out only if merchant specific sub domains are causing issues' end end end diff --git a/lib/checkout_sdk/environment_subdomain.rb b/lib/checkout_sdk/environment_subdomain.rb index f5dab5f..965a625 100644 --- a/lib/checkout_sdk/environment_subdomain.rb +++ b/lib/checkout_sdk/environment_subdomain.rb @@ -23,36 +23,35 @@ def initialize(environment, subdomain) private - # 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 original_url [String] The original URL to transform. # @param subdomain [String] The subdomain to prepend to the host. - # @return [String] The transformed URL with subdomain, or original URL if subdomain is invalid. + # @return [String] The transformed URL with subdomain. + # @raise [CheckoutArgumentException] If the subdomain is not a valid merchant-specific + # subdomain. def create_url_with_subdomain(original_url, subdomain) - new_environment = original_url - - if subdomain =~ /^(?:pl-)?[a-z0-9]+$/ - url_parts = URI.parse(original_url) - new_host = "#{subdomain}.#{url_parts.host}" - - port = url_parts.scheme == 'https' && url_parts.port == 443 ? nil : url_parts.port + unless subdomain =~ /^(?:pl-)?[a-z0-9]+$/ + raise 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)' + end - new_url_parts = URI::Generic.build( - scheme: url_parts.scheme, - userinfo: url_parts.userinfo, - host: new_host, - port: port, - path: url_parts.path, - query: url_parts.query, - fragment: url_parts.fragment - ) + url_parts = URI.parse(original_url) + new_host = "#{subdomain}.#{url_parts.host}" - new_environment = new_url_parts.to_s - end + port = url_parts.scheme == 'https' && url_parts.port == 443 ? nil : url_parts.port - new_environment + URI::Generic.build( + scheme: url_parts.scheme, + userinfo: url_parts.userinfo, + host: new_host, + port: port, + path: url_parts.path, + query: url_parts.query, + fragment: url_parts.fragment + ).to_s end end end diff --git a/lib/checkout_sdk/previous/checkout_previous_static_keys_sdk_builder.rb b/lib/checkout_sdk/previous/checkout_previous_static_keys_sdk_builder.rb index 1985602..74371d8 100644 --- a/lib/checkout_sdk/previous/checkout_previous_static_keys_sdk_builder.rb +++ b/lib/checkout_sdk/previous/checkout_previous_static_keys_sdk_builder.rb @@ -7,6 +7,12 @@ class CheckoutPreviousStaticKeysSdkBuilder < StaticKeysBuilder PUBLIC_KEY_PATTERN = '^pk_(test_)?(\\w{8})-(\\w{4})-(\\w{4})-(\\w{4})-(\\w{12})$' private_constant :SECRET_KEY_PATTERN, :PUBLIC_KEY_PATTERN + # The Previous (ABC) platform predates merchant-specific subdomains, so it is exempt from + # the mandatory with_environment_subdomain/with_legacy_domain configuration. + def requires_environment_subdomain? + false + end + def build @secret_key_pattern = SECRET_KEY_PATTERN @public_key_pattern = PUBLIC_KEY_PATTERN diff --git a/spec/checkout_sdk/accounts/accounts_integration_spec.rb b/spec/checkout_sdk/accounts/accounts_integration_spec.rb index 3129fc6..9712167 100644 --- a/spec/checkout_sdk/accounts/accounts_integration_spec.rb +++ b/spec/checkout_sdk/accounts/accounts_integration_spec.rb @@ -367,9 +367,14 @@ def payout_schedules_checkout_api .oauth .with_client_credentials( ENV.fetch('CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_ID', nil), - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_SECRET', nil)) + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_SECRET', nil) + ) .with_scopes([CheckoutSdk::OAuthScopes::MARKETPLACE]) .with_environment(CheckoutSdk::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. + .with_legacy_domain .build end @@ -378,9 +383,12 @@ def accounts_checkout_api .oauth .with_client_credentials( ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_ID', nil), - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET', nil)) + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET', nil) + ) .with_scopes([CheckoutSdk::OAuthScopes::ACCOUNTS, CheckoutSdk::OAuthScopes::FILES]) .with_environment(CheckoutSdk::Environment.sandbox) + # See payout_schedules_checkout_api above for why the legacy domain is used here. + .with_legacy_domain .build end @@ -389,8 +397,11 @@ def files_checkout_api .oauth .with_client_credentials( ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_ID', nil), - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET', nil)) + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET', nil) + ) .with_scopes([CheckoutSdk::OAuthScopes::FILES]) .with_environment(CheckoutSdk::Environment.sandbox) + # See payout_schedules_checkout_api above for why the legacy domain is used here. + .with_legacy_domain .build end diff --git a/spec/checkout_sdk/configuration/configuration_spec.rb b/spec/checkout_sdk/configuration/configuration_spec.rb index 314375c..5fffd10 100644 --- a/spec/checkout_sdk/configuration/configuration_spec.rb +++ b/spec/checkout_sdk/configuration/configuration_spec.rb @@ -72,38 +72,12 @@ class FakeLogger end end - [ - ['', 'https://api.sandbox.checkout.com/'], - [' ', 'https://api.sandbox.checkout.com/'], - [' ', 'https://api.sandbox.checkout.com/'], - [' - ', 'https://api.sandbox.checkout.com/'], - ['a b', 'https://api.sandbox.checkout.com/'], - ['ab bc1', 'https://api.sandbox.checkout.com/'], - ['foo-', 'https://api.sandbox.checkout.com/'], - ['-foo', 'https://api.sandbox.checkout.com/'], - ['FOO', 'https://api.sandbox.checkout.com/'], - ['Foo-Bar', 'https://api.sandbox.checkout.com/'], - ['test-123', 'https://api.sandbox.checkout.com/'], - ['foo-bar', 'https://api.sandbox.checkout.com/'], - ['pl-', 'https://api.sandbox.checkout.com/'] - ].each do |subdomain, expected_url| - it "should create configuration with bad subdomain #{subdomain}" do - environment_subdomain = CheckoutSdk::EnvironmentSubdomain.new(CheckoutSdk::Environment.sandbox, subdomain) - - configuration = CheckoutSdk::CheckoutConfiguration.new( - @credentials, - CheckoutSdk::Environment.sandbox, - @http_client, - @multipart_http_client, - @logger, - environment_subdomain - ) - - expect(configuration.credentials).to eq(@credentials) - expect(configuration.environment.base_uri).to eq(CheckoutSdk::Environment.sandbox.base_uri) - expect(configuration.http_client).to eq(@http_client) - expect(configuration.environment_subdomain.base_uri).to eq(expected_url) - expect(configuration.environment_subdomain.authorization_uri).to eq('https://access.sandbox.checkout.com/connect/token') + ['', ' ', ' ', ' - ', 'a b', 'ab bc1', 'foo-', '-foo', 'FOO', 'Foo-Bar', 'test-123', 'foo-bar', + 'pl-'].each do |subdomain| + it "should fail with bad subdomain #{subdomain}" do + expect do + CheckoutSdk::EnvironmentSubdomain.new(CheckoutSdk::Environment.sandbox, subdomain) + end.to raise_error(CheckoutSdk::CheckoutArgumentException, /invalid environment subdomain/) end end diff --git a/spec/checkout_sdk/issuing/issuing_helper.rb b/spec/checkout_sdk/issuing/issuing_helper.rb index 0d02956..8e52926 100644 --- a/spec/checkout_sdk/issuing/issuing_helper.rb +++ b/spec/checkout_sdk/issuing/issuing_helper.rb @@ -3,17 +3,23 @@ module IssuingHelper def get_issuing_api if @issuing_api.nil? - @issuing_api = CheckoutSdk.builder - .oauth - .with_client_credentials(ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID', nil), - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET', nil)) - .with_scopes([CheckoutSdk::OAuthScopes::VAULT, - CheckoutSdk::OAuthScopes::ISSUING_CLIENT, - CheckoutSdk::OAuthScopes::ISSUING_CARD_MGMT, - CheckoutSdk::OAuthScopes::ISSUING_CONTROLS_READ, - CheckoutSdk::OAuthScopes::ISSUING_CONTROLS_WRITE]) - .with_environment(CheckoutSdk::Environment.sandbox) - .build + builder = CheckoutSdk.builder + .oauth + .with_client_credentials( + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID', nil), + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET', nil) + ) + .with_scopes([CheckoutSdk::OAuthScopes::VAULT, + CheckoutSdk::OAuthScopes::ISSUING_CLIENT, + CheckoutSdk::OAuthScopes::ISSUING_CARD_MGMT, + CheckoutSdk::OAuthScopes::ISSUING_CONTROLS_READ, + CheckoutSdk::OAuthScopes::ISSUING_CONTROLS_WRITE]) + .with_environment(CheckoutSdk::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. + .with_legacy_domain + @issuing_api = builder.build end @issuing_api end diff --git a/spec/checkout_sdk_spec.rb b/spec/checkout_sdk_spec.rb index 961e69f..04a94e5 100644 --- a/spec/checkout_sdk_spec.rb +++ b/spec/checkout_sdk_spec.rb @@ -15,6 +15,7 @@ .with_secret_key(ENV['CHECKOUT_DEFAULT_SECRET_KEY']) .with_public_key(ENV['CHECKOUT_DEFAULT_PUBLIC_KEY']) .with_environment(CheckoutSdk::Environment.production) + .with_environment_subdomain('12345domain') .build expect(default_sdk.class).to eq(CheckoutSdk::CheckoutApi) end @@ -35,6 +36,7 @@ .static_keys .with_secret_key(ENV['CHECKOUT_DEFAULT_SECRET_KEY']) .with_environment(CheckoutSdk::Environment.production) + .with_environment_subdomain('12345domain') .build expect(default_sdk.class).to eq(CheckoutSdk::CheckoutApi) end @@ -48,6 +50,63 @@ .build expect(default_sdk.class).to eq(CheckoutSdk::CheckoutApi) end + + it 'builds default sdk with the legacy domain opt-out' do + default_sdk = CheckoutSdk.builder + .static_keys + .with_secret_key(ENV.fetch('CHECKOUT_DEFAULT_SECRET_KEY', nil)) + .with_environment(CheckoutSdk::Environment.production) + .with_legacy_domain + .build + expect(default_sdk.class).to eq(CheckoutSdk::CheckoutApi) + end + end + + context 'when the domain configuration is missing or contradictory' do + it 'raises without a subdomain and without the legacy domain opt-out' do + expect do + CheckoutSdk.builder + .static_keys + .with_secret_key(ENV.fetch('CHECKOUT_DEFAULT_SECRET_KEY', nil)) + .with_environment(CheckoutSdk::Environment.production) + .build + end.to raise_error(CheckoutSdk::CheckoutArgumentException, + /environment subdomain is required/) + end + + it 'raises when both the subdomain and the legacy domain are set' do + expect do + CheckoutSdk.builder + .static_keys + .with_secret_key(ENV.fetch('CHECKOUT_DEFAULT_SECRET_KEY', nil)) + .with_environment(CheckoutSdk::Environment.production) + .with_environment_subdomain('12345domain') + .with_legacy_domain + .build + end.to raise_error(CheckoutSdk::CheckoutArgumentException, /cannot both be set/) + end + + it 'raises with an invalid subdomain' do + expect do + CheckoutSdk.builder + .static_keys + .with_secret_key(ENV.fetch('CHECKOUT_DEFAULT_SECRET_KEY', nil)) + .with_environment(CheckoutSdk::Environment.production) + .with_environment_subdomain('not a subdomain') + .build + end.to raise_error(CheckoutSdk::CheckoutArgumentException, + /invalid environment subdomain/) + end + + it 'does not require a subdomain on the Previous platform' do + previous_sdk = CheckoutSdk.builder + .previous + .static_keys + .with_secret_key(ENV.fetch('CHECKOUT_PREVIOUS_SECRET_KEY', nil)) + .with_environment(CheckoutSdk::Environment.sandbox) + .build + expect(previous_sdk.class).to eq(CheckoutSdk::Previous::CheckoutApi) + end end context 'when building default sdk with incorrect parameters' do @@ -57,6 +116,7 @@ .static_keys .with_secret_key('my wrong key') .with_environment(CheckoutSdk::Environment.production) + .with_environment_subdomain('12345domain') .build end.to raise_error(CheckoutSdk::CheckoutArgumentException) end @@ -68,6 +128,7 @@ .with_secret_key(ENV['CHECKOUT_DEFAULT_SECRET_KEY']) .with_public_key('my wrong key') .with_environment(CheckoutSdk::Environment.production) + .with_environment_subdomain('12345domain') .build end.to raise_error(CheckoutSdk::CheckoutArgumentException) end @@ -85,6 +146,7 @@ .with_scopes([CheckoutSdk::OAuthScopes::VAULT, CheckoutSdk::OAuthScopes::GATEWAY]) .with_environment(CheckoutSdk::Environment.sandbox) + .with_environment_subdomain('12345domain') .build expect(oauth_sdk.class).to eq(CheckoutSdk::CheckoutApi) @@ -118,6 +180,7 @@ .with_scopes([CheckoutSdk::OAuthScopes::VAULT, CheckoutSdk::OAuthScopes::GATEWAY]) .with_environment(CheckoutSdk::Environment.sandbox) + .with_environment_subdomain('12345domain') .with_http_client(http_client) .build @@ -136,6 +199,7 @@ .with_scopes([CheckoutSdk::OAuthScopes::VAULT, CheckoutSdk::OAuthScopes::GATEWAY]) .with_environment(CheckoutSdk::Environment.sandbox) + .with_environment_subdomain('12345domain') .build end.to raise_error(CheckoutSdk::CheckoutArgumentException, 'Invalid OAuth "client_id" or "client_secret"') @@ -151,6 +215,7 @@ .with_scopes([CheckoutSdk::OAuthScopes::VAULT, CheckoutSdk::OAuthScopes::GATEWAY]) .with_environment(CheckoutSdk::Environment.sandbox) + .with_environment_subdomain('12345domain') .build end.to raise_error(CheckoutSdk::CheckoutArgumentException, 'Invalid OAuth "client_id" or "client_secret"') @@ -165,6 +230,7 @@ .with_scopes([CheckoutSdk::OAuthScopes::VAULT, CheckoutSdk::OAuthScopes::GATEWAY]) .with_authorization_uri('https://invalid.checkout.com/') + .with_environment_subdomain('12345domain') .build end.to raise_error(CheckoutSdk::CheckoutArgumentException) end @@ -215,6 +281,7 @@ .static_keys .with_secret_key('my wrong key') .with_environment(CheckoutSdk::Environment.production) + .with_environment_subdomain('12345domain') .build end.to raise_error(CheckoutSdk::CheckoutArgumentException) end @@ -227,6 +294,7 @@ .with_secret_key(ENV['CHECKOUT_PREVIOUS_SECRET_KEY']) .with_public_key('my wrong key') .with_environment(CheckoutSdk::Environment.production) + .with_environment_subdomain('12345domain') .build end.to raise_error(CheckoutSdk::CheckoutArgumentException) end diff --git a/spec/support/sandbox_test_fixture.rb b/spec/support/sandbox_test_fixture.rb index 9680c98..b0491b8 100644 --- a/spec/support/sandbox_test_fixture.rb +++ b/spec/support/sandbox_test_fixture.rb @@ -8,9 +8,13 @@ module SandboxTestFixture def default_sdk CheckoutSdk.builder .static_keys - .with_secret_key(ENV['CHECKOUT_DEFAULT_SECRET_KEY']) - .with_public_key(ENV['CHECKOUT_DEFAULT_PUBLIC_KEY']) + .with_secret_key(ENV.fetch('CHECKOUT_DEFAULT_SECRET_KEY', nil)) + .with_public_key(ENV.fetch('CHECKOUT_DEFAULT_PUBLIC_KEY', nil)) .with_environment(CheckoutSdk::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. + .with_legacy_domain .build end @@ -29,10 +33,14 @@ def oauth_sdk @oauth_sdk = CheckoutSdk.builder .oauth .with_client_credentials( - ENV['CHECKOUT_DEFAULT_OAUTH_CLIENT_ID'], - ENV['CHECKOUT_DEFAULT_OAUTH_CLIENT_SECRET']) + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_CLIENT_ID', nil), + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_CLIENT_SECRET', nil)) .with_scopes(get_oauth_scopes) .with_environment(CheckoutSdk::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. + .with_legacy_domain .build end @oauth_sdk