- My role
- Designed, built and operate it.
- For
- LNS Corporation
- Status
- In service
An employer or client system. This room describes the architecture and my role; it contains no proprietary code, internal addresses, credentials or customer data.
Problem
The store's stock and accounts lived in ERPNext, the till and stock master. The old WooCommerce site had no reliable link to it, so online stock drifted from the shelf, overselling was a constant risk and web orders were re-keyed by hand. ERPNext had to stay the single source of truth.
What I built
I designed and built a headless commerce platform for Comfy Shoes, LNS Corporation’s comfort-footwear retailer with a physical store at Northmead Square, Benoni. The online store and the shop’s till share one stock and accounting system: a pair sold in-store disappears online within seconds, and every web order lands in ERPNext as proper accounting documents.
Stock and prices: ERPNext to Medusa (push and reconcile)
- A Frappe hook fires on every stock ledger entry and sends an HMAC-SHA256 signed webhook after the database commit.
- Medusa verifies the signature and a ±300 s timestamp window, deduplicates on event ID, drops stale events and takes a per-SKU lock before applying.
- Sellable stock is computed, not copied: on-hand stock, minus units on web fulfilments the ERP hasn’t seen yet, minus a one-unit safety buffer when stock runs low. This prevents overselling the last pair.
- A reconciliation job pages through every ERPNext bin every 15 minutes, repairs drift and alerts above 2%.
- Price webhooks are only triggers. Medusa re-reads the price from ERPNext, so nothing can sell at a stale price.
Orders, payments and returns: Medusa to ERPNext (transactional outbox)
- Every commerce event writes an outbox row with a unique idempotency key, so redelivered events never create duplicates.
- An order becomes a Sales Order, a fulfilment a Delivery Note, a captured payment a Sales Invoice and Payment Entry, a refund a credit note, and a return a return Delivery Note.
- A worker drains the outbox every minute. Retryable failures back off exponentially; anything else, or anything still failing after 10 attempts, is dead-lettered.
- Staff get an ERP sync admin page with health badges and one-click retry. Alerts fire on a backlog, any dead-lettered row, drift above 2%, or a missed reconciliation.
Deployed, backed up and monitored
- Production and staging run on separate Arm VMs on Oracle Cloud’s Always Free tier, so a staging deploy can never touch production. The running cost is R0 a month.
- Cloudflare handles DNS, TLS and edge caching. Oracle has no Johannesburg region, so caching covers the latency gap, and the origin accepts traffic only from Cloudflare.
- Nightly database dumps are copied off the VM to two providers, and boot volumes are backed up weekly. A restore was tested before launch and is repeated monthly.
- The accepted trade-off is that one VM is a single point of failure, with a runbook restore target under an hour. The till keeps selling regardless.
- Everything is containerised, so moving to Cloud Run with Cloud SQL, or to AWS, is about a day’s work when traffic justifies it.
- Quality gates: a Playwright checkout journey that confirms the Sales Order arrived in ERPNext, axe accessibility checks on 14 pages and Lighthouse CI budgets. The gates caught a real launch blocker: a legacy redirect that sent
/checkoutto/cart. - Monitoring: Sentry, uptime checks and the built-in sync health alerts.
The design is recorded in 18 architecture decision records, an integration specification, a runbook and a cutover checklist. The store is live on a temporary domain, and the cutover to comfyshoes.co.za is in progress. More in the case study, published with LNS Corporation’s permission.
Architecture
Stack
- Medusa 2
- Next.js
- ERPNext v16 (Frappe)
- PostgreSQL
- Redis
- Paystack
- Cloudflare
- Docker Compose · Caddy
- Sentry
- Playwright · Lighthouse CI
Links
Restoration log
No restorations recorded yet.

