SubSovereign
All guides

SubSovereign 웹 컴포넌트 — 통합 가이드 (Vue, Svelte, Angular, 일반 HTML)

이 가이드는 프레임워크에 구애받지 않는 브라우저 SDK인 **sdk-web/**에 대해 다룹니다. 별도의 빌드 단계나 종속성 없이 Vue, Svelte, Angular 또는 일반 HTML 페이지에서 동작하는 일반 fetch 데이터 클라이언트와 두 개의 웹 컴포넌트 <subsovereign-paywall><subsovereign-withdrawal>을 제공합니다. (React에서 개발 중이라면 React 가이드를 참고하세요.)

시작하기 전에

실행 중인 SubSovereign 서버, 대시보드에 등록된 앱(appId 및 SDK 역할 API 키), 그리고 게시된 결제창이 필요합니다.

1단계 — 로드 및 구성

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

2단계 — 사용자 접근 권한 확인

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

간단한 게이트의 경우 hasAccess()를 사용할 수 있으며, 특정 접근 수준에 대해 선택적으로 사용할 수 있습니다. 이 함수는 안전하게 닫힙니다: 네트워크 오류 시 false를 반환하므로 순간적인 오류로 유료 콘텐츠가 잘못 해제되지 않습니다.

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

checkEntitlements()는 실패 시 예외를 던지므로 사용자의 마지막 알려진 접근 권한을 유지할 수 있습니다. 반면 hasAccess()는 예외를 던지지 않습니다. 원하는 실패 동작에 따라 선택하세요.

3단계 — 결제창 표시 및 판매

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

크기 조정은 페이지의 역할입니다. 기본 너비는 420px이며, CSS 사용자 정의 속성(사용자 정의 속성은 그림자 DOM을 통과)으로 너비를 늘리거나 균일하게 축소할 수 있습니다.

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

구매 권한 부여: 이 SDK는 클라이언트 측 validate… 호출을 제공하지 않습니다. 제공업체와 결제를 완료한 후 서버 측에서 권한을 부여하세요 — 즉, 백엔드에서 SubSovereign 서버를 호출하거나 서명된 직접 결제 브리지(POST /entitlements/external)를 통해 권한을 부여한 다음 다시 확인하세요.

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

기능 플래그

서버에서 배포 없이 기능을 롤아웃할 수 있습니다. 안전하게 닫힙니다: 네트워크 오류 시 {}를 반환하므로 모든 플래그가 꺼진 상태로 읽힙니다.

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

AI 답변 엔진 기여도 추적 (GEO)

획득/GEO 기능을 사용하는 경우 방문자의 출처를 캡처하세요 — AI 추천인은 랜딩 페이지에서만 존재하며, 사용자 ID는 로그인 후에만 존재하므로 두 단계로 나누어 저장하고 플러시해야 합니다.

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

SDK는 ssCaptureLanding()을 호출할 때까지 방문자의 기기에 아무것도 저장하지 않습니다. 패키지를 가져와도 아무것도 기록되지 않습니다. 저장된 정보(추천인 + UTM 태그)는 GDPR 하에서 개인 데이터이며 ePrivacy 하에서 저장 데이터이므로 언제 캡처할지는 사용자의 동의 결정이며, 동의 배너의 처리기에 호출해야 합니다. 캡처하지 않으면 recordAttributionTouch()는 로그인 페이지 자체에서 볼 수 있는 UTM 태그와 자체 사이트가 아닌 경우의 추천인만 보고합니다. 즉, 이전 랜딩 페이지에서 발생한 AI 또는 검색 추천인은 기여도로 집계되지 않습니다. 이는 캡처하지 않기로 한 선택의 결과이며 방문자의 선택입니다.

recordAttributionTouch()는 저장된 첫 랜딩 정보(현재 페이지보다 우선)를 전송하고, 성공적인 전송 후에만 저장소를 지웁니다. 이후 로그인 시 재시도할 수 있도록 하며 절대 예외를 던지지 않습니다. 랜딩 페이지에서 configure()를 호출할 수 없는 경우(사용자 없는 정적 마케팅 사이트) ssFlushAttribution(userId, { apiUrl, apiKey })가 명시적 자격 증명을 사용하여 동일한 전송을 수행하거나 window.SS_API_URLwindow.SS_API_KEY를 설정할 수 있습니다.

웹사이트의 오리진은 서버의 ALLOWED_ORIGINS에 포함되어야 하며, 그렇지 않으면 브라우저가 조용히 아무 것도 보고하지 않습니다.

개인정보 보호 (GDPR 및 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

세 가지 개인정보 보호 호출은 실패 시 예외를 던집니다(ApiError, .status 포함). 법적 권리를 행사하는 사용자에게는 실패가 전달되어야 합니다. 오류를 잡아 사용자에게 알리고 절대 성공을 무음으로 표시하지 마세요. 결제 기록은 GDPR 제17조(3)(b)에 따라 서버에 보관되며 익명화됩니다.

EU 철회 버튼 (준수 패스포트)

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

눈에 띄는 레이블이 있는 버튼 → 단일 확인 → 멱등성 있는 제출 → Директива (EU) 2023/2673에 따른 확인. 커스텀 UI의 경우: SubSovereign.withdraw(subscriptionId, opts)SubSovereign.getWithdrawalConfig().

외관 — light, dark 또는 auto(기본값)로만 설정 가능합니다. 대화 상자는 자체 카드와 텍스트를 렌더링하므로 호스트 페이지의 배경과 관계없이 가독성이 보장됩니다. auto는 방문자의 브라우저 설정(prefers-color-scheme)을 따르며 언제든지 속성을 변경할 수 있습니다(카드가 다시 그려집니다). 통계상 알림의 경우 색상을 전달할 수 있는 방법은 없습니다. 통계상 알림은 모든 테넌트 고객에게 동일하게 읽히도록 고정되어 있으므로 색상과 문구는 고정됩니다. 단어는 서버에서 고객의 언어(configure()locale)로 제공되며, 오래된 서버의 경우 영어(빈 값이 아닌)로 대체됩니다.

이 요소는 withdrawal-shown, withdrawal-submitted(세부 정보: 영수증) 및 withdrawal-error(세부 정보: { reason, status? }) 이벤트를 발생시켜 분석할 수 있습니다. subscription-id를 잊으면 고객은 번역된 오류를 보게 되며 당신은 콘솔 오류와 withdrawal-error 이벤트를 받게 됩니다. 요소가 설정을 로드할 수 없는 경우(잘못된 키, 잘못된 앱 ID, 서버가 이 페이지의 오리진 거부) 고객은 여전히 버튼을 보게 되며 — 영어로 표시됩니다 — 당신은 원인을 명시하는 console.warnreason: 'config-unavailable'이 포함된 withdrawal-error 이벤트(HTTP status가 있는 경우 포함)를 받게 됩니다. 모든 환경에서 해당 이벤트를 수신하세요. 이는 독일 고객이 영어로 표시되는 문제를 감지하는 방법입니다. 사용자 액션 전에 로드 시점에 발생하므로 실패한 취소로 간주하지 마세요. 잘못된 구성은 개발자에게는 결코 무음으로 처리되지 않으며 고객에게는 결코 표시되지 않습니다.

빠른 참고

당신이 원하는 것 사용법
SDK 설정 SubSovereign.configure(config)
사용자가 해제한 기능 확인 await checkEntitlements(){ hasAccess, … } (실패 시 예외 발생)
간단한 게이트(안전하게 닫힘) await hasAccess('pro')boolean
결제창 로드 및 표시 pw.config = await getPaywallConfig() + select-product 이벤트
결제창 크기 조정 --ss-paywall-max-width · transform: scale(…)
결제 후 권한 부여 서버 측(백엔드 또는 BYO-결제 브리지), 그 후 재확인
기능 플래그(안전하게 닫힘) await getFeatureFlags(){ [flag]: boolean }
GEO 기여도 추적 동의 후 ssCaptureLanding() · 식별 후 recordAttributionTouch()
동의 기록 await recordConsent({ purpose, granted })
GDPR / CCPA(실패 시 예외 발생) requestErasure() · exportMyData() · setDoNotSell(bool)
EU 철회 <subsovereign-withdrawal subscription-id="…" appearance="light|dark|auto"> · withdraw()

다음 단계