Building a charging backend is not primarily a protocol exercise. The protocol is documented and finite. The difficulty is holding accurate state for thousands of long-lived connections whose counterparties are unreliable and inconsistent.
Every hard problem in a charging platform is a variant of that.
Long-lived connections change the architecture
Chargers hold persistent connections for weeks. That is unlike request-response web traffic and it constrains how the platform can be deployed: a routine restart drops every connection, and a fleet reconnecting simultaneously arrives as a thunderherd.
Handling this needs connection state that survives process restarts, or a deployment model that drains connections gracefully rather than dropping them.
Transaction integrity across interruptions
A session may begin, the connection may drop, the charger may reboot, and the stop may arrive hours later or never. The platform must resolve every one of those into a defensible billing record.
That means transactions cannot be held in memory tied to a connection. They are durable state with a lifecycle independent of whether the charger is currently reachable.
Duplicates are guaranteed
A charger that does not receive an acknowledgement will resend. Sooner or later every message type arrives twice, and the backend must recognise it rather than treat it as new.
Idempotency is therefore a design requirement, not a refinement. Without it a network will double-bill drivers, which costs more in trust than the energy was worth.
Tolerate divergent charger behaviour
Chargers differ in configuration keys supported, error code mapping, status transitions, and how strictly they follow the specification. A backend written against one vendor's behaviour will break on the second.
The pattern that survives is a core that handles the specification and a per-vendor adaptation layer that normalises the differences, with the raw exchange retained so unexpected behaviour can be investigated rather than guessed at.
Retain the exchange
The single most valuable operational feature is the ability to read what was actually exchanged with a specific charger at a specific time. It settles vendor disputes, diagnoses intermittent faults and answers billing questions.
Platforms that store only outcomes make every incident a matter of opinion. Storage costs money; not having the trace costs more.
Supporting two protocol versions is permanent
Fleets do not migrate atomically, so a backend serving any real network supports more than one version indefinitely. Treating that as a transitional state produces an architecture that fights the reality.
The versions differ enough in device and transaction model that this is close to supporting two protocols, and planning for it from the start is far cheaper than retrofitting.
Time is harder than it looks
Chargers drift, report in their own timezone handling, and deliver backdated transactions after outages. Tariffs are time dependent, so timestamp handling is a billing correctness problem rather than a formatting one.
Storing everything in a single absolute reference, and keeping the charger's reported time alongside the received time, is what makes later reconciliation possible.