Multivaluta: architettura per il commercio senza confini
Come servire 150 valute senza danneggiare la cache CDN. Una guida approfondita all'internazionalizzazione (i18n), alla matematica in virgola mobile e all'Edge Routing.
La vendita a livello globale non è più una funzionalità “Enterprise”. Un ragazzino in un garage di Brooklyn può vendere magliette agli utenti di Tokyo. Ma mostrare “Prezzo: € 25,00” a un utente giapponese è un punto di attrito. Devono fare calcoli mentali (€ 25 * 150 = ¥ 3750?). Nel momento in cui introduci la multivaluta, introduci uno dei problemi più difficili dell’ingegneria web: il caching sensibile al contesto.
A Maison Code Paris, costruiamo architetture Global-First. Non consideriamo la valuta come un interruttore dell’interfaccia utente; lo consideriamo una dimensione fondamentale dello stato dell’applicazione, probabilmente altrettanto importante quanto l’URL stesso.
Perché Maison Code ne parla
In Maison Code Paris, agiamo come la coscienza architettonica dei nostri clienti. Spesso ereditiamo stack “moderni” costruiti senza una comprensione fondamentale della scala.
Discutiamo di questo argomento perché rappresenta un punto di svolta critico nella maturità ingegneristica. Implementarlo correttamente differenzia un MVP fragile da una piattaforma resiliente di livello aziendale.
Il paradosso della memorizzazione nella cache
In un’applicazione web standard, il tuo CDN (Cloudflare/Fastly) memorizza nella cache l’HTML della home page.
- L’utente A (USA) visita “example.com”.
- Il server esegue il rendering dell’HTML: “Prezzo: € 100”.
- La CDN memorizza nella cache questo HTML.
- L’utente B (Francia) visita “example.com” 1 minuto dopo.
- La CDN fornisce HTML memorizzato nella cache: “Prezzo: € 100”.
Questo è un disastro. L’utente B vede dollari. Aggiungono al carrello e forse il checkout passa in euro in un secondo momento, o forse se ne vanno semplicemente perché non vogliono pagare le commissioni di conversione. Per risolvere questo problema, abbiamo bisogno che il server sappia chi sta chiedendo. Ma se il server lo sa, la CDN non può servire solo un file statico.
Strategia 1: La sottocartella (Gold Standard)
La soluzione tecnica più efficace è rendere esplicita la valuta nell’URL.
example.com/en-us-> Serve USD.example.com/fr-fr-> Serve EUR.example.com/jp-jp-> Serve JPY.
Perché questo vince:
- Efficienza della cache:
/fr-frè un URL univoco. Possiamo memorizzarlo in cache in modo aggressivo al limite. Ogni utente francese ottiene un riscontro nella cache. - SEO: Googlebot sa esattamente quale valuta corrisponde a quale regione. Puoi impostare facilmente i tag “hreflang”.
- Chiarezza: l’utente sa dove si trova.
Implementazione in Remix/Hydrogen:
“tsx // app/routes/(€locale)._index.jsx esporta caricatore di funzioni asincrone ({ richiesta, contesto, parametri }) { const { locale } = parametri; // “fr-fr” valuta const = getCurrencyFromLocale(locale); // “EURO”
// Passa all’API Shopify const prodotti = attendono contesto.storefront.query(PRODUCTS_QUERY, { variabili: {paese: getCountryCode(valuta)} });
return json({ prodotti }); }
## Strategia 2: L'intestazione Vary (la soluzione dinamica)
Se *devi* mantenere l'URL pulito (`example.com` per tutti), devi istruire il CDN a memorizzare nella cache più versioni dello stesso URL.
Usiamo l'intestazione "Vary".
"Varia: CF-IPPaese".
Questo dice a Cloudflare: "Memorizza una copia separata di questa pagina per ogni codice paese univoco".
* Utente (USA) -> CDN cerca "Homepage + USA". Miss. Recupera dal server. Cache.
* Utente (FR) -> CDN cerca "Homepage + FR". Miss. Recupera dal server. Cache.
* Utente (USA) -> CDN cerca "Homepage + USA". **Colpo**.
**Lo svantaggio**: **Frammentazione della cache**.
Se supporti 200 paesi, dividi effettivamente il rapporto di riscontri della cache per 200. Il tuo server di origine richiede più carico.
## Strategia 3: pittura lato client (l'ibrido)
Per le pagine ad alte prestazioni, memorizzate rigorosamente nella cache, a volte memorizziamo nella cache lo **scheletro generico**.
L'HTML restituito non contiene prezzo né segnaposto.
Quindi, "useEffect" sul client recupera il prezzo locale.
"tsx
funzione Prezzo({ baseImporto }) {
const {valuta, tasso} = useCurrencyContext();
if (!rate) return <Scheletro />;
return <span>{formatMoney(baseAmount * rate, valuta)}</span>;
}
Attenzione: ciò causa un Layout Shift (CLS) e un “Flash di contenuto senza prezzo”. Sembra economico. Utilizzare solo come ultima risorsa.
L’architettura dei mercati Shopify
Nell’ecosistema Shopify, utilizziamo l’API Contestual Storefront. Non calcoli tu stesso i tassi di cambio. Lascia che sia il backend di Shopify a gestirlo. Perché? Perché Shopify consente ai commercianti di impostare Tassi di cambio fissi o Adeguamenti dei prezzi per mercato. Forse la maglietta costa € 20 negli Stati Uniti, ma € 25 in Europa (per coprire IVA e spedizione), non solo una conversione diretta di € 20 * 0,92.
# La Direttiva Magica: @inContext
query GetProduct($handle: String!, $country: CountryCode!)
@inContext(paese: $paese) {
prodotto(handle: $handle) {
fascia di prezzo {
minVariantPrezzo {
importo
valutaCodice # Restituisce automaticamente la valuta locale
}
}
}
}
Passando il codice paese, scarichiamo la complessità della logica dei prezzi sul motore della piattaforma.
Matematica monetaria: incubi in virgola mobile
Non utilizzare mai numeri JavaScript standard per denaro.
0,1 + 0,2 === 0,30000000000000004
Se stai costruendo un carrello personalizzato, perderai un centesimo ogni 10 ordini. Oltre un milione di ordini, ovvero € 10.000 persi a causa di errori in virgola mobile. Regola: archivia il denaro in centesimi (numeri interi).
- € 10,00 ->
1000 - 19,99 € ->
1999
Utilizza librerie come “Dinero.js” o “Currency.js” per tutti i calcoli lato client.
importa Dinero da 'dinero.js';
prezzo costante = Dinero({ importo: 5000, valuta: 'EUR' }); //€ 50,00
imposta costante = prezzo.percentuale(20); // IVA
const totale = prezzo.add(tassa);
Prezzi e arrotondamenti psicologici
La conversione algoritmica è brutta.
- € 100,00 * 0,92 = € 92,00.
- Ma € 92,00 sembrano “casuali”. *Il prezzo psicologico dovrebbe probabilmente essere di 95,00€ o 89,00€.
Implementiamo strategie di arrotondamento a livello di applicazione (se non gestite dal backend).
“dattiloscritto”. funzione roundToCharmPrice(importo: numero): numero { // 92,34 -> 95,00 // 98.10 -> 99.00 const heavyPart = Math.floor(importo); const lastDigit = heavyPart % 10;
if (lastDigit < 5) restituisce heavyPart - lastDigit + 5; // Arrotonda a 5
return heavyPart - lastDigit + 9; // Arrotonda a 9
}
*Nota: solitamente consigliamo ai clienti di impostarli esplicitamente in Shopify Markets, ma avere una logica di fallback è utile per il raggruppamento dinamico.*
## SEO: i metadati del denaro
Google non riesce a indicizzare i tuoi prezzi se sono dinamici (inseriti da JS).
Se utilizzi la Strategia 1 (sottocartelle), i dati strutturati JSON-LD standard funzionano perfettamente.
```html
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Prodotto",
"nome": "Orologio di lusso",
"offerte": {
"@type": "Offerta",
"priceCurrency": "EUR",
"prezzo": "5000,00"
}
}
</script>
Assicurati che questo blocco corrisponda al contenuto visibile. Se JSON-LD indica EUR e il testo visibile indica USD (a causa del passaggio lato client), Google Merchant Center escluderà il tuo account per “Mancata corrispondenza del prezzo”.
13. Il rischio valutario del rimborso
L’utente acquista per € 100 ($ 110). La prossima settimana il dollaro crolla. 100€ ora sono 120$. L’utente richiede il rimborso. Rimborserai € 100 (il cliente è felice, tu perdi $ 10)? Oppure rimborsi $ 110 convertiti in euro (€ 92) (il cliente è furioso)? Risposta legale: rimborsare l’importo in valuta originale (€100). Risposta finanziaria: ti assumi il rischio FX. Costruiamo la logica delle “Riserve di rimborso” nel registro per tenere conto di questa fluttuazione come “COGS - Perdita FX”.
14. Copertura e gestione della tesoreria
Se vendi 10 milioni di dollari in euro, detieni molti euro. Se l’euro crolla, perdi denaro prima di poter pagare la tua fabbrica americana. È qui che entra in gioco la copertura automatizzata. Ci integriamo con Wise Business API o Airwallex. Quando il saldo è > 10.000 €, converti automaticamente in USD. Questa strategia di “Micro-Hedging” riduce l’esposizione agli eventi macroeconomici.
15. Rifiuti del gateway di pagamento (mancata corrispondenza di valuta)
Stripe ti consente di caricare in qualsiasi valuta. Ma alcune banche locali (ad esempio in Brasile o India) rifiutano le transazioni in “valuta estera” anche se la carta la supporta. La soluzione: tentativi intelligenti. Se un addebito fallisce con “do_not_honor”, riprova immediatamente nella valuta di emissione della carta (se rilevabile tramite BIN). “Hai provato ad addebitare $ 100. Fallito. Riprova ad addebitare € 92. Successo.” Ciò migliora i tassi di autorizzazione del 4%.
16. Contrassegno (COD) e valuta
Nel Medio Oriente (GCC), il 60% degli ordini sono COD. Il corriere ritira i contanti. Il corriere non accetta USD. Accettano solo AED/SAR. Se indichi USD alla cassa, il corriere arriverà e ti chiederà un importo diverso (tasso di cambio del giorno). Regola: se viene selezionato COD, DEVI bloccare il prezzo nella valuta locale al momento del pagamento e stamparlo sul manifesto fisico. La discrepanza in questo caso porta a “Consegna rifiutata”.
17. Conclusione
La multivaluta non è un “Plug-in”. È una decisione architetturale fondamentale che tocca il routing, la memorizzazione nella cache, la progettazione del database e il SEO. Noi di Maison Code crediamo che il vero commercio senza confini sia invisibile. L’utente atterra, vede la sua valuta, paga con il metodo locale e riceve la merce. La complessità è un peso nostro, non loro.
Diventare globali?
Se hai problemi con le intestazioni “Vary” o con cache danneggiate. Assumi i nostri architetti.