Vai al contenuto
Micro Frontend

Impostazione dell'utente e dell'autenticazione

Il micro-frontend visualizza la biglietteria in modo nativo nella tua pagina. Chi è l'utente viene dal TUO backend: la pagina host è responsabile dell'autenticazione dell'utente. È obbligatorio un token di sessione: senza di esso il micro-frontend rimane inattivo. Questa pagina spiega il motivo e il flusso della sessione da server a server.

La tecnologia dietro il micro-frontend

La biglietteria ADITUS, intessuta invece che teletrasportata. Il micro-frontend visualizza nativamente l'intero flusso di acquisto nella tua pagina: nessun iframe, nessun corpo estraneo visivo. È costruito in modo snello e nativo dell'host, quindi sembra e sembra il tuo sito, non un widget incorporato.

React 19: compatibile fino a React 18

Il flusso completo (eventi, articoli, carrello, registrazione, pagamento, completamento) viene eseguito come un unico flusso React completamente indipendente dai tipi. Eseguiamo già noi stessi React 19, ma manteniamo deliberatamente aperta la compatibilità fino a React 18 (peerDependency react >=18) in modo che il micro-frontend si incorpori nel maggior numero possibile di pagine host. Questo intervallo peer è inteso letteralmente: internamente l'adattatore utilizza solo il set di funzionalità di React 18 (useState, useEffect, useMemo & co.) e nessuna API esclusiva di React-19 — costruito e testato con React 19, garantito per funzionare con React 18. React è solo una peerDependency: l'adattatore porta zero dipendenze di runtime. Nessuna libreria dell'interfaccia utente, nessun framework per il recupero dei dati, nessun toolkit statale: solo hook React selezionati manualmente. Il risultato è un piccolo pacchetto senza nulla che si trascina nella tua pagina.

DOM leggero invece di iframe

Nessun iframe, nessun DOM ombra, nessun corpo estraneo. Il negozio viene visualizzato direttamente nel DOM della tua pagina: eredita la tua tipografia, si adatta al tuo layout, è completamente reattivo e accessibile e viene letto dal tuo visitatore come una parte nativa del tuo sito web.

Indipendente dal framework: un mount(), ovunque

Una singola chiamata — mount(container, config) — collega lo shop a qualsiasi elemento DOM. Che si tratti di React, Vue, Angular o semplice HTML: l'adattatore è indipendente dal framework e funziona allo stesso modo ovunque.

Lo stile è tuo

Ogni elemento porta un hook CSS stabile (classi BEM aditus-shop__* più data-state). Puoi creare liberamente lo stile tramite il tuo CSS oppure impostare i colori, i raggi e la spaziatura del tuo marchio in pochissimo tempo con una manciata di token di progettazione --aditus-*. Facoltativamente con stili di base isolati in modo pulito tramite CSS @layer che non sovrascrivono mai la tua pagina.

Multilingue, commutabile dal vivo

DE/EN sono integrati. Il cambio di lingua viene inserito direttamente nell'istanza in esecuzione tramite handle.update(), senza reimpostare il percorso dell'utente. Carrello e progressi vengono preservati.

Sicuro fin dalla progettazione

Senza un token di sessione valido l'adattatore è inattivo e quindi non effettua una singola richiesta. La sessione viene creata da server a server con un segreto; nessuna chiave raggiunge mai il browser. Tutte le chiamate vengono eseguite tramite un proxy che inserisce le credenziali lato server.

Requisiti dell'ambiente host

A livello di integrazione, il micro-frontend è deliberatamente compatibile al massimo: qualsiasi framework, qualsiasi CSS host, React 18+. I punti fermi inevitabili sono pochi e innocui: un client nel browser, React presente e una sessione con una publicKey inserita nella whitelist.

Cosa è richiesto

  • React 18+ e react-dom 18+ nel pacchetto host — dichiarati come peerDependency react >=18; mount() utilizza createRoot da react-dom/client. L'host decide la versione React.
  • Un browser con un DOM (lato client). L'adattatore si monta in un vero elemento DOM (dom leggero). Gli host SSR vanno bene finché mount() viene eseguito sul client.
  • Un browser sempreverde attuale: utilizza API Web standard (fetch, AbortController, ResizeObserver, CSS @layer), linea di base dal 2022 circa. Niente IE.
  • Una sessione più publicKey: senza un sessionToken il micro-frontend rimane inattivo. Crea server-server con il segreto (host con backend) o dal browser tramite /shop/embed-session con dominio controllato (host senza backend). Il dominio deve essere nella whitelist di publicKey.

Ciò che deliberatamente NON richiediamo

  • Nessun framework particolare: mount(container, config) è neutrale rispetto al framework: React, Vue, Angular, HTML semplice.
  • Nessun iframe, nessuno shadow DOM: viene visualizzato in modo nativo nella tua pagina.
  • Nessun CSS host particolare: gli stili di base risiedono in @layer aditus-shop (qualsiasi regola host senza livelli vince), con specificità per classe singola, più reset-armor contro un host * { margin:0; imbottitura:0 }. Completamente disattivabile con baseStyles: false.
  • Nessuna dipendenza di runtime: solo React peerDeps, niente che entri in conflitto nel bundle host.
  • Nessuna configurazione di build particolare oltre all'importazione ESM.

Sicurezza dei pagamenti, PCI e CSP

L’integrazione di componenti di terze parti come le biglietterie tradizionalmente impone un compromesso tra esperienza dell’utente, prestazioni e sicurezza IT. Il micro-frontend risolve questo conflitto con un paradigma ibrido: il flusso di interazione viene visualizzato nel DOM nativo della tua pagina mentre la fase di pagamento rimane strettamente isolata: tutto ciò che è innocuo accade nella tua pagina, il pagamento stesso non accade mai.

Nessun dato di pagamento nel DOM leggero

La preparazione completa dell'acquisto (evento, biglietti, componenti aggiuntivi, carrello, registrazione) viene visualizzata in modo nativo nella tua pagina. Nel momento in cui l'utente avvia un pagamento di reindirizzamento, il flusso sensibile lascia completamente la tua pagina: una normale navigazione di primo livello passa alla pagina di pagamento esterna dedicata del fornitore di pagamenti, e il viaggio di ritorno riprende il viaggio e completa l'ordine. I dati della carta o della banca vengono inseriti solo lì: il micro-frontend non visualizza, trasporta o memorizza mai le credenziali di pagamento nella tua pagina.

