9

WiraPay

The live payments service for the ike.work products—card and M-Pesa checkout, one-off or recurring, hosted or by API. Built on Paystack with signed webhooks and a dependency-injected, 165-test core; processing real subscriptions and one-off sales since July 2026.

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 via verify—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 thin src/server wiring 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.