Case 04FAHA Scrubs · fahascrubs.com
Made-to-order commerce for medical scrubs
A WooCommerce shop rebuilt as a custom Persian commerce platform — name printing, made-to-measure, corporate pricing — cut over on the same domain with every indexed URL intact.
- Role
- Full-Stack Engineer — sole developer, design to deployment
- Timeline
- Jul 2026 – Oct 2026 · 10 wks to launch
- Status
- Live
- Stack
- TypeScriptNext.jsMedusa v2PostgreSQLRedisDocker
The problem
FAHA sells medical scrubs, lab coats and caps across Iran, through its own shop and a franchise network of 13 branches. Much of what it sells is personalised: a name printed on the chest, a sleeve shortened, a garment cut to the wearer's own measurements — and a hospital may order a hundred sets at once. A stock WooCommerce install modelled none of that.
Replacing the store could not cost what it had already earned. The old site ranked on Google for its core keyword, so all 308 indexed URLs had to keep resolving on the same domain, and past orders and customers had to come across with order numbering continuing where WooCommerce left off.
Constraints
- 01
Hosted inside Iran: Vercel and AWS are unreachable from there, so the platform runs self-hosted on a single server behind ArvanCloud's CDN, and builds have to work through a local npm mirror.
- 02
A same-domain cutover. The new platform replaced WordPress on fahascrubs.com overnight — URLs, redirects, accounts and order numbers had to be right from the first minute, not patched in the following week.
- 03
Persian end to end: right-to-left layout, Persian digits, Persian text normalised so differently typed letters still match, and Toman prices converted to Rial only at the payment gateway.
What I built
The storefront on Next.js 16: 15 categories and 22 colour landing pages, product pages with per-size pricing and a colour × size stock grid, 24 size-guide tables, reviews with structured data, the migrated blog, guest checkout with phone-OTP sign-in, five couriers, Zarinpal payments and an order-tracking timeline.
The made-to-order layer: a name-print studio with a live preview — Persian or Latin fonts picked by the alphabet you type, four positions, three thread colours — plus tailoring adjustments and a made-to-measure option that takes body measurements instead of a size. Corporate orders get automatic 5–20% tiers from ten garments up and a quote request above 200.
The back-end on Medusa v2 with a custom FAHA module, a custom OTP auth provider and a custom Zarinpal payment provider — and FAHA's own Persian staff panel: orders and a printable production queue, inventory, size charts, print and tailoring prices, a CMS for articles and pages, and four staff roles.
The operations around it: Docker Compose with Postgres 16 and Redis 7 behind Caddy, a scripted check of every legacy URL before and after launch, and nightly database backups to ArvanCloud object storage with 30-day retention.
The hard decision
Deploys the server pulls, instead of CI pushing them
The usual pipeline has CI build the app and push it to the server. That assumes the server is reachable from GitHub's runners and the build can reach the public npm registry — and from inside Iran, neither is something to bet a shop on.
So the direction is inverted. A systemd timer on the server polls main every two minutes, builds on the box through an Iranian npm mirror, and only switches over once the build succeeds. A failed build retries with backoff while the previous release keeps serving — a deploy can be slow, but it can never take the store down.
Security & reliability
Prices are re-derived on the server — printing, tailoring and corporate discounts are never trusted from the browser.
Payments are verified server-to-server with Zarinpal when the customer returns from the bank, before an order is confirmed.
Phone-OTP sign-in through a custom Medusa auth provider, with a second SMS provider as fallback.
Staff access split across four roles, rate limiting at Caddy, and nightly off-site database backups.
Results
- weeks from first commit to live cutover
- 10
- indexed URLs, every one still resolving
- 308
- automated tests, green on every deploy
- 550+
What I'd do differently
I'd get the catalogue re-photographed before building the name-print preview, not after. The live overlay on product photos is switched off until the re-shoot, so the store's most distinctive feature launched only half-visible.