# ProcureGlobal — Product Requirements Document

**Version:** 3.8
**Status:** Locked — switches the monorepo package manager from pnpm to **npm workspaces**, removing the Corepack/version-juggling DX cost in CI. Trade-off accepted: slightly slower CI installs and larger node_modules vs pnpm's content-addressable store. Locked Node target stays at 22 LTS (or 24, both supported by npm workspaces).
**Author:** Architecture review (Accenture Song lens)
**Last updated:** 16 May 2026
**Supersedes:** v3.7

---

## 1. Document control

| Item | Detail |
|---|---|
| Product name | ProcureGlobal |
| Product type | B2B + B2C e-commerce platform with integrated lightweight CRM |
| Primary market | Ghana (GHS settlement), with cross-border buyers transacting in USD presentment |
| Owner | Ekow Thompson (founder) |
| Source repository | [github.com/ekowtee/ProcureGlobal](https://github.com/ekowtee/ProcureGlobal) |
| Review cadence | Weekly until v1.0 ships; bi-weekly thereafter |

### 1.1 Decisions locked in this revision

1. Architecture path: **API-first, two-deployable** — custom **Express.js (TypeScript) API** as the single business-logic backend, with a **Next.js storefront on Vercel** and a **native/cross-platform mobile app** as two independent clients. No Medusa.
2. Payment processors: **Paystack primary, IT Consortium secondary** (Flutterwave dropped).
3. Settlement currency: **GHS only**. USD presentment supported via Paystack FX.
4. Returns policy: **Minimal manual process** at launch; no RMA workflow.
5. Shipping rates: **Manual rate cards** at launch; no carrier API.
6. Brand and design system: **Deferred workstream**, must complete before storefront engineering begins.
7. Settlement bank: **Stanbic Bank Ghana** for the GHS settlement account.
8. Design ownership: **Claude Opus 4.7** owns the brand and design system workstream, working from founder-supplied briefs.
9. Brand identity: **Locked v1.0.** Logo (founder-supplied wave-and-arrow mark), palette anchored on Midnight `#1C1A47`, Aqua `#60C9D9`, Lavender `#B18FDF` with Coral `#F472B6` as urgency accent. Typography: Bricolage Grotesque (display) + DM Sans (body). See `brand-system-v1.html` for the full specification.
10. Auth provider: **Better Auth** (self-hosted TypeScript library running as Next.js route handlers on Vercel, sessions in Neon Postgres). Clerk dropped.
11. Launch courier partners: **Speedaf, Bolt courier, DHL Express.** Initial rate cards published against these three.
12. Data Protection Commission registration: **in progress.** Treated as a parallel workstream; not a blocker on the build timeline.
13. IT Consortium settlement: **confirmed on Stanbic Bank Ghana via TransFlow.** No clearing hop; same-day settlement from ITC to the GHS account.
14. API layer: **Express.js (TypeScript)** as a standalone deployable, separate from the Next.js storefront. All business logic — auth, catalogue, cart, orders, payments, B2B, tax, FX, CRM — lives in Express. Storefront and mobile app are both clients of the same REST API. Webhook receivers (Paystack, ITC, Resend) live on Express.
15. API host: **DigitalOcean App Platform** (Frankfurt region) via the **Node.js buildpack** (no Dockerfile in the repo). App Platform auto-detects via `package.json`, runs `npm install && npm run build && npm start`. Same DX as Railway/Render and Vercel. Managed PaaS: git-push-to-deploy, auto-SSL, autoscaling, built-in logs and metrics. Option to integrate DO Managed Postgres or DO Spaces later if we choose to consolidate within DO. Dockerfile path is available as a fallback for future needs (system deps for PDF rendering, custom binaries) but not used at MVP.

16. Package manager: **npm workspaces** (not pnpm). Picked after a brief pnpm trial surfaced Corepack/Node-version compatibility friction on Vercel CI. npm ships with Node, supports workspaces natively from v7, and removes one moving part from the build pipeline. Trade-off accepted: slower CI installs (a few seconds at MVP scale, more as the repo grows) and a flat `node_modules` layout that allows phantom imports — engineering discipline expected to compensate.
16. Mobile app: **In scope.** Stack: **React Native + Expo** (iOS and Android, single codebase). Auth via Better Auth bearer-token flow; payments via the Paystack mobile SDK with MoMo deep links. Push notifications via Expo Notifications, with Pusher Beams as the cross-platform fan-out layer. Architecture is API-first from day one. **Launch timing: mobile MVP ships approximately three months after web v1.0**, with mobile design and engineering overlapping Phase 3 (web hardening).

---

## 2. Executive summary

ProcureGlobal is a unified storefront and operations console for selling physical goods to both retail consumers (B2C) and business buyers (B2B). The platform combines a public catalogue with self-service checkout, a quote-to-order workflow for B2B accounts, and an internal console for inventory, fulfilment, and customer relationship management.

The platform is built API-first as two deployables: a **TypeScript Express.js API** that owns all business logic (catalogue, cart, orders, inventory, tax, FX, payments, refunds, B2B quoting, CRM, admin, webhooks) and a **Next.js (App Router) storefront on Vercel** that consumes that API. A **mobile app** is a second client of the same API, shipping after the web MVP. External managed services provide the supporting infrastructure: Neon for Postgres, Upstash for Redis, Inngest for durable background jobs, Cloudflare R2 for object storage, Resend for transactional email, Pusher Channels for real-time notifications, and Better Auth (self-hosted on the Express API) for authentication.

The trade-off accepted by this path is timeline: custom build to web MVP is estimated at 7–9 months, compared to 4–5 months on a headless commerce engine. In exchange the team owns every line of the commerce stack, has no third-party platform dependency, and gets a clean API surface that a mobile client can consume from day one.

---

## 3. Vision and objectives

### 3.1 Vision

To be the procurement platform of choice for Ghanaian SMEs and the diaspora, offering retail convenience and B2B-grade pricing, terms, and account management in one experience.

### 3.2 Strategic objectives (12 months post-launch)

1. Reach **GHS 1M GMV** within 6 months of launch.
2. Onboard **50 B2B accounts** within 9 months, with at least 30% transacting on credit terms.
3. Maintain **storefront p75 LCP ≤ 2.5s** on 3G Ghana mobile profiles.
4. Achieve **<1% payment failure rate** on Mobile Money and card.

### 3.3 Non-goals for v1.0

- Marketplace functionality (third-party sellers).
- Subscription commerce.
- Full marketing-automation CRM (campaigns, lead scoring, attribution).
- Multi-warehouse inventory (single fulfilment location at launch).
- Automated returns/RMA workflow.
- Real-time carrier rate APIs.

---

## 4. Personas

| Persona | Description | Primary needs |
|---|---|---|
| Ama, retail shopper | Accra-based consumer browsing on mobile | Fast catalogue, MoMo checkout, delivery tracking |
| Kwame, SME buyer | Procurement officer at a 20-person business | Request quotes, buy on credit, manage multiple sub-users |
| Akua, diaspora buyer | Ghanaian abroad buying for family at home | Pay in USD presentment, ship to a Ghana address, track delivery |
| Yaw, ProcureGlobal staff | Operations / sales | Manage orders, approve quotes, see customer 360 |
| Adwoa, finance | Internal finance | GHS reconciliation, refunds, tax reporting |

---

## 5. Scope

### 5.1 In scope for v1.0 (MVP)

Storefront, customer accounts, catalogue with search and filters, cart and guest checkout, payments (Paystack with MoMo + card + bank, IT Consortium as fallback), multi-currency display (GHS, USD presentment) with GHS settlement, basic B2B quoting (request → admin approval → checkout), order management console, customer 360 view, transactional email, real-time notifications, inventory tracking with stock reservations, Ghana VAT/NHIL/GETFund/COVID-levy tax engine, role-based admin access, minimal returns policy with manual ops handling.

### 5.2 Out of scope for v1.0

Marketplace, multi-warehouse, subscriptions, loyalty programmes, automated RMA workflow, advanced segmentation and marketing automation, full accounting integration (export CSVs at launch), real-time carrier rate APIs, buyer-side approval chains (phased to v1.1), credit terms (phased to v1.1).

---

## 6. Functional requirements

### 6.1 Storefront (B2C)

- Product listing pages with category, brand, price, availability, and attribute filters.
- Product detail page with images (R2-hosted, served via Cloudflare Images for transforms), variants, stock indicator, related products.
- Search: Postgres full-text search for v1.0; Meilisearch when catalogue exceeds 5,000 SKUs.
- Currency switcher (GHS / USD presentment). Default by IP geolocation; user override persists per session.
- Guest checkout supported.
- Pages rendered via React Server Components with ISR on catalogue and PDP routes. Server components fetch from the Express API at build and revalidate time; client components call the same API via the Better Auth session cookie or — for the mobile app — a bearer token.

### 6.2 Customer accounts

- Email + password with verification.
- Optional social login (Google) post-MVP.
- Profile, addresses, saved payment methods (tokenised by Paystack/ITC — never store PANs).
- Order history with reorder action.
- **Auth provider: Better Auth (locked).** Self-hosted TypeScript library mounted on the Express API as middleware; user and session storage in Neon Postgres, with optional Upstash Redis session caching. Argon2id password hashing, email verification, password reset, MFA, and the Organization plugin for B2B accounts and sub-user roles. Issues **session cookies** (HttpOnly, Secure, SameSite=Lax) for the Next.js web storefront and **bearer tokens** for the mobile app — both backed by the same session store, so logging out of one channel can revoke the other when needed. Trade-off vs Clerk: ~2–3 weeks of additional engineering to bring auth flows and admin user management to feature parity, in exchange for $0 recurring SaaS cost and one fewer vendor in the critical path.

### 6.3 B2B accounts and quoting

- **Account model:** parent organisation can have N sub-users with role permissions (Owner, Buyer, Approver, Viewer).
- **Quote lifecycle:** `draft → submitted → under_review → revised → approved → expired | converted_to_order | declined`. Every state transition is audited.
- **Pricing controls (admin side):** line-level discount, customer-specific price lists, freight overrides, notes.
- **Quote expiry:** default 14 days, configurable per quote.
- **Buyer-side approval (v1.1):** sub-user submits, designated approver in the buyer org approves before it reaches ProcureGlobal staff.
- **Credit terms (v1.1):** Net 7/14/30/60. Credit limit per account. Outstanding balance visible.
- **Purchase orders (v1.1):** buyer can attach a PO reference and PDF to a quote/order.

### 6.4 Cart and checkout

- Cart persists for authenticated users; cookie-scoped for guests.
- Inventory reserved for 15 minutes when checkout begins; released on abandonment via Inngest scheduled cleanup.
- Address validation against a known Ghana region/district list. GhanaPost GPS digital address field supported.
- Shipping options: standard courier, express courier, store pickup. Rates from manual rate cards in v1.0; see §6.10.
- Tax computed server-side based on shipping destination and product taxability flags.

### 6.5 Payments

**Primary processor:** Paystack. Cards, Mobile Money (MTN MoMo, Telecel Cash, AirtelTigo Money), bank transfer.

**Secondary processor:** IT Consortium. TransFlow Checkout for cards and bank, UniWallet API for mobile money aggregation. Acts as failover when Paystack is degraded, and as the institutional rail of choice for high-value B2B settlements with bank partners already on TransFlow.

**USD acceptance:** Paystack USD payment channel for international cardholders. Settlement converts to GHS at Paystack's FX rate.

**Processor abstraction:** the codebase defines a single `PaymentProvider` interface with methods `initiate`, `verify`, `refund`, `webhook`. Paystack and ITC are two implementations. Routing rules (primary vs secondary, B2C vs B2B, GHS vs USD) live in a configuration table editable in admin without code deploys.

**Webhooks:** signed and verified per provider. Handlers are idempotent — repeated webhook deliveries must converge on the same state. Failed webhooks queued via Inngest with exponential backoff.

**PCI posture:** SAQ-A. All card capture via Paystack-hosted inline/popup or ITC-hosted checkout. Raw PANs never touch our infrastructure.

**Operational note:** ITC integration is enterprise-onboarded, not self-serve. Engage their sales team in **Phase 0 week 1** to start KYC and contracting; expect 3–6 weeks to live credentials.

### 6.6 Multi-currency and FX

- All monetary values stored as **integer minor units** (`pesewas` for GHS, `cents` for USD). No floats anywhere in the money path.
- **Pricing currency:** GHS is the canonical base. USD prices are computed dynamically from GHS via a daily-refreshed FX rate plus a configurable margin (default +2%).
- **Presentment currency:** the currency shown to the buyer and used to charge their payment method. Either GHS or USD.
- **Settlement currency:** **always GHS.** Paystack converts USD presentment to GHS at their rate before settling to our GHS bank account.
- Every `Order`, `Quote`, and `Refund` persists `presentment_currency`, `presentment_amount_minor`, `settled_amount_ghs_minor`, `gateway_fx_rate`, and `our_displayed_fx_rate_at_quote`.
- Refunds: when refunding a USD-presentment order, the customer is refunded in USD at Paystack's prevailing FX rate. Any FX delta between original capture and refund is a P&L line item, not a customer-facing issue.
- FX rate source: scheduled Inngest job pulls from a reliable feed (Open Exchange Rates, exchangerate.host, or Bank of Ghana published rate) into an `fx_rates` table with full history. Manual override available in admin.

### 6.7 Order management (staff console)

- Order list with filters (status, channel, value, customer, date, payment method).
- Order detail with timeline, payment status, fulfilment status, internal notes.
- Manual actions: capture/refund payment, mark shipped, cancel, edit address pre-shipment, generate invoice PDF.
- Inventory adjustments are journalled — every change has a reason code and actor.
- All admin actions audit-logged.

### 6.8 Lightweight CRM (customer 360)

- Unified customer record showing orders, quotes, support notes, last activity, lifetime value in GHS.
- Staff can log activities: call, email, meeting, note.
- Tags / segments (manual only in v1.0).
- **Explicitly not in scope:** email campaigns, lead scoring, attribution. Integrate HubSpot Free or Zoho if marketing CRM is needed later.

### 6.9 Notifications

| Channel | Provider | Use cases |
|---|---|---|
| Transactional email | Resend (preferred) or Postmark, with Nodemailer SMTP as fallback abstraction | Order confirmation, shipment, password reset, quote approved, invoice, refund issued |
| Real-time (in-app, web + mobile) | Pusher Channels (web JS SDK + `pusher-js` inside React Native) | Staff: new order, new quote request, low stock. Buyer: quote status change, order status change |
| Push notifications (native) | Expo Notifications (APNs + FCM) with Pusher Beams as cross-platform fan-out layer | OS-level push for delivery status, quote approved, abandoned cart. Server sends via Expo Push API and Pusher Beams from Inngest jobs |
| SMS (v1.1) | Hubtel SMS or mNotify | Delivery OTP, dispatch alerts |

Email templates versioned and previewable in admin. All sends logged with delivery status from the provider webhook.

### 6.10 Shipping and fulfilment

- **Rate model v1.0:** manual rate cards keyed on `(origin, destination_region, weight_band, service_level)`. Stored in a `shipping_rates` table editable from admin.
- **Launch courier partners (locked):**
  - **Speedaf** — pan-African logistics, strong in Ghana e-commerce; primary for `standard` and `express` domestic and selected cross-border lanes.
  - **Bolt courier** — `same_day` in Accra and Kumasi metros via the Bolt rider network.
  - **DHL Express** — premium domestic and all international destinations.
- Service levels: `same_day` (metro only), `standard` (1–3 day), `express` (next day), `store_pickup` (free).
- Fulfilment status tracked manually by ops in v1.0: `pending → packed → handed_to_courier → in_transit → delivered | failed_delivery`.
- Tracking numbers entered manually; v1.1 will integrate courier APIs where available.

### 6.11 Returns policy (v1.0, minimal)

**Customer-facing policy (draft — to be reviewed by Ghanaian counsel):**

- 7 calendar days from delivery to request a return on unopened, undamaged items in original packaging.
- Customer pays return shipping unless the item was defective or incorrectly shipped.
- Refunds issued to the original payment method within 10 business days of receiving the returned item.
- No returns on: perishables, opened consumables, custom or made-to-order items, items marked "final sale".
- B2B orders: returns subject to terms in the approved quote.

**Operational handling (manual at launch):**

- Customer emails support to request a return.
- Ops creates a "Return" record against the order in admin (manual state, not a full RMA workflow).
- On receipt of returned goods, ops marks the order `returned`, triggers a refund via the appropriate processor's admin tool, and updates inventory.
- No customer-facing return portal in v1.0.

### 6.12 Admin and RBAC

- Roles: Super Admin, Operations, Sales, Finance, Read-Only.
- All admin actions audit-logged with actor, IP, before/after diff.
- 2FA required for Super Admin and Finance.

---

## 7. Non-functional requirements

### 7.1 Performance

- Storefront p75 LCP ≤ 2.5s on mid-tier Android over 3G (tested via WebPageTest Accra node).
- Native mobile cold-start to first interactive screen ≤ 2.5s on mid-tier Android (Snapdragon 6-series) over 3G.
- API p95 latency ≤ 250ms for read endpoints, ≤ 700ms for checkout submission. Targets are tighter than the v3.2 figures because the Express API now runs as a long-running Node process on DO App Platform with warm connection pools to Neon and Upstash, not as a cold-start-prone serverless function.
- Image delivery via Cloudflare CDN with AVIF/WebP negotiation.
- Aggressive use of ISR for catalogue and PDP routes; on-demand revalidation when products change.
- Choose a Neon Postgres region in EU-West (Frankfurt area) to keep round-trip between Neon and the Express API on DO App Platform Frankfurt sub-5ms intra-region.
- Mobile clients use HTTP/2 keep-alive against the Express API and aggressive client-side caching (image cache via `expo-image`, product list cache with stale-while-revalidate).

### 7.2 Availability

- 99.5% target in v1.0; 99.9% target in v1.1 with Postgres read replicas.
- Vercel and Upstash both publish SLAs; rely on Neon's standard plan SLA at minimum.
- Status page (e.g. Instatus) public from launch.

### 7.3 Security

- TLS 1.3 everywhere, HSTS preload.
- Argon2id for password hashing via Better Auth (locked). Email verification, password reset, optional MFA via TOTP all handled inside the Express API.
- Rate limiting on all auth, checkout, and webhook endpoints — Upstash Ratelimit recommended.
- OWASP ASVS Level 2 as baseline.
- Secrets in Vercel Environment Variables (web) + DigitalOcean App Platform env management (API) + EAS Secrets (mobile build-time), with Doppler for cross-environment rotation across all three.
- Quarterly dependency audit; Dependabot enabled.
- Webhook signatures verified on every inbound request (Paystack, ITC, Resend, Pusher).
- **Mobile API authentication:** every native client request carries a short-lived bearer token issued by Better Auth's JWT plugin. Verification on Express via Better Auth middleware. Refresh tokens rotated on use, stored in platform secure storage (Keychain on iOS, Keystore on Android) via `expo-secure-store`. Revoked on logout, password change, or anomalous activity.
- **Mobile certificate pinning** for iOS and Android against the Express API hostname (TLS 1.3 leaf + backup cert). Disabled in debug builds; configured via Expo's app config plugins.
- **App-level secrets:** Paystack public keys and Pusher app keys are public-key-safe; private keys stay server-side. Native code must never embed Paystack secret keys or ITC merchant secrets.

### 7.4 Compliance

- **Ghana Data Protection Act, 2012 (Act 843):** registration with the Data Protection Commission; documented lawful basis for each processing activity; subject access request workflow.
- **PCI DSS:** SAQ-A scope (no PAN handling).
- **Consumer protection:** clear pricing inclusive of all taxes, returns policy (per §6.11), T&Cs reviewed by Ghanaian counsel.
- **Cookie consent:** banner with granular toggles (necessary, analytics, marketing).

### 7.5 Accessibility

- WCAG 2.1 AA across the storefront. Automated checks in CI (axe-core), manual audit before each major release.

### 7.6 Observability

- Structured logs (pino) shipped to Better Stack or Axiom (Vercel-native integration).
- Error tracking via Sentry on Next.js (server, client, edge) and Inngest function runs.
- Metrics: request rate, latency, error rate, queue depth (Inngest), payment success rate per provider, FX refresh status.
- Synthetic uptime checks on storefront, checkout, and webhook endpoints (Better Stack or Checkly).

---

## 8. Architecture (Plan A — locked)

### 8.1 Component overview

| Layer | Choice | Rationale |
|---|---|---|
| Storefront + Admin UI | **Next.js 15 App Router** on **Vercel** | RSC for fast PDP/PLP, ISR for catalogue, edge caching, image optimisation; talks to the Express API for all data |
| Mobile app | **React Native + Expo** (locked) | Single TypeScript codebase for iOS and Android; same engineers move between web and mobile; consumes the Express API via bearer tokens. Expo Notifications + Pusher Beams for push, Paystack mobile SDK + MoMo deep links for payments |
| API | **Express.js (TypeScript)** as a standalone deployable | Single business-logic backend serving both the web storefront (server-to-server fetches) and the mobile app (bearer-token REST). All commerce primitives, payment webhooks, and admin endpoints live here |
| API hosting | **DigitalOcean App Platform** (Frankfurt) · **Node.js buildpack** (no Dockerfile) | Managed PaaS: git-push-to-deploy, auto-SSL, autoscaling, near-zero ops time. Buildpack auto-runs `npm install && npm run build && npm start` from `apps/api/`. Cheaper than Railway at equivalent production specs; opens the door to DO Managed Postgres / DO Spaces later. Dockerfile available as future fallback if system deps emerge. |
| Background jobs | **Inngest** (or Trigger.dev) | Express jobs that need durability, retries, or scheduling are offloaded to Inngest rather than run as in-process cron — keeps the API host stateless and horizontally scalable |
| Database | **Neon Postgres** (EU-West region) | Serverless Postgres with branching, scales to zero, low cold-start, good Vercel latency from Europe |
| Cache / Rate limit / Reservations TTL | **Upstash Redis** | Serverless, pay-per-request, no idle cost |
| Object storage | **Cloudflare R2** | Already chosen; S3-compatible; zero egress fees |
| Image transforms | **Cloudflare Images** or Next/Image with R2 loader | AVIF/WebP, on-the-fly resizing |
| Auth | **Better Auth** (self-hosted on Express) | Mounted as Express middleware, sessions in Neon, optional Redis cache; issues cookies for web and bearer tokens for mobile; Organization plugin for B2B orgs and sub-users; no auth vendor |
| Email | **Resend** | Excellent DX, native React Email templates, transactional only |
| Real-time | **Pusher Channels** | Already chosen; simple pub/sub for staff alerts and buyer status updates |
| Search | Postgres FTS v1.0 → **Meilisearch** v1.1 | Defer until catalogue scale demands it |
| Payments | **Paystack** primary, **IT Consortium** secondary | Per §6.5 |
| Observability | **Sentry** + **Axiom** (or Better Stack) | First-class Vercel integrations |
| Secrets | Vercel env vars + **Doppler** for rotation | |

### 8.2 Why this shape

Two deployables, one repo (monorepo with pnpm workspaces: `apps/web`, `apps/api`, `apps/mobile`, plus shared `packages/` for types, validation schemas, and the API client SDK). The Express API is the single source of truth for business logic; the Next.js storefront and the mobile app are two equal clients of that API. The cost of running a second service is real (more CI, two health surfaces, an extra host bill), and it is paid back the moment the mobile client exists — there is no second API to build, no parallel implementation to keep in sync, no business logic duplicated across platforms. Vercel remains best-in-class for Next.js storefront performance, and offloading every stateful concern to managed services (Neon, Upstash, Inngest, R2) means neither deployable has a server to SSH into.

### 8.3 Accepted trade-offs

- **Build cost.** Cart, order state machine, inventory reservations, refund flows, tax engine, admin UI — none of these come for free. Approximately 3–4 months of engineering above the Medusa-based path. The Express + Better Auth + mobile-ready API surface adds a further ~3–4 weeks vs an all-Next.js single deployable. Acknowledged.
- **Two-deployable overhead.** Two CI pipelines, two health surfaces, an extra host bill, and shared-types discipline (zod schemas in a shared package, generated SDK consumed by web and mobile) needed from day one to avoid drift. Without this discipline the architecture becomes a footgun.
- **Webhook and long-job constraints.** Express on Render/Fly can hold connections longer than a Vercel function, but durable retries and scheduled work still belong on Inngest. Webhook handlers must remain fast and idempotent.
- **Cost at scale.** Vercel Pro starts at $20/seat/month and DO App Platform adds another $12–50/mo at MVP scale (autoscaling beyond that). Expect $150–500/mo on infrastructure once traffic ramps; budget accordingly.
- **Vendor lock-in surface area.** Inngest, Vercel, DigitalOcean App Platform, Neon, Upstash, Resend — six managed dependencies. Each is replaceable, but six replacements is six migration projects. Mitigate by writing adapter interfaces around each (especially queue and email) from day one. (Express on App Platform is a portable Node service — replacing it with Render, Fly, or a self-managed droplet is a redeploy, not a rewrite.)

### 8.4 Estimated monthly infrastructure cost (MVP scale, USD)

| Service | Cost (USD) |
|---|---|
| Vercel Pro (storefront) | $20–60 (1–3 seats) |
| Vercel usage (bandwidth, functions, image opt) | $30–150 |
| Express API host (DigitalOcean App Platform, Frankfurt) | $12–50 (Professional to Pro tier at MVP; autoscale beyond) |
| Neon Postgres | $0–25 (free tier viable at MVP) |
| Upstash Redis | $0–20 (pay-per-request) |
| Inngest | $0–20 (free tier covers MVP traffic) |
| Cloudflare R2 + Images | $5–20 |
| Better Auth | $0 (self-hosted; storage cost rolls into Neon) |
| Resend | $0–20 (free tier 3k emails/mo) |
| Pusher Channels | $0–49 |
| Sentry | $0–26 |
| Doppler | $0–8 |
| **Total** | **~$67–475/month**, depending on traffic |

Paystack and IT Consortium take percentage fees per transaction (no fixed monthly).

---

## 9. Data model (high-level)

- `customers` (1) — (N) `customer_addresses`
- `accounts` (B2B) (1) — (N) `customer_memberships` — (1) `customers`, with `role` per membership
- `products` (1) — (N) `product_variants` (1) — (N) `prices` (currency-scoped; GHS base)
- `inventory_items` (1) — (N) `inventory_levels` (per location; single location v1.0) — (N) `inventory_reservations` (15-min TTL)
- `carts` (1) — (N) `cart_items`
- `quotes` (1) — (N) `quote_items`, with `status`, `expires_at`, `audit_trail`
- `orders` (1) — (N) `order_items`, with `presentment_currency`, `presentment_amount_minor`, `settled_amount_ghs_minor`, `gateway_fx_rate`
- `payments` (1) — (N) `payment_attempts`, with `provider` enum and provider-specific `metadata` JSON
- `refunds` linked to payments, with `presentment_refund_amount`, `settled_refund_ghs`
- `tax_lines` per order item (VAT, NHIL, GETFund, COVID levy each as separate rows for transparency)
- `fx_rates` (date, from_currency, to_currency, rate, source)
- `shipping_rates` (origin, destination_region, weight_band, service_level, price_ghs_minor)
- `activities` (CRM: customer_id, actor_id, type, body, timestamp)
- `audit_log` (global, structured, immutable)
- `payment_routing_rules` (configurable primary/secondary routing without code deploys)

All money columns are `BIGINT` minor units. All timestamps are `TIMESTAMPTZ`. All FX rates stored as `NUMERIC(18,8)` for precision.

---

## 10. Phasing

Custom-build timeline reflects Option 2 with the Express + mobile-ready architecture. Roughly 8–9 months to web v1.0; mobile MVP follows.

### Phase 0 — Foundations (weeks 1–3)

- Monorepo scaffolded (`apps/web`, `apps/admin`, `apps/api`, `apps/mobile`, shared `packages/`). Vercel project for `web` and `apps/admin`; DigitalOcean App Platform provisioned for `api` in Frankfurt via the **Node.js buildpack** with auto-deploy from the `main` branch (App Spec checked in at `apps/api/.do/app.yaml`); Expo project initialised for `mobile` with EAS build set up.
- Neon, Upstash, Inngest, Cloudflare R2, Pusher, Resend, Sentry, Axiom all provisioned.
- Better Auth wired into the Express API; storefront integrated via cookies; bearer-token flow stubbed for the mobile app.
- Paystack merchant account approved.
- **IT Consortium contracting kickoff (week 1)** — non-negotiable; their lead time gates Phase 1's payment work.
- DPC registration tracked as parallel workstream (in progress, not blocking).
- CI/CD with preview environments on Vercel (web) and DigitalOcean App Platform (api). Expo EAS preview builds for the mobile app via shareable QR-code links.
- Shared API client SDK package set up with zod schemas; codegen wired into CI so web and mobile both consume typed clients.
- Design system kickoff workstream (see §11).

### Phase 1 — B2C MVP on the web (weeks 4–17, ~14 weeks)

- Express API: catalogue, product/variant/price model, cart, inventory reservations, order state machine, payments (Paystack: cards + MoMo + bank), tax engine, FX, audit log, webhook receivers. All exposed as versioned REST endpoints with zod-validated request/response.
- Next.js storefront pages (home, PLP, PDP, search) with ISR, consuming the Express API server-to-server.
- Currency switcher with FX refresh job (Inngest scheduled, triggered from Express).
- Admin order management UI in the Next.js app, calling the same Express endpoints staff actions go through.
- Transactional email (Resend templates for order confirmation, shipment, refund).
- Customer 360 v1 (orders + activities tabs).
- Auth via Better Auth (B2C customers): email + password with verification, password reset, session management, optional MFA scaffolding.

### Phase 2 — B2B v1 on the web (weeks 18–25, ~8 weeks)

- B2B account model with sub-users and roles (Better Auth Organization plugin: Owner, Buyer, Approver, Viewer).
- Quote request → admin approval → checkout flow.
- Customer-specific price lists.
- Quote PDFs (React-PDF or PDFKit).
- IT Consortium integration as secondary processor.
- Payment routing rules with admin UI.

### Phase 3 — Hardening and web v1.1 (weeks 26–34, ~9 weeks)

- Buyer-side approval chains for quotes.
- Credit terms (Net 7/14/30/60) and invoice payment.
- Carrier API integration where available.
- Meilisearch migration.
- SMS notifications (Hubtel or mNotify).
- Customer-facing returns portal (still no full RMA, just a self-serve request form).
- Performance optimisation pass; accessibility audit; security review.

### Phase 4 — Mobile app MVP (weeks 28–42, ~14 weeks, overlaps Phase 3)

- React Native + Expo app (iOS + Android, single codebase), consuming the same Express API.
- Auth via Better Auth bearer-token flow, with biometric unlock on returning sessions.
- Catalogue, cart, checkout (Paystack mobile SDK + MoMo deep links), order history, push notifications via Expo Notifications + Pusher Beams fan-out.
- Submitted to the App Store and Play Store; phased rollout in target geographies via Expo EAS.
- Mobile design (Round 5, §11) lands during Phase 3 so engineering can begin on time.

**Total: ~34 weeks (~8.5 months) to web v1.0 + v1.1 feature completeness; ~42 weeks (~10 months) including mobile MVP.**

---

## 11. Brand and design system workstream

**Status as of 16 May 2026: v1.0 brand system delivered.** Logo, palette, typography, and storefront design tokens are locked. The artifact `brand-system-v1.html` is the canonical reference for engineering.

Phase 1 storefront engineering can now begin against the v1.0 system. Remaining design rounds, all owned by Claude Opus 4.7, to be delivered alongside (not blocking) Phase 1 engineering:

- **Round 2** — Product detail page (PDP), cart drawer, full cart page.
- **Round 3** — Three-step checkout (mobile-first responsive web), customer account dashboard.
- **Round 4** — B2B quote-request flow, admin shell and order management UI.
- **Round 5** — Native mobile app screens (onboarding, catalogue, PDP, cart, checkout, account, push notifications). To begin during Phase 3, ahead of Phase 4 engineering.
- **System extras** — Email templates (React Email), empty states, loading skeletons, error pages.

**Owner:** Claude Opus 4.7, working from briefs supplied by the founder. Final trademark clearance on the wordmark and logomark is the founder's responsibility.
**Risk if late:** Phase 1 engineering can start against v1.0 today, but later rounds are needed to keep the build from stalling at PDP/checkout work. Round 2 should land within two weeks of Phase 0 completion.

**Cross-platform design constraint (effective from Round 2 onward):** every web design must be authored with the awareness that the same flow will reappear in the native iOS and Android app (React Native, Round 5). Prefer component patterns that translate cleanly across platforms — cards, lists, drawers, bottom sheets, full-screen modals, snap carousels, tab strips. Avoid hover-only states, multi-column layouts that don't reflow gracefully, and any interaction that has no native equivalent. Tokens live once (a TypeScript file in the shared `packages/` workspace) and are consumed by Tailwind on the web side and by a React Native theme module on the mobile side. Touch targets meet a 48px floor (Android Material 3, comfortably above iOS's 44pt) on every interactive element, web and mobile.

