Softechinfra
Development

We Built a Multi-Vendor Marketplace for an FMCG Distributor (37 Brands, Rs 140Cr GMV) — The Stack

A Hyderabad FMCG super-distributor went from 37 brand-by-brand WhatsApp orderbooks to a single multi-vendor B2B marketplace in 18 weeks. ~3,400 retailer customers, payment routing, settlement engine, fixed-price build.

Vivek KumarVivek Kumar
August 12, 202516 min read
We Built a Multi-Vendor Marketplace for an FMCG Distributor (37 Brands, Rs 140Cr GMV) — The Stack

A Hyderabad FMCG super-distributor moves ₹140 crore of goods a year across 37 brands — soaps, biscuits, packaged foods, beverages, personal care — to ~3,400 retailer customers from kirana stores to mid-tier supermarkets across Telangana, Andhra Pradesh, and northern Karnataka. Until April 2025 they ran the entire orderbook on 37 brand-by-brand WhatsApp groups, an Excel master, and a 9-person tele-calling team typing orders into a desktop ERP. We shipped a multi-vendor B2B marketplace that consolidated all 37 brands into one app, one checkout, one settlement engine. Order capture time went from 11 minutes per order (call + retype) to 90 seconds (retailer self-serve). This is the architecture and the trade-offs.

37
Brands on One Marketplace
~3,400
Active Retailer Buyers
Rs 140Cr
Annual GMV Routed Through Stack

The Answer in 60 Words

We built a Next.js + React Native marketplace, a Node.js + Fastify API backed by PostgreSQL 16, a settlement engine that routes payments across 37 brand virtual accounts via Razorpay Route, BullMQ-backed reconciliation workers, and a Metabase analytics layer. ~3,400 active retailers, ~94,000 orders/month, ₹140 cr annual GMV.

Why This Matters Now

Indian FMCG distribution has been pulled in three directions since 2023. ONDC (Open Network for Digital Commerce) hit ~12 million transactions/month by mid-2025 and forced even traditional distributors to think about API-first ordering. Brand-direct platforms (Udaan, Jumbotail, ElasticRun) have squeezed super-distributor margins. And kirana retailers — the demand side — got smartphone-first during COVID and never went back to paper. A 37-brand super-distributor running on WhatsApp groups in 2025 is leaving 8-12% of GMV on the table to faster competitors. The build was defensive as much as offensive.

The other shift is on the payment side. Razorpay Route and Razorpay X (and the equivalent products from Cashfree, Easebuzz) made multi-vendor settlement programmable in a way it was not three years ago. Pre-Route, splitting a ₹14,000 retailer order across 5 brands meant manual T+2 reconciliation. Post-Route, it is automatic per-transaction.

