BoilerplateHub

Expo + Supabase + Better Auth + Stripe

Expo with Supabase, Better Auth and Stripe is a mobile client on a backend you operate, taking payment for things that happen in the real world. Bookings, deliveries, physical goods and services between people all fit. Digital features unlocked inside the app do not, and that boundary decides whether this stack is legal for your product.

Expo SupabaseBetter AuthStripe

Database

Supabase

Supabase is used here as a Postgres your own server queries rather than one the app talks to directly, because with Better Auth there is no Supabase token for row level security to evaluate. That has a real architectural consequence: the Expo app calls your API and your API is the only thing holding a database credential. Orders and payment records written from Stripe events stay entirely server-side, which is where they belong on hardware you do not control.

Authentication

Better Auth

Better Auth has an Expo integration covering secure token storage and the deep link round trip for OAuth, which are the two details people underestimate on mobile. Sessions live in your Postgres, so revoking a lost device is a delete rather than a wait for a token to expire on its own. Because the schema is yours, the Stripe customer ID hangs directly off the user row and one query answers both identity and billing.

Payments

Stripe

This combination only makes sense when the money is for something the app stores do not classify as digital content: a reservation, a delivery, a physical item, a service performed by a person. Stripe's React Native SDK provides native payment sheets with Apple Pay and Google Pay for exactly those cases, and confirmation happens on your server before an order row exists. Tax on real-world sales is yours, which for goods can mean nexus rules rather than only VAT.

What to watch out for

Choosing Better Auth over the bundled Supabase option means you now need a server to host the auth handler and every query, because a shipped app cannot safely hold a database credential. That is a deployment target, a domain and an uptime commitment you did not have with the client-only approach. Weigh it honestly at the start, since it is the difference between using a platform and operating a backend.

Boilerplates close to this stack

Matched on Expo plus the parts of this stack our catalog tags. Each card shows which pieces actually line up, so you can see how much you would still wire yourself.

A CLAUDE.md for this stack

The rules that matter for this combination specifically, including who owns entitlement state. Adapt the commands to your repository before committing it.

CLAUDE.md
# CLAUDE.md

This project is a Expo SaaS on Supabase, Better Auth and Stripe.

## Stack

- Framework: Expo
- Database: Supabase
- Auth: Better Auth
- Payments: Stripe

## Boundaries

- Never edit a migration that has already run. Write a new one.
- Never hardcode Stripe price or product identifiers in components. They belong in config.
- Never trust a client-supplied user id. Read the session from Better Auth on the server.
- Treat webhook handlers as idempotent. The same event will arrive twice.

## Entitlements

- Stripe is the source of truth for what a customer paid for.
- The database mirrors that state; it never decides it.
- Any check for "can this user do X" reads the mirrored entitlement, not a live API call.

## Before you say a change is done

- The app builds.
- Tests pass.
- No secret, key or webhook signing secret appears in a committed file.

Related stacks