Tonne Fitness
A gym's lead-gen site with a fully wired ecommerce checkout bolted on for supplements.
What it does
Two products in one app
The gym side — trial bookings, corporate wellness inquiries, an AI chat that qualifies leads — writes to DynamoDB and hands off to a person over WhatsApp. There's no automated booking or membership payment; a human closes the sale. The supplements side is a real store: browse, a server-persisted cart, Razorpay checkout, and a payment pipeline that only trusts its own server, not the browser.
Architecture
Order confirmation only ever comes from the webhook
Cart
Persisted server-side per user, not just localStorage — checkout starts from what the server has on file.
Create order
Server re-derives the total and 18% GST from the cart it holds — never the client's numbers — creates the Razorpay order, and writes a Pending order record before any money moves.
Razorpay checkout · third-party
Customer pays through Razorpay's hosted widget.
Payment callback
Flips the order to 'Awaiting Confirmation' so the customer sees progress — this path is never trusted as proof of payment.
Razorpay webhook · third-party
The actual confirmation: verifies the signature, flips the order to Processing, deletes the cart, and emails the customer and the store in parallel.
Order confirmed
The full raw Razorpay payment and order payloads are stashed alongside the structured record — nothing about a transaction gets lost to a schema gap.
The payment callback in the browser can be interrupted, closed early, or spoofed, so it never gets the final say — it only nudges the UI forward. The webhook is the one path allowed to mark an order paid, exactly the way Razorpay's own integration guidance describes it.
Worth being precise about
A shared database, not a headless CMS
The site and a small internal CMS are two separately deployed Next.js apps that never call each other — there's no API between them. What connects them is a shared DynamoDB table set and one S3 bucket: editing a product in the CMS writes straight to the same table the site reads from, picked up on the site's next catalog refresh rather than instantly. It's a shared-database integration, not the API-driven headless-CMS pattern the term usually implies.
Worth stating plainly: the gym side of the site is lead capture, not a booking platform. Trial requests and the corporate wellness form log a lead and stop there — closing the sale happens over WhatsApp, by a person, not by any code in this repo.