A full-featured digital wallet application similar to GCash, built with Next.js, Supabase, and TypeScript.
app/
├── (dashboard)/ # Protected dashboard routes
│ ├── page.tsx # Dashboard home
│ ├── send/ # Send money page
│ ├── request/ # Request money page
│ ├── history/ # Transaction history
│ ├── contacts/ # Contacts management
│ ├── topup/ # Top-up wallet
│ ├── cashout/ # Cash out
│ ├── settings/ # User settings
│ └── layout.tsx # Dashboard layout
├── auth/
│ ├── login/ # Phone OTP login
│ ├── signup/ # Phone OTP signup
│ └── error/ # Auth error page
├── api/
│ ├── contacts/
│ │ └── search/ # Search users by phone
│ └── transactions/
│ ├── send/ # Send money API
│ └── request/ # Request money API
└── page.tsx # Root redirect page
components/
├── dashboard-header.tsx # Dashboard header with user info
├── dashboard-nav.tsx # Navigation sidebar
├── protected-route.tsx # Auth guard component
├── wallet-card.tsx # Wallet balance display
└── recent-transactions.tsx # Recent transactions widget
hooks/
├── useAuth.ts # Authentication hook
└── useWallet.ts # Wallet data hook
lib/
├── supabase/
│ ├── client.ts # Browser client setup
│ ├── server.ts # Server client setup
│ └── proxy.ts # Session proxy for middleware
└── utils.ts # Utility functions
auth.users (Supabase built-in)profilesid (UUID, FK to auth.users)phone_number (VARCHAR)display_name (VARCHAR)avatar_url (TEXT, optional)created_at, updated_at (TIMESTAMP)walletsid (UUID, PK)user_id (UUID, FK to profiles)balance (NUMERIC)currency (VARCHAR, default: ‘PHP’)created_at, updated_at (TIMESTAMP)transactionsid (UUID, PK)wallet_id (UUID, FK to wallets)from_user_id (UUID, FK to profiles)to_user_id (UUID, FK to profiles)amount (NUMERIC)type (VARCHAR: transfer_in, transfer_out, topup, cashout)status (VARCHAR: completed, pending, failed)description (TEXT, optional)payment_intent_id (VARCHAR, for Stripe)reference_code (VARCHAR, unique)created_at, updated_at (TIMESTAMP)payment_requestsid (UUID, PK)from_user_id (UUID, FK to profiles)to_user_id (UUID, FK to profiles)amount (NUMERIC)reason (TEXT, optional)status (VARCHAR: pending, accepted, rejected)created_at, updated_at (TIMESTAMP)contactsid (UUID, PK)user_id (UUID, FK to profiles)contact_user_id (UUID, FK to profiles)nickname (VARCHAR)created_at (TIMESTAMP)All tables have Row-Level Security (RLS) policies enabled to ensure users can only access their own data.
npm install
# or
pnpm install
.env.local:
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=your_stripe_key
STRIPE_SECRET_KEY=your_stripe_secret
# The database schema is created via the setup-schema.sql script
npm run dev
# or
pnpm dev
Visit http://localhost:3000 to see the app.
/auth/signup/api/contacts/searchSearch for user by phone number
Request: { "phone": "+63 9XX XXX XXXX" }
Response: { "profile": { "id", "display_name", "phone_number" } }
/api/transactions/sendSend money to another user
Request: { "recipientPhone", "amount", "message" }
Response: { "success": true, "message": "..." }
/api/transactions/requestCreate payment request
Request: { "recipientPhone", "amount", "reason" }
Response: { "success": true, "paymentRequest": {...} }
The app runs in demo mode by default:
For issues or questions, please refer to the documentation or create an issue in the repository.
MIT License - Feel free to use this project for personal or commercial purposes.