The Challenge
Healthcare in Bangladesh runs on disconnected channels
Patients buy medicine from one shop, book doctors through phone calls, and arrange lab tests through a third channel. Retail pharmacies source stock from depots on paper credit (বাকির খাতা) with no visibility into pricing tiers. Prescription medicines are routinely sold without verification.
We set out to build one platform that serves the patient, the pharmacy owner, the doctor, the pharmacist, the rider and the lab technician — each with their own workspace, all sharing one source of truth, and all safe enough to handle protected health information and real money.
The Solution
Five health-tech pillars, one cohesive codebase
• Consumer e-pharmacy (B2C) — prescription & OTC medicine delivery, health & wellness goods, typeahead search, postcode delivery zoning and generated packshots.
• Retail pharmacy wholesale (B2B) — tiered trade pricing across a 34,000+ medicine reference catalogue, manufacturer discount rules, DGDA drug-licence verification and a credit ledger.
• Telemedicine & clinic consultations — BMDC-registered doctors, conflict-free slot booking, private Jitsi video rooms and an e-prescription studio with live drug-interaction alerts.
• Home diagnostic lab testing — multi-test and package booking, home sample-collection windows and secure PDF report delivery.
• Omnichannel mobile — a Flutter customer app and a multi-role Flutter staff app, both driven by the same REST API as the website.
Tech Stack
System Architecture
MVC with a dedicated domain service layer and a stateless API
Client layer
Desktop & mobile web (HTML, cookie session) · Flutter Customer App (JSON, Bearer token) · Flutter Staff App (JSON, Bearer token)
Edge — front controller
index.php bootstraps the environment, error traps, security headers and sub-directory auto-detection, then hands off to a fast static + regex router with separate Web and API v1 route catalogues.
Middleware pipeline
CSRF · session & Bearer auth · RBAC capability checks · sliding-window rate limiting.
Controllers (thin)
Storefront · Admin · Consumer API · Staff API · M2M Integration API — validate, call a service, render or serialise.
Domain services (all business logic)
OrderService · Cart & PriceBook · PrescriptionService · RetailerService · ReferenceCatalogue · DrugLibrary · AppointmentService · LabService · DeliveryService · SslCommerz gateway · ResourceService (config-driven CRUD) · Audit logger.
Core platform
PDO database singleton · file/Redis cache · session manager · integer-poisha Money engine · finfo-sniffing Upload · SMTP mailer · bilingual Lang engine.
Storage
MySQL 8 (InnoDB, utf8mb4) for data · private storage outside the web root for prescriptions and lab PDFs · public uploads with PHP execution disabled.
Because the website, both apps and the integration API all call the same services, pricing, stock and RBAC rules can never drift between channels.
Order-to-Doorstep Workflow
How a prescription order moves through the system
-
Cart & quote
Session or API cart stores only ids and quantities; prices, stock and Rx flags are re-read on every render, so a stale price can never reach checkout.
-
Rx barrier
If any line requires a prescription, checkout blocks until an image or PDF is uploaded. The file goes to private storage, never the public web root.
-
Atomic checkout
One transaction locks product rows with SELECT … FOR UPDATE in id order (deadlock-safe), re-verifies stock and price, deducts tracked stock and writes the order — guarded by a unique idempotency key.
-
Pharmacist review
The order sits in awaiting_rx. A pharmacist reviews the prescription in an inline viewer: approve confirms the order, reject cancels it and restocks automatically.
-
Dispatch & delivery
The dispatch board assigns a rider; the rider's run-sheet (web or staff app) tracks pick-up to delivery, and COD collection flips the order to paid.
Core Modules
FULLTEXT search with typeahead, Rx/OTC filters, medical-info tabs, moderated reviews, postcode delivery check, coupons and a free-delivery threshold.
Pharmacist queue with inline image/PDF viewer, authorised streaming of PHI files, and audit logging on every access.
Retailer onboarding with DGDA licence review, price tiers in basis points, per-manufacturer overrides and price requests for unpriced items.
Signed-amount ledger with invoices booked on delivery, recorded repayments and available credit computed live against open orders.
Specialty and mode filters, live slot picker, private Jitsi rooms and a doctor workspace with appointments and patient history.
Searches the store plus a 34k reference catalogue, autosaves drafts, shows pairwise drug-interaction alerts and prints a bilingual A4 pad with a verification ID.
Multi-test and package booking, home-collection date and time windows, lab operations board and secure PDF report delivery.
Toggle any of 17 home sections, pause entire services with a friendly 503, override any sentence in either language, and manage branding and media.
B2B Wholesale Flow
From licence submission to credit trading
-
Retailer applies
A pharmacy owner registers and submits shop details, drug licence number, trade licence and the licence document.
-
Admin review
The application lands in a review queue. Rejected applicants are notified with a review note.
-
Tier & credit assigned
On approval the retailer gets a price tier (e.g. 12% or 14% in basis points) and a credit limit.
-
Trade pricing active
PriceBook computes trade price as the lower of consumer price and the override or MRP minus the tier or manufacturer discount.
-
Order on credit
Credit orders are allowed only within available credit. When delivered, an invoice is auto-booked to the ledger; repayments reduce the balance.
Mobile Apps & API
Flutter + Riverpod clean architecture. Guest carts are priced via /cart/quote and merge into the server cart on login; tokens live in Keychain/Keystore.
Builds its navigation from the signed-in staff member's permissions — riders see their run-sheet, pharmacists the Rx queue, doctors their workspace.
The staff app fetches each resource's field schema from the API and renders native forms, so new admin fields ship without an app-store update.
Uniform {ok, data, meta} envelope, X-Lang negotiation, idempotent order placement and 503 service_paused responses mirrored from the CMS.
/api/v1/admin/* reuses every web RBAC, anti-escalation and audit rule unchanged through the same middleware and services.
Key-authenticated price and stock sync endpoints for external ERP/POS systems such as PharmaPOS.
Security, Privacy & Correctness
Guarantees enforced in code and in the database
• Overselling — product rows locked with SELECT … FOR UPDATE inside one id-ordered transaction.
• Double submit — unique per-checkout idempotency key on orders.
• Coupon over-redemption — atomic UPDATE … WHERE used_count < usage_limit.
• Double-booked doctors — generated slot_lock column plus a UNIQUE (doctor, date, time, slot_lock) key; cancelled slots free up automatically.
• Payment spoofing — every SSLCommerz callback re-validated server-to-server; amount, currency and transaction id must match.
• Money — integer poisha everywhere, never floating point.
• PHI isolation — prescriptions and lab reports stored outside the web root and streamed only to their owner or authorised staff, with audit logging.
• RBAC — database-driven roles across Super Admin, Admin, Manager, Pharmacist, Employee, Doctor, Delivery Rider and Lab Technician, with anti-escalation: the Super Admin role is immutable, staff can only grant permissions they hold, and nobody can edit their own role.
• Web hardening — CSRF on every state change, bcrypt with automatic rehash, constant-time login, strict sessions, sliding-window rate limits, finfo MIME sniffing, CSP, HSTS and nosniff headers, open-redirect guards and a contact honeypot.
By the Numbers
Database tables
InnoDB, utf8mb4, idempotent migrations
Routes
159 web + 146 REST API endpoints
Lines of code
21k PHP across 245 files + 34k Dart across 176 files
Automated API checks
92 consumer + 67 staff smoke tests, plus a wholesale suite
Reference medicines
Plus 138 generics, 916 presentations and 38 interaction rules
Staff roles
Each with its own portal and permission set
Documentation & Operations
Shipped with a full engineering manual
The platform ships with a 680-line technical manual covering architecture, the complete data model, every business workflow, the RBAC matrix, both mobile apps, the full REST API reference, CLI tooling and a production go-live checklist — plus a dedicated B2B wholesale architecture blueprint.
Operations tooling
• bin/install.php and an idempotent bin/migrate.php that runs safely on every deploy
• Importers for the 34k reference catalogue and the PharmaPOS drug library with interaction matrix
• Bilingual content seeders for articles, FAQs, pages and banners
• Smoke-test suites for the consumer API, staff API and wholesale platform that clean up after themselves
• A cron daemon that cancels and restocks abandoned online payments after 60 minutes
Scaling path
Stateless apart from sessions: switch sessions and cache to Redis, put private storage on shared or object storage, run PHP-FPM with OPcache, front assets and generated packshots with a CDN, and add a MySQL read replica when needed.
Product Gallery
Storefront, admin console and mobile apps