An online grocery store with live payment updates
A grocery shop with a Next.js storefront and admin area and a NestJS API, using QR bank payments, live order and payment updates, and push notifications.
The challenge
Customers pay by scanning a bank QR code, so the storefront has to learn when the payment arrives without the customer refreshing. Staff need to see new orders as they come in.
My contribution
Sole developer: storefront, admin area, API, database schema, payment and notification integrations, and the PM2 production setup.
The approach
A Next.js storefront and admin area built with Tailwind and Recharts. It talks to a NestJS API with modules for products, cart, orders, promotions, payments, notifications and site settings, using Prisma over PostgreSQL. Payments create VietQR/PayOS QR codes. When a payment is confirmed, a Socket.IO gateway pushes the status to that checkout session's room and alerts an admin feed room. Firebase Cloud Messaging sends push notifications. The API runs under PM2 in cluster mode.
The outcome
A working storefront and admin dashboard with QR checkout, live payment status, promotions and push notifications. The frontend is deployed on Vercel.
Context
An online grocery store with a customer storefront and an admin area for products, orders, promotions and site settings.
Architecture
- Frontend (Next.js App Router): a
(shop)route group for customers and anadminarea, styled with Tailwind, with Recharts for the admin charts. - Backend (NestJS): separate modules for auth (JWT), products, cart, orders, promotions, payments, notifications and site settings. It uses Prisma with the PostgreSQL adapter.
- Realtime: a Socket.IO gateway. Each checkout session joins its own room, and staff join an admin feed room.
- Operations: PM2 runs the API in cluster mode across all CPU cores, restarts it on memory growth, and keeps timestamped logs.
A problem I solved: knowing when a QR payment lands
With a bank QR payment, the money moves outside the website, so the page has no natural signal
that the customer has paid. When the payment service confirms a payment, the backend emits
paymentStatusUpdate to that checkout session's room only. The customer sees the order confirm
without polling or reloading, and no other customer receives the event. The same confirmation
posts to the admin feed and sends a push notification through Firebase Cloud Messaging.