Skip to main content

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

  1. Shop tab → Stores sub-tab
  2. Browse stores or search
  3. Tap store → see catalog
  4. Tap product → Add to Cart
  5. Cart → Proceed to Checkout
  6. Enter delivery address (or pick "Pickup")
  7. Select payment method (currently only COD or pay-at-pickup — see Feature Flags)
  8. Place Order → seller receives notification + customer phone for verification call
  9. Order moves through statuses: pendingacceptedpreparingready / out_for_deliverydelivered

Ad (resale) inquiry

  1. Shop tab → Ads sub-tab
  2. Browse ads grid (2-column on mobile)
  3. Tap ad → product detail
  4. Tap "Contact on WhatsApp" → opens WhatsApp with pre-filled message to the consumer-seller
  5. Off-platform: buyer and seller transact directly

3. Web ↔ Mobile parity

CapabilityWebMobile
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/cartcart.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 tabs
  • src/app/(consumer)/products/[id]/page.tsx — Product detail (handles both store products + ads)
  • src/app/(consumer)/stores/[id]/page.tsx — Store catalog + checkout
  • src/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 + checkout
  • mobile/app/product/[id].tsx — Product detail
  • mobile/app/cart.tsx — Cart
  • mobile/app/my-ads.tsx — Profile → My Ads (separate route from Shop's "My Ads" filter)

5. APIs

EndpointMethodPurpose
/api/productsGETBrowse products with filters (status, productType, category, search, sort)
/api/products/[id]GETSingle product detail (includes store/seller relation)
/api/adsGETAuthenticated consumer's own ads (returns { ads: [...] })
/api/adsPOSTCreate new ad
/api/ads/[id]PUT, DELETEUpdate / delete ad
/api/store-ordersPOSTPlace store order (validates flag + pickup auto-default)
/api/store-orders/[id]GETOrder detail for tracking
/api/storesGETList active stores
/api/stores/[id]GETStore detail

6. Database touchpoints

  • Product — single table for both store products and ads. storeId set → store product. consumerId set → ad. Has images relation, category relation.
  • Store — owned by Seller, has Product[], 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 on StoreOrder.platformFee.
  • Pickup forces paymentMethod=pay_at_pickup server-side (resolvedPaymentMethod logic in /api/store-orders POST). Client cannot override.
  • Order status flow is enforced by getValidStatusTransitions() in src/lib/fulfillment.ts. E.g., you can't go deliveredpending.
  • 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=true skips 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 logic
  • tests/build-integrity.test.ts (Store Order API + Store Checkout sections)
  • tests/offline-only-launch.test.ts — pickup auto-default + payment gate
  • tests/api-shape-contracts.test.ts/api/ads response shape
  • mobile/tests/mobile-code-integrity.test.tsMy Ads filter regression guard

10. Known gotchas

  • /api/ads returns { ads: [...] } — NOT { products: [...] }. Mobile shop.tsx briefly broke on this 2026-04-17 ("My Ads" filter showed empty). Fix in data.ads unwrap. 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.