Context
An EV charging operator runs a public network of charge points, serving both retail customers and dealers. Every charge brings together three pieces that have to stay in step: the driver's app, the physical unit on the street and the payment.
Problem
Charge points speak OCPP 1.6 over WebSocket, an asynchronous stateful protocol that a conventional web application cannot handle. The network needed reliable remote start and stop, live energy readings, and payments that settle only when a session actually ends. If the connection drops halfway through a charge, the customer is left with an open pre-authorisation and the operator with no consumption data.
What I built
Two separate services, each with one job. A TypeScript microservice holds the open connections to the charge points and manages session state, command idempotency and timeouts: a command with no answer is never blindly reissued. A Laravel backend covers customers and tariff plans, bookings with Stripe pre-authorisation, coupons, invoicing, and the operator dashboard with charger status, consumption and exports. I handled time zones explicitly, storing everything in universal time and converting only for display, because sessions cross the daylight saving change.
Outcome
In production and under continuous development for more than two years: charge points stay connected, sessions start and stop remotely, and payment settles only when a session really ends.