All case studies
Case study · Mobility & Tolling

Every gantry crossing, charged correctly

How Kentro re-engineered the transaction core of a Gulf tolling operator so crossings, balances, violations and settlements stay correct under constant load.

Mobility & TollingGulf road tolling operatorTransaction core re-engineeringReconciliation and customer channels
Engineers reviewing traffic monitoring screens in a tolling operations control room overlooking a highway toll gantry.
The story

The client is a road mobility and tolling operator in the Gulf. Its gantries sit over some of the busiest corridors in the region, reading tags and plates on every vehicle that passes and turning each crossing into a charge against a prepaid account. There is no quiet hour in this business. Traffic is constant, and every crossing is a small financial transaction that has to be correct.

The platform underneath had grown out of a batch era. Crossings landed as files, charges posted overnight, and balances were updated in place. That held until traffic growth and new corridors compressed the batch window to nothing. Failed runs were replayed by hand, which produced duplicate charges on some nights and missing ones on others. Reconciliation against payment providers was a spreadsheet exercise that finished days after the money had moved. The portal showed customers balances that were hours old, and the dispute queue grew accordingly.

Kentro was engaged to re-engineer the transaction core end to end: crossing ingestion, the account ledger, violation handling, reconciliation with payment providers, and the customer portal and app that sit on top of it all.

The challenge

The engagement brief reduced to a hard property: every crossing becomes exactly the right charge, under constant load, with proof. The existing platform could not make that guarantee, and the reasons compounded each other.

  • Batch processing under streaming load.: Crossings arrived continuously but charged overnight in batches. When a run failed midway, manual replays produced duplicate charges on some accounts and silent gaps on others, and both surfaced weeks later as disputes.
  • Balances with no history behind them.: Account balances were mutable fields updated in place, with no journal to explain them. Concurrent top-ups and charges raced each other, and when a customer challenged a balance, nobody could reconstruct how it got there.
  • Violations managed from spreadsheets.: Crossings that failed tag or plate matching were exported and worked by hand. Grace periods, re-matching and escalation to enforcement all depended on an operator remembering the next step.
  • Reconciliation days behind the money.: Settlement files from payment providers were matched manually, well after funds had moved. Discrepancies were found late, argued cold, and often written off.
The solution

Kentro rebuilt the transaction path as an event-driven core. Gantry crossings enter the platform as immutable events on Apache Kafka, partitioned by account so each account's events process in strict order. A ledger service owns every movement of money. Violations, reconciliation and the customer channels all read from the same ledger, and any component can be rebuilt by replaying the event log.

  • Idempotent ingestion.: Every crossing event carries a stable identifier assigned at the gantry, and consumers deduplicate on it. Retries, replays and network partitions can repeat delivery without ever repeating a charge.
  • An append-only account ledger.: Balances became values derived from journaled postings rather than fields to overwrite. Charges, top-ups, refunds and adjustments are all postings with a recorded reason, guarded by optimistic concurrency in PostgreSQL.
  • Violation lifecycles as state machines.: Unmatched crossings enter an explicit lifecycle: grace period, automatic re-matching against the tag and plate registries, then escalation. Every transition is recorded and reversible, so a late tag match unwinds a violation cleanly instead of leaving debris.
  • Reconciliation as a continuous process.: Provider settlement files are ingested and matched against ledger postings as they arrive, with crossings, postings and settlements checked against each other. Breaks route to an exception queue with the evidence attached.
  • Portal and app on the ledger itself.: The customer portal and mobile app were rebuilt on the same transaction APIs the core uses, so balances, statements and dispute submissions reflect ledger state directly instead of a lagging copy.
The outcome

The result is best described by what the operator's teams stopped doing by hand.

  • Duplicate and missing charges stopped being an operational category.: Idempotent ingestion and the journaled ledger removed the failure modes that created them. Replays are now routine maintenance, not a risk to customer accounts.
  • Finance closes from the system.: Reconciliation breaks arrive as a worked queue while settlements are still fresh. The manual matching ritual, and the write-offs it quietly produced, went away.
  • Violations run themselves until judgment is needed.: Grace periods, re-matching and escalation execute automatically and leave an audit trail. Operators handle the genuine edge cases instead of tracking reminders.
  • Customers argue with facts, not stale screens.: Balances in the portal and app are the ledger, so disputes that began with an out-of-date balance disappeared, and the ones that remain can be answered from the posting history.
Let's talk

Let's build the next release together

Book a discovery call with our team and we'll map the fastest, lowest-risk path from where your technology is today to where your business needs it to be.