01  /  Web & Product

ElCharis Pharmacy

A regulated pharmacy commerce platform with WhatsApp-first ordering, HMO claim processing, and same-day delivery — split across a marketing site and a transactional Care app.

Client
ElCharis Pharmacy · Uyo, Akwa Ibom
Role
Solo builder — design, AI-orchestrated implementation, testing, deployment
Timeline
2025 — present
Status
Live in production
Live
elcharispharmacy.com  ·  care.elcharispharmacy.com
Stack
Next.js · Supabase · Paystack · WhatsApp Cloud API · Vercel
Architecture
Marketing + transactional split, async-queued messaging.
Scope
Two apps · WhatsApp intake · prescription imaging · HMO claims · payments · dispatch
Compliance
PCN-registered pharmacy workflow · NDPR-compliant data handling

The problem

Pharmacy retail in Nigeria runs on WhatsApp, paper, and goodwill. ElCharis was no exception. Orders came in over WhatsApp, were typed into a desktop POS, paid for at a counter or by transfer, dispensed by a pharmacist, then delivered or collected — and the only system that knew all four steps had happened was the pharmacist's memory. HMO claims, the highest-value channel, lived in a separate ledger and got reconciled monthly, sometimes quarterly, against statements from seven different insurers.

The pharmacy wanted to grow online without growing the chaos. The constraint was real: any system that asked customers to learn a new ordering surface would lose them. The system had to meet them on WhatsApp, where they already were, and quietly hold the operations together behind that.

The approach

Three principles drove every other decision.

1. Meet customers where they are. The ordering surface stays on WhatsApp. The Care app is what staff use; customers see only the conversation. This is not a chat widget — it is a real intake channel routed through the WhatsApp Cloud API, with messages queued, threaded, and tied to a customer record before the pharmacist sees them.

2. Compliance is a feature, not paperwork. PCN-registered pharmacy workflow and NDPR-compliant data handling are baked into the schema, the storage layout, and the retention policy — not bolted on at audit time. Prescription images are handled with the assumption that a regulator will eventually ask to see the chain of custody.

3. Split surface area. The public marketing site and the transactional Care app are two deployments with different risk profiles, different deploy cadences, and different blast radii. A bad copy push on the marketing site cannot take down Care intake; an outage in Care cannot take down the front door.

Two apps, one customer experience. WhatsApp is the surface; the Care app is the spine. Every message becomes a row before it becomes a task.

What I built

  • Two deployed appselcharispharmacy.com for marketing, care.elcharispharmacy.com for the transactional Care platform.
  • WhatsApp intake— orders, prescription images, and HMO authorizations come in as messages, parsed into structured intents through the WhatsApp Cloud API, queued, and surfaced to the pharmacist as a workable inbox.
  • Prescription image pipeline— images stored on Cloudflare R2, tagged to a customer record, surfaced to the pharmacist with redaction controls and a chain-of-custody trail.
  • HMO claim processing— integration across seven insurers (Hygeia, AXA Mansard, Reliance, Leadway, Avon, Clearline, Total Health Trust); claim raise, status tracking, and a weekly reconciliation surface.
  • Payments— Paystack-backed card, bank transfer, and HMO co-pay. One checkout, three local payment methods, zero customer-side wallet.
  • Same-day dispatch— order → fulfilment → handover modelled as a tracked sequence, not three loose tasks.

Technical decisions worth calling out

Neon over Supabase. I needed serverless Postgres with schema branching for safe migrations against a live operations table. Schema-branch-per-PR is how I keep payment and HMO migrations from cratering an active counter. Supabase would have added more product than the project needed and bundled an auth layer I deliberately wanted to roll myself.

Cloudflare R2 over S3. Prescription images get re-read during pharmacist review and again during HMO reconciliation. R2's no-egress economics make that read pattern affordable. Everything is S3-compatible, so leaving is cheap if I ever need to.

Upstash for Redis and QStash, from one provider. One billing surface, one auth boundary, one mental model. QStash handles the async WhatsApp message processing and the nightly HMO reconciliation jobs; Redis handles short-lived state and rate limits. Splitting these across two vendors would have bought nothing.

JWT auth, deliberately simple. User-rolled JWT with rotation. No NextAuth, no Clerk. The pharmacy has a small, known staff list; the defences here are the simplicity of the surface and a careful boundary between authenticated and anonymous routes, not a framework's complexity. This is a conscious tradeoff I would revisit if the staff count crossed thirty or if a customer-facing login were ever introduced.

Paystack over Stripe. Customers pay in Naira, by card, bank transfer, or HMO co-pay. Paystack is the local standard; Stripe would have added friction at the most fragile moment in the funnel.

Hard problem worth mentioning

HMO reconciliation. The pharmacy is paneled with seven insurers. Each sends a different monthly statement format. Claims raised in Care need to match line items in those statements, allowing for partial pays, denied lines, code substitutions, and timing skew across months. The old process tolerated drift; an automated system cannot.

The fix was not a heuristic. It was modelling each claim as a normalized line item at write-time — drug, quantity, code, HMO, member, plan tier — and writing a small reconciliation engine that diffs against the insurer's statement format, surfaces unmatched lines as exceptions for the pharmacist, and never auto-closes anything financial. Reconciliation went from a quarterly all-hands ordeal to a Monday morning that takes under an hour.

Outcome

Live across two domains. WhatsApp intake is the dominant order channel. HMO reconciliation runs weekly instead of quarterly, and the pharmacist's clinical hours are no longer being absorbed by ledger work. The platform has been stable in production through several Lagos-style power and connectivity events without losing intake or double-dispatching an order.

The pharmacy continues to operate on the same two surfaces it launched with. New work is concentrated on tightening the HMO surface and the dispatch view.

What I'd do differently

Admin-first, not customer-first. I built the customer-facing surface ahead of the operations surface because the demo was a customer demo, and I paid for that decision for weeks afterwards — every time the pharmacist needed to do something the system didn't quite let her do, I was building it under live load.

The ordering surface is the thing customers see. The operations surface is the thing that decides whether the pharmacy survives the rollout. Next time, the admin view ships first and the customer view is built on top of an interface the operators already trust.