Malus Garden (opens in a new tab) · Freelance
From social orders to a storefront.
A bilingual shop and admin for an organic produce business that took its orders over Instagram and Facebook: Stripe payments, BoxNow lockers and door-to-door delivery, live since December 2025.

The context
Malus Garden sells organic fruit and vegetables. Orders used to arrive as Instagram and Facebook messages, which took time to handle and led to mistakes. The business also planned to grow the site beyond a shop, which a hosted shop template would have made hard.
My contribution
I built the whole application on my own in about four months: a shop and an admin, both React apps in Greek and English that share packages in a pnpm monorepo; the Node.js/Express API; the PostgreSQL schema and migrations; and the Stripe and BoxNow integrations. A designer supplied three page designs, typography, photos and a landing-page video; I designed the remaining screens and the admin. The hardest parts were the edge cases, and turning a non-technical client’s requests into precise requirements.
Key decisions
Model the catalog as the business sells it
Products are sold per item, per kilo or in packs, with prices shown to match, and carry a category, offers and an organic flag that shows a BIO badge. Names and descriptions come in Greek and English, and products can be archived. The admin warns when stock changed while a product was being edited, and checks for payments still in progress before a product’s unit changes. The business manages its catalog itself.

Let Stripe confirm payment, not the browser
Prices and stock are checked on the server against the database; the browser’s cart is only a display, and it survives a refresh or a cancelled payment. An order becomes paid only when Stripe’s signed webhook arrives, in the same database transaction that deducts stock; an abandoned checkout is cancelled when its Stripe session expires. If stock ran out between checkout and payment, the order is held, and the shop releases it to delivery after restocking. Refunds go through Stripe with an idempotency key, so a retried request can’t refund twice.

Deliver the way the business ships
At checkout, customers pick a BoxNow locker on BoxNow’s map widget or, in selected postal codes, door-to-door delivery, with areas, dates and fees managed in the admin. For a locker order, the shop picks the box size and weight and sends it to BoxNow; a background worker creates the parcel and retries. Orders waiting over 24 hours are highlighted, and each order is marked shipped, or refunded, from its page. Customers track an order with its number and checkout email for 90 days, and see its progress, sending date and BoxNow voucher, never personal details.

Keep releases predictable
One script runs every release: it tries the new build on a spare port first, and a release can be rolled back in seconds. The database is backed up before any migration; migrations are additive, each runs in its own transaction and is rehearsed on a copy of production data, and the server never changes the schema on its own at startup. A staging environment with its own database sits beside production, and 2,300+ automated checks cover the backend, the frontend and the database.
The outcome
The shop has been live since December 2025 and processes customer orders daily. Its accessibility checks show 0 axe violations against WCAG 2.2 A/AA rules on 20 pages, in Greek and English, at desktop and phone sizes, and it has a published statement of partial WCAG 2.2 AA conformance. After four years of trying to get an online shop built, the business is delighted with this one. Next time, I’d put scope, milestones and payment terms in writing before starting.