All work

Case study · Order dispatch

DropCare — getting a prescription to the right pharmacy

A pharmacy delivery platform that offers each order to pharmacies in tiers, then widens the search for a courier until someone takes it.

The problem

A customer photographs a prescription, or records a voice note, and wants the medicine delivered. The platform has to find a pharmacy that will fill the order, agree a price, take payment and find a courier. Five kinds of user take part: customers, pharmacies with their pharmacists, delivery companies, couriers and admins. An order moves through 14 statuses.

My role

I led the backend and wrote most of it: order dispatch, the wallet, invoices, settlements, coupons, support chat, financial reports, the realtime server and the deployment pipeline. A second developer joined for part of the project. I assigned their tasks and reviewed their merge requests.

Key decisions

Pharmacies are offered the order in tiers. The client’s rule was that pharmacies offering customers a higher discount should see an order first. I implemented it as a staged fan-out.

  1. The customer posts an order

    With prescription images or a voice note.

  2. Find the pharmacies in range

    A distance query around the delivery address.

  3. Notify them tier by tier

    Grouped by discount, highest first. Each lower tier is a queued job with a short delay.

  4. A pharmacy accepts and sends an invoice

    Acceptance opens a chat room. A timeout job handles the case where nobody answers.

  5. The customer pays

    Wallet, cash or Apple Pay.

  6. Search for a courier in a widening ring

    A delayed job reschedules itself with a larger radius, up to 100 km.

  7. Pickup, delivery, settlement

An order's path from the customer to a courier.

A courier search that widens itself. Notifying every courier in the city at once is noisy, and a fixed radius can leave an order stuck. The search starts close and grows.

  • Each round searches a ring, from the previous radius to the new one, so nobody is notified twice for being inside both.
  • Before each round the job reads the order again under a row lock. If a courier has already accepted, it stops.
  • A ledger table records who was notified, with a unique constraint. A retried job that tries to notify the same courier again is harmlessly rejected by the database.

Three parties share each payment. The pharmacy, the courier and the platform each take a part. Commissions include VAT, some products are taxable and some are not, and a coupon can target the service fee or the delivery commission. One service computes the breakdown and settlements read from it.

A wallet that survives two clicks. Debits and charges lock the wallet row. Top-up requests go through admin approval, guarded so that the same request cannot be approved twice.

Realtime outside PHP. Chat, calls and live courier tracking run on a small Node server with Socket.IO, so the Laravel application stays stateless.

Also built

  • Invoices with a QR code and a PDF, reachable through a tokenised public link.
  • A status log written by an observer on every transition, which the reminder jobs read.
  • A GitLab pipeline that deploys over SSH and always takes the site out of maintenance mode, even when a step fails.
  • Financial reports for admins with Excel export.

Outcome

Five API surfaces and an admin panel, with dispatch, payment and settlement working from the first order to the provider’s payout.

What I would do next

  • Apply the courier search’s lock-and-recheck pattern to pharmacy acceptance, so that two pharmacies accepting in the same instant are resolved by the database.
  • Add automated tests around the money calculations and the dispatch jobs.

CV

Download PDF
CV page 1CV page 2