Fintech · 2024 — Present
PayGov & Revco
Six role-specific apps merged into one multi-tenant platform for government revenue collection.
- Role
- Frontend Engineer, Paysure
- Timeline
- 2024 — Present
- Stack
- Next.js, TypeScript, NextAuth v5, SWR, Edge Middleware, AWS
- Links
- Live site ↗ · 3 min read

The problem
PayGov is a billing, invoicing and revenue-collection platform for government agencies, ministries and their authorised partners. It covers the whole lifecycle: a bill is created, collected, remitted and reconciled for compliance.
When I joined Paysure, that lifecycle was spread across six separate Next.js apps, one per role. Each had its own login, its own copy of shared UI and its own drift. A fix in one rarely reached the other five, and every new tenant meant another round of copy-and-configure.
What I built
One multi-tenant platform in place of the six, serving 2,000+ users and ₦45M in yearly payment volume.
- apps consolidated
- 6 → 1
- BFF endpoints secured
- 280
- route handlers guarded
- 243
- roles
- 10
- permission scopes
- 4
- downtime from two RCE CVEs
- 0
Tenancy that lives in the URL
Each tenant is scoped by path. The slug is resolved on the server before anything renders, so a page never shows one agency's data under another's name. A reserved-segment guard stops a tenant slug from shadowing a real route, and sessions are bound to the tenant they were issued for, so a token from one agency is useless at another.
Access control as a schema, not a scattering of ifs
The platform talks to its backends through a 280-endpoint backend-for-frontend. I centralised authentication on NextAuth v5 JWT sessions, with refresh tokens rotated before they expire rather than after a request fails. Authorisation lives in one role-and-compliance schema that drives both the route guards (243 handlers, 10 roles, 4 scopes) and the navigation itself, so a user never sees a link they cannot follow.
The hard part: two RCE CVEs and no time to upgrade
Two remote-code-execution vulnerabilities were disclosed in the framework while an upgrade was not immediately viable. Rather than wait, I shipped app-wide edge middleware that rejects and logs the text/x-component POST payloads that carry the exploit. Both production apps were protected with zero downtime, and the logs showed us what was being attempted.
Smaller, everyday wins
Dashboards were refetching the same lists on every interaction, and a filtered view could not be shared. I moved table state into typed query parameters behind a debounced router push, served from SWR with stable cache keys and keepPreviousData. Redundant requests dropped, and any filtered view became a link a colleague can open.
Revco
Revco is the public face of the same work: a revenue and tax portal for state boards, with custom subdomains per tenant and admin dashboards for transaction tracking. The Taraba State deployment is live.