The server prices the order
The API refetches menu items and calculates totals. Prices sent by the browser are never authoritative.
Flagship / Engineering case study
From a table QR code to payment to a physical kitchen printer.
Customer and admin apps, a Node.js API, role-based access and Stripe webhooks.
TypeScript / Next.js / Fastify / Node.js / PostgreSQL / Supabase / Prisma / Stripe Connect / Vercel / Railway
The browser cannot declare an order paid. Print jobs, KDS and notifications start at this transition.
The local Node process asks the cloud for work. No inbound route through venue NAT is needed.
01 / The problem
An order has to travel from a guest’s phone to payment, kitchen and bar without trusting browser prices or losing the connection to physical venue hardware.
02 / What I built
A multi-tenant hospitality platform with four runtime targets: customer Next.js app, admin Next.js app, Fastify API and an on-site Node print bridge. Guests scan, browse, order and pay on their phones. Stripe Connect settles payments directly to the venue; the platform holds neither venue funds nor raw card data.
03 / Architecture
Follow the flow as you scroll, or select a step.
A table QR code opens the customer Next.js app. The guest selects menu items on their own phone.
Fastify refetches menu items and computes totals server-side. Browser-supplied prices are not trusted.
Payments settle directly to the venue. The platform does not hold venue funds or raw card information.
Only the verified Stripe webhook can mark an order paid. This is the choke point for idempotent payment side effects.
PostgreSQL-backed work routes the paid order to print jobs, a KDS ticket and notifications.
The on-site Node bridge requests queued jobs from the cloud, crossing the venue's NAT boundary without inbound configuration.
The bridge sends the job to its physical printer. Queued work can resume after temporary outages.
The admin Next.js app operates alongside this flow with guarded venue operations. Together with the customer app, Fastify API and on-site bridge, it forms four runtimes.
04 / Key decisions
The API refetches menu items and calculates totals. Prices sent by the browser are never authoritative.
Only the Stripe webhook can mark an order paid and trigger printing, KDS tickets and notifications. Keeping this transition in one place makes webhook replay and idempotency manageable.
Printers sit behind private LANs and NAT. The local bridge polls for queued jobs, avoiding inbound configuration and persistent-connection state. Jobs can resume after temporary outages.
Central access decisions and route guards protect the API. Supabase Row Level Security closes a different path through its generated API. Per-printer credentials replace a shared platform-wide bridge secret.
05 / What went wrong
06 / Evidence

07 / Current status & limitations
Live production system. CI and staging, some security/audit work, dependency upgrades and complete E2E coverage remain areas to improve. Production verification is not a claim that every possible failure is covered.
Evidence basis: project implementation review and research notes supplied for this portfolio, September 2026. Project tests described here are distinct from the portfolio’s own checks.