Markets
One WooCommerce install per market (ADR-007), one frontend deployment for all of them. A market is
selected by Host, never by locale or cookie. Shared code contains no country conditionals
(invariant 4); everything market-specific is one of the three levels below.
Three levels
| Level | Where | What | A change is |
|---|---|---|---|
| 1 · Values | plugin/config/markets/{CODE}.php and frontend/markets/{code}.ts | currency, locale, COD limits, VAT rules, gateway list, carriers, phone prefixes, rounding step; UPN IBAN and receiver | a config PR, no logic review |
| 2 · Behaviour | one named strategy class in plugin/src/**/Service/Market/, selected by config | the rare case where a market needs a different algorithm, not a different number (cash rounding, customs) | a code PR, one class, with tests. Chosen by configuration, never by if. |
| 3 · Rollout | NF_FF_* env + nf_commerce_flags | switching a finished feature on for some stores before the rest | a Doppler change. A flag that is on for a year is a bug. |
Test: if two markets would want a different value, level 1. Different code, level 2. Same for all and just unfinished, level 3.
What defines a market
| Piece | Lives in | Holds |
|---|---|---|
plugin/config/markets/{CODE}.php | nf-commerce | Level-1 values WooCommerce needs at runtime. |
frontend/markets/{code}.ts | frontend | Static display facts: public host, WP_HOME host, locale, currency formatting, legal entity. What the store can do right now is not here — it comes from GET /nf/v1/capabilities at request time, so a store on an older plugin version serves its market safely. |
store-template .env per install | Doppler | WP_HOME, DB, X-NF-Key, Cloudflare zone token, PSP credentials. |
| DNS | Cloudflare zone | Public host → frontend cluster; wp.{market} host → WordPress host. |
| Cloudflare zone config | infra/cloudflare/ as code | Cache Rules (key host+path+_rsc, bypass list), URL rule stripping marketing params, Access on /wp/wp-admin, WAF, Turnstile. Plan tier per zone is an owner decision. |
proxy.ts | frontend | Resolves Host → market and sets Cache-Control. |
Adding a market
bin/new-market.sh {CODE} scaffolds steps 1–3; the rest is a checklist until automated.
plugin/config/markets/{CODE}.php— values, gateway list (from F1), carriers, UPN data if BACS.frontend/markets/{code}.ts— hosts, locale, currency formatting;next-intlcatalog for the language if new.contracts/fixtures/nf/capabilities/{code}.json.- Provision the install from
store-template(wp nf provisiononce it exists) with the market’s.envfrom Doppler;composer.jsonis shared — a market never adds a plugin alone. - DNS: public host → frontend;
wp.{market}→ WordPress host. Cloudflare zone frominfra/cloudflare/(cache rules, bypass rules, Access, WAF). - PSP accounts and webhooks per gateway in the market’s list; return URLs point at
wp.{market}. verify:*clean, Phase 0curl -sIgates pass on the new host, then the phase-6 wave.
Open
| Id | Question | Owner | Status |
|---|---|---|---|
| M3 | Pilot market — the first store rebuilt and cut over. Names the gateway set, locales and e-mail templates for Phases 3–5; unplaces DNK quiz (C18) and answers-endpoint until named. | business | open (Phase −1 proposal) |
| F1 | Gateway × market matrix: Stripe, Mollie, BLIK, PayPal, Bankart, UPN, COD per market. Known only for SI (6 active gateways); needs the fleet. | business + PHP | open (Phase 3 at the latest) |
| O1 | WordPress hosting for 28 stores. | business | open |
| — | Cloudflare plan tier per zone. | business | open |