ThirdMeal is a direct-to-consumer e-commerce platform for premium health-focused food products. Here's how I approached building it from scratch.
The Challenge
Build a complete checkout experience that handles:
- Cart management with real-time updates
- Multi-step checkout flow
- Phone verification via OTP
- Multiple payment methods through Razorpay
- Secure database design
Tech Stack
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite |
| Backend | Supabase (PostgreSQL) |
| Payments | Razorpay |
| Forms | React Hook Form + Yup |
| Animations | Framer Motion |
Multi-Step Checkout Flow
The checkout follows a 4-step wizard pattern:
Cart → Contact Details → Payment → Confirmation
Each step persists state, so users can navigate back without losing data. I used React Hook Form's FormProvider to share form state across steps.
OTP Verification System
Phone verification was critical for order security. The flow:
- User enters phone number
- Backend generates 6-digit OTP with 5-minute expiry
- OTP stored in database with timestamp
- Verification compares input against stored code
- Expired codes are automatically cleaned up
// Simplified OTP verification const verifyOTP = async (phone: string, code: string) => { const { data } = await supabase .from('otp_codes') .select('*') .eq('phone', phone) .eq('code', code) .gt('expires_at', new Date().toISOString()) .single(); return !!data; };
Razorpay Integration
Razorpay handles UPI, cards, net banking, and wallets. The integration pattern:
- Create order on backend (get
order_id) - Open Razorpay checkout modal
- Handle success/failure callbacks
- Verify payment signature on backend
Key learning: Always verify the payment signature server-side before marking an order as paid.
Database Design
Normalized schema with proper foreign keys:
customers- user detailsaddresses- shipping addressesorders- order metadataorder_items- line itemsproducts- product catalog
Row-Level Security (RLS) policies ensure customers can only access their own data.
Key Takeaways
- State persistence matters - Users abandon carts when they lose progress
- Verify everything server-side - Never trust client-side payment confirmations
- OTP expiry is essential - Prevents code reuse and brute force attacks
- Supabase RLS is powerful - Database-level security simplifies backend logic
The project handles the complete order lifecycle from browsing to delivery confirmation.