StatsHub Docs
Features

Account and paywall

Onboarding, sign-in, entitlements and account deletion — the surface where two backends meet.

RouteWhat it is
onboardingFirst run
paywallRevenueCat's paywall
customer-centerSubscription management
profile/*, admin-settings/*Account, filter defaults, app administration
legal/[document]The legal documents

Two backends meet here

Match data comes from the API. Accounts do not: Supabase holds the session and the user record, the same project the web apps sign in against.

There is no profile table. auth.users is the whole account model — Supabase keeps the display name and avatar in user_metadata, whose keys belong to whichever identity provider wrote them — and everything else about a person is their entitlement, which RevenueCat owns.

lib/api/auth/auth-client is the only place a client is constructed. Tokens go to the keychain rather than AsyncStorage: SecureStore rejects an entry over 2048 bytes and a session is bigger than that, so the storage adapter chunks it.

Entitlements

src/lib/core/paid-access.tsx, paid-access-model.ts and feature-access-model.ts — what is unlocked, derived from RevenueCat's customer info. Both models have tests beside them, because an entitlement bug either gives away the product or locks out someone who paid.

revenuecat-key.ts picks the platform's API key, and is also tested — a wrong key is an empty offerings list, which looks identical to a network failure.

The paywall is a route

Like everything else. purchases-ui.ts presents RevenueCat's own paywall, so the design lives in their dashboard rather than in this repo — which is what makes changing a price or a trial a non-deploy.

Account deletion

deleteAccountAndEndSession in lib/core/session calls DELETE /api/v2/me, then signs out, then clears the device. The order is the point: clearing first and failing the server call leaves an account nobody can reach and Apple's reviewers can find — see App Store.

The delete is an API call because the account spans three systems — a web subscription at Stripe, the saved rows in the API's database, and the Supabase auth user — and removing the auth user needs the service-role key, which cannot ship in a bundle. The API reads whose account it is from the bearer token and deletes in that order, so a failure partway leaves the person signed in and able to retry. SUPABASE_SERVICE_ROLE_KEY has to be set on the production API before a build ships, or deletion answers 503 at the point Apple tests it.

Profile's confirmation says what goes and that it cannot be undone, and for a subscriber adds that an App Store subscription has to be cancelled in Settings: the API cancels a web subscription, but only the person can cancel Apple's.

On this page