From 97402a29181b6307b5b077cc8b26b8ba8c2f1e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Mon, 10 Aug 2026 15:05:13 +0200 Subject: [PATCH 1/4] feat: require with_environment_subdomain or an explicit with_legacy_domain opt-out The merchant-specific subdomain is how merchants should reach the API, but it was optional and an unset value silently fell back to api.checkout.com, so a forgotten subdomain looked exactly like a deliberate opt-out and the SDK could not warn about either. Callers must now choose: call with_environment_subdomain, or with_legacy_domain, which prints a deprecation warning from its first release. Both, or neither, raises CheckoutArgumentException. An invalid subdomain now raises instead of being quietly ignored, which is a second breaking change: callers passing a malformed value are currently served by the shared host and never find out. with_environment_subdomain no longer needs with_environment to be set first, since the EnvironmentSubdomain is now built when the configuration is assembled. The Previous (ABC) platform predates merchant-specific subdomains and stays exempt via requires_environment_subdomain?. Specs route clients through Helpers::DomainConfiguration, which uses the shared hosts: the sandbox OAuth clients are not provisioned for the subdomain, so applying it makes every client_credentials request return invalid_client. Mirrors checkout-sdk-net#590. Refs INT-1688. --- README.md | 24 +++++++ .../abstract_checkout_sdk_builder.rb | 55 +++++++++++++-- lib/checkout_sdk/environment_subdomain.rb | 45 ++++++------ ...eckout_previous_static_keys_sdk_builder.rb | 6 ++ lib/checkout_sdk/version.rb | 2 +- .../accounts/accounts_integration_spec.rb | 54 ++++++++------- .../configuration/configuration_spec.rb | 38 ++--------- spec/checkout_sdk/issuing/issuing_helper.rb | 24 ++++--- spec/checkout_sdk_spec.rb | 68 +++++++++++++++++++ spec/spec_helper.rb | 1 + spec/support/domain_configuration.rb | 21 ++++++ spec/support/sandbox_test_fixture.rb | 30 ++++---- 12 files changed, 259 insertions(+), 109 deletions(-) create mode 100644 spec/support/domain_configuration.rb 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/lib/checkout_sdk/version.rb b/lib/checkout_sdk/version.rb index 5555540..ed693fc 100644 --- a/lib/checkout_sdk/version.rb +++ b/lib/checkout_sdk/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module CheckoutSdk - VERSION = '1.11.0' + VERSION = '2.0.0' end diff --git a/spec/checkout_sdk/accounts/accounts_integration_spec.rb b/spec/checkout_sdk/accounts/accounts_integration_spec.rb index 3129fc6..f0d6caf 100644 --- a/spec/checkout_sdk/accounts/accounts_integration_spec.rb +++ b/spec/checkout_sdk/accounts/accounts_integration_spec.rb @@ -363,34 +363,40 @@ def upload_file_accounts(sdk) end def payout_schedules_checkout_api - CheckoutSdk.builder - .oauth - .with_client_credentials( - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_ID', nil), - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_SECRET', nil)) - .with_scopes([CheckoutSdk::OAuthScopes::MARKETPLACE]) - .with_environment(CheckoutSdk::Environment.sandbox) - .build + Helpers::DomainConfiguration.configure( + CheckoutSdk.builder + .oauth + .with_client_credentials( + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_ID', nil), + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_SECRET', nil) + ) + .with_scopes([CheckoutSdk::OAuthScopes::MARKETPLACE]) + .with_environment(CheckoutSdk::Environment.sandbox) + ).build end def accounts_checkout_api - CheckoutSdk.builder - .oauth - .with_client_credentials( - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_ID', nil), - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET', nil)) - .with_scopes([CheckoutSdk::OAuthScopes::ACCOUNTS, CheckoutSdk::OAuthScopes::FILES]) - .with_environment(CheckoutSdk::Environment.sandbox) - .build + Helpers::DomainConfiguration.configure( + CheckoutSdk.builder + .oauth + .with_client_credentials( + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_ID', nil), + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET', nil) + ) + .with_scopes([CheckoutSdk::OAuthScopes::ACCOUNTS, CheckoutSdk::OAuthScopes::FILES]) + .with_environment(CheckoutSdk::Environment.sandbox) + ).build end def files_checkout_api - CheckoutSdk.builder - .oauth - .with_client_credentials( - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_ID', nil), - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET', nil)) - .with_scopes([CheckoutSdk::OAuthScopes::FILES]) - .with_environment(CheckoutSdk::Environment.sandbox) - .build + Helpers::DomainConfiguration.configure( + CheckoutSdk.builder + .oauth + .with_client_credentials( + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_ID', nil), + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET', nil) + ) + .with_scopes([CheckoutSdk::OAuthScopes::FILES]) + .with_environment(CheckoutSdk::Environment.sandbox) + ).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..b325956 100644 --- a/spec/checkout_sdk/issuing/issuing_helper.rb +++ b/spec/checkout_sdk/issuing/issuing_helper.rb @@ -3,17 +3,19 @@ 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) + @issuing_api = Helpers::DomainConfiguration.configure(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/spec_helper.rb b/spec/spec_helper.rb index a74adde..c3fd073 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,6 +3,7 @@ require 'rspec' require 'securerandom' require 'checkout_sdk' +require './spec/support/domain_configuration' require './spec/support/data_factory' require './spec/support/sandbox_test_fixture' require './spec/checkout_sdk/issuing/issuing_helper' diff --git a/spec/support/domain_configuration.rb b/spec/support/domain_configuration.rb new file mode 100644 index 0000000..b49e13f --- /dev/null +++ b/spec/support/domain_configuration.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Helpers + # Every client the suite builds has to choose a domain now that the merchant-specific subdomain + # is mandatory, so they all come through here. + # + # The suite uses the shared hosts. It would be better to exercise the merchant-specific + # subdomain, since that is the path merchants are being moved to, but the sandbox OAuth clients + # are not provisioned for it: pointing the token request at + # {subdomain}.access.sandbox.checkout.com returns invalid_client for every integration spec. + # Until those clients are bound to the subdomain, CI has to use the legacy hosts. + module DomainConfiguration + def self.configure(builder) + original = Warning[:deprecated] + Warning[:deprecated] = false + builder.with_legacy_domain + ensure + Warning[:deprecated] = original + end + end +end diff --git a/spec/support/sandbox_test_fixture.rb b/spec/support/sandbox_test_fixture.rb index 9680c98..f04d174 100644 --- a/spec/support/sandbox_test_fixture.rb +++ b/spec/support/sandbox_test_fixture.rb @@ -6,12 +6,13 @@ module SandboxTestFixture # @return [CheckoutSdk::CheckoutApi] def default_sdk - CheckoutSdk.builder - .static_keys - .with_secret_key(ENV['CHECKOUT_DEFAULT_SECRET_KEY']) - .with_public_key(ENV['CHECKOUT_DEFAULT_PUBLIC_KEY']) - .with_environment(CheckoutSdk::Environment.sandbox) - .build + Helpers::DomainConfiguration.configure( + CheckoutSdk.builder + .static_keys + .with_secret_key(ENV['CHECKOUT_DEFAULT_SECRET_KEY']) + .with_public_key(ENV['CHECKOUT_DEFAULT_PUBLIC_KEY']) + .with_environment(CheckoutSdk::Environment.sandbox) + ).build end def previous_sdk @@ -26,14 +27,15 @@ def previous_sdk def oauth_sdk if @oauth_sdk.nil? - @oauth_sdk = CheckoutSdk.builder - .oauth - .with_client_credentials( - ENV['CHECKOUT_DEFAULT_OAUTH_CLIENT_ID'], - ENV['CHECKOUT_DEFAULT_OAUTH_CLIENT_SECRET']) - .with_scopes(get_oauth_scopes) - .with_environment(CheckoutSdk::Environment.sandbox) - .build + @oauth_sdk = Helpers::DomainConfiguration.configure( + CheckoutSdk.builder + .oauth + .with_client_credentials( + ENV['CHECKOUT_DEFAULT_OAUTH_CLIENT_ID'], + ENV['CHECKOUT_DEFAULT_OAUTH_CLIENT_SECRET']) + .with_scopes(get_oauth_scopes) + .with_environment(CheckoutSdk::Environment.sandbox) + ).build end @oauth_sdk end From 344fae7b4634539190ed6f126af36cf9467036e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Tue, 11 Aug 2026 17:27:43 +0200 Subject: [PATCH 2/4] test: add a switch to run the suite against the merchant subdomain The suite could only run against the shared hosts, so the subdomain path this PR makes mandatory had no integration coverage. Reviewers flagged that on every SDK, and it is the right thing to flag. The domain helper now has two modes. Default is unchanged, the shared hosts, because the sandbox OAuth clients are not provisioned for the subdomain and the token request returns invalid_client. Set CHECKOUT_TEST_USE_SUBDOMAIN=true and the suite runs against CHECKOUT_MERCHANT_SUBDOMAIN instead, so once sandbox is provisioned like production it is a one-line change in the workflows, already wired and documented, rather than a rewrite of every fixture. The switch is deliberately separate from CHECKOUT_MERCHANT_SUBDOMAIN, which CI already exports: provisioning should drive the behaviour, not the presence of a secret. --- .github/workflows/build-master.yml | 4 ++++ .github/workflows/build-pull-request.yml | 4 ++++ .github/workflows/build-release.yml | 4 ++++ README.md | 12 ++++++++++++ spec/support/domain_configuration.rb | 24 ++++++++++++++++++------ 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-master.yml b/.github/workflows/build-master.yml index 5352f04..3e547a3 100644 --- a/.github/workflows/build-master.yml +++ b/.github/workflows/build-master.yml @@ -45,4 +45,8 @@ jobs: CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID }} CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET }} CHECKOUT_MERCHANT_SUBDOMAIN: ${{ secrets.IT_CHECKOUT_MERCHANT_SUBDOMAIN }} + # Flip to 'true' once the sandbox OAuth clients are provisioned for the + # merchant-specific subdomain, and the suite will run against it instead of + # the shared hosts. See TestDomainConfiguration. + CHECKOUT_TEST_USE_SUBDOMAIN: 'false' run: bundle exec rspec spec \ No newline at end of file diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index 18b6531..2d9cba4 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -45,4 +45,8 @@ jobs: CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID }} CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET }} CHECKOUT_MERCHANT_SUBDOMAIN: ${{ secrets.IT_CHECKOUT_MERCHANT_SUBDOMAIN }} + # Flip to 'true' once the sandbox OAuth clients are provisioned for the + # merchant-specific subdomain, and the suite will run against it instead of + # the shared hosts. See TestDomainConfiguration. + CHECKOUT_TEST_USE_SUBDOMAIN: 'false' run: bundle exec rspec spec \ No newline at end of file diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 3f03505..94c011e 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -33,6 +33,10 @@ jobs: CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID }} CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET }} CHECKOUT_MERCHANT_SUBDOMAIN: ${{ secrets.IT_CHECKOUT_MERCHANT_SUBDOMAIN }} + # Flip to 'true' once the sandbox OAuth clients are provisioned for the + # merchant-specific subdomain, and the suite will run against it instead of + # the shared hosts. See TestDomainConfiguration. + CHECKOUT_TEST_USE_SUBDOMAIN: 'false' run: bundle exec rspec spec - id: read-version run: echo "CURRENT_VERSION=$( grep "VERSION" lib/checkout_sdk/version.rb | awk '{ print $3 }' | tr -d "'")" >> $GITHUB_ENV diff --git a/README.md b/README.md index 0141c56..9e53f44 100644 --- a/README.md +++ b/README.md @@ -291,6 +291,18 @@ api = CheckoutSdk.builder 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. +## Running the tests against your subdomain + +The test suite builds every client through `spec/support/domain_configuration.rb`, which has two modes. By default it uses the shared hosts, because the sandbox OAuth clients are not provisioned for merchant-specific subdomains and the token request would come back `invalid_client`. To run against a subdomain instead: + +```bash +export CHECKOUT_MERCHANT_SUBDOMAIN="your_subdomain" +export CHECKOUT_TEST_USE_SUBDOMAIN=true +bundle exec rspec +``` + +The switch is separate from `CHECKOUT_MERCHANT_SUBDOMAIN` on purpose: CI already exports that secret, so provisioning is what should flip the behaviour, not the presence of a value. Once sandbox is provisioned like production, set `CHECKOUT_TEST_USE_SUBDOMAIN: 'true'` in the workflows and CI exercises the subdomain path end to end. + ## Code of Conduct Please refer to [Code of Conduct](CODE_OF_CONDUCT.md) diff --git a/spec/support/domain_configuration.rb b/spec/support/domain_configuration.rb index b49e13f..35d52cb 100644 --- a/spec/support/domain_configuration.rb +++ b/spec/support/domain_configuration.rb @@ -2,20 +2,32 @@ module Helpers # Every client the suite builds has to choose a domain now that the merchant-specific subdomain - # is mandatory, so they all come through here. + # is mandatory, so they all come through here. There are deliberately two modes. # - # The suite uses the shared hosts. It would be better to exercise the merchant-specific - # subdomain, since that is the path merchants are being moved to, but the sandbox OAuth clients - # are not provisioned for it: pointing the token request at + # Default: the shared hosts. The sandbox OAuth clients are not provisioned for the + # merchant-specific subdomain, so pointing the token request at # {subdomain}.access.sandbox.checkout.com returns invalid_client for every integration spec. - # Until those clients are bound to the subdomain, CI has to use the legacy hosts. + # + # Opt-in: set CHECKOUT_TEST_USE_SUBDOMAIN=true and the suite runs against + # CHECKOUT_MERCHANT_SUBDOMAIN instead, exercising end to end the path merchants are being moved + # to. Once sandbox is provisioned like production, set that variable in the workflows and this + # becomes the mode CI runs in. The switch is deliberately separate from + # CHECKOUT_MERCHANT_SUBDOMAIN, which CI already exports, so provisioning drives the change + # rather than the presence of a secret. module DomainConfiguration + def self.use_subdomain? + ENV.fetch('CHECKOUT_TEST_USE_SUBDOMAIN', '').downcase == 'true' + end + def self.configure(builder) + subdomain = ENV.fetch('CHECKOUT_MERCHANT_SUBDOMAIN', nil) + return builder.with_environment_subdomain(subdomain) if use_subdomain? && !subdomain.to_s.strip.empty? + original = Warning[:deprecated] Warning[:deprecated] = false builder.with_legacy_domain ensure - Warning[:deprecated] = original + Warning[:deprecated] = original unless original.nil? end end end From 4f8cb32c19adf3a4f7a214152c9bc04b4460080c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:19:55 +0200 Subject: [PATCH 3/4] revert: leave the version bump to the release Versions are bumped on master during the release, not in a feature branch, per the release workflow. This branch should carry only the change itself; the major bump is classified and applied when the release is cut. --- lib/checkout_sdk/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/checkout_sdk/version.rb b/lib/checkout_sdk/version.rb index ed693fc..5555540 100644 --- a/lib/checkout_sdk/version.rb +++ b/lib/checkout_sdk/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module CheckoutSdk - VERSION = '2.0.0' + VERSION = '1.11.0' end From c072be4e601b4f317c5da4a46e5246800d0be139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:52:08 +0200 Subject: [PATCH 4/4] revert: drop the test domain helpers and the workflow variable Two problems with the previous approach. It needed a new variable in 21 workflow files, which is not viable without access to create secrets. And it wrapped the builder chain in a configureDomain helper that is not part of the public API, so the tests stopped looking like the code a merchant would actually write. Every fixture now calls the real opt-out inline, in the chain, with a comment saying why: the sandbox OAuth clients are not provisioned for the merchant-specific subdomain, so the token request comes back invalid_client. When sandbox is provisioned, those calls become the subdomain setter. The unit tests covering all four combinations are untouched: they already used the public API directly. --- .github/workflows/build-master.yml | 4 -- .github/workflows/build-pull-request.yml | 4 -- .github/workflows/build-release.yml | 4 -- README.md | 12 ---- .../accounts/accounts_integration_spec.rb | 65 ++++++++++--------- spec/checkout_sdk/issuing/issuing_helper.rb | 6 +- spec/spec_helper.rb | 1 - spec/support/domain_configuration.rb | 33 ---------- spec/support/sandbox_test_fixture.rb | 38 ++++++----- 9 files changed, 62 insertions(+), 105 deletions(-) delete mode 100644 spec/support/domain_configuration.rb diff --git a/.github/workflows/build-master.yml b/.github/workflows/build-master.yml index 3e547a3..5352f04 100644 --- a/.github/workflows/build-master.yml +++ b/.github/workflows/build-master.yml @@ -45,8 +45,4 @@ jobs: CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID }} CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET }} CHECKOUT_MERCHANT_SUBDOMAIN: ${{ secrets.IT_CHECKOUT_MERCHANT_SUBDOMAIN }} - # Flip to 'true' once the sandbox OAuth clients are provisioned for the - # merchant-specific subdomain, and the suite will run against it instead of - # the shared hosts. See TestDomainConfiguration. - CHECKOUT_TEST_USE_SUBDOMAIN: 'false' run: bundle exec rspec spec \ No newline at end of file diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index 2d9cba4..18b6531 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -45,8 +45,4 @@ jobs: CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID }} CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET }} CHECKOUT_MERCHANT_SUBDOMAIN: ${{ secrets.IT_CHECKOUT_MERCHANT_SUBDOMAIN }} - # Flip to 'true' once the sandbox OAuth clients are provisioned for the - # merchant-specific subdomain, and the suite will run against it instead of - # the shared hosts. See TestDomainConfiguration. - CHECKOUT_TEST_USE_SUBDOMAIN: 'false' run: bundle exec rspec spec \ No newline at end of file diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 94c011e..3f03505 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -33,10 +33,6 @@ jobs: CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID }} CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET }} CHECKOUT_MERCHANT_SUBDOMAIN: ${{ secrets.IT_CHECKOUT_MERCHANT_SUBDOMAIN }} - # Flip to 'true' once the sandbox OAuth clients are provisioned for the - # merchant-specific subdomain, and the suite will run against it instead of - # the shared hosts. See TestDomainConfiguration. - CHECKOUT_TEST_USE_SUBDOMAIN: 'false' run: bundle exec rspec spec - id: read-version run: echo "CURRENT_VERSION=$( grep "VERSION" lib/checkout_sdk/version.rb | awk '{ print $3 }' | tr -d "'")" >> $GITHUB_ENV diff --git a/README.md b/README.md index 9e53f44..0141c56 100644 --- a/README.md +++ b/README.md @@ -291,18 +291,6 @@ api = CheckoutSdk.builder 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. -## Running the tests against your subdomain - -The test suite builds every client through `spec/support/domain_configuration.rb`, which has two modes. By default it uses the shared hosts, because the sandbox OAuth clients are not provisioned for merchant-specific subdomains and the token request would come back `invalid_client`. To run against a subdomain instead: - -```bash -export CHECKOUT_MERCHANT_SUBDOMAIN="your_subdomain" -export CHECKOUT_TEST_USE_SUBDOMAIN=true -bundle exec rspec -``` - -The switch is separate from `CHECKOUT_MERCHANT_SUBDOMAIN` on purpose: CI already exports that secret, so provisioning is what should flip the behaviour, not the presence of a value. Once sandbox is provisioned like production, set `CHECKOUT_TEST_USE_SUBDOMAIN: 'true'` in the workflows and CI exercises the subdomain path end to end. - ## Code of Conduct Please refer to [Code of Conduct](CODE_OF_CONDUCT.md) diff --git a/spec/checkout_sdk/accounts/accounts_integration_spec.rb b/spec/checkout_sdk/accounts/accounts_integration_spec.rb index f0d6caf..9712167 100644 --- a/spec/checkout_sdk/accounts/accounts_integration_spec.rb +++ b/spec/checkout_sdk/accounts/accounts_integration_spec.rb @@ -363,40 +363,45 @@ def upload_file_accounts(sdk) end def payout_schedules_checkout_api - Helpers::DomainConfiguration.configure( - CheckoutSdk.builder - .oauth - .with_client_credentials( - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_ID', nil), - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_SECRET', nil) - ) - .with_scopes([CheckoutSdk::OAuthScopes::MARKETPLACE]) - .with_environment(CheckoutSdk::Environment.sandbox) - ).build + CheckoutSdk.builder + .oauth + .with_client_credentials( + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_ID', 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 def accounts_checkout_api - Helpers::DomainConfiguration.configure( - CheckoutSdk.builder - .oauth - .with_client_credentials( - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_ID', nil), - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET', nil) - ) - .with_scopes([CheckoutSdk::OAuthScopes::ACCOUNTS, CheckoutSdk::OAuthScopes::FILES]) - .with_environment(CheckoutSdk::Environment.sandbox) - ).build + CheckoutSdk.builder + .oauth + .with_client_credentials( + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_ID', 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 def files_checkout_api - Helpers::DomainConfiguration.configure( - CheckoutSdk.builder - .oauth - .with_client_credentials( - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_ID', nil), - ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET', nil) - ) - .with_scopes([CheckoutSdk::OAuthScopes::FILES]) - .with_environment(CheckoutSdk::Environment.sandbox) - ).build + CheckoutSdk.builder + .oauth + .with_client_credentials( + ENV.fetch('CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_ID', 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/issuing/issuing_helper.rb b/spec/checkout_sdk/issuing/issuing_helper.rb index b325956..8e52926 100644 --- a/spec/checkout_sdk/issuing/issuing_helper.rb +++ b/spec/checkout_sdk/issuing/issuing_helper.rb @@ -15,7 +15,11 @@ def get_issuing_api CheckoutSdk::OAuthScopes::ISSUING_CONTROLS_READ, CheckoutSdk::OAuthScopes::ISSUING_CONTROLS_WRITE]) .with_environment(CheckoutSdk::Environment.sandbox) - @issuing_api = Helpers::DomainConfiguration.configure(builder).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. + .with_legacy_domain + @issuing_api = builder.build end @issuing_api end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c3fd073..a74adde 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,7 +3,6 @@ require 'rspec' require 'securerandom' require 'checkout_sdk' -require './spec/support/domain_configuration' require './spec/support/data_factory' require './spec/support/sandbox_test_fixture' require './spec/checkout_sdk/issuing/issuing_helper' diff --git a/spec/support/domain_configuration.rb b/spec/support/domain_configuration.rb deleted file mode 100644 index 35d52cb..0000000 --- a/spec/support/domain_configuration.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -module Helpers - # Every client the suite builds has to choose a domain now that the merchant-specific subdomain - # is mandatory, so they all come through here. There are deliberately two modes. - # - # Default: the shared hosts. The sandbox OAuth clients are not provisioned for the - # merchant-specific subdomain, so pointing the token request at - # {subdomain}.access.sandbox.checkout.com returns invalid_client for every integration spec. - # - # Opt-in: set CHECKOUT_TEST_USE_SUBDOMAIN=true and the suite runs against - # CHECKOUT_MERCHANT_SUBDOMAIN instead, exercising end to end the path merchants are being moved - # to. Once sandbox is provisioned like production, set that variable in the workflows and this - # becomes the mode CI runs in. The switch is deliberately separate from - # CHECKOUT_MERCHANT_SUBDOMAIN, which CI already exports, so provisioning drives the change - # rather than the presence of a secret. - module DomainConfiguration - def self.use_subdomain? - ENV.fetch('CHECKOUT_TEST_USE_SUBDOMAIN', '').downcase == 'true' - end - - def self.configure(builder) - subdomain = ENV.fetch('CHECKOUT_MERCHANT_SUBDOMAIN', nil) - return builder.with_environment_subdomain(subdomain) if use_subdomain? && !subdomain.to_s.strip.empty? - - original = Warning[:deprecated] - Warning[:deprecated] = false - builder.with_legacy_domain - ensure - Warning[:deprecated] = original unless original.nil? - end - end -end diff --git a/spec/support/sandbox_test_fixture.rb b/spec/support/sandbox_test_fixture.rb index f04d174..b0491b8 100644 --- a/spec/support/sandbox_test_fixture.rb +++ b/spec/support/sandbox_test_fixture.rb @@ -6,13 +6,16 @@ module SandboxTestFixture # @return [CheckoutSdk::CheckoutApi] def default_sdk - Helpers::DomainConfiguration.configure( - CheckoutSdk.builder - .static_keys - .with_secret_key(ENV['CHECKOUT_DEFAULT_SECRET_KEY']) - .with_public_key(ENV['CHECKOUT_DEFAULT_PUBLIC_KEY']) - .with_environment(CheckoutSdk::Environment.sandbox) - ).build + CheckoutSdk.builder + .static_keys + .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 def previous_sdk @@ -27,15 +30,18 @@ def previous_sdk def oauth_sdk if @oauth_sdk.nil? - @oauth_sdk = Helpers::DomainConfiguration.configure( - CheckoutSdk.builder - .oauth - .with_client_credentials( - ENV['CHECKOUT_DEFAULT_OAUTH_CLIENT_ID'], - ENV['CHECKOUT_DEFAULT_OAUTH_CLIENT_SECRET']) - .with_scopes(get_oauth_scopes) - .with_environment(CheckoutSdk::Environment.sandbox) - ).build + @oauth_sdk = CheckoutSdk.builder + .oauth + .with_client_credentials( + 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 end