Ambito PCI minimo

Poiché nessun dato relativo a carte di credito o banche viene mai elaborato, trasportato o archiviato nell'applicazione host, il tuo ambiente rimane nell'ambito PCI-DSS minimo del modello di reindirizzamento classico (in genere SAQ A: la classificazione vincolante viene sempre effettuata dall'acquirente o dal QSA). Metodi di pagamento senza reindirizzamento, come fattura, completi lato server, senza dati di pagamento nel browser.

Politica di sicurezza dei contenuti snella

Oltre alla policy esistente, il micro-frontend stesso necessita solo di due autorizzazioni: connect-src per l'origine dell'API ADITUS (ogni chiamata al negozio passa attraverso di essa) e img-src per gli URL delle risorse ADITUS se mostri il banner dell'evento o le immagini degli articoli. In particolare, non necessita di voci script-src o frame-src per i fornitori di pagamento, perché la fase di pagamento è un semplice reindirizzamento di primo livello: nessuno script di pagamento e nessun frame di pagamento vengono mai caricati all'interno della tua pagina.

Il meglio di entrambi i mondi

Classicamente dovevi scegliere: un iframe (isolato, ma rigido e un corpo estraneo visivo) o una build API completamente integrata (senza soluzione di continuità, ma il tuo team di sicurezza improvvisamente possiede l'intero ambito PCI). Il micro-frontend risolve questo dilemma: l'interazione innocua - navigazione, carrello, configurazione del ticket - vive nativamente nella tua pagina; l’interazione critica – la raccolta di denaro – è completamente interrotta e isolata dall’esterno.

In cosa differisce dalle alternative del mercato

Rispetto al classico iframe

Gli iframe sono considerati sicuri grazie al rigido isolamento del browser, ma si comportano come documenti isolati: non ereditano né stili globali come la tipografia o le regole di layout reattive, né il loro contenuto è indicizzabile in modo affidabile per i crawler; per gli screen reader spesso significano una pausa mediatica. Il micro-frontend rende l'intera preparazione all'acquisto (selezione dell'articolo, carrello, registrazione) come vero HTML direttamente nel DOM della tua pagina, rendendolo accessibile e reattivo come il tuo markup. L'effetto isolante di un iframe viene ricreato solo al momento del pagamento: il flusso di dati critici viene consegnato completamente, tramite un reindirizzamento di primo livello, alla pagina di pagamento esterna dedicata.

Flessibilità rispetto al puro Shadow DOM

Molti componenti web si incapsulano in un DOM ombra per evitare conflitti di specificità CSS – in pratica questo spesso porta a un branding incompleto, perché gli stili globali sono bloccati e le regole di progettazione devono essere elaborate faticosamente tramite attributi delle parti o proprietà personalizzate. Il micro-frontend utilizza invece la cascata CSS nativa: i suoi stili di base risiedono in @layer aditus-shop, un livello volutamente basso: ogni regola CSS senza livelli della tua pagina vince automaticamente. Il tuo design aziendale viene ereditato in modo nativo, senza guerre di specificità contro i componenti dell'interfaccia utente del negozio.

Efficienza delle risorse e dimensioni del pacchetto

I widget monolitici spesso forniscono i propri client HTTP, gestione dello stato e librerie dell'interfaccia utente e possono forzare il browser a caricare runtime ridondanti in parallelo, danneggiando il tempo di caricamento della pagina (Core Web Vitals). Il micro-frontend dichiara React e React-DOM esclusivamente come peerDependency e non ha dipendenze di runtime proprie: se la tua pagina utilizza già React 18 o versioni successive, si collega direttamente al runtime esistente. Per gli ambienti non React, il componente web opzionale incapsula la dipendenza React nel proprio bundle isolato, senza occupare variabili globali sulla tua pagina.

Gli approcci a confronto

CriterioIFrame classicoWidget Shadow-DOMMicrofrontend ADITUS
SEO e accessibilitàIl contenuto risiede in un documento separato, spesso invisibile ai crawler, più difficile per gli screen reader.Isolato; la rilevabilità e l'accessibilità in genere richiedono lavoro extra.Parte nativa del tuo DOM: indicizzabile e accessibile come il tuo markup.
Stile e marchioRigido: nessuna ereditarietà CSS, personalizzazione solo tramite API postMessage.Gli stili globali non arrivano all'interno; tematizzazione solo tramite proprietà e parti passate.Il tuo CSS vince per definizione tramite @layer; la skin di base è sostituibile o completamente disattivata.
Pacchetto e prestazioniIn genere carica una seconda app completa, incluso il relativo runtime all'interno del frame.Spesso fornisce il proprio runtime e dipendenze duplicate.Utilizza React della tua pagina; zero dipendenze di runtime proprie.
Sicurezza dei pagamenti e ambito PCIFortemente isolato, ma al prezzo di UX, design e reattività.I dati di pagamento passerebbero attraverso il DOM host se venissero catturati lì.Selezione nel DOM leggero, pagamento completamente isolato sulla pagina di pagamento esterna.

Come si ottiene il micro-frontend

Il microfrontend non è su npm pubblico e non è su CDN. Viene fornito come pacchetto sorgente direttamente dalla tua istanza ADITUS: TypeScript, ESM, completamente tipizzato, con zero dipendenze runtime proprie: il tuo bundler (Vite, webpack, Next.js, Nuxt) lo compila insieme alla tua app come il tuo codice. Il nome di importazione negli snippet di questa pagina (@workspace/aditus-shop-embed) è il nome del pacchetto dalla nostra integrazione di riferimento; il pacchetto scaricabile si chiama @aditus/shop-embed — l'API è identica.

Cosa contiene la consegna

L'adattatore completo come sorgente TypeScript leggibile con dichiarazioni di tipo per ogni opzione di configurazione, callback ed evento. React e react-dom rimangono peerDependencies: la tua pagina le fornisce, quindi nulla si scontra nel tuo bundle e non esiste un secondo React.

Ricevere il pacco

Il pacchetto è identico per ogni host e viene scaricato direttamente da questa istanza, senza credenziali richieste:{{API_BASE_URL}}/api/embed/v1/aditus-shop-embed.tgz. Installalo direttamente da quell'URL (npm install accetta un URL tarball: utilizza la destinazione del collegamento sulla tua istanza, percorso /api/embed/v1/aditus-shop-embed.tgz) o decomprimilo nel tuo repository. Ciò che effettivamente fornisce l'onboarding è la tua publicKey con la sua whitelist di domini; senza di esso il micro-frontend rimane inattivo. Contatto: pagina dei contatti. Gli host senza una propria fase di creazione (WordPress, HTML semplice) non hanno affatto bisogno del pacchetto sorgente: utilizzano il bundle di componenti Web self-hosted descritto nella sezione successiva.

Componente Web: non è richiesta alcuna fase di creazione

Per le pagine senza il proprio bundler (WordPress, Typo3, HTML semplice) il micro-frontend viene fornito anche come bundle precostruito e ospitato autonomamente: un tag script registra l'elemento <aditus-shop>, React incluso, nessun npm, nessuna build. Viene fornito dalla tua istanza ADITUS sotto un URL con versione (/api/embed/v1/…), non da un CDN pubblico — per questo sistema demo che è https://developers.aditus.com/api/embed/v1/aditus-shop.js. L'URL è deliberatamente pubblico e non richiede credenziali: il bundle è semplice codice pubblicato, il cancello vero e proprio è la tua chiave pubblicabile con la sua whitelist di domini più la sessione. Questo percorso è strettamente facoltativo e puramente additivo: gli host con la propria build continuano a integrare il pacchetto sorgente tramite mount() esattamente come documentato in questa pagina: stesso percorso, stessi hook CSS, stessa tematica.

index.html
<!-- 1. Das selbst gehostete Bundle einbinden (einmal pro Seite). -->
<script src="https://ihre-aditus-instanz.de/api/embed/v1/aditus-shop.js" defer></script>

<!-- 2. Das Element platzieren — der Shop rendert nativ an dieser Stelle. -->
<aditus-shop
  public-key="pk_live_ihrkey"
  culture="de-DE"
></aditus-shop>

<!-- 3. Optional: Journey-Events als ganz normale DOM-Events konsumieren. -->
<script>
  document.querySelector("aditus-shop").addEventListener("cart:update", (e) => {
    console.log("Positionen im Warenkorb:", e.detail.itemCount);
  });
</script>
AttributoSenso
public-keyLa tua chiave pubblicabile (si applica la whitelist dei domini). Con solo questo attributo impostato, l'elemento crea la propria sessione del browser: la modalità giusta per le pagine senza backend.
session-tokenUna sessione generata dal TUO backend (server-to-server, vedi sotto). Se è impostata, l'elemento non genera mai autonomamente una sessione: la tua pagina ne gestisce il ciclo di vita.
api-baseURL di base dell'API ADITUS. Il valore predefinito è l'origine da cui è stato caricato lo script del bundle: di solito non lo imposti mai.
cultureLingua/impostazione internazionale del negozio, ad es. de-DE o en-GB. La modifica dell'attributo rilocalizza sul posto: il percorso dell'utente viene preservato.
article-layout / event-layout / article-select-mode / show-headerLe opzioni di presentazione flat, stessi valori della configurazione mount() (carte, quantità, ...). Il PUNTO DI INGRESSO (selettore eventi rispetto a un evento bloccato) non è un attributo: è fisso lato server quando viene coniata la sessione.
survey-columnsColonne di opzioni di risposta (1–4) per le domande del sondaggio RadioButtonList/CheckBoxList — come SurveyColumns nella configurazione mount(). Unset mantiene l'impostazione predefinita a colonna singola (o l'impostazione predefinita del client memorizzata sul lato server); le finestre strette vengono compresse nuovamente in una colonna.
base-stylesImposta su "false" per eliminare la skin predefinita neutra e modellare tu stesso ogni hook.

Sessione e autoguarigione

Le regole di sessione di questa pagina si applicano invariate. Con un backend, conia server-to-server e imposta il token di sessione: l'elemento non si conia mai da solo. Senza un backend, imposta solo la chiave pubblica: l'elemento conia una sessione del browser tramite l'endpoint controllato dal dominio e la reimposta in modo trasparente quando scade, in modo che l'utente non si trovi mai in un vicolo cieco. Light DOM come sempre: niente iframe, niente shadow DOM: il tuo CSS raggiunge ogni hook documentato.

Eventi e versioni

Il bus degli eventi emerge come semplice DOM CustomEvents sull'elemento (bubbling, payload in event.detail) — cart:update, checkout:complete e il mirror di analisi, stessi nomi e payload di handle.on(). L'URL riporta la versione principale: v1 riceve gli aggiornamenti compatibili sul posto; una modifica sostanziale viene fornita come /embed/v2/ quindi non cambia nulla nella tua pagina senza preavviso. Tutto ciò che va oltre gli attributi semplici (oggetti tematici, callback come onUserRequired, contenuto della scheda) rimane una funzionalità del pacchetto sorgente in base alla progettazione.

Attributi, non proprietà

L'elemento è configurato esclusivamente tramite attributi HTML e gli attributi sono sempre stringhe. base-styles="false" è la stringa letterale "false"; l'elemento lo analizza, non assegni mai proprietà JavaScript. I framework legano comunque i nomi con trattino come la chiave pubblica come attributi, quindi non è necessaria alcuna sintassi di associazione speciale. Tutto ciò che non è esprimibile come stringa piatta (oggetti del tema, callback, contenuto della scheda) non è deliberatamente un attributo: per questo utilizzare il pacchetto sorgente.

Ripristinare deliberatamente il viaggio

L'unica eccezione a "solo attributi": l'elemento espone un metodo clearJourney() — la controparte senza build dell'esportazione clearJourney() del pacchetto sorgente. Chiama document.querySelector("aditus-shop").clearJourney() (ad esempio da un pulsante "ricomincia") prima di aggiungere nuovamente o ricaricare l'elemento e il record di viaggio persistente localmente per la pagina corrente viene eliminato: il montaggio successivo ricomincia dal punto di ingresso invece di riprendere. Viene rimosso solo il puntatore di ripristino locale; qualsiasi stato del carrello lato server rimane intatto.

Aggiornamenti senza rimontaggio

La modifica di un attributo sull'elemento live aggiorna l'istanza in esecuzione sul posto, esattamente come handle.update() nel pacchetto sorgente. Gli attributi della presentazione (cultura, layout) preservano il percorso dell'utente; solo la modifica dell'identità della sessione (chiave pubblica, token di sessione, base API) ristabilisce la sessione. La rimozione dell'elemento dal DOM smonta il negozio in modo pulito, inclusi tutti gli ascoltatori.

Host SSR: Next.js e Nuxt

Il micro-frontend è volutamente solo client. Il suo contenuto è live e legato alla sessione: disponibilità, prezzi e carrello dell'utente esistono solo per una sessione autenticata al momento della richiesta, quindi non c'è nulla di significativo che un server host possa pre-renderizzare. mount() crea una nuova root React lato client (createRoot); Il markup rappresentato dal server all'interno del contenitore non è idratato. Ciò rende l'integrazione SSR semplice e prevedibile: esegui il rendering di un segnaposto sul server, montalo sul client e riserva lo spazio in modo che nulla salti.

ticketshop-section.tsx
// Next.js (App Router) — das Micro-Frontend ist bewusst client-only.
"use client";
import { useEffect, useRef } from "react";
import { mount, type ShopHandle } from "@workspace/aditus-shop-embed";

export function TicketshopSection({ sessionToken }: { sessionToken: string }) {
  const el = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!el.current) return;
    const handle: ShopHandle = mount(el.current, {
      publicKey: "pk_deine_seite",
      sessionToken, // server-seitig gemintet, als Prop an die Client-Komponente
    });
    return () => handle(); // Unmount beim Routenwechsel
  }, [sessionToken]);

  // Server und Client rendern DENSELBEN Platzhalter — kein Hydration-Mismatch.
  // min-height reserviert den Platz, damit die Seite beim Mount nicht springt (CLS).
  return <div id="aditus-shop" ref={el} style={{ minHeight: 560 }} />;
}

