Canten · Food & beverage · 2026
Canten
Online ordering, outlet POS and a 5×5 rewards matrix for a fresh sugarcane & coconut juice brand.
- Role
- Full-stack developer at Bloom IT Solutions — rewards engine, wallets, withdrawals, wallet checkout, POS-sales integration and their admin & customer UIs.
- Platforms
- Customer web · Outlet POS · Back office · NestJS API
- Status
- Live · actively developed
income_ledger · append-only
- purchasercredited
- level 1credited
- level 2credited
- replayed eventignored
Illustration of the system — not a screenshot.
- Breadth-first rewards matrix
- 5×5
- Breadth-first rewards matrix
- Backend domain modules
- 22
- Backend domain modules
- Test suites — MLM, wallet, payments
- 28+
- Test suites — MLM, wallet, payments
- Real-time WebSocket gateways
- 8
- Real-time WebSocket gateways
Overview
Overview
Canten is a multi-outlet juice brand platform: customers order online, outlet staff ring up walk-in sales on a POS, and super admins run inventory, payments and payouts from a back office. I built the rewards layer — every customer, online or in-store, is placed in a 5×5 matrix and each purchase pays the buyer and up to five upline levels through an append-only wallet ledger.
The challenge
Canten sells online and across walk-in outlets, and wanted every customer rewarded wherever they buy — commissions to five upline levels, paid out to real bank accounts. With signups arriving concurrently and payment webhooks retrying, nothing could ever be placed twice or paid twice.
The approach
I built the rewards layer on a NestJS + Prisma monorepo: a breadth-first placement engine protected by row locks and database constraints, commission plans that admins configure instead of developers, and an append-only wallet ledger with idempotency keys. Online orders and POS sales emit the same events, so every purchase flows through one path.
Walkthrough
How it works

- 01
A juice brand, online and in-store
Customers order fresh sugarcane and coconut juice for delivery from their nearest outlet, while outlet staff ring up walk-in sales on a POS. My work was the rewards layer that sits across both — so every customer earns, wherever they buy.

- 02
Placement that's safe under load
Every new customer is placed automatically in a strict breadth-first 5×5 matrix — no referral codes. Concurrent signups are serialised with row locks and a bounded retry, and the database itself rejects an impossible tree. Admins browse it as a live placement tree.

- 03
Commission plans, not hard-coded numbers
Admins manage plans with a purchaser share and a share per level 1–5. Plans move through draft, active and retired, and a partial unique index guarantees only one is active.
spec · 03
Commission plans, not hard-coded numbers
- ›DRAFT → ACTIVE → RETIRED
- ›purchaser % + L1…L5 %
- ›partial unique index: 1 ACTIVE
- ›rates live in the DB, not code
- 04
Every purchase pays exactly once
Each purchase writes one immutable income row per beneficiary, keyed for idempotency — triggered by events from both online orders and in-store POS sales.
spec · 04
Every purchase pays exactly once
- ›1 row per (purchase, beneficiary)
- ›idempotency key
- ›events: order.paid · pos.sale
- ›replayed event → no-op
- 05
A wallet that can't go negative
Balance and reserved balance sit on an append-only ledger that records the balance before and after every movement. Admins see each customer's wallet, orders and tree placement in one place.

- 06
Payouts to real bank accounts
Customers withdraw earnings to a saved bank account. Requests get sequence-numbered references and go through admin approve, fail or cancel flows with configurable rules.

- 07
Pay with wallet, atomically
At checkout, one transaction creates the order, debits the wallet, marks it paid, records the payment and clears the cart — all or nothing.

- 08
One Razorpay settlement path
Checkout confirmation, webhooks and the pending-payment expiry job share a single idempotent settlement path that uses compare-and-set on payment status.
spec · 08
One Razorpay settlement path
- ›compare-and-set on status
- ›PAID · PENDING · NOT_PAID
- ›REVIEW_REQUIRED
- ›confirm = webhook = expiry job
- 09
Walk-in sales count too
An orchestrator joins in-store POS purchases to the same customer account, so counter sales earn the same rewards as online orders.

- 10
Numbers for HQ and every outlet
The back office gets an executive view across outlets — revenue, orders, new customers and offline sales — while each outlet sees its own insights, including POS sales from a cached summary.

Every screen
Gallery
Sample data · scroll → · click to enlarge
Built with
Stack
- Backend
- NestJSTypeScriptPrismaPostgreSQLRedisBullMQSocket.IOSwagger
- Payments
- RazorpayWebhooksAWS S3
- Security
- Argon2TOTP MFAHelmetRate limiting
- Tooling
- pnpm workspacesTurborepoDocker ComposeNginxJest
- Web apps
- Next.jsReactTailwind CSSTanStack QueryZustandRechartsMapbox
Open to full-time roles & select freelance projects