feat: require environment_subdomain or an explicit use_legacy_domain opt-out - #226
Conversation
🔴 Risk Classification: MAJORApproval route: AI Review + Human Approval Required Classification reasons
Operational gates
Files analysed: 15 wall-e 2026.06.19-02 · policy |
🔬 Debug — why this classification?Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.
Kinds:
See issue #3 for the proposal to formalise this map as Appendix A of the standards doc. wall-e 2026.06.19-02 · debug |
🟠 Advisory review: Concerns worth a lookThis PR needs a human approval. Before you give it, these are the things I'd want resolved. The PR enforces mandatory merchant-specific subdomains (or an explicit legacy-domain opt-out) for Default/OAuth SDK builders, with a correct deprecation path and tests. One concrete bug exists: Concerns
This is not an approval. wall-e cannot auto-approve this PR — it is an opinion to help whoever does. Advisory review · us.anthropic.claude-sonnet-4-6 · wall-e 2026.06.19-02 |
🔴 Risk Classification: MAJORApproval route: AI Review + Human Approval Required Classification reasons
Operational gates
Files analysed: 15 wall-e 2026.06.19-02 · policy |
🔬 Debug — why this classification?Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.
Kinds:
See issue #3 for the proposal to formalise this map as Appendix A of the standards doc. wall-e 2026.06.19-02 · debug |
🔴 Risk Classification: MAJORApproval route: AI Review + Human Approval Required Classification reasons
Operational gates
Files analysed: 18 wall-e 2026.06.19-02 · policy |
🔬 Debug — why this classification?Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.
Kinds:
See issue #3 for the proposal to formalise this map as Appendix A of the standards doc. wall-e 2026.06.19-02 · debug |
🔴 Risk Classification: MAJORApproval route: AI Review + Human Approval Required Classification reasons
Operational gates
Files analysed: 17 wall-e 2026.06.19-02 · policy |
🔬 Debug — why this classification?Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.
Kinds:
See issue #3 for the proposal to formalise this map as Appendix A of the standards doc. wall-e 2026.06.19-02 · debug |
🔴 Risk Classification: MAJORApproval route: AI Review + Human Approval Required Classification reasons
Operational gates
Files analysed: 14 wall-e 2026.06.19-02 · policy |
🔬 Debug — why this classification?Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.
Kinds:
See issue #3 for the proposal to formalise this map as Appendix A of the standards doc. wall-e 2026.06.19-02 · debug |
…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: set environment_subdomain, or call use_legacy_domain(), which raises a DeprecationWarning 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. environment_subdomain no longer needs environment() to be set first, since the EnvironmentSubdomain is now built when the configuration is assembled. That also removed the duplicated with/without-subdomain branches in all three build() methods. The Previous (ABC) platform predates merchant-specific subdomains and stays exempt via _requires_environment_subdomain(). Fixtures route clients through conftest.configure_domain, 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. The long import line in the APM test is wrapped only because pre-commit lints staged files, so touching that file surfaced a pre-existing violation. Mirrors checkout-sdk-net#590. Refs INT-1688.
Flagged in review: the test called use_legacy_domain() directly, so it emitted the deprecation warning on every run, inconsistent with every other fixture. It now goes through conftest.configure_domain like the rest.
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.
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.
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.
193295e to
042c2d1
Compare
🔴 Risk Classification: MAJORApproval route: AI Review + Human Approval Required Classification reasons
Operational gates
Files analysed: 15 wall-e 2026.06.19-02 · policy |
🔬 Debug — why this classification?Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.
Kinds:
See issue #3 for the proposal to formalise this map as Appendix A of the standards doc. wall-e 2026.06.19-02 · debug |
🔴 Risk Classification: MAJORApproval route: AI Review + Human Approval Required Classification reasons
Operational gates
Files analysed: 14 wall-e 2026.06.19-02 · policy |
🔬 Debug — why this classification?Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.
Kinds:
See issue #3 for the proposal to formalise this map as Appendix A of the standards doc. wall-e 2026.06.19-02 · debug |
…th client The dedicated sandbox clients are not provisioned for the merchant subdomain; the default client now carries every scope the suites need.
🔴 Risk Classification: MAJORApproval route: AI Review + Human Approval Required Classification reasons
Operational gates
Files analysed: 14 wall-e 2026.06.19-02 · policy |
🔬 Debug — why this classification?Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.
Kinds:
See issue #3 for the proposal to formalise this map as Appendix A of the standards doc. wall-e 2026.06.19-02 · debug |
…ault OAuth client" This reverts commit 7691d6d.
🔴 Risk Classification: MAJORApproval route: AI Review + Human Approval Required Classification reasons
Operational gates
Files analysed: 14 wall-e 2026.06.19-02 · policy |
🔬 Debug — why this classification?Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.
Kinds:
See issue #3 for the proposal to formalise this map as Appendix A of the standards doc. wall-e 2026.06.19-02 · debug |
…w findings - switch the subdomain regex check to re.fullmatch so values with a trailing newline are rejected, and cover it with a unit test - remove the unreachable base_uri accessor shadowed by the instance attribute in EnvironmentSubdomain - run the static-keys integration fixture through the merchant-specific subdomain (CHECKOUT_MERCHANT_SUBDOMAIN); the legacy-domain opt-out stays only for OAuth clients, with the sandbox OAuth provisioning gap as the rationale - validate the environment settings before the key validation so a shared misconfiguration surfaces the same first error as the other SDKs - build the environment-subdomain object once into a local in build() - reword the invalid-subdomain guidance to 'typically your client ID excluding the cli_ prefix' - README: note that Private Link merchants use their pl- prefixed subdomain, which the SDK also accepts
🔴 Risk Classification: MAJORApproval route: AI Review + Human Approval Required Classification reasons
Operational gates
Files analysed: 14 wall-e 2026.06.19-02 · policy |
🔬 Debug — why this classification?Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.
Kinds:
See issue #3 for the proposal to formalise this map as Appendix A of the standards doc. wall-e 2026.06.19-02 · debug |
🔴 Risk Classification: MAJORApproval route: AI Review + Human Approval Required Classification reasons
Operational gates
Files analysed: 14 wall-e 2026.06.19-02 · policy |
🔬 Debug — why this classification?Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.
Kinds:
See issue #3 for the proposal to formalise this map as Appendix A of the standards doc. wall-e 2026.06.19-02 · debug |
|



