Skip to content
Urnish Tech logo
All case studies
D2C · eCommerce · 2026

Paushtik Farms - Ghee eCommerce Platform

End-to-end eCommerce platform for a premium ghee brand - clean storefront, integrated payment gateway, inventory management, and shipment handling, deployed for scale.

Build something like this

The challenge

Ship a D2C platform that orchestrates the complete order flow - storefront → cart → payment → inventory → shipping - while staying resilient to the real-world failures that hit live commerce: webhooks arriving out of order, payment captures racing with client-side verifies, and shipment creation failing without ever rolling back a captured payment. Premium ghee customers expect a polished checkout, accurate inventory, and live tracking from day one.

What we built

A three-service architecture: a Next.js storefront with a Razorpay checkout modal, a separate Next.js admin panel for catalog and order ops, and a NestJS REST API on PostgreSQL with Redis. Versioned endpoints under /api/v1/ cover the full lifecycle - product catalog with ISR, stateful cart with guest-merge, transactional order placement with pessimistic stock locks, Razorpay capture with client-verify + webhook + admin reconciliation, and Shiprocket shipment orchestration with AWB assignment and live tracking. Storefronts deploy to Vercel; the API runs on ECS Fargate with RDS, Redis ElastiCache, and S3 + CloudFront for media.

Key features

Storefront & Cart

  • Next.js 14 storefront with Tailwind, responsive product grid, and ISR-cached PDPs
  • Redux Toolkit cart with localStorage persistence and guest → authenticated merge
  • Razorpay checkout modal with client-side signature verification (HMAC-SHA256)
  • Live stock availability and low-stock warnings on the product page
  • Address book, order list, and tracking page tied to a customer's account

Payments

  • Razorpay smart-collect with live + mock modes for offline development
  • Webhook idempotency: payment.captured and order.paid events deduplicated server-side
  • Triple-path reconciliation: client verify, webhook, and admin reverify all converge on a single atomic capture
  • Admin reverify endpoint pulls live state from Razorpay to recover stuck or abandoned payments

Inventory & Orders

  • Transactional order placement with TypeORM pessimistic_write locks on product rows
  • Conditional stock decrement (WHERE stock >= qty) to prevent overselling under concurrency
  • JSONB address snapshot on orders so a later address edit never mutates a shipped order's record
  • Order state machine: pending_payment → paid → shipped → delivered/cancelled/refunded
  • Free-shipping threshold logic (₹999+) with flat ₹49 fee below

Shipping & Tracking

  • Shiprocket order creation, AWB assignment, label and manifest generation
  • Webhook ingestion mapping 10+ Shiprocket status IDs to internal states with regex fallback on status text
  • Auto-shipment creation triggered after payment capture, fire-and-forget with idempotent admin retry
  • Customer tracking endpoint surfacing live EDD and event history from Shiprocket
  • Graceful degradation - if Shiprocket is unreachable, local shipment state is preserved and retried

Images & Admin

  • Sharp pipeline generating four WebP variants per upload: full, medium (600px), thumb (160px), and blur (24px)
  • S3 PutObject in parallel; frontend swaps suffix dynamically for responsive loading
  • Immutable cache headers (max-age=31536000) for CDN efficiency
  • Admin dashboard: revenue today, low-stock alerts, pending payments, and shipment recovery
  • Soft-delete (isActive) plus hard-delete with referential validation

Challenges solved

Transactional row-level locking for race-free stock

OrdersService.place() acquires pessimistic_write locks per product row, then decrements conditionally (WHERE stock >= qty). Concurrent orders that lose the race get a ConflictException - no overselling, no distributed locks needed.

Fire-and-forget shipment with idempotent recovery

Shipment creation runs via setImmediate after payment commit, so a Shiprocket outage never rolls back a captured payment. The admin's Create-shipment button is idempotent, serving as a manual retry.

Webhook idempotency + triple-path payment reconciliation

A single payment can be finalized via client verify, Razorpay webhook, or admin reverify. All three converge on one atomic capture() that becomes a no-op on retries - safe across tab closes, slow networks, and clock skew.

Shiprocket status mapping with ID + regex fallback

12 internal states mapped from 10+ Shiprocket status IDs, with case-insensitive regex on status text catching anything new. Order and shipment status update in the same write so the admin view never drifts.

S3 image variants with client-side suffix swap

Four WebP variants generated server-side and stored under a single base key; the frontend swaps suffixes (full.webp → full-thumb.webp) for responsive loading. The 24px blur variant doubles as a CSS background placeholder.

Guest-to-authenticated cart merge

On login, the guest cart is collapsed by product, validated against the live catalog, then upserted with ON CONFLICT and LEAST(qty, MAX_QTY_PER_LINE). Stale items drop silently - no UX-breaking errors on a deleted SKU.

Full-stack mock mode for offline development

RAZORPAY_MOCK and SHIPROCKET_MOCK flags swap in deterministic responses (order_mock_*, MOCKAWB*, fake EDD). The team can ship and test the entire happy path without live credentials.

Outcomes

Integrated

Payment gateway

Managed

Inventory

Automated

Shipment

Let's talk

Have a product idea or a system to scale?

Tell us what you're building. You'll hear back within one business day - from a senior engineer, not a sales rep.

  • Free 30-min discovery call
  • Fixed-scope or T&M engagements
  • NDA on request - first reply within 24h