← Work

Jun 2024 – Feb 2025 · 9 months

EV Charging Roaming Platform

I implemented the OCPI 2.2.1 protocol stack and cross-service data integration for a large-scale EV charging network, enabling interoperability with roaming partners.

126
commits
5
services
9
months
18
DB collections
LaravelPHPMongoDBMySQLRedisOCPI 2.2.1

Overview

I joined an EV charging platform team to implement OCPI (Open Charge Point Interface) 2.2.1 protocol support, enabling the platform to exchange charging station data with roaming partners including a major national power company. My work spanned five interconnected services: the OCPI hub, data integration layer, corporate billing API, main backend service, and admin dashboard.

System Architecture

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Roaming CPOs   │────▶│    OCPI Hub     │────▶│ Integration Svc │
│  (Partners)     │     │   (MongoDB)     │     │   (MySQL)       │
└─────────────────┘     └─────────────────┘     └─────────────────┘
                                                        │
                        ┌─────────────────┐             │
                        │  Corporate API  │◀────────────┤
                        │  (Enterprise)   │             │
                        └─────────────────┘             │
                                                        ▼
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Mobile App    │◀───▶│  Main Backend   │◀───▶│ Admin Dashboard │
│   (Consumer)    │     │   (Core API)    │     │   (Internal)    │
└─────────────────┘     └─────────────────┘     └─────────────────┘

What I Built

OCPI Protocol Implementation (Jun – Jul 2024)

Built the complete OCPI 2.2.1 specification from scratch using MongoDB for flexible document storage:

  • Versions & Credentials: Token A/B/C authentication flow, party registration
  • Locations Module: CPO push and EMSP pull interfaces for 18 nested collection types
  • Sessions & CDRs: Real-time charging session tracking, billing reconciliation
  • Commands Module: Remote start/stop, reserve, unlock connector operations
  • Tariffs & Tokens: Pricing sync and authorization validation across networks

Cross-Service Data Integration (Sep 2024 – Feb 2025)

Created the transformation layer syncing OCPI data into the platform's existing MySQL schema:

  • Transfer service converting OCPI locations and EVSEs into internal station records
  • Hierarchical location mapping (building → space → charger)
  • Shared PHP submodule for consistent OCPI models across services
  • Artisan commands for batch data sync with Link header pagination

Hardware Integration (Aug – Sep 2024)

Extended the main backend for charger hardware communication:

  • Command flow with async results endpoint following OCPI patterns
  • Charging status priority system (FAULTED > CHARGING > RESERVED > AVAILABLE)
  • Session restart functionality and energy calculation fixes
  • QR code scanning API for unauthenticated station discovery

Corporate Billing System (Oct – Nov 2024)

Implemented B2B charging features across multiple services:

  • Corporation and member management API for enterprise partners
  • Monthly billing payment method and kWh-based fee calculation
  • Business registration validation and refund processing workflows
  • Member activation emails and admin panel billing tools

Architecture Decisions

MongoDB for OCPI Data, MySQL for Operations OCPI objects are deeply nested (Location → EVSE → Connector → Status) with varying schemas between operators. Used MongoDB for the Hub to match OCPI's document-oriented nature, then transformed data into MySQL for the platform's relational operations layer.

Shared PHP Submodule Pattern Rather than duplicating OCPI models across five services, created a Git submodule containing Location, EVSE, Credential models and shared middleware. Changes propagate consistently, and services stay in sync during protocol updates.

Single Transformation Service Centralized all OCPI-to-platform field mapping in one transfer service. When partner data caused issues, I only needed to fix one file. The service handles PUT (create) and PATCH (update) with different merge strategies for nullable fields.

Dual Interface Pattern (CPO/EMSP) The same platform acts as both Charge Point Operator (CPO) and E-Mobility Service Provider (EMSP) depending on business relationships. Implemented separate controller namespaces with Sender and Receiver interfaces for each OCPI module.

Key Technical Challenges

Challenge 1: PATCH Operations Overwriting Local Data

Problem: When roaming partners sent PATCH updates, internal reference fields were reset to null because the partner didn't include them—but absence meant "no change," not "set to null."

Solution: Modified transformation logic to preserve existing values when incoming fields are absent. Only explicit null values trigger overwrites.

Challenge 2: Commands Module Timeout Handling

Problem: Remote commands (RESERVE_NOW, START_SESSION) failed silently. Timeout responses were created at the wrong point, and Authorization headers leaked into async callbacks.

Solution: Moved timeout creation to the correct controller lifecycle point. Removed auth headers from callbacks per OCPI spec. Added nullable handling for partial session updates.

Challenge 3: EVSE Status Resolution

Problem: Chargers with multiple simultaneous states showed misleading availability. A faulted charger could display as "available."

Solution: Implemented priority-based status resolution: FAULTED > CHARGING > RESERVED > OCCUPIED > AVAILABLE. The system returns the highest-priority applicable status.

Impact

  • Enabled the platform to connect with a national power company's charging network
  • Corporate billing features serving enterprise customers with monthly invoicing
  • Admin dashboard improvements reducing support ticket resolution time
  • Production-ready OCPI implementation tested against real partner systems

Reflection

This project taught me that protocol implementation is 30% reading the spec and 70% handling edge cases from real-world data. My OCPI hub code went through multiple iterations as I discovered ambiguities in the specification—things that seemed clear on paper broke when tested against actual partner implementations. The lesson: integrate with real systems early, even before features feel "complete." The interoperability testing caught issues that unit tests never would have found.

Working across five interconnected services also showed me the value of shared modules and centralized transformation logic. When a bug appeared in one service's OCPI handling, it usually existed in others too. The submodule pattern meant fixes propagated everywhere, while the single transfer service meant mapping issues had one source of truth.