How ChargeVeta works, end to end.
A technical guide for your engineers, your charger installer and your finance team: what runs where, how to connect a real charger, and what happens at every step from a card tap to a GST receipt.
01
The system at a glance
ChargeVeta is a charging station management system (CSMS) that many operators share, each seeing only their own chargers, drivers and money. Three programs make it run. The OCPP engine is the only part that talks to chargers. It holds one long-lived WebSocket per charger. The business core holds every rule: who may charge, what it costs, who pays. The apps are the operator console, the driver app and the fleet portal.
Chargers never talk to the business core directly, and browsers never talk to it directly either. Each goes through its own front door, so a fault or an attack on one side cannot walk through to the other.
Commands: core → charger
Remote start and stop, reset, unlock, configuration, firmware. The core asks the engine, and the engine sends the OCPP message on the charger's open socket.Questions: charger → core, answered at once
May this charger connect? May this card charge, and who pays? These need an answer while the charger waits, so they are direct calls, not queued.Events: charger → core, in order
Boot, status, meter readings, session start and end flow through Kafka, keyed per charger so each charger's events are processed in the order they happened.One record of every session
The console, the driver app, the fleet portal, the receipt and the dashboards all read the same rows. A dashboard can never disagree with the invoice behind it.
02
Addresses on chargeveta.in
Everything lives under one domain. The marketing site is www.chargeveta.in. The product is on its own subdomain, so the apps, their cookies and their installable versions are kept apart from the website.
Why the apps share one subdomain
The console, driver app and fleet portal are one web application served from the root ofapp.chargeveta.in, each with its own sign-in and its own session cookie. The driver app installs with its own scope (/driver), so a driver’s home-screen icon opens only the driver app.Why chargers get their own host
A charger holds its socket for days.ocpp.chargeveta.ingoes straight to our servers with hour-long timeouts and a ping every 30 seconds, and serves only/ocpp/…and a health check. A web CDN in between would cut idle sockets and hide the charger’s address.No tokens in the browser
The apps keep sign-ins in httpOnly, secure cookies and call the core through their own same-origin proxy. Nothing a script on the page can read ever holds a token.Live updates
Charger status and running sessions update live over a WebSocket on the same host (/realtime), so they work behind any firewall that allows the app itself.
03
Connecting a real charger
Any charger that speaks OCPP 1.6J, 2.0.1 or 2.1 can join. You do not need a ChargeVeta-specific firmware or adapter. Commissioning takes two parts: create the charger in the console, then point the charger at us.
Step 1: in the console
- Sign in at
app.chargeveta.in/sign-inas an admin. Create the site first (address, map location, GSTIN state, tariff), if it is not there yet. - Chargers → Add charger. Enter the charger ID exactly as the charger will send it (1–48 letters, digits,
._~-; e.g.DEL-CP-001), the OCPP version it runs, and the site. - On the charger’s page, open Connection password and generate one (20–128 characters). Copy it now: it is stored only as a hash and can never be shown again, only replaced.
Step 2: on the charger
Step 3: check it
- The charger shows Online on the Chargers page within seconds of connecting, and each connector appears with its status (connectors register themselves from what the charger reports).
- Its page shows the boot details (vendor, model, serial, firmware) and the address it connected from.
- Try Remote start with a test card, or tap a card that is linked to a driver, and watch the session appear live.
Refused at connect?
A wrong password, an unknown charger ID, a wrong operator code or a non-TLS connection on profile 2 is refused before the WebSocket opens (HTTP 401), never accepted and then dropped. Too many failures from one address are blocked for a few minutes. Check the ID's exact spelling and case first.Charger can't do TLS?
Older chargers on security profile 1 (Basic auth without TLS) can usews://ocpp.chargeveta.in/ocpp/…on port 80. An admin switches that one charger to profile 1 under its settings, Connection security; every other charger stays on profile 2 and is refused without TLS. Profile 0 (no password) is never accepted.Client certificates (profile 3)
Supported by the engine, with the charger's certificate pinned by its SHA-256 fingerprint. Not yet open on the shared network; talk to us if your chargers require it.Firewalls and SIM routers
The charger only makes outbound connections toocpp.chargeveta.inon 443 (or 80 for profile 1). Nothing needs to reach the charger. A 4G router with a short NAT timeout is fine: we ping every 30 seconds.
04
A charger connects and boots
The engine keeps no database, so every connection is decided by the business core before the WebSocket opens. Which operator the charger belongs to comes from that answer, never from the URL the charger chose.
- 1
Charger → Engine: WebSocket upgrade to /ocpp/<operator>/<charger ID> with Basic auth and the OCPP subprotocol.
- 2
Engine: Abuse guards first: connections per address, connect rate, a bounded queue of checks in flight.
- 3
Engine → Core: May this charger connect? The core checks the password hash, the security profile and that the transport was TLS.
- 4
Core → Engine: Yes: the charger, its operator, its OCPP version. Or no, and the upgrade is refused.
- connected
- 5
Charger → Engine: BootNotification. Validated against the official OCPP schema for that version.
- 6
Engine → Kafka: Boot event recorded first, then the charger is told Accepted with our heartbeat interval. A charger is never accepted by a system with no record of it.
- 7
Charger → Engine: StatusNotification for each connector: Available, Preparing, Charging, Faulted…
- 8
Engine → Charger: After the boot: the meter-value interval (60 s), the device model report on 2.x, and on 2.1 our price as SetDefaultTariff.
- every minute while connected
- 9
Engine → Core: Is every socket I hold still allowed? A charger that was deactivated, deleted or quarantined, or whose operator was suspended, is disconnected.
05
A card tap: may it charge, and who pays?
The same decision answers a card tap, a session start, a remote start from the app and a reservation, so they can never disagree. First the card itself is checked; then, in a fixed order, who would pay.
- 1
Card usable?
Not blocked, not expired, not already charging elsewhere. Otherwise refused.
- 2
Does a fleet pay?
The card's driver is in a fleet billed monthly: accepted, charged to the fleet's statement.
- 3
Free charging?
Staff granted the card free charging (optionally until a date): accepted, the driver pays nothing.
- 4
Is it a driver's card?
A card linked to no driver has nobody to pay: blocked.
- 5
Enough credit?
Wallet above the start minimum, or a card hold waiting at this charger: accepted with a budget.
- 6
Otherwise
NoCredit on 2.0.1 and 2.1 (shown as Blocked on 1.6, which has no such status).
If the core cannot be reached, the charger gets an error and falls back to its own local list of cards. We keep that list in sync, and it only holds cards someone else pays for (fleet cards and free charging), so an outage never lets an unpaid session through.
06
Starting from the app or the console
A driver taps Start in the app, or an operator starts a session from the console. The card is checked before the charger is ever asked, so a refused card fails fast with a clear reason.
- 1
Driver app → Core: Start charging at this connector. A fleet driver with several vehicles picks the car first.
- 2
Core: Who is asking and for which operator comes from the signed-in session. The card is adjudicated with the rule above.
- 3
Core → Engine: Remote start command, over the internal network only.
- 4
Engine → Charger: RequestStartTransaction (2.x) or RemoteStartTransaction (1.6) on the charger's open socket.
- 5
Charger → Engine: Accepted or Rejected.
- 6
Core → Driver app: The real outcome: accepted, rejected, timed out, or charger offline. Every command is recorded with who sent it.
- then
- 7
Charger → Engine: The session starts (TransactionEvent Started / StartTransaction): next section.
07
A session, from start to GST receipt
The tariff is fixed when the session starts. A wallet or card-hold session gets a budget, and we stop the charger before the money runs out, not after.
- 1
Charger → Engine: Session started, with the card.
- 2
Engine → Core: Allocate the session: tariff fixed, budget set (card hold plus wallet), fleet vehicle recorded if known.
- 3
Core → Charger: Accepted. On 2.1, the budget also travels as the charger's own transaction limit, a backstop if it loses its connection.
- every meter reading
- 4
Charger → Worker: Energy, power, state of charge, through Kafka.
- 5
Worker: Prices the session so far with the same code the receipt uses, tax included. Cost plus a safety reserve reaches the budget: ask for a stop.
- 6
Driver app: Shows energy, time and cost so far, live.
- the session ends
- 7
Charger → Worker: Session ended, with the final meter reading.
- 8
Worker: Prices the whole session: energy, charging time, idle time past the grace period, session fee.
- 9
Worker: Issues a numbered receipt (R-000042) with CGST + SGST, or IGST across states, and settles the wallet, the card hold, the fleet and any sponsor in the same database transaction.
- 10
Worker → Driver app: Push notification: session complete, receipt ready.
Receipts never change
Numbered per operator without gaps. A correction is a credit note (CN-…), never an edit, so your books always reconcile.Idle time, as the charger reports it
On 2.x, charging time is the time the charger reported Charging. Idle fees start only after the car stopped drawing power and the grace period passed; a charger holding power back is never idle.Energy you can audit
Billed from the charger's cumulative meter, or from its interval readings if it sends only those. The receipt records which.A charger that lost its link
It keeps charging on its own. When it reconnects, its queued readings arrive and the session is priced in full. Any shortfall becomes a debt the driver settles before the next session.
08
Payments through Razorpay
Drivers pay with a prepaid wallet (UPI, cards, netbanking) or a card hold: an amount authorised before charging and captured for the real price afterwards, with the rest refunded. ChargeVeta never holds the money; Razorpay does.
The rule throughout: Razorpay is asked, never told. A webhook, a browser confirmation or a retry is only a reason to fetch the payment from Razorpay and act on what it says now. A repeated or out-of-order webhook changes nothing, and nothing is credited twice.
- wallet top-up
- 1
Driver app → Core: Add ₹500.
- 2
Core → Razorpay: Create an order; Razorpay Checkout opens in the app.
- 3
Driver → Razorpay: Pays by UPI, card or netbanking.
- 4
Razorpay → Core: Signed webhook to /api/v1/payments/razorpay/webhook, and the app's own confirmation.
- 5
Worker → Razorpay: Fetch the payment: captured? Then credit the wallet exactly once.
- card hold
- 6
Driver app → Razorpay: Authorise ₹X on a card (held, not taken); the charger starts.
- 7
Worker → Razorpay: After the receipt: capture the hold, refund the difference, follow the refund until it is processed.
- withdrawals
- 8
Driver app → Core: Withdraw unused wallet money: paid back as refunds of the driver's own recent top-ups, newest first.
09
Operators, staff and drivers
There is no public sign-up for operators. The ChargeVeta platform team creates each operator (a tenant) and its owner; the owner builds their own team. Staff, drivers, fleet managers and the platform team are four separate kinds of account, with separate sign-ins, sessions and permissions.
- a new operator
- 1
Platform team: Creates the operator: name, operator code (e.g. acme), owner's email. Switches on optional modules such as fleets.
- 2
Owner: Receives a one-time setup link (valid 72 hours), chooses a password, signs in with the operator code.
- 3
Owner: Adds sites, tariffs and chargers, and invites staff by email. Each invitee sets their own password.
- a driver
- 4
Driver: Opens the driver app, enters the operator code and a mobile number, types the six-digit code. The first code creates the account; the phone number is the account.
- 5
Driver: Gets an app card automatically, so they can start a charger at once. Can add and confirm an email later, then set a password.
- 6
Staff: Link physical RFID cards to a driver. Drivers can't claim a card themselves: a number printed on a card doesn't prove you hold it.
10
Fleets
A fleet is a company whose drivers charge on an operator’s chargers: a bus depot, a delivery firm, a carmaker’s fleet. The module is switched on per operator. Each fleet chooses how it is billed, and that choice is snapshotted on every session, so changing it never rebills an old one.
Driver pays
Members pay each session from their own wallet or card; the fleet sees the sessions.Fleet pays, monthly
Members charge without paying; the fleet gets a monthly statement (price and GST shown apart), emailed from the 3rd of each month.Vehicles on sessions
A driver with several vehicles picks the car in the app before starting, so cost per vehicle and per kWh are exact.Fleet managers
Sign in atapp.chargeveta.in/fleet: dashboard, members, sessions, depots, statement and CSV export. Membership and billing stay with the operator.
11
When something fails
Internet links drop and servers restart. Each part is built so that a driver mid-session doesn’t notice, and nothing is billed wrongly afterwards.
The core is down
Connected chargers stay connected. A charger reconnecting with exactly the credentials it used successfully in the last day is let back in. Card taps fall back to the charger's local list.The event stream is down
The engine keeps answering chargers and buffers their events on disk, then sends them in order when the stream returns. Nothing is lost.A charger goes offline mid-session
It keeps charging. Its final readings arrive when it reconnects and the session is priced in full. On 2.1 its own transaction limit stops it at the budget.A stop that never lands
If a session reaches its budget and the charger doesn't stop, staff get a critical alert that retries the stop and escalates by email and text until someone acts.A deploy
The engine drains on shutdown: new connections wait, answers in flight are sent, and chargers reconnect within seconds. Database changes are backed up first and rolled back if a health check fails.A payment webhook arrives twice
Nothing happens twice. Every credit, capture and refund is keyed to the Razorpay payment and checked against Razorpay itself.
12
Security and data separation
Many operators share one database. Each operator’s rows are kept apart by PostgreSQL row-level security, enforced by the database itself rather than by remembering to filter in code.
- The operator comes only from the signed-in session, never from a header, a URL or a form field. Asking for another operator’s record by its ID answers “not found”, so it doesn’t even confirm the ID exists.
- The application connects as a role that cannot bypass row-level security, and refuses to start if it could.
- Charger passwords and user passwords are stored only as argon2 hashes. Charger passwords sent as configuration are hidden in the audit log and command history.
- The engine’s command interface is internal only and never reachable from the internet. The engine and the core each hold separate secrets; the event worker holds none of them.
- Every change by staff is in the operator’s audit log: who, what, when, from which address. Failed sign-ins are recorded too.
- Production refuses simulated text messages outright, and runs with test payment keys or a fixed sign-in code only when each one is switched on deliberately, by its own named setting.
13
OCPP support
OCPP 1.6J, 2.0.1 and 2.1 run side by side, chosen per charger. Every message in both directions is validated against the Open Charge Alliance’s own JSON schemas, and every request gets an answer, because a charger left without one retries forever.
Tested against the open-source EVerest charger firmware on 1.6, 2.0.1 and 2.1, and against scripted simulators. Have a charger model you want checked before you buy? Write to support@chargeveta.in.
Bring a charger, we'll connect it with you.
Send us the charger's make, model and OCPP version. We'll set it up on a demo network and run a session with your team, start to receipt.