A mobile app for splitting expenses with friends, built with Expo (React Native) and Supabase (Postgres + Auth + Edge Functions).
Create groups, add expenses, split fairly, and settle up.
- Features
- Project Goals
- Tech Stack
- Getting Started
- Supabase Setup
- Testing
- Project Structure
- Troubleshooting
- Security Notes
- Create groups and invite members (by email or placeholder names)
- Add expenses and split per member
- Record settlements that can cover multiple expenses
- Store payment handles for Venmo, Cash App, and PayPal
- Planned: public expense links so non-app participants can review and pay their share
- Planned: receipt image parsing with item-level assignment to group members
- Email/password and OAuth sign-in (Google, Apple)
- Secure by default with Supabase RLS
The current product direction and feasibility notes are documented in:
PROJECT_GOALS.md
This covers public expense links for non-app participants, payment app links, receipt parsing, transaction linking, invoice parsing, and on-device classification tradeoffs.
- Expo React Native (TypeScript)
- Supabase: Postgres, Auth, Edge Functions
- Jest + Testing Library (unit tests)
- Node 18+ and npm
- Expo CLI (
npm i -g expo) and optionally the Expo Go app on your device - A Supabase project (or Supabase CLI for local dev)
npm installCreate .env.local in this folder with your public Supabase credentials (anon key is safe to ship thanks to RLS):
SUPABASE_URL=your_supabase_url
SUPABASE_ANON_KEY=your_supabase_anon_keyThese are loaded by app.config.js, which checks .env.local first and falls back to .env, then exposes them to the app through expo.extra.
# Start Metro
npm run start
# iOS simulator
npm run ios
# Android emulator
npm run android
# Web
npm run webSign up or sign in from the app screens. For OAuth in development, add a redirect in Supabase Auth: expense-split://auth/callback (matches the scheme in app.config.js).
The canonical schema is in:
schema.sql
It defines:
profiles(app users, internally referenced byprofiles.id)groups,memberships(membership links useprofiles.id)expenses,expense_splitssettlements,settlement_items- optional
invoices,settlement_shares
RLS policies and design notes are documented in:
LLMCONTEXT.md
The app uses two functions:
create_group_and_seed_admin: Creates a group and adds the caller as admin.invite_member: Invite by existing profile or email (creates a placeholder profile if needed). Any member can invite; only admins can grant admin.
Local development:
# 1) Optional: start a local Supabase stack
supabase start
# 2) Provide service secrets to functions
# (or set via `supabase secrets set ...`)
cat > supabase/.env.local <<EOF
URL=your_supabase_url
SERVICE_ROLE_KEY=your_service_role_key
EOF
# 3) Serve functions locally with env
supabase functions serve --env-file supabase/.env.localDeploy to your Supabase project:
supabase functions deploy create_group_and_seed_admin invite_member
supabase secrets set --env-file supabase/.env.localNotes:
- Functions require a user JWT (the app calls them via
supabase.functions.invoke(...)after sign-in). - Service role key is only used in Edge Functions and never shipped in the app.
npm testTests mock the Supabase client and cover hooks like useGroups, useMembers, and expense flows.
.
├─ app.config.js # Loads .env and exposes values to Expo
├─ schema.sql # DB schema reference
├─ src/
│ ├─ lib/supabase.ts # Supabase client (uses expo extra values)
│ ├─ hooks/ # React hooks (groups, members, expenses)
│ ├─ types/ # Generated DB types + app types
│ └─ screens/ # SignIn/SignUp, Groups, etc.
└─ supabase/
├─ config.toml # Functions config
├─ .env.local # Local function secrets (URL, SERVICE_ROLE_KEY)
└─ functions/
├─ create_group_and_seed_admin/
└─ invite_member/
- Missing env vars: Ensure
SUPABASE_URLandSUPABASE_ANON_KEYare set in.env.localor.env. - OAuth callback: Set redirect URL in Supabase Auth to
expense-split://auth/callback. - 401 from functions locally: Ensure you’re signed in (functions require a user JWT) or test via curl with a valid JWT.
- RLS errors: Ensure policies from
LLMCONTEXT.mdare applied (memberships/expenses/expense_splits are the usual culprits).
- The anon key is public by design (enforced by RLS).
- Never expose the service role key in the app; keep it only in Edge Functions via Supabase secrets.
For deeper technical details (schema, RLS, helper functions, invariants), see LLMCONTEXT.md.
- Implement public expense links for non-app participants
- Add multilingual receipt parsing and item-level assignment
- Add transaction-linking candidate detection for commonly split expenses
- Implement invoice parsing for bill documents where transaction data is insufficient
- Add push notifications
- Add offline support
- Some Supabase queries may need optimization for complex joins
- OAuth redirect URLs need to be configured in Supabase
- Custom expense shares feature needs backend implementation
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
This project is licensed under the MIT License.