Checkout Live Online payments gated
The point where a cart becomes a StoreOrder. Three payment methods, two fulfillment types, server-enforced rules.
1. Overview
Two entry points: (a) Cart page → "Proceed to Checkout" (multi-product), (b) Store page → in-line "Place Order" (single-store). Both POST to /api/store-orders with the same payload shape. Server validates fulfillment + payment compatibility, calculates 10% commission, creates StoreOrder + StoreOrderItem rows, notifies seller, sends order-confirmation email to consumer.
2. User journey
- Cart → Proceed to Checkout
- Enter delivery address (or select Pickup)
- Enter contact phone
- Pick payment method (currently auto-defaults — see Business Logic)
- Tap Place Order
- See success state, order ID
- Receive order confirmation email + push (seller calls phone to verify)
3. Web ↔ Mobile parity
| Capability | Web cart checkout | Web store checkout | Mobile store checkout |
|---|---|---|---|
| Delivery option | ✅ | ✅ | ✅ |
| Pickup options (immediate / scheduled) | ❌ | ✅ | ❌ |
| COD / Pay-at-Pickup | ✅ | ✅ | ✅ |
| Online (UPI / PhonePe) | 🟡 Gated | 🟡 Gated | 🟡 Gated |
| Address autocomplete | ✅ Nominatim | ✅ Nominatim | ✅ Saved + recent |
4. Key components
- Web (cart):
src/app/(consumer)/cart/page.tsx— inline form per store - Web (store):
src/app/(consumer)/stores/[id]/page.tsx— has pickup support - Mobile:
mobile/app/store/[id].tsx,mobile/app/cart.tsx
5. APIs
| Endpoint | Method | Purpose |
|---|---|---|
/api/store-orders | POST | Place order. Validates flag, auto-defaults pickup payment, creates rows. |
/api/payments/initiate-store | POST | Online payment session — returns 503 when PAYMENTS_ONLINE_ENABLED=false |
/api/payments/upi-config | GET | Returns current paymentEnabled flag for UI gating |
/api/payments/callback | POST | PhonePe webhook (gateway flow) |
6. Database touchpoints
StoreOrder(created on success) —paymentMethod,paymentStatus,fulfillmentType,pickupTime,cancellationReason, all status timestampsStoreOrderItem(one row per cart item)Contribution(created in background, 0.5% of total → charity ledger)ReelEarning(PENDING row created if order came from a tagged reel)
7. Business logic
Server is single source of truth
The /api/store-orders POST builds resolvedPaymentMethod server-side:
- If
fulfillmentTypeis pickup → forced topay_at_pickup(client cannot override) - If
paymentMethod=onlineandPAYMENTS_ONLINE_ENABLED=false→ 400 with helpful error - If
paymentMethod=onlineand flag on → must use/api/payments/initiate-storeinstead (rejects from this endpoint) - Otherwise →
cod(delivery default)
Commission
- 10% default (
Store.commissionRate) - Stored on
StoreOrder.platformFee - Seller gets
subtotal - platformFee→StoreOrder.sellerAmount
Order confirmation email
- Sent on POST success (best-effort, fire-and-forget, never blocks order placement)
- Uses central email lib (
sendOrderConfirmation) - Includes itemized list + total + Track Order CTA
8. Feature flags / env vars
PAYMENTS_ONLINE_ENABLED— primary gatePLATFORM_UPI_ID— direct UPI (vs PhonePe gateway)PHONEPE_*— gateway config (optional)
9. Tests
tests/offline-only-launch.test.ts— 31 tests around the flag behaviortests/store-product-flow.test.tstests/build-integrity.test.ts— Store Order API surface
10. Known gotchas
paymentStatusmirrorspaymentMethodby default —cod/pay_at_pickupuntil handover, then transitions topaid/refunded. Don't confuse them.- Multi-store checkout is NOT supported — one POST = one store. UI splits cart accordingly.
- Online payment orders take a different code path — they're created in
payments/callbackafter the gateway confirms, NOT in/api/store-ordersPOST. So the order ID exists only after payment success. - Seller's "Call to Confirm" badge appears on
pendingorders so they verify the customer phone before accepting.
11. Related
- Cart · Orders
- Feature Flags —
PAYMENTS_ONLINE_ENABLEDdeep dive - Email Infrastructure — order confirmation template