← back to work

SikaDeals

A multi-vendor deals marketplace, built for a market that pays by phone.


role
Product design, front-end and back-end
type
Our own product
status
In development, 2026
surfaces
storefront
vendor fulfilment
admin back office
payments
moolre
paystack
stack
tanstack start
react
typescript
drizzle orm
tailwind css
vite

The problem

Most checkout code assumes a card. In Ghana that assumption fails: people pay with mobile money, from a handset, and the flow is nothing like a card charge.

  • The payment is asynchronous. The customer approves a prompt on their phone. Confirmation arrives seconds later over a callback — or it never arrives at all, and the order is left in limbo with the money gone.
  • One provider is a single point of failure. If a gateway is down or a network is unreachable through it, every sale stops.
  • Three audiences, one system. A shopper, a vendor filling orders, and an operator settling payouts want completely different screens from the same data.

Three surfaces

Each one is a separate set of screens over a shared schema, so a change to how an order works shows up correctly in all three rather than being reimplemented.

Storefront

Browse deals, pay, then follow the order without calling anyone.

  • catalogue & product pages
  • checkout
  • order tracking
  • wallet & store credit
  • support tickets

Vendor fulfilment

An order queue and a payout view, scoped so a vendor sees only their own.

  • incoming orders
  • own product list
  • stock
  • settlement

Admin back office

The operator’s view: everything that needs a person to decide.

  • orders & refunds
  • vendors
  • products & pricing
  • warranty claims
  • settings

Making the money reliable

This is where most of the engineering went. The rules below exist because each one has a failure it prevents.

Every amount is an integer. Prices, totals and balances are held in pesewas, never in floating point. Exchange rates are stored scaled by a million so a manually entered rate keeps its precision instead of drifting a pesewa at a time.

Two gateways sit behind one interface. Which one collects is a setting an operator can change. But a payment records the gateway that started it, and every later step — the OTP, the status poll, the callback — is routed by that stored name rather than the current preference.

So switching the default never strands a prompt already sitting on somebody’s handset.

Webhook deliveries are recorded before they are acted on. Providers retry. Without a record of what has already been seen, a retry credits an order twice.

A scheduled sweep does the jobs nothing else can. It chases payments still pending past a timeout and asks the gateway directly what happened, catching the sales whose callback was lost. It also retires prompts stuck waiting on an OTP the payer walked away from, so they stop being carried forward as if they might still succeed.

What’s underneath

A marketplace grows more moving parts than a shop. The schema carries all of it in one place rather than spreading it across services.

  • 24tables
  • 3surfaces
  • 2payment gateways
  • vendors & vendor products
  • orders & order items
  • payments
  • wallets & entries
  • stock items
  • warranty claims
  • support tickets
  • coupons & redemptions
  • referral rewards
  • reviews
  • audit log
  • webhook events

How it was built

One TypeScript codebase, server and client, on TanStack Start. Drizzle keeps the schema and the queries in the same language as the interface, so a column rename fails at compile time instead of at runtime in front of a customer.

Vendor integrations go through an adapter, with a mock implementation used in development — the fulfilment flow can be exercised end to end without touching a live vendor system.

Want one like this?

Tell us what you’re trying to build in your own words. We’ll reply within a working day.

[email protected]
YebsTech D Yeb’s Technologies yebstech.com © 2026