Evitare lo spostamento del layout (CLS)

Assegna al contenitore di montaggio un'altezza minima che corrisponda all'incirca alla prima visualizzazione del negozio e, facoltativamente, esegui il rendering del tuo scheletro al suo interno: server e client producono lo stesso segnaposto, quindi non vi è alcuna mancata corrispondenza di idratazione. L'adattatore esegue il rendering immediatamente dopo il montaggio e sostituisce il segnaposto in una vernice. Non tentare hydraRoot sul contenitore: non esiste un albero del negozio renderizzato dal server a cui collegarsi.

La sessione in un host SSR

Gli host SSR hanno un vantaggio naturale: il runtime del server che esegue il rendering della pagina può anche coniare la sessione, da server a server con il segreto, esattamente come il flusso di conio riportato di seguito (Next.js: gestore del percorso o componente server; Nuxt: percorso del server). Consegnare il token opaco risultante al componente client come supporto o carico utile. Il segreto non viene mai visualizzato nel codice client e il token rimane in memoria.

Specifiche di Next.js

Contrassegna il componente di montaggio "usa client" e montalo in useEffect (vedi snippet). Con il Pages Router, next/dynamic con ssr: false ottiene lo stesso risultato. Restituisce l'handle dall'effetto come pulizia in modo che le modifiche al percorso lato client vengano smontate in modo pulito. Un cambio di cultura sull'istanza in esecuzione passa attraverso handle.update() senza reimpostare il percorso; un nuovo token di sessione si rimonta (come nello snippet): anche il carrello sopravvive, perché il micro-frontend ripristina il viaggio sul monte.

