Shatakshi Store
Shatakshi Store sells Indian spiritual and artisan goods, from Vastu yantras and pooja samagri to scented candles and resin art. We designed and built the whole stack: a React storefront, a Django REST API, a payment flow hardened against real-world failure, and a self-serve admin so the catalogue runs without a developer in the loop.

- Role
- Design & full-stack build
- Domain
- Spiritual & artisan retail
- Surfaces
- Storefront · Admin · API
- Core stack
- Django REST · React
The brief
Shatakshi sells physical, handmade craft, and a lot of it carries real variation: a candle has a fragrance and a size, resin art comes in different colours, a yantra in different dimensions. A generic store template flattens all of that into one price and one photo, which is exactly the wrong shape for this catalogue.
So the work had three non-negotiables from the start: a product model that could carry genuine variants, a checkout the owner could trust with money, and a back office a non-developer could run day to day.
Modelling real products
The catalogue is built bottom-up around variants. A Category groups products with its own ordering and visibility; a Product belongs to one category and owns its media and options. On top sit colour, fragrance, and size variants, where fragrances carry their own descriptions and sizes carry a per-size price adjustment, so a larger candle can cost more without becoming a separate product.
Media is first-class. Each product has ordered image and video galleries with focal-position and object-fit controls, so a tall resin piece and a wide yantra both frame correctly without hand-cropping every upload.
Crucially, every order line snapshots the exact variant chosen and the unit price at the moment of purchase. When the owner re-prices the catalogue later, historical orders still read back exactly as they were paid.
- Category → Product → colour / fragrance / size
- Per-size price adjustments
- Ordered image + video galleries
- Variant + price snapshot on each order line
The storefront
The shopfront is React, TypeScript, Vite, and Tailwind. The hero is CMS-driven, so campaigns change without a deploy; below it, product carousels and grids lead into product pages with full image and video galleries and colour, fragrance, and size pickers.
Customers get the things a real store needs and template demos skip: reviews and ratings, a persistent cart, an order-history view, account profiles, and the full set of shipping, refund, privacy, and terms pages. Delivery is calculated automatically, with a flat charge applied under a small-order threshold.
- CMS-driven hero + product carousels
- Galleries with colour / fragrance / size pickers
- Reviews, persistent cart, order history
- Policy pages + automatic delivery rules
Accounts, email, and abuse control
Authentication is JWT-based. New accounts go through an email-verification flow delivered over Mailjet, with a throttled resend path for the messages that inevitably get lost, so checkout stays attached to confirmed customers.
Every sensitive endpoint is rate limited. Login is capped at five attempts a minute per IP to blunt brute force; registration and verification resends at three an hour; payment initiation at ten an hour per user; reviews at five. Over-limit requests get a clean 429 instead of leaking load downstream.
- JWT auth + Mailjet email verification
- Login 5/min · register & resend 3/hr
- Payment init 10/hr · reviews 5/hr
- Clean 429s on every sensitive route
A back office without deploys
The owner runs the shop from a separate admin app. Products and their variants, the homepage hero, and incoming orders are all editable there, so routine merchandising never touches code.
The whole stack is containerised with Docker, giving a repeatable path from a local machine to production and keeping the database, API, and media handling consistent across environments.
- Separate admin for catalogue + orders
- Editable hero and merchandising
- Dockerised, repeatable deploys
Deep dive
A checkout that survives retries
Payment callbacks are the part of any store most likely to go wrong: gateways retry, networks duplicate, and two callbacks for the same order can land at once. The flow is built so none of that can double-charge a customer or double-send a confirmation.
- 1
Create the order
Checkout creates an order in a PENDING state with its subtotal, computed delivery charge, total amount, and an Easebuzz transaction reference.
- 2
Hand off to Easebuzz
The customer is handed to Easebuzz to pay. Payment initiation is itself rate limited to ten attempts an hour per user.
- 3
Lock on callback
When Easebuzz calls back, the order row is locked with select_for_update inside an atomic transaction, so concurrent callbacks for the same order queue instead of colliding.
- 4
Idempotency gate
If the order is already marked paid, the callback is a duplicate; it is acknowledged and ignored, so nothing is processed a second time.
- 5
Settle exactly once
Otherwise the order is marked paid, the cart is cleared, and the confirmation email is sent, each side effect happening exactly once.
What carried the build
Idempotent, race-safe checkout
Row locks, an atomic transaction, and an is-paid gate mean retries and concurrent webhooks can't double-charge or double-email.
Variants that keep their history
Each order line snapshots the chosen colour, fragrance, or size and the price at purchase, so past orders stay accurate when the catalogue is re-priced.
Verified, throttled accounts
JWT sessions with Mailjet verification and resend, behind per-endpoint rate limits tuned to each route's risk.
Owner-run, no deploys
Products, variants, hero, and orders are all managed from the admin; merchandising never waits on engineering.
Built with
Frontend
- React
- TypeScript
- Vite
- Tailwind
- React Router
Backend
- Django 5
- DRF
- MySQL
- SimpleJWT
Payments & email
- Easebuzz
- Mailjet
Ops
- Docker
- Admin app