SubSovereign Web Component — integratiehandleiding (Vue, Svelte, Angular, plain HTML)
Deze handleiding behandelt sdk-web/ — de framework-onafhankelijke browser-SDK. Deze bevat een plain-fetch dataclient en twee Web Components, <subsovereign-paywall> en <subsovereign-withdrawal>, die werken op elke site — Vue, Svelte, Angular of een plain HTML-pagina — zonder buildstap en zonder afhankelijkheden. (Gebruik je React? Raadpleeg dan de React-handleiding.)
Voordat je begint
Een draaiende SubSovereign-server, een app geregistreerd in het dashboard (een appId en een SDK-rol API-sleutel), en een gepubliceerde paywall.
Stap 1 — Laden en configureren
<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>
Stap 2 — Controleren wat de gebruiker kan doen
const { hasAccess } = await SubSovereign.checkEntitlements();
if (hasAccess) showPremiumContent();
else showPaywall();
Voor een eenvoudige toegangspoort is er hasAccess(), optioneel voor één specifieke toegang (bijv. "pro"). Het sluit af bij fouten: bij een netwerkfout wordt false geretourneerd, zodat een storing nooit per ongeluk betaalde inhoud ontgrendelt.
if (await SubSovereign.hasAccess('pro')) showPremiumContent();
checkEntitlements() gooit een fout bij mislukking, zodat je de gebruiker op de laatst bekende toegang kunt houden; hasAccess() gooit nooit een fout. Kies de functie waarvan je het foutgedrag wilt.
Stap 3 — Toon de paywall en verkoop
<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>
De grootte bepaal je zelf op je pagina. De standaardbreedte is 420px; verbreed deze met de CSS-custom property (custom properties dringen de shadow DOM binnen) en/of schaal deze uniform:
subsovereign-paywall { --ss-paywall-max-width: 900px; transform: scale(1.4); }
Het verlenen van de aankoop: deze SDK bevat geen client-side validate…-aanroep. Voltooi de checkout met je provider, verleen de toegang server-side — je backend roept je SubSovereign-server aan, of de ondertekende bring-your-own-billing bridge (POST /entitlements/external) — en controleer vervolgens opnieuw:
const again = await SubSovereign.checkEntitlements();
if (again.hasAccess) unlock();
Feature flags
Rol functies uit vanaf de server zonder een deploy. Sluit af bij fouten: bij een netwerkfout krijg je {}, dus elke flag leest als uitgeschakeld.
const flags = await SubSovereign.getFeatureFlags(); // { newPlayer: true, ... }
if (flags.newPlayer) showNewPlayer();
AI-antwoordmachine-attributie (GEO)
Als je de Acquisition/GEO-functies gebruikt, leg dan vast waar bezoekers vandaan kwamen — de AI-verwijzer bestaat alleen op de landingspagina, en de gebruikers-ID bestaat pas na aanmelding, dus het is een tweestaps opslag-en-flush:
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();
De SDK slaat niets op op het apparaat van de bezoeker tot je ssCaptureLanding() aanroept. Het importeren van het pakket schrijft niets. De opgeslagen data (verwijzer + UTM-tags) vallen onder persoonsgegevens volgens de GDPR en opslag onder ePrivacy, dus wanneer je deze vastlegt, is een toestemmingsbeslissing van jou — roep het aan vanuit de acceptatiehandler van je banner. Zonder vastlegging rapporteert recordAttributionTouch() alleen wat de aanmeldingspagina zelf kan zien — UTM-tags op die URL, en een verwijzer als deze niet van je eigen site komt — dus een AI- of zoekverwijzing die eerder op een landingspagina plaatsvond, wordt niet toegeschreven. Dat is de prijs van niet vastleggen, en het is de keuze van de bezoeker.
recordAttributionTouch() stuurt de opgeslagen eerste landingspagina (deze overschrijft de huidige pagina), wist de opgeslagen data pas na een succesvolle verzending zodat een latere login opnieuw probeert, en gooit nooit een fout. Als je landingspagina’s configure() niet kunnen aanroepen (een statische marketingwebsite zonder gebruiker), doet ssFlushAttribution(userId, { apiUrl, apiKey }) hetzelfde met expliciete referenties — of stel window.SS_API_URL en window.SS_API_KEY in.
Je website’s origin moet in de server’s ALLOWED_ORIGINS staan, anders zal de browser niets rapporteren.
Privacy (GDPR en 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
De drie privacy-aanroepen gooien een fout bij mislukking (ApiError, met .status). Een gebruiker die een wettelijk recht uitoefent, moet worden geïnformeerd als het niet is gelukt — vang de fout op en geef dit aan; toon nooit een stilstaand succes. Factureringsgegevens worden door de server bewaard onder GDPR Art. 17(3)(b) en geanonimiseerd.
EU-opzegknop (Compliance Passport)
<subsovereign-withdrawal subscription-id="SUB_ID" appearance="auto"></subsovereign-withdrawal>
Opvallende gelabelde knop → enkele bevestiging → idempotente verzending → bevestiging, volgens Richtlijn (EU) 2023/2673. Voor aangepaste UIs: SubSovereign.withdraw(subscriptionId, opts) en SubSovereign.getWithdrawalConfig().
Uiterlijk — light, dark of auto (de standaard), en niets anders. De dialoog tekent zijn eigen kaart en zijn eigen tekst, zodat deze leesbaar is op elke hostpagina; auto volgt de browserinstelling van de bezoeker (prefers-color-scheme), en het attribuut kan op elk moment worden gewijzigd — de kaart wordt opnieuw getekend. Er is bewust geen manier om een kleur door te geven: dit is een wettelijke mededeling, en de tekst en kleuren zijn vastgelegd, zodat deze voor elke klant van elke tenant hetzelfde leest. De tekst komt van je server in de taal van de klant (locale in configure()); met een oudere server vallen ze terug op het Engels in plaats van blanco.
Het element geeft withdrawal-shown, withdrawal-submitted (details: de ontvangst) en withdrawal-error (details: { reason, status? }) af voor je analyse. Als je subscription-id vergeet, ziet de klant de vertaalde fout en jij krijgt een consolefout en een withdrawal-error-gebeurtenis. Als het element zijn instellingen niet kan laden (verkeerde sleutel, verkeerde app-ID, je server weigert de origin van deze pagina), krijgt de klant nog steeds de knop — in het Engels — en jij krijgt een console.warn met de waarschijnlijke oorzaak plus een withdrawal-error-gebeurtenis met reason: 'config-unavailable' en, indien aanwezig, de HTTP status (een geweigerde origin of een verkeerde URL heeft geen status). Luister naar deze gebeurtenis in elke omgeving: zo leer je dat Duitse klanten Engels zien. Houd er rekening mee dat deze gebeurtenis bij het laden afgaat, voordat de klant iets doet — tel deze niet als een mislukte opzegging. Een verkeerde configuratie is nooit stil voor de ontwikkelaar en nooit zichtbaar voor de klant.
Snelle referentie
| Je wilt… | Gebruik |
|---|---|
| De SDK instellen | SubSovereign.configure(config) |
| Zien wat de gebruiker heeft ontgrendeld | await checkEntitlements() → { hasAccess, … } (gooit een fout bij mislukking) |
| Eenvoudige toegangspoort (afsluiten bij fouten) | await hasAccess('pro') → boolean |
| Laad + teken de paywall | pw.config = await getPaywallConfig() + select-product-gebeurtenis |
| De paywall groter maken | --ss-paywall-max-width · transform: scale(…) |
| Verlenen na checkout | server-side (je backend of de BYO-billing bridge), controleer vervolgens opnieuw |
| Feature flags (afsluiten bij fouten) | await getFeatureFlags() → { [flag]: boolean } |
| GEO-attributie | ssCaptureLanding() na toestemming · recordAttributionTouch() eenmaal geïdentificeerd |
| Toestemming vastleggen | await recordConsent({ purpose, granted }) |
| GDPR / CCPA (gooit een fout bij mislukking) | requestErasure() · exportMyData() · setDoNotSell(bool) |
| EU-opzegging | <subsovereign-withdrawal subscription-id="…" appearance="light|dark|auto"> · withdraw() |
Volgende stappen
- React-apps: React-handleiding. Diepgaande datalaag: Web & React Native.
- Nieuw in deze concepten? Lees Hoe SubSovereign werkt.