Specifiche Nuxt/Vue

Racchiudi la destinazione del montaggio in <ClientOnly> o monta in onMounted: l'adattatore necessita di un vero elemento DOM nel browser. Ricorda i requisiti del bundle host: react e react-dom 18+ devono essere installati come dipendenze della tua app Nuxt; il negozio viene visualizzato nel proprio contenitore e non interferisce con il DOM virtuale di Vue.

Il micro-frontend necessita di una sessione

Nessun gettone

Nessun token di sessione: il micro-frontend rimane inattivo

Senza un token di sessione, il micro-frontend fornisce un avviso neutro e non avvia alcuna chiamata API. Nessun carrello può essere riempito, nessuna registrazione, nessun pagamento. Una sessione è obbligatoria prima dell'attivazione del negozio, ma non deve essere legata a un utente conosciuto: una sessione ospite è sufficiente per navigare e riempire il carrello, e l'utente reale può essere stabilito successivamente al momento del pagamento (vedi il flusso utente facoltativo di seguito).

mount.ts
import { mount } from "@workspace/aditus-shop-embed";

const el = document.getElementById("aditus-shop");
if (!el) throw new Error("Mount-Ziel #aditus-shop nicht gefunden");

// Ohne sessionToken bleibt das Micro-Frontend inaktiv: es zeigt nur einen
// neutralen Hinweis und startet KEINE API-Calls — kein Warenkorb,
// keine Registrierung, kein Checkout. Erst eine Session aktiviert den Shop.
mount(el, {
  publicKey: "pk_deine_seite",
  // sessionToken fehlt -> Shop wird nicht aktiviert.
});
Utente per host

Token di sessione: un utente specifico

Il tuo backend conia un token di breve durata legato a un utente. Il micro-frontend agisce quindi come quell'utente: il suo carrello, i suoi dati. Il token è opaco, con scadenza, monoutente e legato alla tua chiave pubblica e il tuo segreto non raggiunge mai il browser.

mount.ts
import {
  mount,
  type ShopHandle,
  type AditusShopConfig,
} from "@workspace/aditus-shop-embed";

// getElementById kann null sein — in TypeScript sauber prüfen statt "!".
const el = document.getElementById("aditus-shop");
if (!el) throw new Error("Mount-Ziel #aditus-shop nicht gefunden");

// AditusShopConfig typisiert alle Optionen — Autovervollständigung inklusive.
const config: AditusShopConfig = {
  publicKey: "pk_deine_seite", // Pflicht, sobald eine Session im Spiel ist
  sessionToken,                // vom Backend gemintet, nur im Speicher halten
};

const handle: ShopHandle = mount(el, config);

// Das Micro-Frontend sendet bei jedem Shop-Call den Header X-Aditus-Session.
// Beim Logout die Session serverseitig widerrufen und neu mounten/aktualisieren.

