TrafficGrid — Implementation Tracker¶
Last audited: 2026-10-13
Sources: TrafficGrid.md, TrafficGrid System Design.md, Data Model.md, API Contracts.md, codebase scan
Use this to see where the project stands and what to pick up next. Status is relative to Phase 1 scope in the architecture docs (not the future backlog).
| Status | Meaning |
|---|---|
| Done | Present and usable for its intended Phase 1 role |
| Partial | Module/entity exists but APIs, wiring, or behaviour are incomplete |
| Missing | Not in the repo / not started |
| Deferred | Explicitly out of Phase 1 (future backlog) |
| Retired | Dropped from the architecture; its scope lives in another service |
1. Snapshot — Where you left off¶
2026-11-02 — citizen fine payments. Citizens pay fines from the mobile app for real: one fine, all of a vehicle's fines, or all their fines, as one ECOCASH payment with one reference. The app sends one idempotency key per attempt and resends it on network errors and 5xx (a 409 returns the existing payment), then polls the payment until it settles. payments-service's mock EcoCash provider now confirms payments by itself after ECOCASH_CONFIRMATION_DELAY (default 3s, dev stack only), so fines reach PAID through the usual SUCCEED_PAYMENT event; a payer phone ending in 0000 fails twice before passing, to exercise retries, and it runs only where ECOCASH_MOCK_ENABLED=true and ECOCASH_MOCK_OUTCOME can force SUCCESS or FAILED. Payment items now carry the fine's offence, plate, vehicle id and live status. The receipt shows the real payment and goes back to the paid vehicle. The USD 0.50 service fee is shown before paying but never sent or stored. See the spec specs/Citizen Fine Payments.md and API Contracts.md §6.8.
2026-11-02 — in-app and push notifications. notification-service now consumes trafficgrid.fines, trafficgrid.payments and trafficgrid.vehicles (group notifications-service) and turns events into citizen notifications: vehicle verified or not, fine issued or referred to court, fine paid by someone else, dispute decided, court outcome, fine cleared or cancelled, payment succeeded or failed. The recipient is the claiming citizen, the payer, or the fine's vehicle's verified owner (looked up through vehicle-service's service-key internal endpoint). Each notification is recorded once per event and recipient (unique event_id, user_id; V2), then pushed through Expo to every registered device (device_tokens, V3). A push failure leaves it FAILED but listed. The API moved to /api/v1/notifications with the citizen's own list, unread count, read, read-all and device endpoints; the userId parameter is gone. The mobile Alerts tab runs on the real API with push, deep links and badges. Preferences are kept but not used when sending; SMS, email, iOS push and Expo receipts are Deferred. See the spec specs/Notifications.md, API Contracts.md §6.9, Data Model.md §2.11 and Kafka Design.md §3.
2026-11-02 — vehicle ownership verification. Linking a vehicle is now a claim that the Central Vehicle Registry (CVR) verifies. A claim starts PENDING_VERIFICATION (the app shows "Verifying…"), and vehicle-service's poller settles it to VERIFIED or REJECTED with the mock CVR, which decides after 2 minutes and rejects plates ending in 0. A vehicle has at most one pending or verified claim (vehicle-service V6), so a second claimant gets 409 "This vehicle is already registered to another account". Only verified links grant access to documents and to a citizen's fines and disputes. New owner lookups GET /vehicles/{id}/owner and the service-key GET /internal/v1/vehicles/{id}/owner. Vehicle events moved from audit.events to trafficgrid.vehicles and gained VERIFY_CITIZEN_VEHICLE and REJECT_CITIZEN_VEHICLE. Events on the fines, payments and vehicles topics carry an eventId, CREATE_FINE goes out only after the fine is saved, and SETTLE_FINE names the payer. A real CVR is Deferred. See the spec specs/Notifications.md, API Contracts.md §6.4 and §6.4.1, Data Model.md §2.4 and Kafka Design.md §3 and §6.
2026-10-31 — fine disputes. A citizen disputes a PENDING, not-yet-due fine on one of their linked vehicles once, ever, with grounds, a statement and up to three evidence files (details only; nothing is uploaded). The fine is DISPUTED and unpayable until the citizen withdraws (back to PENDING) or an admin of the issuing organisation, or a super admin, upholds (CLEARED) or rejects it (PENDING, with at least 7 days from the decision to pay). New tables fine_disputes and fine_dispute_evidence (payments-service V16), permissions FINE_DISPUTE_CREATE/FINE_DISPUTE_REVIEW (auth-service V8), a review queue at GET /fines/disputes, and the status change no longer reaches or leaves DISPUTED. Evidence storage and dispute notifications are Deferred. See the spec specs/Fine Disputes.md, API Contracts.md §6.6.1 and Data Model.md §2.7.1.
2026-10-27 — fine levels and the offence catalogue. Fine categories in system-parameters are now per-organisation levels (requires_court, nullable amount, code and name unique per organisation; V12), with ZRP's Level 1–3 and Escalated seeded under fixed ids (V13). A new offence catalogue sits under the levels (/system-parameters/offences, V14) and takes its price, currency and organisation from its level; ZRP's "Other court offence" catch-all is seeded (V15). Level and offence reads are scoped to the caller's organisation (a super admin sees all); only a super admin manages them, with the existing FINE_CATEGORY_* permissions. Issuing a fine still takes a fineCategoryId until issuing by offence and court outcomes land (payments-service#7). See the spec specs/Fine Levels and Offences.md, Data Model.md §2.6 and API Contracts.md §6.5.
2026-10-13 — backend gaps closed for the admin console. Fines live in payments-service (fines-service is retired), which now snapshots the vehicle, category, officer and organisation onto each fine, records amounts in integer cents with a currency, serves searchable GET /fines and GET /payments lists with per-status counts, and captures payer, cash-receipt, bank-transfer and provider details on payments. The gateway forwards the caller's name (X-User-Name) and organisation (X-Organisation-Id); every write in auth, payments, system-parameters and vehicle-service is audited over Kafka with the actor's name; audit-service serves a paged, filterable GET /audit-logs. system-parameters adds fine-category edit, description and currency, deactivation reasons and stable codes; vehicle-service adds document source, expiry status and free-text search. See API Contracts.md, Data Model.md and Kafka Design.md. The rest of this snapshot is the 2026-09-27 audit; the tables and checklists below are current.
Platform scaffolding is largely in place. Domain work progressed furthest on auth, system-parameters, vehicle,
and now payments. payments-service has a working create/read API (idempotent, cash + mocked EcoCash), permission-based
RBAC, and JWT + gateway-header security, and is wired into config-server, the gateway, and docker-compose.
notification-service exists only as an infra scaffold (builds, registers with Eureka, routed through the gateway) — it has
no domain model or endpoints yet. Fines-service does not exist yet, which also means payments-service currently talks to
a MockPayableClient stand-in instead of a real fines/parking lookup.
You appear to have stopped mid–message-broker migration: root docker-compose.yaml runs Kafka, but Java code still publishes/consumes RabbitMQ for audit events.
Suggested next focus (in order):
- Finish the RabbitMQ → Kafka cutover (auth publisher, audit listener, deps, config-server).
- Create fines-service (core Phase 1 product loop), then replace payments-service's
MockPayableClientwith a real Feign call to it. - Build out notification-service's domain (entities, migrations, consume fine/payment events, preferences + history APIs).
- Publish
payment.success/payment.failedfrom payments-service once there's a Kafka topic and a consumer (fines, notifications) to react to them. - Wire OpenFeign + domain Kafka topics between those services.
2. Services inventory¶
| Service | Port | Status | Notes |
|---|---|---|---|
discovery (discovery/) |
8761 | Done | Eureka registry |
| config-server | 8888 | Done | Native classpath configs for auth/audit/system-parameters/vehicle/payments/notification. Service-to-service defaults are Docker-reachable and env-overridable (EUREKA_URL, KAFKA_BOOTSTRAP_SERVERS, REDIS_PASSWORD, AUTH_SERVICE_URL) |
| api-gateway | 8080 | Partial | JWT validate; forwards X-User-Id, X-User-Role, X-User-Permissions, X-User-Name, X-Organisation-Id and strips client copies; routes /api/v1/fines/** and /api/v1/payments/** to payments-service; CORS origin patterns from CORS_ALLOWED_ORIGIN_PATTERNS with DedupeResponseHeader; no rate limiting yet |
| auth-service | 8081 | Partial | Register/login/refresh/logout/validate + user admin; access tokens carry name and email; /auth/validate returns fullName, email, organizationId; PermissionSeeder + V7 add the read and parking permissions; GET /users needs USER_LIST; Redis token blocklist incomplete |
| audit-service | 8082 | Done | Consumes users/fines/payments/vehicles/audit.events; paged, filterable GET /audit-logs and /{id} with actor names (Flyway V3 actor_name); AUDIT_LOG_READ with organisation scoping |
| system-parameters-service | 8083 | Done | Orgs, currencies, fine categories (per-organisation levels, court flag, seeded ZRP levels), offence catalogue, payment methods, notification channels + seeds; fine-category edit/description/currency; deactivation reasons; channel and method codes; organisation-scoped level and offence reads; audit events |
| vehicle-service | 8084 | Done | Create, search/filtering (incl. q, color), citizen vehicle claims verified by CVR (mock CVR at /api/v1/mock/cvr/verifications, CvrVerificationPoller), one owner per vehicle, owner lookup (/api/v1/vehicles/{id}/owner and service-key /internal/v1/vehicles/{id}/owner), vehicle documents with source and expiryStatus, JWT + Gateway security, events on trafficgrid.vehicles with an eventId |
| fines-service | 8085 | Retired | Fines live in payments-service (Data Model.md §4.11) |
| payments-service | 8086 | Partial | Fines (issue, read, search with statusCounts, status changes) and payments (create, read, search, mock callback), integer cents + currency, name snapshots, payment details, Kafka events and settlement; real EcoCash, bank webhooks and parking tickets still to come |
| notification-service | 8088 | Done | Consumes fines/payments/vehicles events (group notifications-service, retry then <topic>.DLT) into citizen notifications; Expo push to registered devices; citizen-only API under /api/v1/notifications (list by category, unread count, read, read-all, devices, preferences) with JWT + gateway-header security; Flyway V1–V3. SMS/email Deferred |
| core-service (shared JAR) | — | Done | core-common + core-web: JWT helpers, envelopes, exceptions, OpenAPI, Kafka topology |
3. Infrastructure¶
| Component | Planned | Status | Notes |
|---|---|---|---|
| PostgreSQL | Yes | Done | Root + init-scripts compose; schemas created in init SQL |
| Redis | Yes | Partial | Running; used for auth Redis config, but JWT blocklist code commented out |
| Message broker | Kafka (KRaft) | Done | Root + init-scripts compose: Kafka KRaft; auth-service & audit-service use Spring Kafka |
| Docker Compose (infra) | Yes | Done | Postgres + Redis + Kafka |
| Per-service Docker images | Production future | Done | Every service in docker-compose.yaml (discovery, config-server, auth, audit, system-parameters, vehicle, payments, notification, api-gateway) now has a Dockerfile; fines-service will need one once it exists |
| Kubernetes | Production future | Deferred | |
| CI/CD (GitHub Actions) | Production future | Deferred | Docs mention it; not wired for the monorepo services |
| Schema naming consistency | — | Partial | init-scripts: auth_service; auth config: user_service. Vehicle: Hibernate vehicle_service vs migration/entity schema vehicles |
4. Data model vs code¶
| Entity | Owner (docs) | Status | Notes |
|---|---|---|---|
users |
auth-service | Done | Entity + APIs; Flyway file under typo folder db.migation (not applied) — relies on ddl-auto |
organisations |
system-parameters | Done | CRUD + activate/deactivate + seeds |
currencies |
system-parameters | Done | |
fine_categories |
system-parameters | Done | Levels: per-organisation code/name, requires_court with nullable amount (V12); ZRP's four levels seeded with fixed ids (V13) |
offences |
system-parameters | Done | V14; ZRP's "Other court offence" seeded (V15). The full catalogue seed waits for the project lead's SQL |
payment_methods |
system-parameters | Done | Reference data only (not payment processing) |
notification_channels |
system-parameters | Done | Reference data only |
vehicles |
vehicle-service | Done | Create, get by id, search/filter (plate/make/type/verified) |
citizen_vehicles |
vehicle-service | Done | Claims with verification_status, cvr_request_id, verified_at and one pending or verified claim per vehicle (V6; existing links migrated as VERIFIED) |
mock_cvr_verifications |
vehicle-service | Done | V7; the mock CVR's requests. Dropped once the real CVR is integrated |
vehicle_documents |
vehicle-service | Done | Entity + get/add/update API |
fines |
payments-service | Done | Snapshots, currency, status-change reason (V13) |
fine_disputes |
payments-service | Done | V16; one per fine, ever; backfilled an OPEN dispute for fines already DISPUTED |
fine_dispute_evidence |
payments-service | Partial | V16; details only, no file storage (Deferred) |
parking_tickets |
payments-service | Deferred | Table exists; endpoints planned — City Parking API |
payments |
payments-service | Done | Currency, payer/officer name snapshots, provider/cash/bank details, cash receipt sequence (V14) |
payment_items |
payments-service | Done | Validated against the fines table; snapshots the fine's reference (V14) |
notifications |
notification-service | Done | V1 + V2: read_at, payload (JSONB), event_id with unique (event_id, user_id), the new types and the VEHICLE/PAYMENT reference types |
notification_preferences |
notification-service | Partial | Read and replace work; not used when sending |
device_tokens |
notification-service | Done | V3; one row per Expo push token, several per citizen; dead tokens pruned on push |
audit_logs |
audit-service | Done | Flyway V1–V3 incl. actor_name; ip_address/user_agent not stored |
5. API contracts vs implementation¶
External callers use the contract paths (/api/v1/...) through the gateway, every service included: notification-service is no longer the exception (/api/v1/notifications/**; the old /notification-service/** prefix route is gone, and only its API-docs route remains). vehicle-service's /internal/** is deliberately not routed.
Auth — Partial¶
| Contract area | Status |
|---|---|
| Register / login / refresh | Done — /auth/login is officers and admins only; citizens use /auth/citizen-login. Refresh returns 401 (never 500) for bad tokens and deactivated users. |
| Logout | Done (path secured; Redis revoke incomplete) |
| Validate (gateway) | Done — returns userId, role, permissions, fullName, email, organizationId |
| Access-token claims | Done — bare role, permissions, name, email, and organizationId when the user has one |
Password reset (/auth/forgot-password, /auth/reset-password) |
Partial — one-time links work, but are logged rather than emailed (no email delivery on the platform yet) |
| Admin create user / activate / deactivate / list / get | Done — organisation stored and returned; password optional (a link is sent instead); deactivation takes a reason; list filters by role, organisation, status and text |
Vehicles — Done¶
| Contract area | Status |
|---|---|
| Create vehicle | Done (POST api/v1/vehicles) |
| Get / search vehicle | Done — q (plate, make, model, VIN) and color |
| Update vehicle | Missing (no update endpoint) |
| Vehicle documents CRUD | Done (get/add/update; no delete); source and server-computed expiryStatus |
| Citizen link / unlink / list | Done — a link is a claim verified by CVR: verificationStatus, pending links listed, rejected never; 409 when another account holds the vehicle |
Owner lookup (GET /vehicles/{id}/owner, internal /internal/v1/vehicles/{id}/owner) |
Done — verified owner only; the internal one needs X-Service-Key and is not routed by the gateway |
Mock CVR (/api/v1/mock/cvr/verifications) |
Done — temporary; decides after CVR_VERIFICATION_DELAY (default 2 minutes), rejects plates ending in 0 |
Fine categories — Done (in system-parameters)¶
Owned by system-parameters, matching current design. Edit (PATCH /{id}/edit), description, currency and deactivation reasons — Done. Categories are now per-organisation levels with a requiresCourt flag and nullable amount, an organizationId filter for super admins, and organisation-scoped reads — Done.
Offences — Done (in system-parameters)¶
/system-parameters/offences: create, edit, get, list filtered by level, organisation and active status, activate/deactivate with a reason. Each offence carries a summary of its level; reads are organisation-scoped and writes are super-admin only (FINE_CATEGORY_*). Audited as CREATE_OFFENCE / UPDATE_OFFENCE / ACTIVATE_OFFENCE / DEACTIVATE_OFFENCE.
Organisations / currencies / payment methods / notification channels — Done¶
CRUD (or activate/deactivate, with reasons) under system-parameters. Organisation and currency reads are open to ORGANIZATION_READ / CURRENCY_READ; payment methods and channels carry stable codes.
Fines — Done (in payments-service)¶
Issue, get, search (q, plate, filters, statusCounts), status changes with reasons, snapshots and currency. Disputes: file, withdraw, decide and the review queue. Citizens are limited to fines on their own verified vehicles (GET /fines, GET /fines/{id}, GET /citizens/me/fines and filing a dispute); fines on a vehicle still being verified are hidden. /vehicles/{id}/fines is not built.
Parking tickets — Missing / Deferred¶
Payments — Partial¶
| Contract area | Status |
|---|---|
| Initiate payment (multi-item, idempotency key) | Done |
| Cash recording by officer | Done (recordedBy = officer, paidBy = walk-in payer {id: null, fullName}, receipt TGRCT-YYYY-NNNNN) |
| EcoCash | Partial — mocked: a poller confirms ECOCASH payments after ECOCASH_CONFIRMATION_DELAY (default 3s, only where ECOCASH_MOCK_ENABLED=true; phones ending 0000 fail twice, then pass), and the manual mock callback remains; real provider integration Missing |
| Payment items carry fine details (offence, plate, vehicle id, live fine status) | Done — read from the fines table on every payment read |
| Citizen pay flow (mobile) | Done — pay one fine, a vehicle's fines or all fines as one EcoCash payment; safe retry with the idempotency key; status polling; real receipt with "Back to {plate}"; display-only USD 0.50 fee |
| Get by id / by reference number | Done (citizens restricted to their own payments; officers/admins see any) |
Search (GET /payments) |
Done — filters, q, sort, statusCounts, same visibility |
| Payer, provider, cash-receipt and bank-transfer details | Done |
| Receipts | Partial — the mobile receipt is built from the payment object; PDF download Missing |
Publish payment.success / payment.failed |
Done — SUCCEED_PAYMENT / FAIL_PAYMENT; success settles fines to PAID |
Notifications — Done (push only)¶
| Contract area | Status |
|---|---|
GET /notifications/me (paged, newest first, category filter) |
Done |
GET /notifications/me/unread-count, PATCH /notifications/{id}/read, POST /notifications/me/read-all |
Done — another user's id is 404 |
POST /notifications/me/devices, DELETE /notifications/me/devices/{expoPushToken} |
Done — a token moves to the user who registers it; unregister is idempotent |
Preferences (GET/PUT /notifications/preferences) |
Done, but not used when sending |
Identity from the gateway headers (userId parameter removed) |
Done — citizen-only; staff get 403 |
| Event-driven notifications (fines, payments, vehicles) | Done — mapping in Kafka Design.md §3; de-duplicated on (event_id, user_id) |
| Push delivery | Partial — Expo push with the unread count as badge; Android needs the Firebase project and FCM key, iOS the APNs key; Expo receipts not polled |
| SMS / email delivery | Deferred |
| Mobile Alerts tab and push | Done — real API, Fines/Payments/Vehicles filters (Renewals hidden), infinite scroll, mark read, push permission after first sign-in, tap-to-open, tab and app-icon badges |
Audit logs — Done¶
| Contract area | Status |
|---|---|
Paged, filterable list + get by id (/audit-logs) |
Done |
| Query by actor / organisation / entity | Done |
| Immutable ingest via events | Done (auth, payments, system-parameters, vehicle; with actor names) |
Admin users — Done (auth-service)¶
6. Functional requirements (Phase 1)¶
| Requirement | Status | Evidence / gap |
|---|---|---|
| Citizen self-register + login | Done | auth-service |
| Officer/admin account creation (RBAC) | Partial | User create + roles exist; org scoping / creation rules may need hardening |
| Vehicle create / register | Done | Create, get, search |
| Citizen link vehicle by plate | Done | Link/unlink/list under CitizenVehicleController; a link is a claim that CVR verifies |
| Vehicle ownership verification | Partial | One owner per vehicle, verified by the mock CVR in vehicle-service; the real CVR integration is Deferred |
| Vehicle dashboard (docs, fines, renewals) | Partial | Vehicle + documents done; still needs fines + notifications |
| Officer issue fine | Done | payments-service |
| Fine statuses PENDING/PAID/CLEARED/DISPUTED/CANCELLED | Done | Clear or cancel by manual change with a reason; DISPUTED only via a dispute; PAID only via payment |
| Fine disputes | Done | payments-service V16 + auth-service V8: citizen files (grounds, statement, evidence details) and withdraws; issuing organisation's admin upholds or rejects with a note; review queue GET /fines/disputes; *_FINE_DISPUTE audit events. See specs/Fine Disputes.md |
| Dispute evidence storage | Deferred | Only file name, type and size are kept; storage_key is a placeholder, nothing is uploaded |
| Dispute notifications | Done | The vehicle owner is notified when a dispute is upheld or rejected (FINE_DISPUTE_DECIDED); opening and withdrawing notify nobody, and reviewers are not notified |
| Cash payment recorded by officer | Done | payments-service |
| EcoCash / bank transfer | Partial | Mocked provider callback only; real integrations Deferred |
| Payment idempotency | Done | idempotencyKey unique constraint + duplicate-aware create |
| Parking tickets | Deferred | |
| Notifications on fine / payment / expiry | Partial | Fine, payment and vehicle-verification notifications are recorded, listed in the app and pushed (Android, once Firebase is set up); document-expiry and fine-due reminders are not built |
| Audit every write | Done | auth, payments, system-parameters and vehicle-service publish to Kafka with the actor's name |
| OpenAPI / Swagger | Partial | springdoc on services + gateway aggregation for existing services |
| Offline officer sync | Deferred | Client concern |
7. Cross-cutting platform features¶
| Feature | Status | Notes |
|---|---|---|
| JWT auth | Partial | Issued/validated; gateway forwards X-User-Id, X-User-Role, X-User-Permissions, X-User-Name, X-Organisation-Id; logout revocation incomplete |
| RBAC | Done | Permission-based @PreAuthorize on auth, audit, vehicle, system-parameters and payments; catalogue seeded on startup by PermissionSeeder |
| Gateway routing | Done | Contract paths for every service: auth, audit, system-parameters, vehicle, fines, payments and notifications (/api/v1/notifications/**) |
| Rate limiting | Missing | Documented NFR; not implemented on gateway |
| OpenFeign inter-service calls | Done | payments-service calls vehicle-service and system-parameters-service (vehicle, fine category, organisation) |
| Async domain events (fine/payment/vehicle) | Partial | Fine and payment events on Kafka; vehicle document-expiry events not yet |
| Kafka adoption | Done | All producers and consumers on Kafka |
| Distributed locks (payment races) | Missing | |
| Circuit breakers / timeouts | Partial | Feign CB config present; unused without Feign clients |
| Actuator health (liveness/readiness) | Unknown / likely Missing | Not verified as a deliberate setup |
| Dead letter queues | Partial | payments-service and notification-service retry 3 times then dead-letter to <topic>.DLT; no alerting on it yet |
| Automated tests | Partial | Black-box API integration tests run through the gateway against the local stack, with results in the Test Tracker; services themselves still have only smoke/context unit tests |
| Citizen / officer mobile apps | Partial | Citizen app (traffic-grid-mobile) on the live APIs, including the Alerts tab and push; officer app not started |
| Authority / super-admin web portals | Missing |
8. Messaging topology¶
Designed (System Design v2 — RabbitMQ topic exchanges)¶
| Exchange | Events | Status |
|---|---|---|
trafficgrid.fines |
fine.created, fine.status.updated |
Done (as CREATE_FINE, UPDATE_FINE_STATUS, SETTLE_FINE) |
trafficgrid.fines |
fine.dispute.opened, fine.dispute.withdrawn, fine.dispute.decided |
Done (as OPEN_FINE_DISPUTE, WITHDRAW_FINE_DISPUTE, DECIDE_FINE_DISPUTE); notification-service notifies on the decision |
trafficgrid.payments |
payment.success, payment.failed |
Done (as SUCCEED_PAYMENT, FAIL_PAYMENT) |
trafficgrid.vehicles |
vehicle.claim.verified, vehicle.claim.rejected |
Done (as VERIFY_CITIZEN_VEHICLE, REJECT_CITIZEN_VEHICLE) |
trafficgrid.vehicles |
vehicle.document.expiry |
Missing |
trafficgrid.notifications |
internal dispatch | Not needed — notification-service consumes the domain events directly |
Implemented today¶
| Piece | Status |
|---|---|
Kafka producers: auth (trafficgrid.users), payments (trafficgrid.fines, trafficgrid.payments), vehicle (trafficgrid.vehicles), system-parameters (audit.events) |
Done — JSON with actorName, no type headers; payments and vehicle events carry an eventId and are published after commit |
Audit AuditEventListener |
Done (@KafkaListener on all five topics) |
payments-service PaymentEventListener (settles fines) |
Done |
notification-service FineEventListener, PaymentEventListener, VehicleEventListener (group notifications-service) |
Done — retry 3× then <topic>.DLT |
9. External integrations¶
| Integration | Status |
|---|---|
| ZINARA API | Deferred (mock later) |
| VTS API | Deferred (VIN and registration checks; not ownership) |
| Central Vehicle Registry (CVR) | Partial — mocked inside vehicle-service (/api/v1/mock/cvr/verifications, CVR_BASE_URL); the real integration is Deferred and replaces only CvrVerificationClient |
| City Parking API | Deferred |
| EcoCash | Deferred |
| Bank transfer webhooks | Deferred |
| Push: Expo push service + Firebase Cloud Messaging (Android) | Partial — notification-service sends through Expo and the app registers devices; the Firebase project, its FCM v1 key in Expo and the EAS project id are still to be set up |
| Push: APNs (iOS) | Deferred — needs an Apple developer account |
| SMS / email providers | Deferred |
10. Known debt / blockers (fix before new domains)¶
- ~~Broker mismatch~~ — resolved: everything is on Kafka.
- Auth Flyway —
db.migationtypo; config expectsdb/migration. - Schema name drift —
auth_servicevsuser_service;vehicle_servicevsvehicles. - Vehicle security —
permitAlluntil JWT filter is wired. - Auth Redis JWT blocklist — commented out; logout/revoke incomplete.
- Audit Flyway empty + confirm
@EnableMethodSecurity. - init-scripts compose / README — still document RabbitMQ; out of sync with root compose.
- API Contracts vs System Design — contracts sometimes assume modular/monolith paths; system design is microservices. Tracker follows the running microservices layout.
- ~~payments-service depends on a mock~~ — resolved: payments are validated against the real fines table.
- ~~notification-service is infra-only~~ — resolved: it consumes events, pushes, and serves the citizen's notifications API.
- ~~Citizen scoping on fines~~ — resolved: a citizen reads only fines on their own verified vehicles.
- ~~Integration tests on old paths~~ — resolved: the integration tests call
/api/v1/finesand/api/v1/payments, and the case catalogue under Testing matches them.
11. Suggested implementation checklist¶
Use as a working backlog. Check off as you complete.
Platform hygiene¶
- [x] Migrate auth → audit messaging from RabbitMQ to Kafka
- [x] Remove RabbitMQ deps/config; align config-server + init-scripts
- [x] Fix auth Flyway path and schema name
- [x] Align vehicle schema names (config vs migrations)
- [x] Enable vehicle JWT / gateway-header security
- [ ] Restore Redis-backed token blocklist on logout
- [ ] Add gateway rate limiting (at least public auth endpoints)
Vehicle (complete before fines)¶
- [x] GET vehicle by id / by number plate
- [x] List/search vehicles
- [x] Citizen vehicles entity + link/unlink/list APIs
- [x] Vehicle documents entity + CRUD + expiry fields
- [x] Ownership claims verified by CVR (mock), one owner per vehicle, owner lookup
- [x] Vehicle events on
trafficgrid.vehicles, includingVERIFY_CITIZEN_VEHICLE/REJECT_CITIZEN_VEHICLE - [ ] Real CVR integration
- [ ] Emit document-expiry events (when notifications exist)
Fines (in payments-service; fines-service retired)¶
- [x] Gateway route (
/api/v1/fines/**) - [x]
finesschema + Flyway - [x] Issue fine (Feign: vehicle exists, fine category from system-parameters)
- [x] Get / search / status transitions
- [x] Publish
fine.created/fine.status.updated - [x] Fine disputes: file, withdraw, decide, review queue, dispute events (see
specs/Fine Disputes.md) - [x] Dispute notifications (the owner is told the decision)
- [ ] Dispute evidence storage (Deferred)
- [x] Limit citizens to fines on their own linked (verified) vehicles
Fine levels and offences (system-parameters)¶
- [x] Fine categories become per-organisation levels (
requires_court, nullable amount, per-organisation uniqueness) - [x] Seed ZRP's Level 1–3 and Escalated with fixed ids
- [x] Offence catalogue API under the levels, with the level summary
- [x] Organisation-scoped reads; super-admin-only management
- [x] Seed ZRP's "Other court offence" catch-all
- [x] Integration tests and docs (Data Model, API Contracts, Roles and Permissions)
- [ ] Seed the full offence catalogue (SQL from the project lead)
- [ ] Issue fines by offence, "Referred to court" and court outcomes (payments-service#7)
Payments-service (new)¶
- [x] Service scaffold + gateway route
- [x]
payments+payment_items - [x] Idempotent pay flow (stub provider OK for Phase 1)
- [x] Cash recording by officer
- [x] Replace
MockPayableClientwith a real fines lookup (parking, once it exists) - [x] Publish
payment.success/payment.failed; fines consume → mark PAID - [x] Payment search, details and currency
- [ ] Receipts
Notifications-service (new)¶
- [x] Service scaffold + gateway route
- [x]
notifications+notification_preferencesentities + Flyway (V2 read state, payload,event_id; V3device_tokens) - [x] Consume fine, payment and vehicle events
- [ ] Consume document-expiry events and send fine-due reminders
- [x] Preferences + notification history APIs (
/api/v1/notifications/me, unread count, read, read-all, devices) - [x] JWT + gateway-header security (SecurityConfig/filters, same pattern as vehicle/payments)
- [x] Expo push delivery
- [ ] Firebase project + FCM v1 key (Android), APNs key (iOS)
- [ ] Stub channel adapters (log-only) until SMS/email providers
Hardening¶
- [x] Integration tests for auth, payments idempotency and fine issue (see Testing)
- [ ] Real unit tests in each service
- [x] Audit events from all write paths
- [x] DLQ + retry policy on consumers (payments-service, notification-service; alerting still to do)
- [ ] Actuator liveness/readiness
Clients (after core APIs stabilize)¶
- [ ] Citizen mobile
- [ ] Officer mobile (offline later)
- [ ] Authority / admin web
Explicitly later (do not block Phase 1)¶
- [ ] Parking tickets + City Parking sync
- [ ] ZINARA / VTS / EcoCash real integrations
- [ ] USSD / WhatsApp / ZIMRA / national expansion
- [ ] K8s + production CI/CD
12. Rough completion estimate (Phase 1 backend)¶
| Area | Approx. done |
|---|---|
| Platform (discovery, config, gateway, core) | ~80% |
| Auth | ~70% |
| System parameters | ~90% |
| Audit | ~90% |
| Vehicle | ~85% |
| Fines | ~85% (in payments-service) |
| Payments | ~55% |
| Notifications | ~75% (push only; SMS, email and reminders to come) |
| Clients | ~0% |
| Overall Phase 1 backend | ~45–50% |
Update this file whenever a service lands or a checklist item finishes. Prefer status changes over rewriting narrative.