SubSovereign
All guides

SubSovereign localizzazione in React (web) — guida all’integrazione

Questa guida copre @subsovereign/react (sdk-react/) — lo strato UI del paywall per le app web React. Rende il paywall che progetti nel pannello di controllo SubSovereign come componenti React reali, utilizzando lo stesso motore di rendering usato nell’anteprima del pannello, quindi ciò che approvi nel pannello è esattamente ciò che viene distribuito.

Questo SDK si occupa solo di disegnare il paywall; non gestisce fatturazione o diritti di accesso. Abbinalo al Web/JS data SDK (@subsovereign/js-sdk) per checkEntitlement() e la convalida degli acquisti: i due sono progettati per essere usati insieme.

Prima di iniziare

Avrai bisogno di un server SubSovereign in esecuzione, di un’app registrata nel pannello (un appId e una API key di ruolo SDK) e di un paywall pubblicato (pannello → Paywall → Pubblica).

Passo 1 — Installazione

Il pacchetto si trova in sdk-react/ e non è ancora su npm: aggiungilo come dipendenza locale o da git (oppure copia la cartella nel tuo progetto):

npm install ./sdk-react   # path dependency while it's pre-npm
import { Paywall, usePaywallConfig } from '@subsovereign/react';

Passo 2 — Carica il paywall pubblicato

usePaywallConfig recupera la configurazione per questa app/utente. La variante A/B viene scelta server-side tramite userId, quindi lo stesso utente vedrà sempre lo stesso paywall.

const { config, loading, error } = usePaywallConfig({
  apiUrl: 'https://subs.yourdomain.com/api/v1', // YOUR self-hosted server
  appId:  'your-app-id',
  userId: currentUser.id,                        // your own stable user id
  apiKey: 'YOUR_SDK_KEY',
  locale: 'en',                                  // optional, default 'en'
  platform: 'web',                               // optional, default 'web'
});

Passa null invece dei parametri per saltare il recupero (ad esempio mentre l’utente non è autenticato).

Passo 3 — Rendi il paywall

<Paywall
  config={config}
  loading={loading}
  loadingState={<Spinner />}          // optional
  emptyState={<FallbackPaywall />}    // optional — shown when no config is published
  onSelectProduct={(productId) => startCheckout(productId)}
/>

onSelectProduct viene attivato quando l’utente seleziona un prodotto: collegalo al tuo checkout (Stripe sul web). Dopo il successo del checkout, convalida con il data SDK e ricontrolla i diritti di accesso:

await SubSovereign.validateStripeSubscription({ subscriptionId, productId, accessLevelId: 'pro' });
const { hasAccess } = await SubSovereign.checkEntitlements();
if (hasAccess) unlockProFeatures();

Se hai già una configurazione (recuperata tramite il data SDK), salta il hook e passala direttamente. Per un controllo completo esiste il renderer grezzo: <PaywallRenderer config={config} onSelectProduct={…} />.

Pulsante di recesso UE (Compliance Passport)

Se vendi abbonamenti a consumatori UE online, la Direttiva (UE) 2023/2673 richiede una funzione di recesso chiaramente etichettata. L’SDK la include già pronta:

import { WithdrawalButton, useWithdrawalConfig } from '@subsovereign/react';

function CancelSubscription({ user, sub }: { user: { id: string; locale: string }; sub: { id: string } }) {
  // Fetches the withdrawal settings AND the notice's wording in the customer's language.
  const { config, strings, error } = useWithdrawalConfig({
    apiUrl: 'https://subs.yourdomain.com/api/v1',
    appId: 'your-app-id',
    apiKey: 'YOUR_SDK_KEY',
    locale: user.locale,          // the CUSTOMER's language, not your console's
  });
  // Log it: a silent failure here is how a tenant ships English to German customers and never finds
  // out. The customer is never shown this — they get the button, in English.
  if (error) console.warn('SubSovereign: withdrawal settings unavailable', error);
  if (config && config.enabled === false) return null;   // the tenant turned it off

  return (
    <WithdrawalButton
      apiUrl="https://subs.yourdomain.com/api/v1"
      appId="your-app-id"
      apiKey="YOUR_SDK_KEY"
      userId={user.id}
      subscriptionId={sub.id}
      strings={strings}           // ← without this the dialog is ENGLISH for every customer
      locale={user.locale}        // ← and without this the DATE is formatted for the device
      labelKey={config?.labelKey} // ← the label the tenant chose in the console
      appearance="auto"
    />
  );
}

