Ethio Coffee Store
A live Saudi e-commerce platform that takes Mada and Apple Pay, ships through Torod in one click, and updates order status from payment and delivery webhooks.
Context
A registered commercial center in Jeddah sells Ethiopian coffee, spices and leather goods, plus products from Turkey and other countries, to customers across Saudi Arabia. It needed a store that takes the payment methods Saudi customers actually use, books shipments with local couriers and keeps customers informed, without staff copying order numbers between dashboards.
What I built
- An Arabic-first storefront with an English toggle in Next.js, with RTL-correct product, cart and checkout flows.
- A NestJS API that owns the order lifecycle through an explicit state machine (pending → paid → processing → shipped → delivered), on PostgreSQL (Neon) with Drizzle ORM.
- Moyasar payments with Mada, Visa/Mastercard and Apple Pay. Every payment webhook is signature-checked, de-duplicated and confirmed by re-fetching the payment from Moyasar before the order changes.
- A Next.js admin dashboard where staff ship an order through Torod: search the delivery city, compare courier rates and buy the label in one step.
- Torod webhooks that mark orders delivered automatically, and customer emails (via Resend) when an order is paid and when it ships.
- Production on Vercel (storefront and admin) and Render (API), deployed automatically on every push.
Architecture
- Users
- My code
- Services
- Third-party APIs
- Data
Hover or focus a component to see what it talks to.
Hard problems solved
Token lifecycle and retry
Torod issues expiring bearer tokens. The client caches the token, refreshes it five minutes before expiry, shares one token request between concurrent calls, and re-authenticates once if Torod revokes a token early.
Webhook idempotency and the order state machine
Payment webhooks are verified first, de-duplicated on the gateway's event ID, then confirmed against Moyasar. Torod updates re-fetch the shipment instead of trusting the webhook body, so duplicate or out-of-order events can't ship twice or move an order backwards.
Apple Pay domain verification
Apple Pay on the web only appears on a verified merchant domain. The storefront serves Apple's domain-association file, so Safari and iPhone customers get the Apple Pay button at checkout.
Arabic RTL commerce UX
Arabic is the default language. Prices, phone numbers, addresses and order IDs mix Arabic and Latin text, so the UI uses logical CSS throughout and keeps numbers and codes left-to-right inside right-to-left layouts.
Stack
- Next.js
- NestJS
- TypeScript
- PostgreSQL
- Neon
- Drizzle ORM
- Moyasar
- Apple Pay
- Mada
- Torod
- Resend
- Vercel
- Render
Outcome
The store is live and taking real payments from customers in Saudi Arabia. Payments confirm themselves, shipping an order is one click in the admin dashboard, and delivery updates arrive on their own, so the team spends its time on customers instead of copying tracking numbers.
Need something like this?
Tell me about your business and the systems you already use. I'll tell you honestly what it takes.
Book a call