Summary
Makes the merchant-specific subdomain (MSSD) mandatory. Callers must now call
environment_subdomain(...), or explicitly opt out with the newuse_legacy_domain(), which raises aDeprecationWarningfrom its first release. Setting both, or neither, raisesCheckoutArgumentExceptionat build time. MSSD is no longer beta and non-MSSD usage will be deprecated, so the previous silent fallback toapi.checkout.comhad to go.Changes
checkout_sdk/checkout_sdk_builder.py— the subdomain is held as a string and theEnvironmentSubdomainis built when the configuration is assembled, soenvironment_subdomain()no longer has to be called afterenvironment(); newuse_legacy_domain(); new_validate_environment_settings()and_requires_environment_subdomain()checkout_sdk/environment_subdomain.py—create_url_with_subdomainraises on an invalid subdomain instead of returning the URL unchangedcheckout_sdk/default_sdk.py,oauth_sdk.py,previous/previous_sdk.py— validate before building; Previous/ABC exempted; the duplicated with/without-subdomain branches in all threebuild()methods are gonetests/checkout_default_sdk_test.py— covers all four combinations plus an invalid subdomaintests/checkout_configuration_test.py— the parameterised bad-subdomain case now asserts the raise instead of the silent fallbacktests/conftest.py+ five other fixtures — every client the suite builds now chooses a domain, throughconfigure_domainFixed along the way
environment_subdomain()used to build the URLs from whatever environment was set at call time, so calling it beforeenvironment()silently produced the wrong host.The long import line in
tests/payments/request_apm_payments_integration_test.pyis wrapped only because pre-commit lints staged files, so touching that file surfaced a pre-existingline-too-long.Verification
544 tests passing, 0 failing, 217 skipped.
flake8andpylintpre-commit hooks pass.API Reference
Breaking changes
Yes, two. This needs a major release, classified and versioned when the release is cut.
api.checkout.com/access.checkout.comnow fails at client construction. Migration: set the subdomain, or use the legacy-domain opt-out as a temporary measure. The Previous (ABC) platform is unaffected.README
Updated in this PR: a "Subdomain value" section above the Default example, the subdomain added to the configuration samples, and a "Legacy domain (emergency use only)" section at the bottom.
Notes
The suite routes every client it builds through a single helper that uses the shared hosts. Applying the merchant-specific subdomain there looked better, since it is the path merchants are being moved to, but the sandbox OAuth clients are not provisioned for it: .NET CI failed 224 integration tests with
invalid_clientwhen the token request went to{subdomain}.access.sandbox.checkout.com. Binding those OAuth clients to the subdomain is a platform task and should land before merchants are told the subdomain is mandatory.Reference implementation: checkout-sdk-net#590. Tracked as INT-1688.
No version bump here: that happens on master when the release is cut, per the release workflow.
Review follow-ups (2026-08-31)
Breaking changes, complete list:
Behaviour note: host resolution is now deferred to build time, which also fixes a latent order-dependence bug where setting the subdomain before the environment produced the wrong host.
Deprecation signal: runtime DeprecationWarning.
Option A applied (2026-08-31): an explicit OAuth authorization URI and the environment subdomain are now mutually exclusive at build time; the README OAuth example no longer sets an authorization URI, subdomain-only is the documented path.