Save It Mobile is a React Native fintech client built with Expo and Expo Router. It provides authentication, account verification, transfers, transaction history, notifications, profile/security settings, and Google sign-in.
- Expo SDK 54, React Native 0.81, React 19
- Expo Router (file-based routing)
- React Query for server state
- React Hook Form + Zod for validation
- NativeWind (Tailwind for React Native)
- Axios for API integration
- Firebase Authentication + Google Sign-In
- Expo Secure Store for token persistence
- Expo Notifications + Ably for real-time updates
- Email/password sign-up and login
- Google authentication
- Email verification flow (OTP)
- Forgot password, OTP validation, and password reset
- Transfer flow with confirmation and PIN validation
- First-time transaction PIN setup (4-digit)
- Change password and change transaction PIN
- Transaction history and transaction receipt screen
- Notification stream integration
- Profile and security screens
app/
(auth)/ # Login, signup, forgot/reset flows
(verification)/ # Email verification flow
(app)/ # Main authenticated app routes
(tabs)/ # Home, history, profile tabs
components/ # Reusable UI and form components
contexts/ # Auth context and app-wide providers
hooks/ # Query/mutation and utility hooks
lib/ # Axios, Firebase, schemas, utilities
services/ # API request functions
types/ # Shared TypeScript types
- Node.js 20+
- pnpm (recommended in this repo) or npm
- Android Studio and/or Xcode for native builds
- EAS CLI (for cloud builds):
npm i -g eas-cli
Create a .env file in the project root with the following keys:
EXPO_PUBLIC_BASE_API_URL=
EXPO_PUBLIC_FIREBASE_API_KEY=
EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN=
EXPO_PUBLIC_FIREBASE_PROJECT_ID=
EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET=
EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
EXPO_PUBLIC_FIREBASE_APP_ID=
EXPO_PUBLIC_FIREBASE_MEASUREMENT_ID=
EXPO_PUBLIC_WEB_CLIENT_ID=
EXPO_PUBLIC_IOS_CLIENT_ID=Notes:
- Android emulator commonly uses
10.0.2.2for local backend access. - For a physical device, use your machine LAN IP for the API URL.
pnpm installpnpm startpnpm android
pnpm iospnpm webThis project uses @react-native-google-signin/google-signin, which requires a native binary containing the module.
- Use a development build or a custom native build.
- Expo Go may not include all required native modules for this flow.
- If Google Sign-In is unavailable in the binary, the app now lazy-loads the module and fails gracefully at button press time instead of crashing route registration.
- Route groups:
app/(auth)for unauthenticated routesapp/(verification)for pending verification usersapp/(app)for active authenticated users
- Root guard logic is implemented in
app/_layout.tsxusing token + user status.
pnpm start- start Expo dev serverpnpm android- run Android native buildpnpm ios- run iOS native buildpnpm web- run web targetpnpm lint- lint project
This repo includes eas.json profiles:
development(development client, internal distribution)preview(internal distribution)production(Android APK)
Example commands:
eas build --platform android --profile development
eas build --platform android --profile production
eas build --platform ios --profile previewBase URL is configured through EXPO_PUBLIC_BASE_API_URL.
All payloads below reflect the current frontend service layer usage in services/auth-service.ts and services/transaction-service.ts.
POST /auth/signup- Request:
{ "name": "Jane Doe", "email": "jane@example.com", "password": "password123", "confirmPassword": "password123", "client": "mobile" }
- Request:
POST /auth/login- Request:
{ "email": "jane@example.com", "password": "password123" }
- Request:
POST /auth/google- Request:
{ "idToken": "<firebase-id-token>" }
- Request:
POST /auth/resend-verification-email- Request:
{ "client": "mobile" }
- Request:
POST /auth/verify-email- Request:
{ "token": "123456" }
- Request:
POST /auth/forgot-password- Request:
{ "email": "jane@example.com", "client": "mobile" }
- Request:
POST /auth/validate-otp- Request:
{ "otp": "123456" }
- Request:
POST /auth/reset-password- Request:
{ "password": "newPassword123", "token": "<reset-session-token>" }
- Request:
PATCH /auth/update-password- Request:
{ "currentPassword": "oldPassword123", "newPassword": "newPassword123" }
- Request:
PATCH /auth/pin- Request:
{ "pin": "1234" }
- Request:
PATCH /auth/update-pin- Request:
{ "currentPin": "1234", "newPin": "5678" }
- Request:
GET /users/dashboard- Returns current user profile, account details, status, and related dashboard resources.
POST /transactions/transfer- Request:
{ "recipientAccNumber": "1234567890", "amount": 2500, "pin": "1234" }
- Request:
PATCH /users/push-token- Request:
{ "pushToken": "ExponentPushToken[...]" }
- Request:
- Endpoints that require authentication use:
Authorization: Bearer <token>
- Token injection and 401 handling are managed centrally in
lib/axios.ts.
- Cause: running a binary without the native Google Sign-In module.
- Fix: run with a development/custom build that includes
@react-native-google-signin/google-signin.
- Usually a symptom of module load failure in files imported by the route.
- Fix the underlying runtime import error (for example native module issues), then restart the bundler.
- Expected when a token is invalid/expired.
- Axios interceptor logs out and redirects via auth guards.
- Do not commit private API secrets.
- Public Expo/Firebase client values are expected in frontend builds, but server secrets must stay on backend infrastructure.