WiraPay: Payments for the ike.work products
Card & M-Pesa checkout, one-off or recurring—hosted or by API
Project Overview
WiraPay is the shared payment rail behind my other products. It wraps Paystack (Kenya) into a small, well-tested service that any of my apps can call to charge card or M-Pesa, as a one-off payment or a subscription, through either a hosted checkout or a machine-to-machine API. Roamgap and the Mara Simulator are its first live clients — one on each rail.
The design goal was a payment service I could trust and reuse: domain logic that's dependency-injected and unit-tested, thin route handlers, and reconcile-from-source as the safety net, so a dropped webhook never means a wrong balance.
Status
Live at wirapay.ike.work since 13 July 2026. Paystack approved the business and the go-live checklist ran the same day—zero code changes, purely live keys and operator config—with a real subscription processed and verified end-to-end within hours. Both rails are now proven in production: Roamgap's paid tier sells subscriptions through it, and the Mara Simulator's in-game store sells one-off purchases (the service's first live one-off invoice settled the day that store launched). Print sales are the next client to switch on.
What It Does
- Hosted checkout. A public
/pay/{endpoint}page—one-off or subscription, with recurrence disclosure—plus an in-page M-Pesa STK push that charges on the page with no redirect. - Machine-to-machine API. Bearer-key endpoints to create and verify invoices and subscriptions, so a client app never touches card data.
- Subscriptions, dual-rail. The payer picks the channel: card becomes an auto-renewing Paystack subscription; M-Pesa becomes a "renewing pass" where WiraPay owns the period clock and reminds before it lapses.
- Signed outbound webhooks. WiraPay POSTs signed
invoice.*/subscription.*events to a client's URL within seconds of settlement; clients verify the signature, dedupe, then reconcile viaverify—the webhook is a nudge, never trusted state. - One-off consumables. The same rail sells metered units (credit packs, token bundles), granted idempotently on the settlement webhook.
Technology Stack
- App: Next.js (App Router) on Firebase App Hosting (Cloud Run).
- Data: Supabase (PostgreSQL) with deny-by-default row-level security; the server uses a service-role key.
- Payments: Paystack (Kenya)—card + M-Pesa; HMAC-SHA512 inbound signatures, HMAC-SHA256 outbound.
- Jobs: Cloud Scheduler crons for outbound webhook delivery, subscription reconcile / reminders, and scheduled price migration.
- Architecture: a pure, unit-tested
src/lib+src/core(DI services) with a thinsrc/serverwiring layer—165 Vitest tests, plus a contract → API → webhook → component → browser-E2E test plan.
Skills Demonstrated
- Payments engineering—one-off and recurring billing across two channels (card + M-Pesa), with the correctness concerns (idempotency, signature verification, reconcile-from-source) treated as first-class.
- Service design for reuse—a dependency-injected core with in-memory fakes, thin handlers, and a provisioning CLI, so new client apps onboard against a stable contract.
- Production infrastructure—Firebase App Hosting / Cloud Run, Secret Manager, scheduled jobs, and a documented deploy + go-live checklist.
- System-of-systems thinking—a single payment rail that several of my products (Roamgap, Mara, and a coming print store) depend on.
WiraPay is live at wirapay.ike.work, charging card + M-Pesa for the ike.work products—subscriptions (Roamgap) and one-off purchases (Mara Simulator) both running in production.