Non puoi modificare la dicitura. La console offre due etichette per la funzione di recesso — "Recedi dal contratto qui" e "Annulla qui" — e labelKey trasporta quella scelta dal tenant; le parole stesse arrivano dal server nella lingua del cliente. Non esiste modo di passare un testo personalizzato. In passato la prop label accettava testo libero ed è ora ignorata con un avviso in console: una notifica statutaria che può essere riformulata è una che può essere ammorbidita in termini di marketing, ed è per questo che non puoi passare neppure un colore.

🚩 Passa anche locale. Formatta il timestamp sulla conferma. Senza di esso la data viene formattata in base al dispositivo, quindi un cliente tedesco su un browser americano vedrà 9/3/2026 — che leggerà come 9 marzo, non 3 settembre. Si tratta della data su un atto legale, e l’ambiguità funziona in entrambi i sensi.

🚩 Passa strings, altrimenti la notifica statutaria sarà in inglese per tutti. Le parole vengono fornite dal tuo server SubSovereign nella lingua richiesta — sono nostre, non tue, appositamente, così una notifica legale non può essere ammorbidita in termini di marketing — e il pulsante renderizza ciò che gli viene passato. Non le recupera da solo perché lo stesso contratto dei componenti deve funzionare anche su Roku, dove un componente UI non può effettuare chiamate di rete. useWithdrawalConfig è la singola riga che collega i due mondi.

Se il recupero fallisce, il pulsante appare comunque, in inglese, piuttosto che non apparire. Il tuo obbligo è fornire una funzione di recesso visibile al consumatore; mostrarla nella lingua sbagliata non lo è. L’hook restituisce error così puoi registrarlo; non mostrarlo mai al cliente.

Aspetto — light, dark o auto (impostazione predefinita), e nient’altro. Il dialogo disegna la propria scheda e il proprio testo, quindi è leggibile su qualsiasi pagina, chiara o scura; auto segue le preferenze del browser dell’utente e, dove non viene rilevata alcuna preferenza, passa a dark. Non esiste modo di passare un colore: si tratta di una notifica statutaria e sia la formulazione che i colori sono fissi, così che risulti identica per ogni cliente di ogni tenant. Se applichi la stessa regola altrove nella tua UI, è esportata la funzione resolveAppearance(appearance, scheme) (scheme è 'light' | 'dark' | null).

Mostra il pulsante etichettato in modo prominente, conferma una volta, invia idempotentemente (un nuovo tentativo di rete non potrà mai creare due recessi) e mostra l’avviso di conferma. Il pulsante stesso non effettua chiamate di rete oltre all’invio, quindi spetta a te gestire config.enabled e config.jurisdictions come sopra. Sono esportati componenti di livello inferiore (fetchWithdrawalConfig, useWithdrawal, submitWithdrawal) per UI personalizzate.

Riferimento rapido

Vuoi… Usa
Caricare il paywall pubblicato usePaywallConfig(params){ config, loading, error }
Renderizzarlo <Paywall config onSelectProduct … />
Controllo completo del rendering <PaywallRenderer config onSelectProduct />
Recesso UE, nella lingua del cliente useWithdrawalConfig({…, locale}) → passa strings, locale e labelKey a <WithdrawalButton>
Diritti di accesso / convalida Guida Web & React Native

Passaggi successivi