TRANXIT×CAVITEXClient simulators · dev

Two clients, one API

These web apps stand in for the two mobile apps so the mobile team can see exactly what each screen sends and gets back. They talk to the same FastAPI backend as the dashboard (/api/v1, proxied to 127.0.0.1:8000), with the same tokens and rules. Every screen explains its workflow and shows a live log of its requests.

Expo app · devtranxit.apk

Driver app

A driver signs in, registers a vehicle, tops up the wallet and shows a toll QR at the lane.

  • Username or email + password; JWT access token (15 min) + rotating refresh token (30 days)
  • Wallet balance and ledger, top-ups (mock gateway in dev)
  • Vehicles and RFID cards
  • Signed toll QR (Ed25519, single use, 2 min)
  • Trips: every pass, paid or declined
Open the driver app
NEXGO N86 · TranxitLane

Lane POS

A terminal mounted in a CAVITEX booth reads the QR or card, charges the toll and opens the gate.

  • Device sign-in with code + serial + secret
  • Heartbeat every 30 s; the dashboard shows it online, picks up commands
  • Verify → charge with a retry-safe clientTxId
  • Offline mode: verify the QR signature locally, queue, sync later
  • Today screen and technician install
Open the lane POS

One toll pass, end to end

  1. Driver app asks for a toll QR: POST /driver/toll-qr with the vehicle. The server signs a JWS (driver, vehicle, plate, class, a one-time jti, 2-minute expiry) and returns it with the current balance.
  2. The driver holds the phone to the scanner. Here, Show to lane sends the QR to any open lane-POS tab in this browser (a BroadcastChannel); you can also copy and paste it.
  3. Lane POS calls POST /pos/tolls/verify: no money moves. It answers READY with the plate, class, toll and balance, or a reason it can't pass (expired, used, low balance, plate mismatch…).
  4. The POS charges: POST /pos/tolls/charge with a new clientTxId. If the network drops and it retries, the same id returns the same transaction, so the driver is never charged twice. PAID → gate OPEN, with a receipt number.
  5. The wallet is debited in the same database transaction. The dashboard (port 3000) gets it at once over its live stream; the driver app sees it on its next refresh of trips and balance (the QR screen polls for it).

Try it

  1. Open Lane POS in one tab and pick a seeded terminal (dev only: read from backend/.data/device-secrets.json).
  2. Open Driver app in another tab. Sign in with a driver from backend/.data/driver-accounts.json or create one.
  3. Add a vehicle, top up, then open Pay toll and press Show to lane. The POS tab fills in the QR; press Verify, then Charge.
  4. Watch the gate result on the POS, the balance drop in the app, and the pass appear on the dashboard.

Where the state lives

DataKept byHere (dev)
Driver tokensApp secure storage (expo-secure-store)localStorage of this origin
Device credentials + tokenN86 keystore, provisioned at installlocalStorage of this origin
Offline queueN86 local databaselocalStorage, synced with /pos/tolls/sync
Balance, trips, rates, devicesPostgres behind the API: the only source of truth. Clients never compute balances.

Two tabs of the same app share one session because they share localStorage. Use a private window to run a second driver or a second lane.