Consumer Billing & Self-Service Portal
Members check bills and track outage repairs from their phones, and the billing database is never exposed to the internet.
A self-service portal where consumers see their bills, payments, and usage, report outages and follow the repair, while the billing system itself never touches the internet.
- Live in production, serving member-consumers across 17 towns and cities
- 370+ test cases across 29 suites
- 13-month usage history per account

Team of one· Solo architect & full-stack engineer
No team, no agency, no hand-offs. I owned this project end to end:
- Requirements
- Architecture
- UI & UX
- Backend & APIs
- Database
- Infra & deployment
- Stack
- Next.jsSupabaseCloudflare TunnelSync AgentMS SQL Server
Where AI does the work: Automation keeps the portal current and the queue moving: the sync agent refreshes billing data on demand, reports route themselves to the right office, and unacknowledged tickets escalate on their own.
What was broken, and what fixed it
Screens show demo data; client data stays private.
Problem: Members had to wait for the paper bill, or line up at an office, just to learn what they owed.
Fix: Balance, due date, and an unpaid-month warning on one screen, for every account the member owns, on any phone.

Overview: balance, due date, unpaid warning, last payment, latest usage, and a severe-weather alert. Problem: A higher bill was one number nobody could explain, so members called or walked in to ask why.
Fix: Every bill reads line by line (generation, transmission, distribution, taxes) with a plain-language reason it changed, next to the published rate schedule.

One bill, line by line, with a plain-language reason it changed from last month. Problem: Members only found out their bill would jump after it arrived.
Fix: 13 months of usage, comparisons to last month and last year, a next-bill estimate, and an appliance calculator at the member's own rate.

13-month usage, comparisons to last month and last year, and a next-bill estimate. Problem: A payment made through GCash, Bayad Center, or another office could not be confirmed without a call.
Fix: Payment history across every channel, plus a where-to-pay map with the account number ready to copy.

Payment history across offices, GCash, Bayad Center, and ECPay. Problem: Outage complaints came by phone and walk-in, with no record anyone could follow.
Fix: Members pin the spot on a map. Each report becomes a numbered job order, routed to the office that serves that town, and the member sees its live status.

Report an outage or hazard by pinning the spot on a map, then follow its status. Problem: Nobody could answer “where is the lineman on this one?”, and some tickets sat unnoticed.
Fix: A dispatch timeline stamps every step with who and when. Unacknowledged tickets escalate to the supervisor, then the manager, and staff get push alerts. Linemen read the same job order on their phones.
Problem: Management had no view across area offices.
Fix: One board shows open tickets per office, the oldest unacknowledged one, escalations, and each lineman's workload.

All offices at a glance: new, acknowledged, dispatched, oldest new, escalations, and lineman workload. Problem: Most members are on phones over slow mobile data.
Fix: The phone layout is designed on its own, not shrunk from desktop, and a device cache paints the last known data before the network answers.

Public landing page where members register and sign in.
Approach
- An on-prem sync agent is the only part that reads the billing database. It mirrors only what the portal needs, pushes outbound, and opens no inbound ports.
- Read-through, not a bulk copy: a member's data is mirrored only after they claim the account, and refreshed on demand.
- Data isolation enforced in SQL: every account-scoped query is guarded against the caller's linked accounts, with row-level security set to default-deny.
- Self-hosted behind a tunnel, with automatic deploys, health checks, and rollback, and a cloud host as a warm fallback.
What it does
Member self-service
- Bill and balance for every account a member owns, with due date and unpaid-month warning
- Line-by-line bill breakdown with a plain-language reason it changed from last month
- 13-month usage, comparisons to last month and last year, and a next-bill estimate
- Payment verification across channels
- Where to pay, with a map of offices and mall counters
- Appliance cost estimator at the member's own effective rate
- Severe-weather warnings for the franchise area
Outage and job orders
- Report an outage, hazard, meter, or billing problem with a map pin
- Every report becomes a numbered job order routed to the office that serves that town
- Dispatch timeline: acknowledged, assigned, en route, on site, restored
- Unacknowledged tickets escalate to the supervisor, then the manager, on a schedule
- Members see live status in the app
Staff tools
- Ticket board across all area offices, with transfers
- Office tenders file for phone and walk-in callers
- Web Push alerts for new tickets in the staffer's area
- All-offices management view: open tickets, oldest unacknowledged, escalations, lineman workload
- Account lookup and user management
Security and privacy
- Account claim with an email code plus a detail from a real bill, with rate limits
- Unauthorized and missing records return the same 404, so accounts cannot be probed
- Role and office scoping re-checked on every admin page and server action
- A lineman sees a caller's number only on a job assigned to them
- Strict CSP, HSTS, database TLS pinned to its CA, bot checks on auth, WAF and rate limits
Architecture
Interface
- Next.js
Logic & APIs
- Sync Agent
Data
- Supabase
- MS SQL Server
- PostgreSQL
- Row-level security
Infrastructure & delivery
- Cloudflare Tunnel
- Server
- 1Next.jsSupabasePostgreSQLSQL Server
Billing system (SQL Server, office LAN) → on-prem Node sync agent → Supabase Postgres mirror with default-deny row-level security → Next.js 16 portal.
- 2CloudflareServer
The portal runs on the cooperative's own server behind a Cloudflare Tunnel; a poller deploys the main branch within minutes, health-checks it, and rolls back on failure.
- 3
If a sync fails, the portal keeps serving the last data with an "as of" time instead of going down.
- 4
Charts are hand-built SVG and maps are hand-rolled map tiles, with no chart or map library.
Engineering
- A test asserts the account guard on every exported data function.
- Moved to a transaction pooler after per-instance session pools ran out of connections under load.
- Replaced a 170 ms auth round-trip per request with local JWT verification.
- A stale-while-revalidate device cache paints the last known data before the first network byte on slow mobile data.
- 370+ test cases across 29 suites in Vitest.
Outcome
- Live in production for members across 17 towns and cities.
- Bill, payment, and usage questions that used to need a call or an office visit are answered in the app.
- Every outage report is a numbered job order with a timeline, so members, dispatchers, and management follow the same record.
- The portal reads from a mirror, so member traffic never reaches the billing database.











