Paperless Africa · Civic Tech / Fintech · Dec 2025
Replacing an app store with a QR code: a civic-payments PWA for Zambian councils
Independent contract for Paperless Africa · Built and complete; client deployment pending

Paperless Africa runs a digital-services platform for Zambian local government councils, built on an existing Yii/PHP backend. In December 2025 they brought me in as an independent contractor with a distribution problem: their native mobile app was the bottleneck between councils and the citizens trying to pay them. I authored the product requirements and built the replacement, a Progressive Web App that installs from a QR code, works offline, and serves every council from one codebase. The build is complete; deployment to the client’s councils is pending, so the figures below are design targets from the PRD and facts of the build, not production measurements.
The challenge
The native app created an operational bottleneck at the worst possible layer: distribution. App store approval delays of two to four weeks meant councils could not ship urgent updates or fixes. A broken payment flow stayed broken while a review queue cleared. For a platform whose whole purpose is letting citizens pay invoices, that was untenable.
The app had other problems. There was no direct channel for citizens to communicate with council staff. Payments ran through multiple fragmented channels rather than one interface. And it performed poorly on the hardware its users actually own: budget Android phones (Tecno, Infinix, entry-level Samsung) on 3G connections. An app that assumes a flagship phone on 4G fails the people a council platform exists to serve.
Each council also needed its own identity (branding, contacts, endpoints), which under the old model meant per-council build overhead.
The approach
I started with the product requirements, not the code. The PRD (December 2025) fixed the constraints up front: the existing Yii/PHP backend as the integration boundary, budget Android on 3G as the baseline device, and a compliance posture aligned with Bank of Zambia requirements and Zambia’s Data Protection Act 2021.
PWA instead of a native app. Citizens install the app by scanning a QR code: no store, no review queue. When a council needs a fix shipped, it deploys instantly. Caching is deliberate: cache-first for the app shell so the app opens immediately, network-first with an offline fallback for API data so citizens see fresh balances when they have signal and cached ones when they don’t.
A hard budget for 3G. The PRD sets a sub-60KB gzipped initial bundle target. That number drove the stack: Vite with Bun, React 18 with TypeScript, Tailwind and shadcn/ui for UI, Zustand for state, TanStack Query for server data, and idb-keyval over IndexedDB for offline storage. Every dependency had to justify its bytes.
Multi-tenant by subdomain. One codebase serves all councils. Routing by council subdomain resolves runtime configuration for branding, API endpoints, and contact details, so onboarding a new council is configuration, not a build. Nothing council-specific is compiled in.
One payment interface, three networks. Mobile-money integration covers Airtel, MTN, and Zamtel, so a citizen pays from whichever wallet they have. The flow (open an invoice, pay, get a receipt) is designed to complete in under 60 seconds. Citizens can also check balances, download receipts and permits, and reach council staff through two-way chat and announcements, which turns a payment tool into an actual channel between councils and residents.
Offline-first throughout. Cached balances, transaction history, and downloaded documents stay available without connectivity, and changes sync automatically when the connection returns.
The outcome
The PWA is built and complete. Client deployment to the councils is pending, and I’d rather say that plainly than imply production numbers I don’t have. What the finished build delivers:
- No app store in the loop. The two-to-four-week approval delay is gone by construction. Updates reach citizens the moment a council deploys them.
- One deployment, every council. Multi-tenant subdomain routing with runtime configuration means new councils onboard without a new build.
- Universal payment access. Integration across all three Zambian mobile-money networks, behind one interface, with the sub-60-second payment flow as the design target.
- Built for the real network. The sub-60KB bundle target and offline-first architecture mean the app is designed to work on the budget phones and patchy connections its users actually have, not the ones a spec sheet assumes.
The payment-flow and bundle-size figures are PRD targets; the three-network integration and the multi-tenant architecture are facts of the delivered build. When the client deploys, the targets become measurable. That is the point of writing them down first.