TWO-24754/feat: Sole trader checkout for supported countries#326
TWO-24754/feat: Sole trader checkout for supported countries#326dgjlindsay wants to merge 3 commits into
Conversation
Buyers in countries where Two supports sole traders (per the new GET /registry/v1/supported-company-types/<ISO> endpoint, TWO-24753) can switch the checkout to sole-trader mode, register or log in through Two's hosted signup popup, and have their TWO:ST organization number autofilled from GET /autofill/v1/buyer/current. The order payload is unchanged — the backend derives the company type from the org-number prefix (TWO-24749 spike), mirroring the Magento reference flow. Two gates decide visibility, both server-side: the registry endpoint's country answer (session-cached for its Cache-Control max-age, failing soft to registered-business only) and a new enable_sole_trader admin toggle (default off). Business logic lives in WC_Twoinc_Sole_Trader with two wc-ajax endpoints; twoinc.js renders only (TWO-24767 posture). Delegation + autofill tokens are minted server-side with the merchant API key and read from the two-delegated-authority-token response header. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🖌 Pre-commit success 🏆DetailsExit code: 0 Author ✍️@dgjlindsay |
There was a problem hiding this comment.
Code Review
This pull request implements a sole trader checkout feature for the WooCommerce integration, adding a front-end toggle, hosted signup popup integration, and server-side logic to check country eligibility and mint delegated authority tokens. The review feedback highlights two important improvements: safely encoding the signup popup prefill data to prevent errors with non-ASCII characters when using btoa(), and securing the public ajax_tokens endpoint by verifying that the user has an active cart session to prevent token-minting abuse.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| "&autofillData=" + | ||
| encodeURIComponent(btoa(JSON.stringify(prefill))); |
There was a problem hiding this comment.
Using btoa() directly on a stringified JSON object can throw an InvalidCharacterError if any of the fields (such as the buyer's name, company name, or address) contain non-ASCII/UTF-8 characters (e.g., accented letters, smart quotes, or non-Latin characters). To safely encode the string to Base64 in a cross-browser compatible way, first encode it using encodeURIComponent and unescape before passing it to btoa().
| "&autofillData=" + | |
| encodeURIComponent(btoa(JSON.stringify(prefill))); | |
| "&autofillData=" + | |
| encodeURIComponent(btoa(unescape(encodeURIComponent(JSON.stringify(prefill))))); |
| if (!$gateway || !self::is_enabled($gateway)) { | ||
| wp_send_json_error('Sole trader checkout is not enabled'); | ||
| return; | ||
| } |
There was a problem hiding this comment.
The ajax_tokens endpoint is a public wc_ajax action that can be queried by any guest or bot. Since it performs external API requests to Two's servers to mint delegated authority tokens using the merchant's API key, it is highly recommended to verify that the user has an active checkout session with a non-empty cart. This prevents potential rate-limiting, key suspension, or resource exhaustion from automated token-minting spam.
if (!$gateway || !self::is_enabled($gateway)) {
wp_send_json_error('Sole trader checkout is not enabled');
return;
}
if (function_exists('WC') && (null === WC()->cart || WC()->cart->is_empty())) {
wp_send_json_error('Cart is empty');
return;
}Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The registry endpoint no longer advertises REGISTERED_BUSINESS — registered businesses need no registry enrollment, so the response now lists only enrollable types and an empty list is a valid answer meaning registered-business-only checkout. Empty/error fallbacks therefore resolve to an empty list instead of coercing to [REGISTERED_BUSINESS], and the now-unused constant is dropped. Behaviour is unchanged: the sole trader option shows iff SOLE_TRADER is present. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
What
Sole-trader checkout support (UK + US), stacked on #323. Buyers in countries where Two legally supports sole traders see a Registered company / Sole trader toggle in the payment box. Sole-trader mode suppresses company search, mints delegation + autofill tokens server-side, opens Two's hosted signup popup, and autofills the buyer's
TWO:ST…organization number fromGET /autofill/v1/buyer/current.Design
GET /registry/v1/supported-company-types/<ISO>(TWO-24753, must be deployed first) and a newenable_sole_traderadmin toggle (default off). Registry answers are session-cached for the endpoint's own Cache-Control max-age and fail soft to registered-business only — checkout never blocks on the registry.WC_Twoinc_Sole_Traderowns eligibility + token minting behind two wc-ajax endpoints (two_sole_trader_availability,two_sole_trader_tokens);twoincSoleTraderin twoinc.js is a presentation module mirroring the terms-chips split.TWO:STorg-number prefix carries the semantics and checkout-api enrichescompany_typeitself. The ticket's original "personal name + trading name fields" scope was superseded by the spike: those are collected by Two's hosted signup, exactly as in the Magento reference.two-delegated-authority-tokenresponse header, matching Magento's adapter. Popup completion arrives via postMessageACCEPTEDfrom the checkout-page origin.twoinc_sole_trader_signup_urlfilter lets brand overlays adjust the signup URL. WC-ABN itself needs no sole-trader work: the ABN product is NL-only and NL fails the registry gate.Tests
9 new unit tests: both-gates matrix, registry error/404/malformed-body fail-soft, malformed-country short-circuit, per-request caching, case-insensitive token-header read, fail-closed token pair, signup URL env + filter. Full suite green (
make test-unit).Deploy order
checkout-api TWO-24753 (PR 12277) must deploy before this releases; until then the registry call 404s and the toggle simply never shows (fail-soft path, manually exercised by the 404 unit test).
🤖 Generated with Claude Code