Scan a receipt, split it with friends, and pay — all from your phone.
SplitIt is a cross-platform mobile app that turns the awkward "who owes what" moment into a few taps. Snap a photo of a restaurant or bar receipt and an AI vision model extracts every line item; invite friends to a live splitting session; assign items to people; and settle up through real payment rails — including a SplitIt virtual card you can add to Apple Wallet.
Built with React Native (Expo) + a Node/Express backend on Google Cloud Run, with AI receipt parsing, real-time multiplayer sessions, biometric auth, and card issuing.
- Features
- How it works
- Tech stack
- Architecture
- Project structure
- Getting started
- Environment variables
- Backend
- Security notes
- Known limitations
- 📷 AI receipt scanning — Point your camera at a receipt. An OpenAI vision model first verifies it is a receipt, then extracts the restaurant name, line items, quantities, prices, add-ons/modifiers, tax, subtotal, total, and currency as structured JSON.
- 👥 Real-time split sessions — Host a split and have friends join live over WebSockets. Item assignments and totals update for everyone in real time.
- 🧾 Item-level splitting — Assign individual dishes (and their add-ons) to specific people instead of just dividing the bill evenly.
- 🔐 Phone + biometric onboarding — Sign up with phone-number OTP (Firebase Auth) and protect the app with Face ID / Touch ID.
- 💳 Real payments & virtual cards — Stripe for payment methods and a Lithic-issued virtual card, with Apple Wallet provisioning so the card can be tapped to pay.
- 🤝 Social layer — Friend requests, user search, groups, and invites.
- 🔔 Notifications — Push notifications plus in-app notification banners.
- 📜 History — Browse past transactions and re-open scanned receipts.
- 📱 QR codes — Share or join a split quickly by scanning a QR code.
┌─────────────┐ photo ┌──────────────┐ items+prices ┌──────────────┐
│ Camera / │ ─────────▶ │ OpenAI │ ───────────────▶ │ Split UI │
│ Receipt │ │ vision OCR │ │ (assign to │
└─────────────┘ └──────────────┘ │ friends) │
└──────┬───────┘
│ live sync
┌──────▼───────┐
Socket.IO ◀───▶│ Express API │
│ on Cloud Run│
└──────┬───────┘
Stripe / Lithic ◀──────┤
MySQL (Cloud SQL) ◀────┤
Firebase (Auth/DB/Storage)
Mobile app
- React Native
0.81+ Expo SDK54 - Expo Router for file-based navigation
- TypeScript
- NativeWind (Tailwind CSS for RN) for styling
- React Native Reanimated for animations
expo-camera,expo-local-authentication(Face ID / biometrics),expo-notifications
Services & APIs
- Firebase — phone-number Auth, Firestore, and Storage
- OpenAI — vision model for receipt parsing
- Stripe — payment methods & intents
- Lithic — virtual card issuing + Apple Wallet provisioning
- Socket.IO — real-time split sessions
Backend
- Node.js + Express 5 (ES modules)
- MySQL via
mysql2(Google Cloud SQL) - Deployed on Google Cloud Run
SplitIt is split into a client app and a stateless API server:
app/— the Expo Router app. Screens are grouped into route segments ((onboarding),(tabs)) plus standalone flows (scan, checkout, wallet, etc.). React Context providers handle auth (AuthContext), notifications, and image preloading.backend/— Firebase client config and the receipt-analysis module shared by the app, plusbackend/server/, the Express API that talks to MySQL, Stripe, and Lithic, and runs the Socket.IO server for live sessions.components/— reusable UI (gradient buttons, animated buttons, payment-method selector, virtual card, dynamic alerts, themed primitives).
SplitIt/
├─ app/ # Expo Router screens & navigation
│ ├─ (onboarding)/ # sign-up, OTP, password, address, Face ID
│ ├─ (tabs)/ # home, friends, history
│ ├─ Authentication/ # AuthContext, secure payment hook, API config
│ ├─ Notifications/ # push + in-app notification providers
│ ├─ scan.tsx / selectitems.tsx / summary.tsx # the splitting flow
│ ├─ walletmanager.tsx / addtowallet.tsx / checkout.tsx
│ └─ _layout.tsx # root layout / providers
├─ backend/
│ ├─ FirebaseConfig.js # Firebase web SDK init (config via env)
│ ├─ image.ts # OpenAI receipt OCR + parsing
│ ├─ RealTimeSession.ts # client-side socket session helpers
│ └─ server/ # Express API (index.js), Stripe & Lithic services
├─ components/ # reusable UI components
├─ constants/ • hooks/ • types/
├─ assets/ # icons & images
├─ .env.example # frontend env template
└─ app.json # Expo app config
- Node.js 18+ and npm
- Expo CLI (
npx expo) - iOS Simulator (Xcode) and/or Android Emulator, or the Expo Go / dev client on a device
- Accounts/keys for Firebase, OpenAI, Stripe, and Lithic (for full functionality)
npm installcp .env.example .env
# then fill in your own keys (see "Environment variables" below)npx expo start # then press i (iOS) or a (Android)
# or
npm run ios
npm run androidThis project uses native modules (camera, biometrics, Stripe), so a development build is recommended over Expo Go. See Expo dev clients.
Two .env files are used and both are git-ignored. Copy the matching
.env.example and fill in your own values.
EXPO_PUBLIC_* variables are inlined into the client bundle, so they are not secret.
Restrict the Google Maps key by app/bundle ID in the Google Cloud console.
| Variable | Purpose |
|---|---|
EXPO_PUBLIC_GOOGLE_MAPS_API_KEY |
Google Places address autocomplete |
EXPO_PUBLIC_OPENAI_API_KEY |
OpenAI receipt parsing (see limitations) |
EXPO_PUBLIC_FIREBASE_* |
Firebase web config (API key, project, app id, …) |
| Variable | Purpose |
|---|---|
LITHIC_API_KEY, LITHIC_WEBHOOK_SECRET, LITHIC_APPLE_PARTNER_ID |
Card issuing & Apple Wallet |
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET |
Stripe payments & webhooks |
DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_SOCKET_PATH |
Cloud SQL (MySQL) |
PORT |
API port |
cd backend/server
cp .env.example .env # fill in real values
npm install
npm start # node index.jsThe server exposes the REST API consumed by the app, runs the Socket.IO server for live
splitting sessions, and handles Stripe and Lithic webhooks. It is deployed to Google
Cloud Run; the app points at the deployed URL via app/Authentication/authIp.tsx.
This repository is intentionally free of secrets:
- All API keys and credentials are loaded from git-ignored
.envfiles; only.env.exampletemplates with placeholders are committed. - Firebase web config is included via env for cleanliness, though those values are designed to be public and are secured by Firebase Security Rules — not by being hidden.
- Git history was reset to a clean baseline so no previously committed secret is recoverable from this repo.
If you fork this project, supply your own keys and never commit a real .env.
- OpenAI calls run client-side today.
backend/image.tsexecutes on the device, which means an OpenAI key would ship in the app bundle. This should be proxied through the Express backend so the key never leaves the server. (Planned.) - Automated tests are not yet set up.
- Receipt parsing accuracy depends on photo quality and receipt formatting.
Built by @Ramzi788. All rights reserved.