Shop / Marketplace Live
The primary commerce surface. Two distinct sub-flows under one tab:
- Stores — verified seller catalogs with full checkout (delivery / pickup, COD / pay-at-pickup)
- Ads (Resale) — peer-to-peer listings, WhatsApp-only inquiry (no platform checkout)
1. Overview
A consumer opens Shop → either browses Stores (4 in production at launch) or scrolls Ads (peer-to-peer resale). Tapping a store product adds to cart → Cart page → Place Order. Tapping an Ad opens the product detail with "Send Inquiry via WhatsApp". The platform takes 10% commission on every store order; ad listings are free for the seller and the platform takes nothing.
2. User journey
Store product purchase
- Shop tab → Stores sub-tab
- Browse stores or search
- Tap store → see catalog
- Tap product → Add to Cart
- Cart → Proceed to Checkout
- Enter delivery address (or pick "Pickup")
- Select payment method (currently only COD or pay-at-pickup — see Feature Flags)
- Place Order → seller receives notification + customer phone for verification call
- Order moves through statuses:
pending→accepted→preparing→ready/out_for_delivery→delivered
Ad (resale) inquiry
- Shop tab → Ads sub-tab
- Browse ads grid (2-column on mobile)
- Tap ad → product detail
- Tap "Contact on WhatsApp" → opens WhatsApp with pre-filled message to the consumer-seller
- Off-platform: buyer and seller transact directly
3. Web ↔ Mobile parity
| Capability | Web | Mobile |
|---|---|---|
| Browse Stores | ✅ /shop (Stores tab) | ✅ Shop tab |
| Browse Ads | ✅ /shop (Ads tab) | ✅ Shop tab |
| Filter Ads by category | ✅ | ✅ |
| "My Ads" filter | ✅ | ✅ (fixed 2026-04-17, see Gotchas) |
| Store detail + cart | ✅ /stores/[id] | ✅ store/[id].tsx |
| Cart page | ✅ /cart | ✅ cart.tsx |
| Place store order | ✅ | ✅ |
| Pickup option | ✅ web stores | ❌ mobile store doesn't have pickup yet |
| Online payment (UPI) | 🟡 Gated by PAYMENTS_ONLINE_ENABLED | 🟡 Same gate |
4. Key components
Web
src/app/(consumer)/shop/page.tsx— Stores + Ads tabssrc/app/(consumer)/products/[id]/page.tsx— Product detail (handles both store products + ads)src/app/(consumer)/stores/[id]/page.tsx— Store catalog + checkoutsrc/app/(consumer)/cart/page.tsx— Dual-track cart (store products → checkout, ads → WhatsApp inquiry)
Mobile
mobile/app/(tabs)/shop.tsx— Shop tab (Stores + Ads sub-tabs, "My Ads" filter)mobile/app/store/[id].tsx— Store catalog + checkoutmobile/app/product/[id].tsx— Product detailmobile/app/cart.tsx— Cartmobile/app/my-ads.tsx— Profile → My Ads (separate route from Shop's "My Ads" filter)
5. APIs
| Endpoint | Method | Purpose |
|---|---|---|
/api/products | GET | Browse products with filters (status, productType, category, search, sort) |
/api/products/[id] | GET | Single product detail (includes store/seller relation) |
/api/ads | GET | Authenticated consumer's own ads (returns { ads: [...] }) |
/api/ads | POST | Create new ad |
/api/ads/[id] | PUT, DELETE | Update / delete ad |
/api/store-orders | POST | Place store order (validates flag + pickup auto-default) |
/api/store-orders/[id] | GET | Order detail for tracking |
/api/stores | GET | List active stores |
/api/stores/[id] | GET | Store detail |
6. Database touchpoints
Product— single table for both store products and ads.storeIdset → store product.consumerIdset → ad. Hasimagesrelation,categoryrelation.Store— owned bySeller, hasProduct[],commissionRate(default 10%), opening/closing hours.StoreOrder+StoreOrderItem— checkout history. Payment columns:paymentMethod,paymentStatus,cancellationReason.Cart— server-side cart for store products (mobile uses AsyncStorage too).
7. Business logic
- 10% platform commission on every store order (
Store.commissionRate, override per store). Stored onStoreOrder.platformFee. - Pickup forces
paymentMethod=pay_at_pickupserver-side (resolvedPaymentMethod logic in/api/store-ordersPOST). Client cannot override. - Order status flow is enforced by
getValidStatusTransitions()insrc/lib/fulfillment.ts. E.g., you can't godelivered→pending. - Ads are NOT in the cart checkout flow — the cart page splits items: store products → checkout, ads → WhatsApp inquiry modal.
- Self-delivery flag:
Store.selfDelivery=trueskips auto-rider-assignment.
8. Feature flags / env vars
PAYMENTS_ONLINE_ENABLED— when false (default), hides "Pay via UPI" button + rejects online payment server-side. See Feature Flags.PLATFORM_UPI_ID,PLATFORM_UPI_NAME— UPI direct (vs PhonePe gateway)PHONEPE_*— gateway config (optional; UPI direct is primary)
9. Tests
tests/store-product-flow.test.ts— 41 tests covering the dual-track cart logictests/build-integrity.test.ts(Store Order API + Store Checkout sections)tests/offline-only-launch.test.ts— pickup auto-default + payment gatetests/api-shape-contracts.test.ts—/api/adsresponse shapemobile/tests/mobile-code-integrity.test.ts—My Adsfilter regression guard
10. Known gotchas
/api/adsreturns{ ads: [...] }— NOT{ products: [...] }. Mobile shop.tsx briefly broke on this 2026-04-17 ("My Ads" filter showed empty). Fix indata.adsunwrap. Locked by 2 regression tests.- Mobile store screen doesn't support pickup yet — only delivery. Web does both. Adding pickup to mobile is on the post-launch backlog.
- "My Ads" appears in two places: (a) Profile → My Ads (route
/my-ads), (b) Shop → Ads → "My Ads" filter pill. They show the same data but have separate code paths. - Currency on Ads: ads can be posted in any currency from the dropdown but the checkout only handles INR. Old EUR-priced ads (test data) display correctly but can't be transacted.
11. Related
- CHANGELOG entries: 2026-04-17 offline-only launch, 2026-04-17 my-ads fix
- Cart feature · Checkout feature · Orders feature
- Feature Flags