From a7bfe719e9045319ebe0d9f27c75c8757d0c4cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Fri, 14 Jun 2024 15:34:52 +0200 Subject: [PATCH] Merchant-specific DNS names Support (#145) --- lib/checkout_sdk.rb | 1 + .../abstract_checkout_sdk_builder.rb | 10 ++- lib/checkout_sdk/checkout_api.rb | 7 +- lib/checkout_sdk/checkout_configuration.rb | 15 +++- .../checkout_oauth_sdk_builder.rb | 30 +++---- .../checkout_static_keys_sdk_builder.rb | 18 ++-- lib/checkout_sdk/environment_subdomain.rb | 55 +++++++++++++ ...eckout_previous_static_keys_sdk_builder.rb | 18 ++-- .../configuration/configuration_spec.rb | 82 +++++++++++++++++++ spec/checkout_sdk_spec.rb | 48 +++++++++++ 10 files changed, 250 insertions(+), 34 deletions(-) create mode 100644 lib/checkout_sdk/environment_subdomain.rb create mode 100644 spec/checkout_sdk/configuration/configuration_spec.rb diff --git a/lib/checkout_sdk.rb b/lib/checkout_sdk.rb index c26e0d8..ba7112c 100644 --- a/lib/checkout_sdk.rb +++ b/lib/checkout_sdk.rb @@ -21,6 +21,7 @@ require 'checkout_sdk/checkout_utils' require 'checkout_sdk/platform_type' require 'checkout_sdk/environment' +require 'checkout_sdk/environment_subdomain' require 'checkout_sdk/sdk_credentials' require 'checkout_sdk/sdk_authorization' require 'checkout_sdk/static_keys_sdk_credentials' diff --git a/lib/checkout_sdk/abstract_checkout_sdk_builder.rb b/lib/checkout_sdk/abstract_checkout_sdk_builder.rb index dc2b250..12e26c6 100644 --- a/lib/checkout_sdk/abstract_checkout_sdk_builder.rb +++ b/lib/checkout_sdk/abstract_checkout_sdk_builder.rb @@ -8,7 +8,9 @@ class AbstractCheckoutSdkBuilder # @return [Faraday::Connection] # @!attribute multipart_http_client # @return [Faraday::Connection] - attr_accessor :environment, :http_client, :multipart_http_client, :logger + # @!attribute environment_subdomain + # @return [EnvironmentSubdomain, nil] + attr_accessor :environment, :http_client, :multipart_http_client, :logger, :environment_subdomain # @param [Environment] environment def with_environment(environment) @@ -33,6 +35,12 @@ def with_logger(logger) self end + # @param [String, nil] subdomain + def with_environment_subdomain(subdomain) + @environment_subdomain = EnvironmentSubdomain.new(@environment, subdomain) + self + end + def build with_environment(Environment.sandbox) if environment.nil? if http_client.nil? diff --git a/lib/checkout_sdk/checkout_api.rb b/lib/checkout_sdk/checkout_api.rb index 5d4bbae..4416afc 100644 --- a/lib/checkout_sdk/checkout_api.rb +++ b/lib/checkout_sdk/checkout_api.rb @@ -97,7 +97,12 @@ def initialize(configuration) # @param [CheckoutConfiguration] configuration # @return [ApiClient] def base_api_client(configuration) - ApiClient.new(configuration, configuration.environment.base_uri) + base_uri = configuration.environment.base_uri + subdomain = configuration.environment_subdomain + + base_uri = subdomain.base_uri if subdomain&.base_uri + + ApiClient.new(configuration, base_uri) end # @param [CheckoutConfiguration] configuration diff --git a/lib/checkout_sdk/checkout_configuration.rb b/lib/checkout_sdk/checkout_configuration.rb index 695834b..5d5b2d1 100644 --- a/lib/checkout_sdk/checkout_configuration.rb +++ b/lib/checkout_sdk/checkout_configuration.rb @@ -7,19 +7,30 @@ module CheckoutSdk # @return [Environment] # @!attribute http_client # @return [Faraday::Connection] + # @!attribute multipart_http_client + # @return [Faraday::Connection] + # @!attribute logger + # @return [Logger] + # @!attribute environment_subdomain + # @return [EnvironmentSubdomain, nil] class CheckoutConfiguration - attr_accessor :credentials, :environment, :http_client, :multipart_http_client, :logger + attr_accessor :credentials, :environment, :http_client, :multipart_http_client, :logger, :environment_subdomain + # Initializes the CheckoutConfiguration. + # # @param [SdkCredentials] credentials # @param [Environment] environment # @param [Faraday::Connection] http_client # @param [Faraday::Connection] multipart_http_client - def initialize(credentials, environment, http_client, multipart_http_client, logger) + # @param [Logger] logger + # @param [EnvironmentSubdomain, nil] environment_subdomain (optional, default: nil) + def initialize(credentials, environment, http_client, multipart_http_client, logger, environment_subdomain = nil) @credentials = credentials @environment = environment @http_client = http_client @multipart_http_client = multipart_http_client @logger = logger + @environment_subdomain = environment_subdomain end end end diff --git a/lib/checkout_sdk/checkout_oauth_sdk_builder.rb b/lib/checkout_sdk/checkout_oauth_sdk_builder.rb index 047d059..00202d0 100644 --- a/lib/checkout_sdk/checkout_oauth_sdk_builder.rb +++ b/lib/checkout_sdk/checkout_oauth_sdk_builder.rb @@ -41,21 +41,23 @@ def with_scopes(scopes) # @return [CheckoutSdk::CheckoutApi] def build super - CheckoutSdk::CheckoutApi.new( - CheckoutConfiguration.new( - OAuthSdkCredentials.new(client_id, - client_secret, - scopes, - http_client, - environment, - logger, - authorization_uri), - environment, - http_client, - multipart_http_client, - logger - ) + configuration = CheckoutConfiguration.new( + OAuthSdkCredentials.new(client_id, + client_secret, + scopes, + http_client, + environment, + logger, + authorization_uri), + environment, + http_client, + multipart_http_client, + logger ) + + configuration.environment_subdomain = environment_subdomain if environment_subdomain + + CheckoutApi.new(configuration) end end end diff --git a/lib/checkout_sdk/checkout_static_keys_sdk_builder.rb b/lib/checkout_sdk/checkout_static_keys_sdk_builder.rb index 988cfad..79b91fc 100644 --- a/lib/checkout_sdk/checkout_static_keys_sdk_builder.rb +++ b/lib/checkout_sdk/checkout_static_keys_sdk_builder.rb @@ -11,15 +11,17 @@ def build @secret_key_pattern = SECRET_KEY_PATTERN @public_key_pattern = PUBLIC_KEY_PATTERN super - CheckoutApi.new( - CheckoutConfiguration.new( - StaticKeysSdkCredentials.new(secret_key, public_key), - environment, - http_client, - multipart_http_client, - logger - ) + configuration = CheckoutConfiguration.new( + StaticKeysSdkCredentials.new(secret_key, public_key), + environment, + http_client, + multipart_http_client, + logger ) + + configuration.environment_subdomain = environment_subdomain if environment_subdomain + + CheckoutApi.new(configuration) end end end diff --git a/lib/checkout_sdk/environment_subdomain.rb b/lib/checkout_sdk/environment_subdomain.rb new file mode 100644 index 0000000..a65260a --- /dev/null +++ b/lib/checkout_sdk/environment_subdomain.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module CheckoutSdk + # @!attribute environment + # @return [Environment] + # @!attribute subdomain + # @return [String] + class EnvironmentSubdomain + attr_reader :base_uri, :environment, :subdomain + + # Initializes the EnvironmentSubdomain with the given environment and subdomain. + # + # @param environment [Environment] The environment object which should have a base_uri method. + # @param subdomain [String] The subdomain to add to the environment's API URL. + def initialize(environment, subdomain) + @environment = environment + @subdomain = subdomain + @base_uri = add_subdomain_to_api_url_environment(environment, subdomain) + end + + private + + # Adds the subdomain to the API URL of the given environment. + # + # @param environment [Environment] The environment object which should have a base_uri method. + # @param subdomain [String] The subdomain to add to the environment's API URL. + # @return [String] The new environment URL with the subdomain added. + def add_subdomain_to_api_url_environment(environment, subdomain) + api_url = environment.base_uri + new_environment = api_url + + # Regex to match a subdomain consisting of 8 to 11 lowercase alphanumeric characters + if subdomain =~ /^[0-9a-z]{8,11}$/ + url_parts = URI.parse(api_url) + new_host = "#{subdomain}.#{url_parts.host}" + + port = url_parts.scheme == 'https' && url_parts.port == 443 ? nil : url_parts.port + + 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 + ) + + new_environment = new_url_parts.to_s + end + + new_environment + 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 d074a80..1985602 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 @@ -11,15 +11,17 @@ def build @secret_key_pattern = SECRET_KEY_PATTERN @public_key_pattern = PUBLIC_KEY_PATTERN super - CheckoutApi.new( - CheckoutConfiguration.new( - PreviousStaticKeysSdkCredentials.new(secret_key, public_key), - environment, - http_client, - multipart_http_client, - logger - ) + configuration = CheckoutConfiguration.new( + PreviousStaticKeysSdkCredentials.new(secret_key, public_key), + environment, + http_client, + multipart_http_client, + logger ) + + configuration.environment_subdomain = environment_subdomain if environment_subdomain + + CheckoutApi.new(configuration) end end end diff --git a/spec/checkout_sdk/configuration/configuration_spec.rb b/spec/checkout_sdk/configuration/configuration_spec.rb new file mode 100644 index 0000000..a97d596 --- /dev/null +++ b/spec/checkout_sdk/configuration/configuration_spec.rb @@ -0,0 +1,82 @@ +class FakeHttpClient +end + +class FakeLogger +end + +RSpec.describe CheckoutSdk::StaticKeysSdkCredentials do + before do + @secret_key = ENV['CHECKOUT_DEFAULT_SECRET_KEY'] + @public_key = ENV['CHECKOUT_DEFAULT_PUBLIC_KEY'] + @credentials = CheckoutSdk::StaticKeysSdkCredentials.new(@secret_key, @public_key) + @http_client = FakeHttpClient.new + @multipart_http_client = FakeHttpClient.new + @logger = FakeLogger.new + end + + it 'should create configuration' do + configuration = CheckoutSdk::CheckoutConfiguration.new( + @credentials, + CheckoutSdk::Environment.sandbox, + @http_client, + @multipart_http_client, + @logger + ) + + expect(configuration.credentials).to eq(@credentials) + expect(configuration.environment.base_uri).to eq(CheckoutSdk::Environment.sandbox.base_uri) + expect(configuration.environment.base_uri).to eq("https://api.sandbox.checkout.com/") + expect(configuration.http_client).to eq(@http_client) + expect(configuration.environment_subdomain).to be_nil + end + + [ + %w[123dmain https://123dmain.api.sandbox.checkout.com/], + %w[123domain https://123domain.api.sandbox.checkout.com/], + %w[1234domain https://1234domain.api.sandbox.checkout.com/], + %w[12345domain https://12345domain.api.sandbox.checkout.com/] + ].each do |subdomain, expected_url| + it "should create configuration with 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) + end + end + + [ + ['', 'https://api.sandbox.checkout.com/'], + %w[123 https://api.sandbox.checkout.com/], + %w[123bad https://api.sandbox.checkout.com/], + %w[12345domainBad 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) + end + end +end diff --git a/spec/checkout_sdk_spec.rb b/spec/checkout_sdk_spec.rb index 4382329..961e69f 100644 --- a/spec/checkout_sdk_spec.rb +++ b/spec/checkout_sdk_spec.rb @@ -19,6 +19,17 @@ expect(default_sdk.class).to eq(CheckoutSdk::CheckoutApi) end + it 'builds default sdk with both keys with subdomain' do + 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.production) + .with_environment_subdomain('12345domain') + .build + expect(default_sdk.class).to eq(CheckoutSdk::CheckoutApi) + end + it 'builds default sdk with secret key only' do default_sdk = CheckoutSdk.builder .static_keys @@ -27,6 +38,16 @@ .build expect(default_sdk.class).to eq(CheckoutSdk::CheckoutApi) end + + it 'builds default sdk with secret key only with subdomain' do + default_sdk = CheckoutSdk.builder + .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 end context 'when building default sdk with incorrect parameters' do @@ -69,6 +90,21 @@ expect(oauth_sdk.class).to eq(CheckoutSdk::CheckoutApi) end + it 'should build oauth sdk with default http client correctly with subdomain' do + oauth_sdk = CheckoutSdk.builder + .oauth + .with_client_credentials(ENV['CHECKOUT_DEFAULT_OAUTH_CLIENT_ID'], + ENV['CHECKOUT_DEFAULT_OAUTH_CLIENT_SECRET']) + .with_authorization_uri(CheckoutSdk::Environment.sandbox.authorization_uri) + .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) + end + it 'should build oauth sdk with custom http client correctly' do http_client = Faraday.new(CheckoutSdk::Environment.sandbox.base_uri) do |f| f.response(:raise_error) @@ -148,6 +184,18 @@ expect(default_sdk.class).to eq(CheckoutSdk::Previous::CheckoutApi) end + it 'builds previous sdk with both keys with subdomain' do + default_sdk = CheckoutSdk.builder + .previous + .static_keys + .with_secret_key(ENV['CHECKOUT_PREVIOUS_SECRET_KEY']) + .with_public_key(ENV['CHECKOUT_PREVIOUS_PUBLIC_KEY']) + .with_environment(CheckoutSdk::Environment.production) + .with_environment_subdomain('12345domain') + .build + expect(default_sdk.class).to eq(CheckoutSdk::Previous::CheckoutApi) + end + it 'builds previous sdk with secret key only' do default_sdk = CheckoutSdk.builder .previous