Una ricarica della pagina non perde il viaggio. Il micro-frontend ricorda il carrello e il passaggio localmente, recupera il carrello live dal server al momento del montaggio e continua esattamente da dove l'utente aveva interrotto, anche se la tua pagina conia un nuovo token di sessione ad ogni caricamento. Se il carrello è scaduto lato server, il viaggio ricomincia semplicemente da capo. La tua pagina non deve fare nulla per questo.

Vuoi invece un nuovo inizio deliberato: un vero pulsante "ricomincia da capo"? Il pacchetto esporta clearJourney(): chiamalo prima del montaggio (o del rimontaggio) e il record del viaggio persistente localmente per la pagina corrente viene eliminato, quindi il montaggio successivo inizia da capo dal punto di ingresso. Viene rimosso solo il puntatore di ripristino locale; qualsiasi stato del carrello lato server rimane intatto.

Facoltativo: naviga in modo anonimo, accedi al momento del pagamento

Non è necessario sapere in anticipo chi è l'utente. Monta con una sessione ospite e il micro-frontend consente a un visitatore anonimo di sfogliare eventi, scegliere articoli e riempire il carrello. Solo quando superano il carrello, verso la registrazione e il pagamento, ti viene chiesto di stabilire l'utente reale, tramite il callback onUserRequired. Il resto del flusso prosegue poi con il carro conservato.

  1. 1

    Montare con una sessione ospite

    Per attivare il negozio è comunque necessario un token di sessione, ma può essere una sessione ospite: basta coniarlo SENZA il campo e-mail, non è necessario alcun indirizzo segnaposto. Il visitatore naviga e riempie il carrello sottostante.

  2. 2

    L'abbandono del carrello attiva l'onUserRequired

    Nel momento in cui il visitatore avanza oltre il carrello, il micro-frontend richiama una volta la tua richiamata e mostra uno stato in sospeso neutro. Autentica l'utente (login/SSO), quindi conia una nuova sessione vincolata all'utente esattamente nello stesso modo della prima: server-server con il tuo segreto. Anche l'utente successivo viene stabilito tramite il conio machine-to-machine: il segreto non raggiunge mai il browser e l'utente non viene mai impostato nel codice client. La tua richiamata trasmette solo il token opaco risultante. Fondamentalmente, il tuo backend deriva quell'identità dalla PROPRIA sessione autenticata (cookie/JWT); non deve mai accettare l'e-mail o l'ID utente dal browser, altrimenti un visitatore malintenzionato potrebbe richiedere la sessione di qualcun altro e leggere i suoi dati personali tramite la precompilazione.

  3. 3

    Restituisci il nuovo gettone: il carrello viene trasferito

    Restituisci il nuovo token di sessione e il micro-frontend lo scambia e lega l'utente identificato al carrello esistente: il carrello, i suoi articoli e le eventuali fasce orarie rimangono invariati. Quindi entra nella registrazione. Restituisci null per interrompere: il visitatore rimane semplicemente sul carrello, nessun errore.

  4. 4

    Omettere onUserRequired per un utente fisso

    Se si lascia fuori il callback, non cambia nulla rispetto al flusso classico: si monta una sessione associata all'utente e il micro-frontend agisce come quell'utente fin dal primo passaggio.

page.ts
import {
  mount,
  type ShopHandle,
  type AditusShopConfig,
} from "@workspace/aditus-shop-embed";

const el = document.getElementById("aditus-shop");
if (!el) throw new Error("Mount-Ziel #aditus-shop nicht gefunden");

// Ein GAST-Session-Token reicht zum Mounten: server-zu-server gemintet wie
// gehabt, nur OHNE das Feld email (keine Platzhalter-Adresse nötig). Der
// Besucher darf browsen UND den Warenkorb füllen, ohne dass ein konkreter
// Nutzer feststeht.
const config: AditusShopConfig = {
  publicKey: "pk_deine_seite",
  sessionToken: guestSessionToken,

  // Wird EINMAL aufgerufen, sobald der Besucher den Warenkorb Richtung
  // Registrierung verlässt. Hier authentifizierst du den Nutzer (Login/SSO)
  // und mintest server-zu-server eine NEUE, nutzergebundene Session.
  // Gib den frischen Token zurück -> das Micro-Frontend übernimmt ihn und baut
  // den Warenkorb unter dem identifizierten Nutzer neu auf (Artikel bleiben
  // erhalten). Gib null zurück, um abzubrechen -> der Besucher bleibt im
  // Warenkorb, ohne Fehler.
  onUserRequired: async () => {
    // Ruft NUR dein Backend auf – ohne Identität im Payload. Dein Backend liest
    // den eingeloggten Nutzer aus SEINER eigenen Session (Cookie/JWT) und mintet
    // server-seitig für GENAU diese Identität. Niemals die E-Mail aus dem Browser
    // übergeben – sonst könnte ein Angreifer hier eine fremde Adresse einsetzen
    // und über den Prefill an deren personenbezogene Daten gelangen.
    const res = await fetch("/api/shop/my-session", { method: "POST" });
    if (res.status === 401) return null;        // nicht eingeloggt -> im Warenkorb bleiben
    const { sessionToken } = (await res.json()) as { sessionToken: string };
    return sessionToken;
  },
};

const handle: ShopHandle = mount(el, config);

La pagina host autentica l'utente

ADITUS non autentica i tuoi utenti finali. Il tuo sito è l'unica fonte di verità per chi è l'utente: esegui il tuo accesso, SSO o sessione. ADITUS si fida solo di un token di sessione che il tuo backend conia con una chiave segreta. La catena di fiducia è: la tua autenticazione dimostra l'identità, il tuo token coniato segretamente lo garantisce, il micro-frontend lo presenta.

  1. 1

    Il tuo sito autentica l'utente

    Accesso, SSO, sessione membro: tutto tuo. ADITUS non è coinvolto qui.

  2. 2

    Il tuo backend conia una sessione

    Server-to-server con la chiave segreta. Passi chi dovrebbe agire il micro-frontend (e-mail) e lo colleghi alla tua chiave pubblica.

  3. 3

    La pagina monta il micro-frontend

    Il token opaco viene consegnato al browser e passato come sessionToken a mount(). Il segreto rimane sul tuo server.

  4. 4

    Ogni chiamata porta con sé la sessione

    Il micro-frontend invia X-Aditus-Session ad ogni richiesta; il proxy risolve l'utente del carrello dalla sessione.

  5. 5

    Nessun token → il micro-frontend rimane inattivo

    Senza una sessione il micro-frontend non si attiva mai: mostra un avviso neutro e non effettua chiamate: niente carrello, niente cassa. Conia una sessione per accenderlo.

