SubSovereign Web Component — guida all’integrazione (Vue, Svelte, Angular, HTML puro)
Questa guida copre sdk-web/ — l’SDK browser agnostico rispetto al framework. Include un client dati basato su fetch e due Web Component, <subsovereign-paywall> e <subsovereign-withdrawal>, che funzionano su qualsiasi sito — Vue, Svelte, Angular o una semplice pagina HTML — senza passaggi di build e senza dipendenze. (Sviluppi in React? Usa la guida React invece.)
Prima di iniziare
È necessario un server SubSovereign attivo, un’app registrata nel dashboard (con un appId e una chiave API di ruolo SDK) e un paywall pubblicato.
Passo 1 — Caricamento e configurazione
<script type="module">
import { SubSovereign } from './sdk-web/src/index.js'; // importing registers the elements
SubSovereign.configure({
apiKey: 'YOUR_SDK_KEY',
appId: 'your-app-id',
baseUrl: 'https://subs.yourdomain.com/api/v1', // YOUR self-hosted server
userId: currentUser.id, // your own stable user id
locale: 'en',
});
</script>
Passo 2 — Verificare cosa può fare l’utente
const { hasAccess } = await SubSovereign.checkEntitlements();
if (hasAccess) showPremiumContent();
else showPaywall();
Per un semplice controllo esiste hasAccess(), opzionalmente per un determinato livello di accesso. Il comportamento predefinito è chiuso: in caso di errore di rete restituisce false, quindi un problema temporaneo non potrà mai sbloccare contenuti a pagamento per errore.
if (await SubSovereign.hasAccess('pro')) showPremiumContent();
checkEntitlements() lancia un’eccezione in caso di errore, così puoi mantenere l’utente nell’accesso noto precedente; hasAccess() non lancia mai eccezioni. Scegli il metodo il cui comportamento in caso di errore si adatta meglio alle tue esigenze.
Passo 3 — Mostrare il paywall e vendere
<subsovereign-paywall id="pw"></subsovereign-paywall>
<script type="module">
const pw = document.getElementById('pw');
pw.config = await SubSovereign.getPaywallConfig(); // platform defaults to 'web'
pw.addEventListener('select-product', (e) => {
startCheckout(e.detail.productId); // wire to your checkout
});
</script>
La dimensione è compito della tua pagina. La larghezza predefinita è 420 px; puoi aumentarla con la proprietà CSS personalizzata (le proprietà personalizzate attraversano lo shadow DOM) e/o scalare uniformemente:
subsovereign-paywall { --ss-paywall-max-width: 900px; transform: scale(1.4); }
Concedere l’acquisto: questo SDK non include chiamate di validate… lato client. Completa il checkout con il tuo fornitore, poi concedi l’entitlement lato server — il tuo backend chiama il server SubSovereign o usa il bridge bring-your-own-billing firmato (POST /entitlements/external) — quindi verifica nuovamente:
const again = await SubSovereign.checkEntitlements();
if (again.hasAccess) unlock();
Feature flag
Distribuisci funzionalità dal server senza effettuare un deploy. Comportamento predefinito chiuso: in caso di errore di rete ottieni {}, quindi ogni flag viene interpretato come disattivato.
const flags = await SubSovereign.getFeatureFlags(); // { newPlayer: true, ... }
if (flags.newPlayer) showNewPlayer();
Attribuzione dell’AI answer-engine (GEO)
Se usi le funzionalità Acquisition/GEO, registra da dove provengono i visitatori — il referrer AI esiste solo sulla pagina di atterraggio, e l’ID utente esiste solo dopo la registrazione, quindi è necessario un processo in due fasi di memorizzazione e invio:
import { ssCaptureLanding } from '@subsovereign/web';
// On every page of your marketing site, ONCE THE VISITOR HAS ACCEPTED YOUR CONSENT BANNER:
onConsentAccepted(() => ssCaptureLanding());
// …later, the moment the user signs up / signs in, after SubSovereign.configure():
await SubSovereign.recordAttributionTouch();
L’SDK non memorizza nulla sul dispositivo del visitatore finché non chiami ssCaptureLanding(). L’importazione del pacchetto non scrive nulla. La memorizzazione temporanea (referrer + tag UTM) è un dato personale ai sensi del GDPR e una memorizzazione ai sensi dell’ePrivacy, quindi quando effettuarla è una tua decisione in merito al consenso: chiamala dal gestore dell’accettazione del banner. Senza la memorizzazione, recordAttributionTouch() riporterà solo ciò che la pagina di registrazione stessa può vedere — i tag UTM nell’URL e un referrer se non proviene dal tuo sito — quindi un referral da AI o da motore di ricerca avvenuto su una pagina di atterraggio precedente non verrà attribuito. Questo è il compromesso derivante dall’assenza di memorizzazione, ed è una scelta del visitatore.
recordAttributionTouch() invia il primo atterraggio memorizzato (sovrascrivendo la pagina corrente), svuota la memorizzazione solo dopo un invio riuscito, così un successivo login ritenta, e non lancia mai eccezioni. Se le tue pagine di atterraggio non possono chiamare configure() (ad esempio un sito di marketing statico senza utente), ssFlushAttribution(userId, { apiUrl, apiKey }) esegue lo stesso invio con credenziali esplicite — oppure imposta window.SS_API_URL e window.SS_API_KEY.
L’origine del tuo sito web deve essere inclusa tra gli ALLOWED_ORIGINS del server, altrimenti il browser non riporterà nulla in modo silenzioso.
Privacy (GDPR e CCPA)
await SubSovereign.recordConsent({ purpose: 'analytics', granted: true });
await SubSovereign.requestErasure(); // "forget me" — GDPR Art. 17
const myData = await SubSovereign.exportMyData(); // data export — GDPR Art. 20
await SubSovereign.setDoNotSell(true); // CCPA "Do Not Sell" / GPC signal
Le tre chiamate relative alla privacy lanciano eccezioni in caso di errore (ApiError, con .status). Se un utente esercita un diritto legale, deve essere informato se l’operazione non è andata a buon fine: gestisci l’errore e comunica l’esito negativo; non mostrare mai un successo silenzioso. I registri di fatturazione vengono conservati dal server ai sensi dell’art. 17(3)(b) del GDPR e anonimizzati.
Pulsante di recesso UE (Compliance Passport)
<subsovereign-withdrawal subscription-id="SUB_ID" appearance="auto"></subsovereign-withdrawal>
Pulsante etichettato in modo prominente → conferma singola → invio idempotente → riconoscimento, secondo la Direttiva (UE) 2023/2673. Per interfacce utente personalizzate: SubSovereign.withdraw(subscriptionId, opts) e SubSovereign.getWithdrawalConfig().
Aspetto — light, dark o auto (predefinito), e solo queste opzioni. Il dialogo disegna la propria scheda e il proprio testo, quindi è leggibile su qualsiasi pagina host; auto segue le preferenze del browser del visitatore (prefers-color-scheme), e l’attributo può essere modificato in qualsiasi momento — la scheda viene ridisegnata. Non esiste alcuna possibilità di passare un colore: si tratta di un avviso statutario, e il testo e i colori sono fissi affinché risulti identico per ogni cliente di ogni tenant. Le parole arrivano dal tuo server nella lingua del cliente (locale in configure()); con un server più vecchio, viene usato l’inglese come fallback invece di lasciare uno spazio vuoto.
L’elemento emette gli eventi withdrawal-shown, withdrawal-submitted (dettaglio: la ricevuta) e withdrawal-error (dettaglio: { reason, status? }) per le tue analisi. Se dimentichi subscription-id, il cliente visualizza l’errore tradotto e tu ricevi un errore in console e un evento withdrawal-error. Se l’elemento non riesce a caricare le impostazioni (chiave errata, app id errato, il server rifiuta l’origine di questa pagina), il cliente vede comunque il pulsante — in inglese — e tu ricevi un console.warn che indica la probabile causa più un evento withdrawal-error con reason: 'config-unavailable' e, se presente, lo stato HTTP (un’origine rifiutata o un URL errato non ne hanno uno). Ascolta questo evento in ogni ambiente: è così che scopri se i clienti tedeschi vedono l’inglese. Nota che viene attivato al caricamento, prima di qualsiasi azione del cliente — non considerarlo come un fallimento della cancellazione. Una configurazione errata non è mai silenziosa per lo sviluppatore e non è mai visibile al cliente.
Riferimento rapido
| Vuoi… | Usa |
|---|---|
| Configurare l’SDK | SubSovereign.configure(config) |
| Vedere cosa ha sbloccato l’utente | await checkEntitlements() → { hasAccess, … } (lancia eccezione in caso di errore) |
| Controllo semplice (chiuso in caso di errore) | await hasAccess('pro') → boolean |
| Caricare e visualizzare il paywall | pw.config = await getPaywallConfig() + evento select-product |
| Dimensione del paywall | --ss-paywall-max-width · transform: scale(…) |
| Concedere l’accesso dopo il checkout | lato server (il tuo backend o il bridge BYO-billing), poi verifica nuovamente |
| Feature flag (chiuso in caso di errore) | await getFeatureFlags() → { [flag]: boolean } |
| Attribuzione GEO | ssCaptureLanding() dopo il consenso · recordAttributionTouch() una volta identificato |
| Registrare il consenso | await recordConsent({ purpose, granted }) |
| GDPR / CCPA (lancia eccezione in caso di errore) | requestErasure() · exportMyData() · setDoNotSell(bool) |
| Recesso UE | <subsovereign-withdrawal subscription-id="…" appearance="light|dark|auto"> · withdraw() |
Passaggi successivi
- App React: guida React. Approfondimento sul livello dati: Web & React Native.
- Nuovo a questi concetti? Leggi Come funziona SubSovereign.