SubSovereign Web Component — guide d’intégration (Vue, Svelte, Angular, HTML)
Ce guide couvre sdk-web/ — le SDK navigateur agnostique de framework. Il fournit un client de données en fetch brut ainsi que deux Web Components, <subsovereign-paywall> et <subsovereign-withdrawal>, compatibles avec tout site — Vue, Svelte, Angular ou une page HTML simple — sans étape de build ni dépendances. (Vous utilisez React ? Consultez le guide React à la place.)
Avant de commencer
Un serveur SubSovereign en fonctionnement, une application enregistrée dans le tableau de bord (un appId et une clé API de rôle SDK), ainsi qu’un paywall publié.
Étape 1 — Charger et configurer
<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>
Étape 2 — Vérifier l’accès de l’utilisateur
const { hasAccess } = await SubSovereign.checkEntitlements();
if (hasAccess) showPremiumContent();
else showPaywall();
Pour une simple porte, utilisez hasAccess(), éventuellement pour un niveau d’accès nommé. Elle échoue en fermeture : en cas d’erreur réseau, elle retourne false, évitant ainsi qu’un incident ne débloque par erreur du contenu payant.
if (await SubSovereign.hasAccess('pro')) showPremiumContent();
checkEntitlements() lève une exception en cas d’échec, permettant de conserver l’accès connu de l’utilisateur ; hasAccess() ne lève jamais d’exception. Choisissez celle dont le comportement en cas d’échec correspond à votre besoin.
Étape 3 — Afficher le paywall et vendre
<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>
Le dimensionnement relève de votre page. La largeur par défaut est de 420 px ; élargissez-la avec la propriété CSS personnalisée (les propriétés personnalisées traversent le shadow DOM) et/ou appliquez une mise à l’échelle uniforme :
subsovereign-paywall { --ss-paywall-max-width: 900px; transform: scale(1.4); }
Octroyer l’achat : ce SDK n’inclut pas d’appel client de validation (validate…). Finalisez le paiement avec votre fournisseur, octroyez l’accès côté serveur — votre backend appelant votre serveur SubSovereign, ou le pont bring-your-own-billing signé (POST /entitlements/external) — puis vérifiez à nouveau :
const again = await SubSovereign.checkEntitlements();
if (again.hasAccess) unlock();
Fonctionnalités conditionnelles
Déployez des fonctionnalités depuis le serveur sans mise à jour de votre application. Échec en fermeture : en cas d’erreur réseau, vous recevez {}, donc toutes les fonctionnalités sont désactivées.
const flags = await SubSovereign.getFeatureFlags(); // { newPlayer: true, ... }
if (flags.newPlayer) showNewPlayer();
Attribution par moteur de réponse IA (GEO)
Si vous utilisez les fonctionnalités Acquisition/GEO, capturez d’où viennent les visiteurs — le référent IA n’existe que sur la page de destination, et l’identifiant utilisateur n’existe qu’après l’inscription. Il s’agit donc d’une opération en deux étapes : mémorisation puis envoi.
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();
Le SDK ne stocke rien sur l’appareil du visiteur avant l’appel à ssCaptureLanding(). L’import du package n’écrit rien. La mémoire temporaire (référent + balises UTM) constitue des données personnelles au regard du RGPD et un stockage au titre de l’ePrivacy. Quand la capturer relève donc de votre décision de consentement : appelez-la depuis le gestionnaire d’acceptation de votre bannière. Sans capture, recordAttributionTouch() ne signale que ce que la page d’inscription voit elle-même — les balises UTM dans l’URL, et un référent si celui-ci provient d’un site tiers — ce qui exclut toute attribution d’un référent IA ou de recherche antérieur. C’est le prix à payer pour ne pas capturer, et c’est le choix du visiteur.
recordAttributionTouch() envoie la première page de destination mémorisée (elle prime sur la page actuelle), ne vide la mémoire qu’après un envoi réussi pour permettre une nouvelle tentative lors d’une connexion ultérieure, et ne lève jamais d’exception. Si vos pages de destination ne peuvent pas appeler configure() (site marketing statique sans utilisateur), ssFlushAttribution(userId, { apiUrl, apiKey }) effectue le même envoi avec des identifiants explicites — ou définissez window.SS_API_URL et window.SS_API_KEY.
L’origine de votre site web doit figurer dans ALLOWED_ORIGINS du serveur, sinon le navigateur ne signalera rien silencieusement.
Vie privée (RGPD et 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
Les trois appels relatifs à la vie privée lèvent une exception en cas d’échec (ApiError, avec .status). Un utilisateur exerçant un droit légal doit être informé si l’opération échoue — interceptez l’erreur et affichez un message en conséquence ; ne montrez jamais un succès silencieux. Les enregistrements de facturation sont conservés par le serveur au titre de l’article 17(3)(b) du RGPD et anonymisés.
Bouton de rétractation UE (Passeport de conformité)
<subsovereign-withdrawal subscription-id="SUB_ID" appearance="auto"></subsovereign-withdrawal>
Bouton étiqueté de manière proéminente → confirmation unique → envoi idempotent → accusé de réception, conformément à la directive (UE) 2023/2673. Pour des interfaces personnalisées : SubSovereign.withdraw(subscriptionId, opts) et SubSovereign.getWithdrawalConfig().
Apparence — light, dark ou auto (par défaut), et rien d’autre. La boîte de dialogue génère sa propre carte et son propre texte, garantissant une lisibilité sur toute page hôte ; auto suit le paramètre du navigateur du visiteur (prefers-color-scheme), et l’attribut peut être modifié à tout moment — la carte se redessine. Il n’existe délibérément aucun moyen de transmettre une couleur : il s’agit d’un avis réglementaire, et les termes ainsi que les couleurs sont fixes pour garantir une lecture uniforme pour chaque client de chaque locataire. Les mots proviennent de votre serveur dans la langue du client (locale dans configure()) ; avec un serveur plus ancien, ils reviennent à l’anglais plutôt qu’à un texte vide.
L’élément émet withdrawal-shown, withdrawal-submitted (détails : le reçu) et withdrawal-error (détails : { reason, status? }) pour vos analyses. Si vous oubliez subscription-id, le client voit l’erreur traduite et vous recevez une erreur console ainsi qu’un événement withdrawal-error. Si l’élément ne peut pas charger ses paramètres (clé incorrecte, app id incorrect, votre serveur refusant l’origine de cette page), le client voit toujours le bouton — en anglais — et vous recevez un console.warn indiquant la cause probable ainsi qu’un événement withdrawal-error avec reason: 'config-unavailable' et, le cas échéant, le code HTTP status (une origine refusée ou une URL incorrecte n’en a pas). Écoutez cet événement dans tous les environnements : c’est ainsi que vous détectez que des clients allemands voient l’anglais. Notez qu’il se déclenche au chargement, avant toute action utilisateur — ne le comptez pas comme une annulation échouée. Une mauvaise configuration n’est jamais silencieuse pour le développeur, et jamais visible pour le client.
Référence rapide
| Vous souhaitez… | Utilisez |
|---|---|
| Configurer le SDK | SubSovereign.configure(config) |
| Voir ce que l’utilisateur a débloqué | await checkEntitlements() → { hasAccess, … } (lève une exception en cas d’échec) |
| Simple porte (échec en fermeture) | await hasAccess('pro') → boolean |
| Charger + afficher le paywall | pw.config = await getPaywallConfig() + événement select-product |
| Dimensionner le paywall | --ss-paywall-max-width · transform: scale(…) |
| Octroyer après paiement | côté serveur (votre backend ou le pont BYO-billing), puis vérifiez à nouveau |
| Fonctionnalités conditionnelles (échec en fermeture) | await getFeatureFlags() → { [flag]: boolean } |
| Attribution GEO | ssCaptureLanding() après consentement · recordAttributionTouch() une fois identifié |
| Enregistrer le consentement | await recordConsent({ purpose, granted }) |
| RGPD / CCPA (lève une exception en cas d’échec) | requestErasure() · exportMyData() · setDoNotSell(bool) |
| Rétractation UE | <subsovereign-withdrawal subscription-id="…" appearance="light|dark|auto"> · withdraw() |
Prochaines étapes
- Applications React : guide React. Approfondissement de la couche données : Web & React Native.
- Vous débutez avec ces concepts ? Lisez Comment fonctionne SubSovereign.