Sep 06, 2026Your Checkout Flow Is a Cultural Assumption
Every e-commerce tutorial ends the same way. Cart, checkout form, card field, confirmation page. It is so standard it stops looking like a decision.
It is a decision. It encodes assumptions about who has a card, who trusts a form with it, and what "buying something online" feels like. Those assumptions hold in some markets and quietly fail in others.
I have spent a year on a production e-commerce platform serving Ghana. The interesting problems were almost never technical.
The first assumption: that people want a form
The standard flow asks a stranger to type card details into a page they have never seen before and trust that something arrives. That works where card payment online is unremarkable and disputes are easy.
Where I was building, the natural way to buy from a small merchant is to message them. You ask if they have it, they say yes, you agree a price, you pay, it arrives. The transaction is a conversation, and the conversation is the trust mechanism.
So checkout is a WhatsApp deep link. You build a cart on the site, and checkout hands off to a wa.me link with the order pre-composed: items, quantities, total, into a real conversation with a real person who replies.
The conventional cart still exists underneath, because some people do want the form. But it is the fallback, not the default. That inversion is the whole design.
I will admit this felt like a step backwards when I built it. It is not a clever technical solution. It is a less automated flow than the one I would have written by reflex. It is also the one that matches how the market actually transacts, and being right beats being elegant.
The second assumption: that payment means card
Mobile Money is the primary rail. Not an alternative payment method tucked behind a "more options" link: the primary one, with card as the secondary path.
This is not unusual or exotic; it is how a very large number of people move money. It reads as unusual only if your mental model of payments was formed somewhere card-first. The architectural consequence is that "payment provider" cannot be a single integration you bolt on at the end. It is a dimension the order model has to carry from the start.
The third assumption: that delivery is a solved problem
Address, courier, tracking number. That chain assumes addresses are precise, couriers are integrated, and tracking numbers exist.
Delivery is dispatch riders. Order tracking is built around that: rider assignment and status transitions that correspond to what actually happens, not to a carrier API that is not there. Cash on delivery is a first-class option, because for a first-time customer the willingness to pay on arrival is the trust mechanism.
What it is built on
Next.js 14 on the front, FastAPI with async MongoDB behind it, in a monorepo with a mobile workspace alongside. S3 with pre-signed URLs so the API never serves binaries. Docker Compose locally, GitHub Actions to deploy. Linting, tests and secret scanning enforced on every commit. 600+ commits over a year, and a Cypress suite covering the browse-to-checkout journey.
None of that is the interesting part, and I want to be careful not to pretend otherwise. The stack is ordinary on purpose. The design decisions that mattered were about the market, and they would have been the same on any stack.
I have made this mistake before, in the other direction
In 2019 I built a church management system, by hand, before AI coding assistance existed. It needed to send SMS notifications. The obvious choice was the international provider everyone reaches for. I used a Ghanaian gateway instead, because that is what actually delivered reliably to the phones in question.
Same instinct, six years apart, and I did not notice it was the same instinct until I was writing this. Build for how the market actually transacts, not how the reference architecture says it should.
The transferable part
If you only work in one market, you can go a whole career without noticing which of your defaults are technical and which are cultural. They look identical from the inside.
The test I use now: for each step in a flow, ask why is it this way, and if the honest answer is "because that is how it is done," that step is a cultural assumption wearing a technical costume. Sometimes the assumption holds. Often enough it does not, and the version that fits is simpler than the one you would have built.