SubSovereign
All guides

SubSovereign Web Component — guia de integração (Vue, Svelte, Angular, HTML puro)

Este guia aborda o sdk-web/ — o SDK de navegador agnóstico de framework. Ele inclui um cliente de dados com fetch puro e dois Componentes Web, <subsovereign-paywall> e <subsovereign-withdrawal>, que funcionam em qualquer site — Vue, Svelte, Angular ou uma página HTML simples — sem etapa de compilação e sem dependências. (A desenvolver em React? Use o guia React em vez disso.)

Antes de começar

Um servidor SubSovereign em execução, um aplicativo registado no painel (um appId e uma chave de API de função SDK), e um painel de pagamento publicado.

Passo 1 — Carregar e configurar

<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 — Verificar o que o utilizador pode aceder

const { hasAccess } = await SubSovereign.checkEntitlements();
if (hasAccess) showPremiumContent();
else showPaywall();

Para um portão simples, existe hasAccess(), opcionalmente para um nível de acesso nomeado. Ele falha fechado: em caso de erro de rede, devolve false, para que um problema não desbloqueie acidentalmente conteúdos pagos.

if (await SubSovereign.hasAccess('pro')) showPremiumContent();

checkEntitlements() lança uma exceção em caso de falha, para que possa manter o utilizador no último acesso conhecido; hasAccess() nunca lança exceções. Escolha aquele cujo comportamento em caso de falha prefere.

Passo 3 — Mostrar o painel de pagamento e vender

<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>

O dimensionamento é da responsabilidade da sua página. A largura padrão é de 420 px; alargue-a com a propriedade personalizada CSS (as propriedades personalizadas atravessam o shadow DOM) e/ou dimensione uniformemente:

subsovereign-paywall { --ss-paywall-max-width: 900px; transform: scale(1.4); }

Conceder a compra: este SDK não tem chamada validate… no lado do cliente. Conclua o pagamento com o seu fornecedor, conceda a entitlement no servidor — o seu backend a chamar o seu servidor SubSovereign ou a ponte de faturação personalizada assinada (POST /entitlements/external) — e verifique novamente:

const again = await SubSovereign.checkEntitlements();
if (again.hasAccess) unlock();

Feature flags

Ative funcionalidades a partir do servidor sem fazer deploy. Falha fechado: em caso de erro de rede, obtém {}, pelo que todas as flags são interpretadas como desativadas.

const flags = await SubSovereign.getFeatureFlags(); // { newPlayer: true, ... }
if (flags.newPlayer) showNewPlayer();

Atribuição por motor de resposta de IA (GEO)

Se usar as funcionalidades de Aquisição/GEO, registe a origem dos visitantes — a referência de IA existe apenas na página de destino, e o user id só existe após o registo, pelo que é necessário um processo de dois passos de armazenamento e envio:

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();

O SDK não armazena nada no dispositivo do visitante até chamar ssCaptureLanding(). Importar o pacote não escreve nada. O armazenamento temporário (referente + tags UTM) é considerado dados pessoais ao abrigo do RGPD e armazenamento ao abrigo da ePrivacy, pelo que quando o capturar é uma decisão sua de consentimento, não nossa — chame-o a partir do handler de aceitação do seu banner. Sem a captura, recordAttributionTouch() regista apenas o que a própria página de registo consegue ver — tags UTM nessa URL e um referente se não for do seu próprio site — pelo que uma referência de IA ou de pesquisa que tenha ocorrido numa página de destino anterior não é atribuída. Este é o custo de não capturar, e é a escolha do visitante.

recordAttributionTouch() envia o primeiro destino armazenado (tem precedência sobre a página atual), limpa o armazenamento temporário apenas após um envio bem-sucedido para que uma login posterior tente novamente, e nunca lança exceções. Se as suas páginas de destino não puderem chamar configure() (um site de marketing estático sem utilizador), ssFlushAttribution(userId, { apiUrl, apiKey }) efetua o mesmo envio com credenciais explícitas — ou defina window.SS_API_URL e window.SS_API_KEY.

A origem do seu website deve estar na lista ALLOWED_ORIGINS do servidor, caso contrário, o navegador não reportará nada silenciosamente.

Privacidade (RGPD 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

As três chamadas de privacidade lançam exceções em caso de falha (ApiError, com .status). Um utilizador que exerça um direito legal deve ser informado quando a operação não foi concluída — apanhe o erro e diga-o; nunca mostre um sucesso silencioso. Os registos de faturação são mantidos pelo servidor ao abrigo do RGPD Art. 17(3)(b) e anonimizados.

Botão de desistência da UE (Passaporte de Conformidade)

<subsovereign-withdrawal subscription-id="SUB_ID" appearance="auto"></subsovereign-withdrawal>

Botão com rótulo proeminente → confirmação única → submissão idempotente → reconhecimento, conforme a Diretiva (UE) 2023/2673. Para IU personalizadas: SubSovereign.withdraw(subscriptionId, opts) e SubSovereign.getWithdrawalConfig().

Aparência — light, dark ou auto (padrão), e apenas estas opções. O diálogo desenha o seu próprio cartão e texto, pelo que é legível em qualquer página anfitriã; auto segue as definições do navegador do visitante (prefers-color-scheme), e o atributo pode ser alterado a qualquer momento — o cartão é redesenhado. Não existe forma de passar uma cor: esta é uma notificação estatutária, e a redação e as cores são fixas para que seja lida da mesma forma por todos os clientes de todos os inquilinos. As palavras chegam do seu servidor na língua do cliente (locale em configure()); com um servidor mais antigo, caem para o inglês em vez de ficarem em branco.

O elemento emite withdrawal-shown, withdrawal-submitted (detalhe: o recibo) e withdrawal-error (detalhe: { reason, status? }) para a sua análise. Se esquecer subscription-id, o cliente vê o erro traduzido e você recebe um erro de consola e um evento withdrawal-error. Se o elemento não conseguir carregar as definições (chave errada, app id errado, o servidor recusar a origem desta página), o cliente ainda vê o botão — em inglês — e você recebe um console.warn com a provável causa, além de um evento withdrawal-error com reason: 'config-unavailable' e, quando existir, o status HTTP (uma origem recusada ou uma URL errada não tem nenhum). Ouça este evento em todos os ambientes: é assim que descobre que clientes alemães estão a ver inglês. Note que é acionado no carregamento, antes de qualquer ação do cliente — não o conte como uma desistência falhada. Uma configuração incorreta nunca é silenciosa para o programador, nem visível para o cliente.

Referência rápida

Pretende… Use
Configurar o SDK SubSovereign.configure(config)
Ver o que o utilizador desbloqueou await checkEntitlements(){ hasAccess, … } (lança exceção em caso de falha)
Portão simples (falha fechado) await hasAccess('pro')boolean
Carregar + desenhar o painel de pagamento pw.config = await getPaywallConfig() + evento select-product
Dimensionar o painel de pagamento --ss-paywall-max-width · transform: scale(…)
Conceder após a compra servidor (o seu backend ou a ponte BYO-billing), depois verifique novamente
Feature flags (falha fechado) await getFeatureFlags(){ [flag]: boolean }
Atribuição GEO ssCaptureLanding() após o consentimento · recordAttributionTouch() após a identificação
Registar consentimento await recordConsent({ purpose, granted })
RGPD / CCPA (lança exceção em caso de falha) requestErasure() · exportMyData() · setDoNotSell(bool)
Desistência da UE <subsovereign-withdrawal subscription-id="…" appearance="light|dark|auto"> · withdraw()

Próximos passos