---

## 12. Success metrics

| Metric | Target by month 6 post-launch | Owner |
|---|---|---|
| GMV | GHS 1M cumulative | Founder |
| Conversion rate (B2C) | ≥ 1.8% | Growth |
| B2B accounts onboarded | 50 | Sales |
| Quote → order conversion | ≥ 40% | Sales |
| Storefront p75 LCP (3G) | ≤ 2.5s | Engineering |
| Payment success rate | ≥ 99% | Engineering |
| Support tickets / order | ≤ 0.15 | Ops |

---

## 13. Risks and mitigations

| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| IT Consortium onboarding slippage blocks Phase 2 | Medium | High | Start contracting in Phase 0 week 1; treat as critical-path |
| Subsequent design rounds (PDP, checkout, admin) slip behind Phase 1 engineering | Medium | Medium | v1.0 is locked and unblocks Phase 1; Round 2 to land within 2 weeks of Phase 0 completion |
| Custom tax engine has bugs at launch | Medium | High | Build with test fixtures against published Ghana Revenue Authority examples; finance review before launch |
| MoMo provider outage during peak | Medium | High | Dual processor routing; queue and retry; visible status page |
| FX volatility on USD-priced inventory | High | Medium | +2% margin buffer on displayed USD; daily refresh; manual override |
| Vercel bill spikes with traffic | Medium | Medium | Cost alerts at 50%/80%/100% of budget; cache aggressively |
| Underestimating commerce primitives complexity (cart, orders, refunds) | High | High | Apply Medusa as reference architecture even though not used; do not invent novel patterns |
| API contract drift between Express, web, and mobile clients | Medium | High | Shared `packages/api-schemas` (zod) as the single source of truth; auto-generated typed SDK consumed by both clients; contract tests in CI; semver-versioned API routes (`/v1/...`) |
| Two-deployable ops overhead (two CI pipelines, two health surfaces) | Medium | Medium | Monorepo with shared tooling, single observability stack (Sentry + Axiom span both services), shared incident runbook |
| Founder bandwidth | Medium | High | Hard prioritisation; weekly steering review |

---

## 14. Open questions remaining for the founder

Following the 16 May 2026 PM decisions, the remaining open item is:

1. **Returns policy legal review** — counsel to confirm the draft policy in §6.11 before launch. Founder to consider extending the request window from 7 to 14 calendar days to match Ghanaian e-commerce norms (Jumia GH 15 days, Melcom 14 on most categories) and reduce friction. Operational handling drafted in `returns-ops-script-v1.md`.

**Post-MVP optimisations parked for later (not blockers):** evaluate DO Managed Postgres vs Neon once the API is live on App Platform and we can measure same-network latency vs Neon's branching and free-tier value.

---

## Appendix A — Plan B: headless commerce engine (Medusa.js v2)

Retained as a fallback in case the timeline cost of fully custom proves unacceptable mid-build. Medusa would replace the custom 