Account and paywall
Onboarding, sign-in, entitlements and account deletion — the surface where two backends meet.
| Route | What it is |
|---|---|
onboarding | First run |
paywall | RevenueCat's paywall |
customer-center | Subscription 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.