Skip to content
All projects

Published

Subscription commerce with a nutrition engine

A direct-to-consumer subscription for fresh pet food: a questionnaire works out what the animal actually needs per day, that becomes a recurring plan, and the card is charged every month without anyone touching it.

  • NestJS
  • TypeScript
  • PostgreSQL
  • TypeORM
  • Redis
  • React
  • Redux
  • MUI
  • Docker
  • Kubernetes

Overview

The pitch is simple — fresh food, portioned for your dog, delivered every month. Everything underneath it is not. The portion has to be derived from the animal's biology, the kitchen can only cook in fixed pack sizes, the payment has to survive a bank redirect that fails halfway, and the subscription has to renew itself for years without a human in the loop.

Three groups share the system: customers on the public site, kitchen staff running production and sale orders, and admins managing promotions, content and the pause and cancel requests.

The parts that were interesting to build

From biology to what the kitchen can cook. A questionnaire collects breed, weight, age, activity and health. From weight the engine computes resting energy, multiplies by a life-stage and activity factor to get the daily requirement, bands that requirement, converts calories to grams for each recipe, and then snaps the result to the nearest pre-portioned pack the kitchen actually produces. The last step is the one that matters commercially: a number that is biologically correct but unproducible is worthless.

A payment that converges from three directions. Card payments here involve a bank redirect, and redirects fail — the customer closes the tab, the network drops, the callback never fires. So payment state is never trusted to one path. A row is written before the charge. Then three independent mechanisms race to resolve it: the gateway's webhook, a job that re-queries every pending payment made in the last half hour, and the browser polling while it waits. Whichever arrives first settles the payment, emits the event and places the order; the other two find it already done. Saving a card for later is verified by charging one baht and immediately refunding it, which is also how a card change is confirmed.

A subscription that renews itself. Eleven scheduled jobs carry the lifecycle: renewals charge the stored card the day they come due and email a daily report of what went through, trials and samplers expire, abandoned carts get two reminders, promo codes expire and retry, and scheduled pauses take effect just after midnight local time.

Shape of the system

A modular API over PostgreSQL with caching, in-process events and scheduled jobs, and a single-page application that carries the public site, the kitchen console and the admin console in one bundle. Deployed as containers to a managed cluster.

Scale

  • 181 endpoints across 41 modules · 43 entities · 11 scheduled jobs · 23 email templates
  • 78 declared routes across three audiences · bilingual throughout
  • Payment integration covering card with 3-D Secure, QR, and two bank wallets
Status
Published
Published
Sep 12, 2026
Stack
NestJS · TypeScript · PostgreSQL · TypeORM · Redis · React · Redux · MUI · Docker · Kubernetes