1. Nuovo sul tuo backend

server.ts
// SERVER-SEITE deines Hosts — der Secret-Key verlässt NIE den Browser.
// Dein Backend hat den Nutzer bereits selbst authentifiziert (Login/SSO/Session).
// WICHTIG: user stammt aus DIESER Server-Session, nie aus dem Request-Body des
// Browsers — sonst könnte ein Angreifer eine fremde E-Mail unterschieben.
const res = await fetch("https://<dein-host>/api/shop/session", {
  method: "POST",
  headers: {
    // Mint-Secret DEINES Clients (im ADITUS-Admin erzeugt), nur server-seitig.
    Authorization: `Bearer ${process.env.ADITUS_MINT_SECRET}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    publicKey: "pk_deine_seite",      // bindet die Session an deinen Key (Pflicht)
    email: user.email,                 // wer das Micro-Frontend sein soll (weglassen = anonyme Gast-Session)
    externalUserId: user.id,           // optionale Metadaten (deine User-ID)
    ttlSeconds: 1200,                  // optional: Lebensdauer (Default 20 min, max 24 h)
    eventSlug: "ExperienceDaysv52024", // optional: Einstieg direkt in dieses Event (sonst Eventübersicht)
  }),
});

// Antwort-Typ deiner Wahl — sessionToken an den Browser reichen (inline oder fetch).
const { sessionToken }: { sessionToken: string; expiresAt: number } = await res.json();

2. Montare con il gettone

page.ts
import {
  mount,
  type ShopHandle,
  type AditusShopConfig,
} from "@workspace/aditus-shop-embed";

// getElementById kann null sein — in TypeScript sauber prüfen statt "!".
const el = document.getElementById("aditus-shop");
if (!el) throw new Error("Mount-Ziel #aditus-shop nicht gefunden");

// AditusShopConfig typisiert alle Optionen — Autovervollständigung inklusive.
const config: AditusShopConfig = {
  publicKey: "pk_deine_seite", // Pflicht, sobald eine Session im Spiel ist
  sessionToken,                // vom Backend gemintet, nur im Speicher halten
};

const handle: ShopHandle = mount(el, config);

// Das Micro-Frontend sendet bei jedem Shop-Call den Header X-Aditus-Session.
// Beim Logout die Session serverseitig widerrufen und neu mounten/aktualisieren.

3. Revoca al logout

server.ts
// SERVER-SEITE — z. B. beim Logout deines Nutzers.
await fetch(`https://<dein-host>/api/shop/session/${sessionToken}`, {
  method: "DELETE",
  headers: { Authorization: `Bearer ${process.env.ADITUS_MINT_SECRET}` },
});
// { revoked: true }

L'ordine finito ritorna alla tua pagina

Una volta completato l'acquisto di un biglietto, non è necessaria una ricerca dell'ordine separata. Poiché il micro-frontend viene eseguito in modo nativo nella tua pagina (dom leggero, non un iframe), restituisce l'ordine finito direttamente al tuo codice tramite il callback onComplete: una chiamata JS diretta, nessun postMessage. Il micro-frontend ha già risolto l'ordine per te, quindi riceverai il numero dell'ordine e i collegamenti al biglietto, non solo un ID.

mount.ts
import {
  mount,
  type ShopHandle,
  type CompletedOrder,
} from "@workspace/aditus-shop-embed";

const el = document.getElementById("aditus-shop");
if (!el) throw new Error("Mount-Ziel #aditus-shop nicht gefunden");

const handle: ShopHandle = mount(el, {
  publicKey: "pk_deine_seite",
  sessionToken,
  // Wird GENAU EINMAL aufgerufen, sobald die Bestellung platziert ist.
  // Das Micro-Frontend hat die Bestellung bereits für dich aufgelöst — du bekommst
  // die aufgelöste Bestellung, nicht nur eine ID.
  onComplete: (order: CompletedOrder) => {
    order.number;   // Bestellnummer (Order-ID als Fallback) — dein Identifier
    order.status;   // Status der Bestellung
    order.buyer;    // Käufer (Name / E-Mail), falls vorhanden
    order.tickets;  // [{ name, links: [{ kind: "pdf" | "apple" | "google", url }] }]

    // -> Order-ID speichern, Bestätigung zeigen, DOI / Newsletter anstoßen ...
  },
  // Wird bei jedem nicht behebbaren Fehler im Ablauf aufgerufen.
  onError: (err: Error) => {
    console.error("Shop-Fehler:", err.message);
  },
});

Quello che ricevi

onComplete si attiva una volta per ogni checkout andato a buon fine con l'ordine risolto: numero (l'ID dell'ordine/numero dell'ordine - il tuo identificatore), stato, acquirente (nome ed e-mail se presente) e biglietti: ogni biglietto contiene collegamenti digitati con il tipo "pdf", "apple" o "google". onError si attiva in caso di errore irreversibile nel flusso.

Metodi di pagamento reindirizzati

I metodi che rimangono sulla pagina (fattura, pagamento anticipato, carta in linea) si attivano immediatamente con Completamento. Un metodo che invia il browser a una pagina di pagamento esterna (ad esempio Saferpay) è gestito dal Micro-Frontend end-to-end: deriva gli URL di ritorno dalla pagina su cui viene eseguito, invia l'utente e sulla via del ritorno finalizza l'ordine e attiva il comando Complete: un viaggio di andata e ritorno completo senza codice dalla tua parte. Solo se il reso deve arrivare a una pagina DIVERSA, sovrascrivi reindirizzamentoUrls.successUrl / cancelUrl / errorUrl.

GA4/eventi e-commerce (facoltativo)

Il micro-frontend può segnalare la canalizzazione dello shopping alle tue analisi, ma solo se lo chiedi. Non include gtag, GTM e GA SDK, non carica nulla, non imposta cookie e non invia nulla da solo. Aggiungi una richiamata facoltativa, onEvent, e ricevi eventi digitati già modellati nello schema di e-commerce avanzato GA4 di Google. Ometti onEvent e non cambia nulla. Quale strumento fornisci e se hai il consenso per l'invio, rimane interamente dalla tua parte.

