TWO-24745/feat: Base brand seams for the ABN overlay migration#321
Open
dgjlindsay wants to merge 10 commits into
Open
TWO-24745/feat: Base brand seams for the ABN overlay migration#321dgjlindsay wants to merge 10 commits into
dgjlindsay wants to merge 10 commits into
Conversation
The fork-divergence inventory found five things the brand layer (C1) cannot yet express that the ABN overlay needs. Add them to the base so the overlay stays thin: - Brand meta prefix: order meta (_twoinc_order_reference, _twoinc_ merchant_id, _twoinc_order_state, _twoinc_req_body_hash, twoinc_order_id), user meta (twoinc_company_id etc.), confirmation request params and the confirmation nonce action all derive from the brand's meta_prefix via WC_Twoinc_Brand::meta_key()/prefixed_name(). Live ABN stores hold data under _abn_* — the prefix is load-bearing for existing orders, so the overlay sets meta_prefix=abn and inherits its installed base's data. Two default 'twoinc': zero behaviour change. The _tillit_* legacy fallback reads stay literal (Two-only history). HTML form input names and $_POST keys stay literal (transient, not data-bearing). - Brand availability gate: config-driven woocommerce_available_payment_ gateways filter (front-end only, inclusive minimum, currency + billing-country match) mirroring the ABN fork's EUR 250/EUR/NL gate semantics. Two brand sets no gate. - twoinc_payment_validation_error filter: a brand overlay vetoes process_payment with a buyer-facing error (ABN's required terms checkbox) without overriding the method. - twoinc_payment_description filter: overlays replace the payment-box copy wholesale (ABN ships its own bullet list). - supported_buyer_countries brand key feeding the checkout JS config (ABN is NL-only). Tests: prefix derivation under an overlay brand, confirmation-URL params following the prefix, gate absent/unmet/exact-minimum, and the validation veto. 18/18 green on PHP 7.4 + 8.2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
🖌 Pre-commit success 🏆DetailsExit code: 0 Author ✍️@dgjlindsay |
Review findings (adversarial round 1): - Log (WC logger, once per request) when the availability gate removes the gateway, with the failing basket and the gate criteria — removing a payment method is invisible to the merchant and a config typo would otherwise read as the gateway vanishing (same lesson as the Magento MinimumOrderGate review). - Guard the gate against a truthy-but-partial availability_gate shape: missing criteria means config bug, not a basket decision — leave the gateway available rather than judging with absent keys. - Document the registration deadline on twoinc_payment_description (applied once at gateway construction; register by plugins_loaded, same contract as twoinc_brand_file). - Test the confirmation READ side: is_confirmation_page() must detect params under the brand's prefix and ignore another brand's — the read side is the half that strands in-flight orders if it drifts from the write side. Reviewer-verified (no change needed): fork-created ABN orders fulfil end-to-end under base+overlay; the prefix sweep is complete repo-wide; the Two-brand path is byte-identical; rollback is safe. Known C2 note: an overlay terms-line filter that doesn't reproduce the fork body byte-for-byte costs one benign self-healing re-sync PUT per pre-cutover order on first edit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… about-block seam Two more divergences the overlay inventory surfaced once the gateway id became brand-driven: - twoinc.js and admin.js hardcoded 'woocommerce-gateway-tillit' in 17 selectors (payment-method radio, payment box, settings field ids) — under an overlay's gateway id none of the checkout or settings JS would bind. Both now read the id from their localized config objects (window.twoinc.gateway_id / twoinc_admin.gateway_id). - twoinc_about_html filter: the about block inside the payment-box subtitle is the piece of the description the ABN edition actually replaces (its own bullet list, no link). Narrower seam than rebuilding the whole description; twoinc_payment_description also now passes the gateway instance for get_option reads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The overlay review's deleted-functionality audit found one regression: init_form_fields hardcoded the Two title default, so a FRESH install of a brand overlay would show 'Business invoice - %s days' instead of the brand's phrasing (the ABN fork defaulted to 'Achteraf betalen - Bestel op factuur'). Existing stores are unaffected (saved titles win). The default now comes from the title_default brand key. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 tasks
The funding partner's server-side risk rule (risk-engine funding_partner_rules) compares NET order value against the EUR 250 threshold; the gate compared the gross cart total, so a ~EUR 250-gross basket (net ~EUR 207 at 21% VAT) showed the method and was then declined at credit check. The gate now subtracts the cart tax. The fork's gross compare was itself misaligned with the risk rule, so this deliberately goes beyond fork parity (confirmed by Doug; risk rule is the source of truth — its missing FX conversion is ABN-446). Gate log line reports the net value; brands/two.php documents the semantics. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…declines Mirror of the Magento change: when a rejected order's net value is below the brand's minimum or within 5% above it, append 'Note: <brand> requires a minimum order value of <amount> <currency> excluding tax.' to the decline message (checkout banner + merchant order note). Same-currency baskets only — WooCommerce has no FX rate source, and the gate already restricts the method to the gate currency anyway. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mirror of the Magento changes (Doug's minimum-order review): - availability_gate is a four-part config: amount + currency + basis (net|gross, explicit) + billing countries. The gate compares the basket on the declared basis. - Decline hint keyed primarily on the API's machine-readable decline_reason (ORDER_BELOW_MIN_INVOICE_AMOUNT), strict below-minimum fallback — the 5% band is gone (Doug: accept the corner case). Wording: 'Minimum order value is <symbol><amount> excluding|including tax.' in the order currency. - Merchant-set Minimum Order Value setting: dynamic description shows the platform minimum it must exceed; validate_..._field rejects values at or below the floor; the gate enforces platform AND merchant minima (merchant minimum rides the platform currency/basis when one exists, else store currency, gross; foreign-currency baskets fail open on the merchant's own bar — no FX source in WC). - Fixed three sprintf formats I'd written as '%1\$s' in single-quoted strings — \$ is only an escape in double quotes, so the literal backslash reached sprintf (ValueError on PHP 8.2; 7.4 was silently lenient). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The merchant-set Minimum Order Value is now interpreted in the STORE currency (the saved woocommerce_currency option, not the active multicurrency display currency) rather than the platform minimum's currency: - Settings description shows the platform floor with its currency symbol; when the store currency differs the floor is shown in its native currency only — WooCommerce has no FX source until TWO-24776, so no converted figure can honestly be displayed. - Validation compares against the floor only when the store currency matches the platform minimum's; otherwise the numeric floor check is skipped on save and both minima are enforced independently at checkout (gate fails open on the merchant's own bar cross-currency, platform gate still applies). - New unit test covers the cross-currency skip; bootstrap gains get_option/get_woocommerce_currency_symbol stubs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ncy label, version line
Mirrors tonight's Magento changes so the WooCommerce plugin reads the
same single source of truth:
- get_platform_minimum_order() resolves min_order_amount/currency/basis
from GET /v1/merchant/{id} (the value checkout-api enforces at order
create/intent), option-cached for 15 minutes following the
get_days_on_invoice() pattern; the no-minimum outcome is cached too,
and a fetch failure resolves to no minimum (server still enforces).
- The availability gate, settings description, save-time floor
validation and decline hint all read the API tuple; the brand
config's availability_gate shrinks to a billing-country restriction.
Each minimum is compared on its own basis.
- New 'Minimum Order Value Tax Basis' select (gross/net, default gross)
feeding the merchant minimum; the value field's label carries the
store currency ('Minimum Order Value, EUR').
- Settings page footer shows plugin/WooCommerce/WordPress versions,
mirroring the Magento and PrestaShop version panels.
The availability gate judged the session cart unconditionally, so on the pay-for-order page (where the cart is empty or unrelated to the order being paid) any configured minimum removed the gateway from the payment-link flow. Skip the value checks when there is no live basket to judge; the billing-country gate still applies and the API still enforces the platform minimum at order creation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
C1.5 of the plugin parity plan: the base-plugin seams the ABN overlay (TWO-24745 / C2) needs but the C1 brand layer could not express. Driven by an exhaustive fork-vs-base divergence inventory of
woocommerce-abn-plugin.Stacked on #320 — merge that first; base retargets when it lands.
Why these seams
The inventory found the four C1 hooks + brand keys cover identity and payload shape, but five divergences are hardcoded in base code paths:
meta_prefixbrand key routed throughWC_Twoinc_Brand::meta_key()/prefixed_name()(order meta, user meta, confirmation params, nonce action)_abn_*order meta. Without the prefix following the brand, every existing ABN order's reference/state/hash becomes unreadable after migration. The confirmation return-side (abn_order_reference/abn_nonceparams,abn_confirm_nonce) is the same prefix family — the outboundtwoinc_confirmation_urlfilter alone could not cover the read side.availability_gatebrand config +woocommerce_available_payment_gatewaysfilter['min_order_amount' => 250, 'currency' => 'EUR', 'billing_countries' => ['NL']].twoinc_payment_validation_errorfilter inprocess_paymentprocess_payment().twoinc_payment_descriptionfiltersupported_buyer_countriesbrand key["NL"].Two brand values reproduce today's behaviour exactly (
meta_prefix=twoinc, no gate, default countries);_tillit_*legacy fallbacks and HTML form input names stay literal (not data-bearing).Test plan
php -lall changed files🤖 Generated with Claude Code