From f8a3dea3e2b55cd6370c0a4a49e75006c5ce1e94 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, 5 May 2026 16:41:44 +0200 Subject: [PATCH 1/3] fix: use correct base URLs for forward and identity services --- checkout_sdk/checkout_api.py | 23 +++++++++++++++++------ checkout_sdk/environment.py | 8 ++++++++ checkout_sdk/environment_subdomain.py | 2 +- tests/checkout_configuration_test.py | 23 +++++++++++++++++++++-- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/checkout_sdk/checkout_api.py b/checkout_sdk/checkout_api.py index 6fbc5552..07230fb4 100644 --- a/checkout_sdk/checkout_api.py +++ b/checkout_sdk/checkout_api.py @@ -59,10 +59,20 @@ def _balances_api_client(configuration: CheckoutConfiguration) -> ApiClient: return ApiClient(configuration, configuration.environment.balances_uri) +def _forward_api_client(configuration: CheckoutConfiguration) -> ApiClient: + return ApiClient(configuration, configuration.environment.forward_uri) + + +def _identity_api_client(configuration: CheckoutConfiguration) -> ApiClient: + return ApiClient(configuration, configuration.environment.identity_uri) + + class CheckoutApi(CheckoutApmApi): def __init__(self, configuration: CheckoutConfiguration): base_api_client = _base_api_client(configuration) + forward_api_client = _forward_api_client(configuration) + identity_api_client = _identity_api_client(configuration) super().__init__(base_api_client, configuration) self.tokens = TokensClient(api_client=base_api_client, configuration=configuration) self.customers = CustomersClient(api_client=base_api_client, configuration=configuration) @@ -87,16 +97,17 @@ def __init__(self, configuration: CheckoutConfiguration): self.issuing = IssuingClient(api_client=base_api_client, configuration=configuration) self.contexts = PaymentContextsClient(api_client=base_api_client, configuration=configuration) self.payment_sessions = PaymentSessionsClient(api_client=base_api_client, configuration=configuration) - self.forward = ForwardClient(api_client=base_api_client, configuration=configuration) + self.forward = ForwardClient(api_client=forward_api_client, configuration=configuration) self.setups = PaymentSetupsClient(api_client=base_api_client, configuration=configuration) self.agentic_commerce = AgenticCommerceClient(api_client=base_api_client, configuration=configuration) self.apple_pay = ApplePayClient(api_client=base_api_client, configuration=configuration) self.google_pay = GooglePayClient(api_client=base_api_client, configuration=configuration) self.standalone_account_updater = StandaloneAccountUpdaterClient(api_client=base_api_client, configuration=configuration) - self.aml_screening = AmlScreeningClient(api_client=base_api_client, configuration=configuration) - self.face_authentication = FaceAuthenticationClient(api_client=base_api_client, configuration=configuration) - self.id_document_verification = IdDocumentVerificationClient(api_client=base_api_client, + self.aml_screening = AmlScreeningClient(api_client=identity_api_client, configuration=configuration) + self.face_authentication = FaceAuthenticationClient(api_client=identity_api_client, configuration=configuration) + self.id_document_verification = IdDocumentVerificationClient(api_client=identity_api_client, configuration=configuration) - self.applicants = ApplicantsClient(api_client=base_api_client, configuration=configuration) - self.identity_verification = IdentityVerificationClient(api_client=base_api_client, configuration=configuration) + self.applicants = ApplicantsClient(api_client=identity_api_client, configuration=configuration) + self.identity_verification = IdentityVerificationClient(api_client=identity_api_client, + configuration=configuration) diff --git a/checkout_sdk/environment.py b/checkout_sdk/environment.py index cc38fa33..7c7a1932 100644 --- a/checkout_sdk/environment.py +++ b/checkout_sdk/environment.py @@ -8,12 +8,16 @@ def __init__(self, base_uri, files_uri, transfers_uri, balances_uri, + forward_uri, + identity_uri, is_sandbox): self.base_uri = base_uri self.authorization_uri = authorization_uri self.files_uri = files_uri self.transfers_uri = transfers_uri self.balances_uri = balances_uri + self.forward_uri = forward_uri + self.identity_uri = identity_uri self.is_sandbox = is_sandbox @staticmethod @@ -23,6 +27,8 @@ def sandbox(): files_uri='https://files.sandbox.checkout.com/', transfers_uri='https://transfers.sandbox.checkout.com/', balances_uri='https://balances.sandbox.checkout.com/', + forward_uri='https://forward.sandbox.checkout.com/', + identity_uri='https://identity-verification.sandbox.checkout.com/', is_sandbox=True) @staticmethod @@ -32,4 +38,6 @@ def production(): files_uri='https://files.checkout.com/', transfers_uri='https://transfers.checkout.com/', balances_uri='https://balances.checkout.com/', + forward_uri='https://forward.checkout.com/', + identity_uri='https://identity-verification.checkout.com/', is_sandbox=False) diff --git a/checkout_sdk/environment_subdomain.py b/checkout_sdk/environment_subdomain.py index 63351caf..290baa37 100644 --- a/checkout_sdk/environment_subdomain.py +++ b/checkout_sdk/environment_subdomain.py @@ -25,7 +25,7 @@ def create_url_with_subdomain(original_url: str, subdomain: str): """ new_environment = original_url - regex = r'^[0-9a-z]+$' + regex = r'^[a-z0-9]+(-[a-z0-9]+)*$' if re.match(regex, subdomain): url_parts = urlparse(original_url) if url_parts.port: diff --git a/tests/checkout_configuration_test.py b/tests/checkout_configuration_test.py index 46203b58..1005601a 100644 --- a/tests/checkout_configuration_test.py +++ b/tests/checkout_configuration_test.py @@ -31,6 +31,20 @@ def test_should_create_configuration(): assert configuration.environment_subdomain is None +def test_environment_sandbox_urls(): + env = Environment.sandbox() + assert env.base_uri == "https://api.sandbox.checkout.com/" + assert env.forward_uri == "https://forward.sandbox.checkout.com/" + assert env.identity_uri == "https://identity-verification.sandbox.checkout.com/" + + +def test_environment_production_urls(): + env = Environment.production() + assert env.base_uri == "https://api.checkout.com/" + assert env.forward_uri == "https://forward.checkout.com/" + assert env.identity_uri == "https://identity-verification.checkout.com/" + + @pytest.mark.parametrize( "subdomain, expected_url", [ @@ -38,7 +52,9 @@ def test_should_create_configuration(): ("ab", "https://ab.api.sandbox.checkout.com/"), ("abc", "https://abc.api.sandbox.checkout.com/"), ("abc1", "https://abc1.api.sandbox.checkout.com/"), - ("12345domain", "https://12345domain.api.sandbox.checkout.com/") + ("12345domain", "https://12345domain.api.sandbox.checkout.com/"), + ("test-123", "https://test-123.api.sandbox.checkout.com/"), + ("pl-abc123", "https://pl-abc123.api.sandbox.checkout.com/") ] ) def test_should_create_configuration_with_subdomain(subdomain, expected_url): @@ -74,7 +90,10 @@ def test_should_create_configuration_with_subdomain(subdomain, expected_url): (" ", "https://api.sandbox.checkout.com/"), (" - ", "https://api.sandbox.checkout.com/"), ("a b", "https://api.sandbox.checkout.com/"), - ("ab c1.", "https://api.sandbox.checkout.com/") + ("ab c1.", "https://api.sandbox.checkout.com/"), + ("foo-", "https://api.sandbox.checkout.com/"), + ("-foo", "https://api.sandbox.checkout.com/"), + ("FooBar", "https://api.sandbox.checkout.com/") ] ) def test_should_create_configuration_with_bad_subdomain(subdomain, expected_url): From fc672e237f4877fedc9413a81535909edf98064b 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, 5 May 2026 16:41:52 +0200 Subject: [PATCH 2/3] chore: add .claude/ and CLAUDE.md to .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index be5b848e..6fa2d975 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ htmlcov .cursor/rules/ .cursor/skills/ .vscode/settings.json + +.claude/ +CLAUDE.md From 0d7fc87b4f94d2a504d94d3222262a208aef5513 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, 11 May 2026 17:01:08 +0200 Subject: [PATCH 3/3] fix: tighten subdomain regex to PrivateLink prefix format Per the AWS PrivateLink docs (https://www.checkout.com/docs/developer-resources/api/private-connections/aws-privatelink), the valid subdomain is the first eight characters of the client_id (alphanumeric only), optionally with the literal pl- prefix when calling through PrivateLink. Tighten the regex from RFC-1123-style hyphenated to ^(?:pl-)?[a-z0-9]+$ and update the test corpus: test-123 moves to the rejected list, pl-vkuhvk4v (the docs example) joins the accepted list, and pl-, foo-bar are added as rejected. --- checkout_sdk/environment_subdomain.py | 2 +- tests/checkout_configuration_test.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/checkout_sdk/environment_subdomain.py b/checkout_sdk/environment_subdomain.py index 290baa37..ff6be2d8 100644 --- a/checkout_sdk/environment_subdomain.py +++ b/checkout_sdk/environment_subdomain.py @@ -25,7 +25,7 @@ def create_url_with_subdomain(original_url: str, subdomain: str): """ new_environment = original_url - regex = r'^[a-z0-9]+(-[a-z0-9]+)*$' + regex = r'^(?:pl-)?[a-z0-9]+$' if re.match(regex, subdomain): url_parts = urlparse(original_url) if url_parts.port: diff --git a/tests/checkout_configuration_test.py b/tests/checkout_configuration_test.py index 1005601a..db197d2b 100644 --- a/tests/checkout_configuration_test.py +++ b/tests/checkout_configuration_test.py @@ -53,7 +53,7 @@ def test_environment_production_urls(): ("abc", "https://abc.api.sandbox.checkout.com/"), ("abc1", "https://abc1.api.sandbox.checkout.com/"), ("12345domain", "https://12345domain.api.sandbox.checkout.com/"), - ("test-123", "https://test-123.api.sandbox.checkout.com/"), + ("pl-vkuhvk4v", "https://pl-vkuhvk4v.api.sandbox.checkout.com/"), ("pl-abc123", "https://pl-abc123.api.sandbox.checkout.com/") ] ) @@ -93,7 +93,10 @@ def test_should_create_configuration_with_subdomain(subdomain, expected_url): ("ab c1.", "https://api.sandbox.checkout.com/"), ("foo-", "https://api.sandbox.checkout.com/"), ("-foo", "https://api.sandbox.checkout.com/"), - ("FooBar", "https://api.sandbox.checkout.com/") + ("FooBar", "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/") ] ) def test_should_create_configuration_with_bad_subdomain(subdomain, expected_url):