mount.ts
import {
  mount,
  type ShopHandle,
  type ShopAnalyticsEvent,
} from "@workspace/aditus-shop-embed";

const el = document.getElementById("aditus-shop");
if (!el) throw new Error("Mount-Ziel #aditus-shop nicht gefunden");

const handle: ShopHandle = mount(el, {
  publicKey: "pk_deine_seite",
  sessionToken,
  // OPTIONAL. Lässt du onEvent weg, ändert sich nichts: das Micro-Frontend
  // sendet nichts, lädt kein gtag/GTM/GA-SDK und setzt keine Cookies.
  // Tool, Consent und Mapping bleiben komplett bei dir — hier GA4 via gtag.
  onEvent: (event: ShopAnalyticsEvent) => {
    // Consent liegt bei dir: erst senden, wenn der Nutzer zugestimmt hat.
    if (!hasAnalyticsConsent()) return;
    // Die Events sind bereits im GA4-Schema (name + items/value/currency ...),
    // also 1:1 an gtag durchreichen.
    const { name, ...params } = event;
    window.gtag?.("event", name, params);
  },
});

Già nella forma GA4

Ogni evento porta un nome evento GA4 più i suoi parametri standard: articoli (con item_id, item_name, prezzo, quantità, item_category e, ove applicabile, sconto e coupon), valore e valuta; add_payment_info aggiunge payment_type e Purchase aggiunge Transaction_id. Destruttura il nome e passa il resto direttamente a gtag("event", name, params) — non è necessaria una nuova mappatura.

Il consenso resta tuo

onEvent è semplicemente una normale funzione JavaScript nella tua pagina che chiamiamo direttamente. Il Micro-Frontend stesso non invia analisi da nessuna parte: nulla lascia la pagina finché il tuo gestore non lo invia, quindi la gestione del consenso mantiene il pieno controllo: controlla l'inoltro del consenso, instrada al dataLayer di GTM invece di gtag, batch o elimina completamente gli eventi. Qualsiasi errore generato dal gestore viene rilevato e isolato, quindi non interrompe mai il flusso dell'utente: mantieni il gestore leggero (un push gtag/dataLayer) poiché viene eseguito in linea.

EventoSpara quando
view_item_listL'utente vede l'elenco degli articoli (si attiva una volta per ogni set mostrato).
add_to_cartUn articolo o un componente aggiuntivo viene aggiunto al carrello.
remove_from_cartUna riga del carrello o un componente aggiuntivo viene rimosso.
view_cartViene mostrata la visualizzazione del carrello (una volta per carrello).
begin_checkoutL'utente abbandona il carrello verso la registrazione/checkout.
add_payment_infoViene scelto un metodo di pagamento (trasporta payment_type).
purchaseL'ordine viene effettuato (include transaction_id, value e items).

Bus eventi: handle.on / handle.off (opzionale)

Oltre ai callback di configurazione, l'handle restituito da mount() trasporta un piccolo bus di eventi completamente opzionale. Iscriviti con handle.on(event, listener) — restituisce la funzione di annullamento dell'iscrizione corrispondente — e disconnettiti con handle.off(event, listener). Se non chiami mai on(), non cambia nulla: il bus non aggiunge dipendenze, non invia nulla e non costa nulla. È pura osservazione della tua pagina – un badge mini-carrello, un banner di conferma, la tua registrazione – e integra i callback anziché sostituirli.

mount.ts
import {
  mount,
  type ShopHandle,
  type ShopCartUpdate,
} from "@workspace/aditus-shop-embed";

const el = document.getElementById("aditus-shop");
if (!el) throw new Error("Mount-Ziel #aditus-shop nicht gefunden");

const handle: ShopHandle = mount(el, {
  publicKey: "pk_deine_seite",
  sessionToken,
});

// OPTIONAL. Ohne on() verhält sich das Micro-Frontend exakt wie bisher —
// der Bus ist reine Beobachtung, kein Event ist Pflicht.
// on() liefert die Abmeldefunktion zurück; alternativ handle.off(name, fn).
const offCart = handle.on("cart:update", (cart: ShopCartUpdate) => {
  // Bei jeder Warenkorb-Änderung: Anzahl, Summe, Positionen (GA4-Item-Form).
  updateMiniCartBadge(cart.itemCount); // z. B. Badge im Seiten-Header
});

handle.on("checkout:complete", ({ order }) => {
  // Bestellung platziert — dieselbe aufgelöste Bestellung wie in onComplete.
  showConfirmationBanner(order.number);
});

handle.on("analytics", (event) => {
  // Spiegel des GA4-Funnels (identische Payloads wie config.onEvent).
  console.debug("Funnel:", event.name);
});

// Später gezielt abmelden — der Rest bleibt aktiv:
offCart();
EventoSpara quando
cart:updateIl carrello cambia: creato, articolo aggiunto o rimosso o svuotato. Contiene cartId, itemCount, valore, valuta e le righe come elementi a forma di GA4.
checkout:completeL'ordine viene effettuato: stesso momento e stesso oggetto dell'ordine risolto di config.onComplete.
analyticsSpecchio del funnel GA4 sopra: ogni payload onEvent viene emesso anche sul bus, invariato.

Gli ascoltatori sopravvivono all'aggiornamento()

Le sottoscrizioni risiedono sull'handle, non su un rendering: handle.update({ ... }) esegue nuovamente il rendering del Micro-Frontend ma mantiene collegato ogni ascoltatore. Lo smontaggio tramite handle() scollega automaticamente tutti gli ascoltatori: è necessario solo off() (o l'annullamento dell'iscrizione restituito) quando si desidera interrompere l'ascolto mentre il negozio continua a funzionare.

Isolati, mai bloccanti

Un ascoltatore che lancia viene catturato e isolato: non interrompe mai il flusso dell'utente e non influisce mai sugli altri ascoltatori. L'evento "analytics" rispecchia la canalizzazione GA4 con payload identici a config.onEvent, quindi puoi consumare la canalizzazione tramite il bus, il callback o entrambi; si applica la stessa regola di consenso: nulla lascia la pagina finché il tuo codice non lo invia.

publicKey e sessionToken

Mantieni i due separati. Rispondono a domande diverse e sono accoppiati, non intercambiabili.

