SubSovereign
All guides

SubSovereign on React Native — integration guide

This guide covers @subsovereign/react-native (sdk-react-native/) — a data client and a native paywall renderer in one package. The paywall you design in the SubSovereign dashboard renders here with native RN primitives (View/Text/Pressable), from the same config as the iOS, Android, web, React and Flutter SDKs.

Already using @subsovereign/js-sdk in React Native? That works too (see the Web & React Native guide) — this package adds the native <Paywall> component and a data client with no native dependencies (plain fetch).

Before you start

A running SubSovereign server, an app registered in the dashboard (an appId and an SDK-role API key), access levels (e.g. pro) linked to your store products, and a published paywall.

Step 1 — Install

The package lives in sdk-react-native/ and is not on npm yet — add it as a path or git dependency (or copy the folder into your project):

npm install ./sdk-react-native   # path dependency while it's pre-npm

Optional niceties, used automatically when present (never hard dependencies):

npx expo install expo-linear-gradient   # gradient paywall backgrounds
npm install react-native-video          # hero video (falls back to image without it)

Step 2 — Configure once, when your app starts

import { SubSovereign } from '@subsovereign/react-native';

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',
});

Step 3 — Check what the user can access

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

Wrap it in try/catch; on error keep the user on their last-known access and retry later — never lock a paying user out over a network blip.

Step 4 — Show the paywall and sell

import { Paywall, usePaywallConfig } from '@subsovereign/react-native';
import { Platform } from 'react-native';

const config = await SubSovereign.getPaywallConfig(Platform.OS); // or usePaywallConfig(params)

<Paywall config={config} onSelectProduct={(productId) => buy(productId)} />

Run the purchase with your billing library as usual, then have the server verify it:

// Android (Play Billing) — built into this client:
await SubSovereign.validateGooglePurchase({ purchaseToken, productId, accessLevelId: 'pro' });

// iOS (StoreKit): use the Web/JS data SDK's validateApplePurchase —
// this client does not ship an Apple call. See web.en.md Step 4.

Re-run checkEntitlements() afterwards and unlock.

Privacy (GDPR)

await SubSovereign.recordConsent({ purpose: 'analytics', granted: true });

EU withdrawal button (Compliance Passport)

What happens to the money. When a customer completes this screen, we record the withdrawal and stop the subscription renewing at the end of the period they have already paid for — they keep access until then. For a Stripe subscription we tell Stripe directly, using the keys you saved for this app; you can turn that off under When a customer cancels under EU law on the app's page in the dashboard, and if you do, stopping the renewal becomes your job. Apple, Google, Amazon and Roku do not allow a merchant to cancel on a customer's behalf — for those the customer must also cancel in the store, and you should say so in your own cancellation copy.

Confirming receipt is yours to do, and it is a legal duty. EU law requires the seller to send the customer an acknowledgement of receipt, on a durable medium such as email, without delay, showing the date and time. We do not send it and cannot: we hold no email address for your customers, by design. The customer is signed in to your app, and the successful cancel call returns the withdrawal reference and the exact submission time — send them the letter below from your own system at that moment, and keep a record of when you sent it. In React Native the drop-in <WithdrawalButton /> cannot hand the receipt back yet: call SubSovereign.withdraw(subscriptionId, { channel: Platform.OS === 'ios' ? 'ios' : 'android' }) from your own control and use the receipt it returns — pass channel exactly as the button does, because without it the call files the withdrawal as android on an iPhone.

Subject: Your withdrawal has been received

Hello,

We confirm we received your withdrawal from your subscription on {submittedAt}. Your reference is {withdrawalId}.

Your subscription will not auto-renew. This is your acknowledgement of receipt.

submittedAt and withdrawalId are the two fields the successful cancel call returns.

import { WithdrawalButton } from '@subsovereign/react-native';

<WithdrawalButton subscriptionId={sub.id} />

Shows the prominent labelled button, confirms once, and submits idempotently. Note: this button does not fetch the app's withdrawal config first — if you disable or jurisdiction-limit withdrawals in the dashboard, gate its visibility yourself with SubSovereign.getWithdrawalConfig(). SubSovereign.withdraw(subscriptionId, opts) is also available for custom UIs (pass channel: Platform.OS === 'ios' ? 'ios' : 'android' — the same mapping the button uses; the server accepts only web, ios, android, roku and tv, and the default is android).

Quick reference

You want to… Call
Set the SDK up SubSovereign.configure(config)
See what the user unlocked await checkEntitlements(){ hasAccess, … }
Load the published paywall await getPaywallConfig(Platform.OS) or usePaywallConfig(params)
Draw it natively <Paywall config onSelectProduct />
Verify a Google purchase await validateGooglePurchase({ purchaseToken, productId, accessLevelId })
Verify an Apple purchase validateApplePurchase on the Web/JS SDK
Record consent await recordConsent({ purpose, granted })
EU withdrawal <WithdrawalButton /> · withdraw() · getWithdrawalConfig()

Next steps