Skip to main content
Sajilo Charge

← All SDKs

Connect your chargers with Spring Boot

A Spring Boot starter — auto-configured endpoints, JPA entities and hub client; you provide one handlers bean.
Works with Spring Boot 3.2+ · Java 17+

sajilocharge-ocpi-springboot.zip v0.1.0 · Spring Boot 3.2+ · Java 17+ · OCPI 2.2.1

⬇ Download SDK
Building with an AI agent? Point it at https://sajilocharge.com/developers/ocpi/ai/ — this whole tutorial in machine-readable form.

How a roaming charge flows

Sajilo Charge driver       Sajilo Charge hub                 Your Spring Boot backend
      │ taps "Start" ────▶ POST /commands/START_SESSION ──▶ onStartSession(charge)
      │                                                      your CSMS starts the charger
      │   charging…   ◀── PUT  sessions push ◀───────────── setActive() / meter(kwh)
      │ taps "Stop"  ────▶ POST /commands/STOP_SESSION ───▶ onStopSession(charge)
      │ wallet debited ◀── POST cdrs ◀──────────────────────  complete(kwh, cost)
      │                    monthly netting ─────────────────▶ you get paid

1.Install the SDK

One download, one package-manager command.

curl -LO https://sajilocharge.com/static/downloads/sajilocharge-ocpi-springboot.zip
unzip sajilocharge-ocpi-springboot.zip && cd sajilocharge-ocpi-spring
mvn install

# pom.xml
<dependency>
  <groupId>com.sajilocharge</groupId>
  <artifactId>sajilocharge-ocpi-spring-boot-starter</artifactId>
  <version>0.1.0</version>
</dependency>

2.Configure and mount

Tell the SDK who you are and where your server is publicly reachable. This mounts the three /ocpi endpoints the hub calls on you — everything else is outbound from your side.

# application.yml
sajilocharge:
  party-id: ABC                       # your 3-char party id
  country-code: NP
  name: ABC Charging Pvt. Ltd.
  base-url: https://csms.example.com  # public URL of this app

3.Wire your two charger callbacks

Every remote-start request becomes a RoamingCharge handed to your code. You implement exactly two methods; keep them fast and do the charger work asynchronously.

@Component
public class MyOcpiHandlers implements SajiloChargeHandlers {

    @Override
    public boolean onStartSession(RoamingCharge charge) {
        // charge.getLocationId() / charge.getEvseUid() say which charger
        myCsms.remoteStart(charge.getEvseUid(), charge.getId());
        return true;   // accepted; do the real work async
    }

    @Override
    public boolean onStopSession(RoamingCharge charge) {
        myCsms.remoteStop(charge.getId());
        return true;
    }
}

4.Drive the charge lifecycle

From your CSMS events, three calls push the matching OCPI Session objects — and the final CDR that bills the driver — to the hub. The CDR is idempotent: it goes out exactly once per charge.

charges.setActive(ref);              // energy started flowing
charges.meter(ref, 4.2);             // periodic session kWh
charges.complete(ref, 18.5, 462.50); // NPR; issues the CDR
The CDR is the bill. The complete call is idempotent and sends exactly one CDR per charge — that CDR debits the driver's wallet and credits your settlement ledger. Send it as soon as the session ends.

5.Join the network

Request a one-shot registration token from partners@sajilocharge.com. Deploy first — the hub calls your /ocpi endpoints back during the handshake — then run the join. Tokens rotate on every re-registration; the registration token dies on first use.

# application.yml — one-shot; auto-joins on startup, then remove it
sajilocharge:
  registration-token: <registration-token>

6.Publish your stations

Locations upsert by id — re-push whenever anything changes, keep live EVSE status fresh, and (optionally) verify drivers in real time for RFID or local starts.

hubClient.pushLocation(Map.of(
    "id", "LOC-KTM-01",
    "name", "ABC Hub Thamel",
    "address", "Thamel Marg 12",
    "city", "Kathmandu",
    "coordinates", Map.of("latitude", "27.7154", "longitude", "85.3123"),
    "publish", true,
    "evses", List.of(Map.of("uid", "EVSE-01", "status", "AVAILABLE",
        "connectors", List.of(Map.of("id", "1", "standard", "IEC_62196_T2",
                                     "format", "SOCKET", "power_type", "AC_3_PHASE"))))));

// live availability + optional driver check
hubClient.updateEvse("LOC-KTM-01", "EVSE-01", Map.of("status", "CHARGING"));
hubClient.authorize(tokenUid);   // ALLOWED | NO_CREDIT | BLOCKED

Money and settlement

  • All amounts are NPR; costs are VAT-exclusive.
  • Each CDR credits your inter-party ledger at the hub; settlement is monthly netting minus the hub clearing fee.
  • Disputes: the hub's CDR dispute endpoint or partners@sajilocharge.com — open disputes pause netting for that CDR.

Troubleshooting

  • Join fails with "could not fetch endpoints" — your base URL isn't publicly reachable, or the OCPI routes aren't mounted. The hub must be able to call you back during the handshake.
  • 401 on hub calls — the connection was re-registered somewhere else; join again with a fresh registration token.
  • Session push returns "No matching session" — push only charges the hub initiated, after START_SESSION was accepted.
  • Rate limits — the hub throttles per-party (default 300 req/min); meter updates every 30–60 s are plenty.

Other stacks

Django (Django ≥ 4.2 · Python ≥ 3.10) · Laravel (Laravel 10 / 11 / 12 · PHP ≥ 8.1) · Node.js (Node ≥ 18 · Express ≥ 4)

Sajilo Charge Spring Boot SDK v0.1.0 · OCPI 2.2.1 · Questions: partners@sajilocharge.com