@webround/commerce
La logica di uno store, in TypeScript, senza interfaccia e senza framework (ESM, Node 22 o successivo).
Crea lo store
import { createCommerce } from '@webround/commerce';
const commerce = createCommerce({
storeId: '<id dello store>',
commerceApiUrl: 'https://commerce-api.webround.com',
catalogApiUrl: 'https://catalog-api.webround.com',
checkoutApiUrl: 'https://checkout-api.webround.com',
});
createCommerce costruisce insieme ciò che a una pagina serve di uno store: auth, catalog, cart, checkout, account, addresses, orders, returns, wishlist, reviews e la cache che condividono. Senza checkoutApiUrl lo store funziona e il checkout fallisce dove lo usi, dicendo perché. Altre opzioni: returnUrls (dove torna il cliente dopo aver pagato), cartStorage (dove si tiene il carrello di un ospite: localStorage di base), langCode e cartPricing.
Catalogo
commerce.catalog ha le chiamate dell'API del catalogo e restituisce ciò che risponde:
products.search(input): una pagina di prodotti ({ data, pagination }).products.getBySlug(variantSlug, input)eproducts.getById(id, input): un prodotto con le sue varianti. Lo slug appartiene a una variante, non a un prodotto.products.getReviews(...)eproducts.getRelated(...).storeData.get(...): i dati dello store (tag, collezioni, filtri, valute, e le impostazioni di recensioni e lista desideri), da cui si disegnano i controlli.
La cache tiene le risposte per tutta la visita: la stessa domanda fatta da più punti è una sola richiesta, un errore non viene mai tenuto, e commerce.catalog.invalidate(...) dimentica ciò che vuoi richiedere.
Lo shop
Le regole di uno shop (cosa ha scelto un cliente e come diventa una ricerca) sono funzioni pure su ShopState: una collezione, un testo, valori di tag, un intervallo per ogni filtro, un ordinamento e una pagina. Ogni modifica restituisce un nuovo stato (setRange, toggleTagValue, setSort…) e riporta alla pagina 1, tranne setPage. toSearch(state, storeData, context) lo trasforma nella ricerca del catalogo e non lancia mai errori. createShopStore tiene lo stato in memoria, o nella barra degli indirizzi con createAddressShopStore, così una ricerca è un link e "indietro" ripercorre le scelte. Nell'indirizzo, q è il testo, tags i valori di tag, r.<filtro>=min..max un intervallo, sort=<campo>:<asc|desc> l'ordine, e page e limit.
Clienti
commerce.auth è la sessione: signup, login, verifyTwoFactor, logout, refresh. Lo stato è unknown, signedOut, signingIn, challenge (serve il codice dell'app di autenticazione) o signedIn.
- Il token (JWT) del cliente dura un'ora e si tiene in memoria. Ciò che sopravvive a un ricaricamento è il cookie di refresh dello store, che JavaScript non vede mai.
- Il token si rinnova un minuto prima della scadenza, e di nuovo se una richiesta torna
401. Molte richieste rifiutate insieme sono un solo rinnovo. - Una chiamata che fallisce lancia ciò che ha detto lo store: spetta al form che l'ha fatta.
- L'autenticazione a due fattori ne fa parte, come le richieste del cliente sul proprio account (
commerce.account): reset della password, verifica dell'email, cancellazione dell'account, configurazione dei due fattori e newsletter.
Carrello
commerce.cart funziona in due modi con le stesse chiamate:
- Un ospite ha le righe nel browser (
wrCart_<storeId>inlocalStorage), e lo store le prezza. - Un cliente ha il carrello dello store: ogni azione è una chiamata. Quando un ospite fa il login, le sue righe vengono unite al carrello del cliente, e dimenticate nel browser solo quando lo store le ha.
Le azioni sono add({ variantId, priceId, quantity }) (aggiunge a una variante già presente), setQuantity(variantId, quantity), remove(variantId), clear() e refresh(). Lo stato ha ciò che il carrello è (lines, count), noto subito, a parte ciò che costa (priced, e isStale quando è indietro di una modifica). Un carrello ha al massimo 99 varianti diverse e una sola valuta e cadenza; un'azione rifiutata lancia ciò che ha detto lo store, e il carrello torna a ciò che lo store ha.
Checkout
commerce.checkout usa l'API del checkout, con lo stesso token del cliente.
quote()è l'anteprima: quanto costa il carrello con le regole dello store e gli hook. Non cambia niente e non viene mai messa in cache: chiederla di nuovo annulla quella in corso. Le righe senza giacenza sufficiente non sono un errore: si mettono da parte inquote.unavailable, eitemsetotalscontano solo ciò che si può comprare.pay(returnUrls?)riserva la giacenza, crea l'ordine e risponde{ url, orderId, reference }: mandi il cliente aurl. Non fa il redirect da solo.- Ciò che il cliente ha scelto (la consegna di ogni variante, gli indirizzi, il metodo di spedizione, i codici promozionali, l'email di un ospite, la fattura) è in
state; le righe sono quelle del carrello e qui non si tengono. - Dopo il pagamento, nella pagina di successo,
commerce.checkout.complete()svuota il carrello e dimentica le scelte.
Lo store rifiuta di proposito con un codice: CHECKOUT_BLOCKED (un hook di validazione), CHECKOUT_HOOK_FAILED e SHIPPING_METHOD_UNAVAILABLE. checkoutRefusalOf(error) lo legge. Se non resta niente da pagare, pay lancia NOTHING_TO_PAY senza chiedere allo store.
Cosa ha un cliente
commerce.addresses: la rubrica di un cliente, tenuta dallo store; gli indirizzi di un ospite stanno in memoria per la pagina.remember(input)tiene un indirizzo appena ricevuto dal checkout.commerce.orders: gli ordini e gli abbonamenti del cliente, una pagina alla volta, come risorse della cache.commerce.returns: le richieste di reso di un cliente (create,cancel) e il flusso dell'ospite:guest.requestLink({ customerEmail, orderId | reference })fa inviare dallo store un link alla pagina/returns?token=…del tuo sito, dove l'ospite sceglie gli articoli. I resi valgono solo per gli ordini pagati.commerce.wishlist: un interruttore (toggle(variantId)) che aggiunge o toglie. Un ospite non ne ha: lanciaLOGIN_REQUIREDsenza chiedere allo store. Uno store può spegnerla.commerce.reviews: un cliente scrive una recensione per ogni variante, con un indirizzo email verificato (altrimentiEMAIL_NOT_VERIFIED). Uno store può spegnere le recensioni.
Lista desideri e recensioni sono attive se le impostazioni dello store non dicono altro: le porta storeData, e la tua interfaccia nasconde ciò che è spento.