SAYED.DEV

Full-Stack Engineer (Personal project)

Fleet Billing (lite)

Subscriptions and billing for a school-bus operator

Fleet Billing (lite) Hero View

The Problem Statement

A school-bus operator needs to manage buses and trips, see revenue and seat utilization, and collect subscription payments from parents without handling card data. Parents need to pick a plan for one or more children, pay online, and see when their subscription is active.

This is a portfolio project: the data is generated by a seed script, Stripe runs in test mode, and the demo signs in with one click as Operator or Parent (test card 4242 4242 4242 4242).

System Architecture & Strategy

Two apps in a pnpm workspace. The web app is Next.js 16 App Router on Vercel: a proxy handles locale and session refresh, Server Components render the operator stats, tables, and the parent's subscriptions, a Server Action starts checkout, and client components cover the fleet editor, the plan picker with its price preview, and login. The API is NestJS 12 deployed as a Vercel function, with an auth guard that verifies Supabase access tokens against the project's JWKS, a roles guard, class-validator DTOs, and modules for buses, trips, children, subscriptions, stats, plans, and the Stripe webhook. Data lives in Supabase Postgres through Prisma 7, with Row Level Security on subscriptions.

Checkout: the Server Action asks the API to check that the children belong to the caller, price the order, store a PENDING subscription, and create a Checkout Session with an idempotency key; the browser is redirected to Stripe. On checkout.session.completed the webhook verifies the signature against the raw body, checks that the charged amount and currency match, and marks the subscription ACTIVE for one month or one term, storing the event id in the same transaction so retries do nothing.

Browser (client components: login · fleet editor · plan picker)
        │
        ▼
Web app (Next.js 16 App Router on Vercel)
        ├── proxy.ts: locale + session refresh
        ├── Server Components: stats · tables · my subscription
        └── Server Action: startCheckout ──► Stripe Checkout
                    │                              │
                    ▼ Bearer JWT                   │ signed event
API (NestJS 12 as a Vercel function)               │
        ├── AuthGuard (JWKS) → RolesGuard           │
        ├── buses · trips · children · stats · plans│
        ├── pricing.ts (pure, unit-tested)          │
        └── POST /stripe/webhook ◄──────────────────┘
                    │
                    ▼
Supabase (Auth + Postgres + RLS via Prisma 7)

Key Technical Decisions

Prepaid periods instead of Stripe Subscriptions

Checkout runs in payment mode for one month or one term, which keeps all pricing, including sibling discounts, in tested code. Auto-renewal and the Customer Portal are left out on purpose.

Roles from a profiles table, not user metadata

The JWT proves who the caller is; the role is read from the database, new sign-ups always get Parent, and operators exist only through the seed.

API as a serverless function

Free and simple to host, at the cost of a cold start on the first request after idle, which the Suspense skeletons on the operator page cover.

Business & Product Outcomes

The product capabilities and improvements delivered through this work:

  • Gives an operator a live view of revenue, active and pending subscriptions, and seat utilization per trip.
  • Lets parents subscribe one or more children and pay online without the product handling card data.
  • Serves Arabic- and English-speaking families with a right-to-left layout.

Engineering Challenges & Solutions

Payments without trusting the browser

Engineered SolutionCheckout runs through a Server Action, so the amount calculation and Stripe calls never reach the browser. The webhook verifies the signature against the raw body, cross-checks amount and currency, and records each event id so Stripe retries are no-ops.

Pricing that is easy to verify

Engineered SolutionPure functions in integer cents: monthly €89 and term €320 per child, sibling discounts of 10% for the second child and 15% from the third, and calendar-month period ends with month-end clamping, all covered by Vitest.

Keeping parents inside their own data

Engineered SolutionThe parent's subscription query has no WHERE clause for the parent id. It runs as the Postgres authenticated role with the caller's claims set, so the Row Level Security policy does the filtering; other tables are revoked from the anon and authenticated roles.

Server or client for each piece

Engineered SolutionRead-only aggregates and tables are Server Components streamed behind Suspense, with the status filter as a shareable query link. The fleet editor and plan picker are client components that call the API with the user's token and then refresh the server parts.

Tech Stack Roles

  • Next.js 16 App Router, React 19Web app with Server Components, a Server Action, and client components
  • next-intlArabic and English with right-to-left layout
  • NestJS 12API with global auth and role guards and class-validator DTOs
  • Prisma 7 on Supabase PostgresData layer with Row Level Security on subscriptions
  • Supabase AuthSign-in; the API verifies tokens against the project's JWKS
  • Stripe Checkout and webhookPayments in test mode with signed, idempotent activation
  • Vitest and PlaywrightPricing, webhook, and HTTP e2e tests plus web smoke tests
  • GitHub Actions, Docker, VercelCI with migrations and seed on Postgres, docker compose build, and deploys

Delivery Highlights

Stripe Checkout with a signed, idempotent webhook that activates subscriptionsPayments
Monthly and term plans with sibling discounts in unit-tested integer-cent codePricing
Row Level Security enforces parent-scoped subscription readsData access
CI runs lint, typecheck, Vitest, Playwright, migrations, seed, and a Docker buildDelivery
Fleet Billing operator dashboard with revenue, seat utilization, and the bus list
Fleet Billing operator dashboard with revenue, seat utilization, and the bus list
Fleet Billing parent plan picker with sibling discount and price preview
Fleet Billing parent plan picker with sibling discount and price preview
Fleet Billing parent plan picker in Arabic with right-to-left layout
Fleet Billing parent plan picker in Arabic with right-to-left layout
Fleet Billing login page with Operator and Parent demo buttons
Fleet Billing login page with Operator and Parent demo buttons

Interested in similar architecture challenges?

If your team is scaling systems and seeks senior engineer positioning with robust product-centric workflows, let's talk.