The Client (Specific Details)

  • Sector: FMCG super-distribution (multi-brand stockist + sub-distributor model)
  • Location: HQ in Secunderabad, 4 warehouses across Telangana + AP + N. Karnataka
  • Brand mix: 37 brands — 22 daily-essentials (soap, biscuit, atta), 8 personal care, 4 beverages, 3 emerging (D2C food brands), all FMCG goods, no perishables
  • Retailer customer base: ~3,400 active (ordered in last 90 days), ~6,200 registered
  • Order shape: average ticket ₹4,800; 24% of orders > ₹10,000; 8% of orders > ₹25,000
  • The trigger: A brand principal (large soaps player) issued a Q1 2025 ultimatum: digital orderbook by August 1 or the brand moves to a competitor distributor. The founder gave us 18 weeks.
  • The Architecture (One Diagram Worth Drawing)

    RN
    Retailer App: React Native
    Browse 37 brand catalogues in one feed, add to cart, place order. Hindi + Telugu + English. Voice search for brand names (kirana shopkeepers prefer voice over typing). ~78% of orders now come via the app.
    FE
    Distributor + Brand Console: Next.js
    Distributor sees the full pipe. Each brand sees only their SKUs, their inventory, their settlement. RBAC at the API layer with brand_id scoping enforced everywhere.
    PAY
    Payment Routing: Razorpay Route
    Each brand has a Razorpay Route account. A retailer order is split per-brand at checkout time. Razorpay handles the per-brand settlement; our settlement engine reconciles against expected splits nightly.
    DB
    DB + Workers: Postgres 16 + BullMQ
    Postgres for the truth-of-record. BullMQ for: order-routing (warehouse selection), inventory-reservation, payment-reconciliation, brand-settlement-attribution, daily MIS rollup. ~340,000 jobs/day at peak.

    The Settlement Engine (The Hardest Part)

    A typical retailer order has 12 SKUs across 4 brands. Some brands have a 7-day credit term, some 14, some immediate. Some brands settle to the distributor, some direct to the brand. The distributor takes a per-brand commission, plus a cash-discount adjustment, plus GST handling. Pre-marketplace, this was a 4-person back-office task running on Excel for 8 hours a day. Post-marketplace, it runs as a pipeline that takes 14 minutes a day for the back-office to review and approve.

    The pipeline:

    1
    Order placed at checkout
    Retailer hits "place order" with 12 SKUs. We compute the per-brand sub-total, per-brand GST, per-brand commission, per-brand settlement instruction. Razorpay Route order created with N split rules (one per brand). Total computed time: ~280ms.
    2
    Payment captured (or credit note issued)
    Cash payments via UPI/card route to the brand sub-accounts directly. Credit-term orders generate an invoice with the appropriate due-date and trigger no immediate movement. Webhook from Razorpay confirms capture; we mark the order as paid.
    3
    Reconciliation worker (nightly)
    For every order in the day, fetch the actual settlement record from Razorpay's settlement API. Compare against expected splits stored at checkout time. Flag discrepancies (typically: refunds, partial captures, Razorpay fee absorption rules). Output a 30-row review queue for the back-office.
    4
    Brand settlement attribution
    Per brand, per day, generate a settlement statement. Show: gross sales, returns, commission deducted, net payable, GST liability. Brand console download as PDF or Excel. Auto-emailed at 9 am daily.
    5
    Audit log immutable
    Every state transition (order placed, paid, dispatched, delivered, returned, refunded) writes to an append-only audit table. ~14 GB/year. Used for the brand-side audits and for our own incident forensics.

    Webhook Idempotency (Where We Earned Our Money)

    Razorpay's webhook spec is honest about it: events may be delivered more than once. Their best-practices doc is explicit: build idempotent handlers, dedupe on event id, expect out-of-order delivery, respond 2xx on success.

    Our handler enforces three invariants:

  • Dedupe by Razorpay event id. Postgres unique index on razorpay_event_id. Re-delivery is a no-op.
  • Order-state transitions are idempotent. Setting order.status = 'paid' twice is fine. Moving it from 'paid' to 'pending' is not allowed; the handler refuses.
  • Settlement amounts are computed, never overwritten by webhook payload. The webhook tells us a payment was captured. The amount we record comes from our own checkout-time computation, not from the webhook body. This protects against a bug or a malicious actor setting a different amount in the payload.
  • We have processed ~1.4 million webhook events without a single double-spend or missed-payment incident. The discipline matters at this scale.

    Multi-Brand Catalogue: The Other Hard Part

    37 brands x ~140 SKUs per brand = ~5,200 SKUs in the catalogue, each with its own pricing, packaging unit, schemes, MOQ, and brand-side promotional rules. The catalogue is the second-most-complicated part of the system after settlement.

    Our approach:

  • Brand-controlled product master. Each brand updates their own SKU master via a CSV upload or API. The distributor cannot edit brand products directly — they can only enable/disable for their region.
  • Distributor-controlled regional pricing. The distributor sets per-region pricing on top of brand MRP, within brand-set guard rails (cannot go below brand floor price).
  • Time-bound schemes. "10+1 free on Surf Excel 1kg from 1-15 August" expressed as a scheme rule with a start date, end date, condition, and outcome. Applied at cart time. Promo logic alone is 480 lines of code.
  • MOQ enforcement at cart. A retailer cannot place an order below the brand-set minimum. If they have 3 brands at MOQ-OK and 1 brand below, the cart shows the warning per-brand, not at the cart level.
  • The 18-Week Plan

    • Weeks 1-2: Discovery — interview 8 retailers, 12 brand contacts, 4 warehouse managers; map the existing WhatsApp + Excel + ERP flow end-to-end
    • Weeks 3-5: Schema design (the catalogue and settlement schemas alone took 10 days of debate), authentication, brand-onboarding tools
    • Weeks 6-8: Retailer app v1 — browse, cart, checkout in English first; payment integration with Razorpay Route
    • Week 9: Settlement engine v1, reconciliation worker, brand console
    • Weeks 10-11: Multi-language (Hindi, Telugu) for the retailer app; voice search; offline-cache for spotty rural networks
    • Week 12: Brand catalogue self-serve uploads, scheme rules engine, MOQ guards
    • Week 13: Warehouse fulfillment workflow — picking lists, dispatch confirmations, delivery proof
    • Week 14: Returns + credit notes — including the messy partial-return-of-bundled-scheme case
    • Weeks 15-16: UAT with 2 brand teams + 12 hand-picked retailers; iterate on scheme rule edge cases
    • Week 17: Soft launch with 4 brands, 200 retailers, ~₹4 cr GMV in 5 days
    • Week 18: Full launch — all 37 brands, all 3,400 active retailers

    Common Mistakes (Each One Hurts)

    Symptom: "Brand settlement is off by Rs 14,000." Cause: Razorpay fee deducted from the settlement but expected-split computation did not subtract it. Fix: read the Razorpay settlement API carefully — fees can be deducted from the platform side or from the linked-account side depending on configuration. Match your config exactly.

    Symptom: "Retailer placed an order then complained 'I did not pay'." Cause: webhook arrived late and the app showed a stale "pending" state. Fix: poll the order status from the app on a 30-second interval until terminal state. Do not rely on push alone for payment confirmation.

    Symptom: "Two brands are arguing over a returned crate." Cause: returns hit a single inventory bucket and were attributed to the wrong brand. Fix: every return record carries the brand_id from the original order. Audit log shows attribution. Run a reconciliation report weekly with brand-side counterparts.

    Symptom: "Some retailers cannot complete UPI payment." Cause: Razorpay's UPI flow occasionally fails on older Android handsets. Fix: enable both Razorpay Standard Checkout (in-app SDK) and a hosted Web Checkout fallback. Detect failure modes and switch automatically.

    Symptom: "Daily MIS report takes 14 minutes to load." Cause: aggregation across 5,200 SKUs x 3,400 retailers x 30 days is slow on the live transactional DB. Fix: use a Postgres logical replica for analytics, materialise the daily-rollup on a cron, serve from the materialised table for sub-second loads.

    When NOT to Build a Multi-Vendor Marketplace

    Skip the build if (a) you carry under 8 brands and your order volume is under 200/day — a vanilla B2B WhatsApp commerce setup will deliver 80% of the value at 5% of the build cost, (b) your brand principals refuse to accept programmable settlements — a Razorpay-Route style stack requires brand-side bank-account onboarding which can take 2-4 weeks per brand, or (c) your sales team is your competitive advantage and digital self-serve will erode that. Some categories (premium spirits, regulated goods) need the conversation.

    For our earlier work on a Diwali-cracker multi-vendor marketplace, see Diwali Crackers Multi-Vendor Marketplace. Same architectural pattern at smaller scale (9 sellers vs 37 brands). The Razorpay Route integration carries over almost identically.

    The honest caveat on multi-vendor: the operational burden post-launch is real. Brand onboarding takes 2-4 weeks each. Scheme rules drift constantly. Returns reconciliation needs a dedicated person. Budget 2 FTE on the distributor side for ongoing operations from day one. Pretending the platform "runs itself" is the most common reason these projects underperform.

    Real Outcome (10 Months Post-Launch)

    90 sec
    Median Order Capture (was 11 min)
    +18%
    GMV Growth Over Pre-Launch Baseline
    14 min
    Daily Reconciliation (was 8 hr)
    94%
    Retailer Adoption (Active Last 30 Days)

    The +18% GMV is the number the brand principals cared about — proof the digital orderbook captured incremental orders, not just shifted existing ones. The 90-second median capture time meant the tele-calling team of 9 dropped to 3 (the others moved to brand-acquisition and credit-collection roles, no layoffs).

    A Detail That Saved Us On Day 41

    On day 41 of UAT, a brand-side promo rule was misread by the scheme engine: "10+1 free on Surf Excel 1kg" was applied as "+1 unit at MRP" instead of "+1 unit free." 14 retailers were each billed for the free unit. We caught it in our daily back-office review (the 14-minute one). We refunded all 14 within 2 hours, posted credit notes by EOD, and added a property-based test that synthesises 1,000 random scheme combinations and asserts the cart total never exceeds the sum of (unit_price x quantity). Eight months later we have not had a similar incident.

    The lesson: marketplace promo rules will eventually betray you. Test the cart total invariant, not just the rule logic.

    The Stack Cost Per Month

    ComponentNotes
    Hetzner CCX43 (app + Postgres primary)16 vCPU / 64 GB RAM, oversized for headroom
    Hetzner CCX23 (read replica + Metabase)Analytics workload isolated
    Hetzner CX22 ×3 (BullMQ workers)Settlement, reconciliation, MIS workers
    Backup S3 (Cloudflare R2)240 GB hot + 7 years archive
    Razorpay Route feesTransactional, scales with GMV
    Twilio + Gupshup (SMS + WhatsApp)Order updates, payment reminders
    Sentry + LogtailError + log infrastructure
    Engineering retainer (0.6 FTE)Higher than a CRM because of payment-side complexity

    The transaction fees scale with GMV. The fixed infrastructure cost is small — the rest is variable on volume.

    FAQ

    Why Razorpay Route and not Cashfree Easy Split?

    We evaluated both. Both work for this use case. Razorpay Route had stronger settlement-API documentation at the time we built (Q2 2025) and the founder already had a Razorpay account. Cashfree's Easy Split has a slightly cleaner sub-account onboarding flow which we would consider for a new build today.

    How did you handle GST for cross-state retailers?

    State-wise GSTIN validation at retailer onboarding. CGST/SGST split for intra-state orders, IGST for inter-state. The brand's GSTIN is the seller of record on the invoice; the distributor's GSTIN appears as the consignor. Two-tier invoice generation with a dedicated GST library. We use ClearTax's e-invoicing API for IRN generation when above the threshold.

    What about credit-terms management?

    Credit-term orders generate an unsettled invoice with a due date. A separate credit-collection workflow runs at T+5, T+12, T+20 days for 7-day terms (escalating). Credit limits are enforced at order placement — a retailer at credit limit cannot place a new credit-term order until they pay down.

    How did you onboard 37 brands?

    A 4-step onboarding: (1) brand legal contract + commission rate schedule, (2) Razorpay Route sub-account creation with brand bank details, (3) brand catalogue upload via CSV template + brand UAT in sandbox, (4) brand-team training on the brand console. Median brand onboarding time: 18 days from contract to live.

    Did you replace the existing ERP?

    No. The ERP stays for accounting, GST, and finance functions. The marketplace pushes daily summary entries into the ERP via a webhook. The two systems live alongside; the marketplace is the truth of orders, the ERP is the truth of books.

    What about offline orders from tele-calling?

    The 3-person tele-calling team uses the distributor console to enter orders on behalf of retailers. Same checkout, same payment flow. ~22% of orders still come via the tele channel — typically larger orders or new retailers being onboarded.

    Yes — about 31% of search queries are voice. Especially for brand names where the spelling is non-obvious in Telugu transliteration. We use the device's native speech-to-text, not a server-side STT, to keep latency low and avoid cloud cost.

    What was the team composition?

    Three full-stack engineers, one engineer dedicated to payment + settlement (the hardest piece), one mobile engineer (React Native), Manvi at 0.6 FTE for QA on the financial flows, our designer at 0.4 FTE, and our founder Vivek at 0.2 FTE for client direction. Total 5.4 FTE for 18 weeks.

    If you found this useful

    Need a Multi-Vendor Marketplace for Your Distribution Business?

    We ship multi-vendor B2B marketplaces with Razorpay Route settlement, brand-self-serve catalogues, and reconciliation workers for Indian distributors and aggregators in 14-20 weeks, fixed price. First call is technical, with the engineer who would lead your build. Suitable for 5+ brand operators with ₹20+ cr annual GMV.

    Book a Marketplace Scoping Call

    Tags:
    Multi-Vendor MarketplaceFMCGRazorpay RouteB2B CommerceSettlement EngineCase StudyIndian SMB
    Share this post:
    Vivek Kumar

    Vivek Kumar

    Founder and CEO at Softechinfra with 10+ years of experience in software development and system architecture.