publicKey — fiducia nell'origine

Pubblicabile. Viene fornito nel tuo codice cliente, quindi NON è un segreto e non garantisce nulla di per sé. Il backend applica una whitelist di domini per chiave: una chiave funziona solo dai suoi domini registrati. Risponde “che sito è questo?”

sessionToken — identità

Coniato segretamente sul tuo backend, opaco e di breve durata. Risponde "chi è l'utente?" Una sessione è legata alla publicKey per la quale è stata coniata: anche una richiesta di sessione deve inviare quella chiave, e una mancata corrispondenza viene rifiutata (403).

Regole di sicurezza

  • La chiave segreta risiede solo sul tuo server, mai nel codice client, nei bundle o nei file env inviati al browser.
  • Conserva il token solo in memoria. Non persisterlo in localStorage; aggiornalo ri-coniandolo.
  • I token sono di breve durata (predefinito 20 minuti, massimo 24 ore) e per utente singolo.
  • Revocare la sessione al momento della disconnessione in modo che un token trapelato non possa essere riprodotto.
  • Accoppia sempre la sessione con la sua publicKey: una chiave mancante o non corrispondente viene rifiutata (403).
  • Una sessione danneggiata o scaduta è un duro 401, mai un silenzioso ripiego per l'utente demo.

Riferimento all'endpoint e alla risposta

POST/api/shop/sessionmint — Bearer secret
DELETE/api/shop/session/:tokenrevoke — Bearer secret

In questa demo, il mint passa attraverso il proxy agli endpoint indicati sopra. In produzione la sessione viene generata nel core ADITUS, con lo stesso contratto. Entrambe le chiamate sono server-to-server: il backend si autentica con il mint secret del client, generato nella console di amministrazione ADITUS, come Bearer token. Una richiesta di mint per ogni login è il modello normale; il browser vede soltanto il token risultante.

Puoi provare questo flusso esatto con le tue credenziali nel filetester di integrazione — mint, mount e percorri il flusso.

Coniare una sessione: richiesta

IntestazioneValoreSenso
AuthorizationBearer <mint-secret>Il mint secret del tuo client, generato nella console di amministrazione ADITUS. Deve restare esclusivamente lato server e non deve mai essere incluso nel browser o nel bundle dell'app.
Content-Typeapplication/jsonIl corpo è un oggetto JSON.
Campo corporeoTipoSenso
publicKeystring · requiredLa chiave pubblicabile PER LA quale viene coniata la sessione. Deve essere la stessa chiave con cui è montato il micro-frontend: ogni chiamata successiva al negozio viene confrontata con essa (403 in caso di mancata corrispondenza). Il segreto del portatore deve appartenere al client di questa chiave.
emailstring · optionalL'utente del negozio come agisce la sessione. Prendilo dalla TUA sessione del server autenticato (login/cookie SSO o JWT) - mai dalla richiesta del browser, altrimenti un visitatore potrebbe ottenere la sessione di qualcun altro. OMETTIlo per coniare una sessione ANONIMA: il visitatore può navigare e riempire il carrello, ma il micro-frontend blocca il passaggio oltre il carrello finché la richiamata onUserRequired non fornisce una sessione associata all'utente (vedere il flusso utente facoltativo). Un'e-mail presente ma non valida viene comunque rifiutata (400).
externalUserIdstring · optionalIl tuo ID utente, memorizzato con la sessione come metadati (utile per il supporto e la correlazione dei log). Non interpretato da ADITUS.
ttlSecondsnumber · optionalDurata della sessione in secondi. Valore predefinito: 20 minuti, fino a un massimo di 24 ore. Alla scadenza, le chiamate al ticketshop restituiscono 401 session_expired: genera quindi un nuovo token, ad esempio tramite l'hook onSessionExpired.
eventSlugstring · optionalCorregge l'ENTRY POINT lato server del viaggio: impostato, il micro-frontend parte direttamente dalla selezione dell'articolo di questo evento; omesso, inizia dalla panoramica degli eventi. Poiché fa parte della sessione coniata, il browser non può manipolarlo. I caratteri non validi restituiscono 400 invalid_event_slug; uno slug che non si risolve in nessun evento live ricade nella panoramica degli eventi.

Conia una sessione - risposta (200)

CampoTipoSenso
sessionTokenstringToken opaco (sess_…). Consegnatelo al browser e passatelo a mount() come sessionToken; il micro-frontend lo invia come X-Aditus-Session ad ogni chiamata in negozio. Non contiene dati utente e non può essere decodificato.
expiresAtnumberScadenza come timestamp Unix in millisecondi. Puramente informativo per la tua pianificazione: il micro-frontend reagisce da solo al 401.

Revoca una sessione

DELETE /api/shop/session/:token con lo stesso segreto del portatore: il segreto deve appartenere al client per cui è stata coniata la sessione. Chiamalo al logout in modo che il token muoia con la tua sessione. Risposta: { "revocato": true } (o false se la sessione era già scaduta). La revoca è idempotente e sicura da licenziare e dimenticare.

Gli errori sono espliciti

StatoCodiceSenso
503session_not_configuredLe sessioni embed non sono configurate per questo client.
401unauthorizedIl mint secret utilizzato per creare la sessione non è valido o è assente.
400invalid_public_keyLa public key fornita non è valida.
400invalid_emailL'indirizzo email fornito non è valido.
400invalid_tokenIl session token fornito non è valido.
429rate_limitedTroppe richieste. Riprova tra poco.
403public_key_invalidLa public key fornita ha un formato non valido.
403public_key_unknownLa public key fornita non è riconosciuta.
403public_key_origin_unresolvedNon è stato possibile determinare l'origine della richiesta.
403public_key_domain_not_allowedIl dominio della richiesta non è autorizzato per questa public key.
401invalid_sessionLa sessione fornita non è valida.
401session_expiredLa sessione fornita è scaduta.
403session_requires_public_keyQuesta sessione richiede una public key.
403session_key_mismatchLa public key non corrisponde alla sessione.
403anonymous_sessionQuesta operazione non è disponibile per una sessione anonima.

Adesso fallo tuo

L'identità è impostata: successivamente, marchia il micro-frontend. Lo strumento di styling genera una configurazione del tema pronta da incollare e spiega ogni parametro, con un'anteprima dal vivo.

Apri lo strumento di styling