Loading devShakib…
Two apps, escrow, identity verification, and a delivery network made of strangers' luggage. Six years and fourteen versions in.
Shpper is a cross-border personal-shopping marketplace. A buyer wants something they cannot get where they live. A traveller is already flying that route with unused luggage space. The platform introduces them, holds the money until the item arrives, and turns an empty few kilos in someone's suitcase into a delivery network.
I am the CTO. I own the platform end to end — the Flutter apps for both sides, the backend, payments and escrow, identity verification, and the release pipeline. We are on version 14.2.0, rated 4.64 on the App Store from 72 ratings.
Fourteen major versions is enough distance to say something useful about what this kind of product actually demands. Almost none of the hard parts were the ones I expected.
The first structural fact about a two-sided marketplace is that "the app" is two products with opposed interests, and you ship both.
The buyer wants their item cheaply, quickly, and with certainty it will arrive. The traveller wants to be paid well, carry as little risk as possible, and not be blamed for customs. Those are not complementary desires. Every feature has to be designed twice, from two points of view, and any change to the shared middle — the request, the offer, the trip — reaches into both.
This has a consequence people underestimate: your release cadence is bounded by the slower side. A change to how offers work is not shipped when the buyer app ships. It is shipped when both apps are live, on both platforms, and enough of both populations have updated. Mobile app review is not a build step you can optimise away; it is a scheduling constraint that shapes how you design changes, which is why so much of the logic has to be able to change without a release.
A marketplace's actual product is trust between strangers. Everything else is plumbing.
Consider what the platform is asking. A buyer sends money for an item that does not exist yet, to be bought by someone they have never met, in a country they are not in, and carried across a border. A traveller agrees to spend their own money on someone else's goods, in exchange for a promise of repayment, and to carry those goods through customs under their own name.
Neither side would do that for a stranger. They do it because the platform stands between them, and everything about the engineering follows from that position:
Escrow is not a payments feature, it is the entire trust mechanism. Money is held from the moment a deal is struck until the item is confirmed delivered. That single design decision is what makes the rest possible — the buyer risks nothing until they have the item, and the traveller knows the money genuinely exists before they spend their own.
Identity verification is what makes escrow meaningful. Held funds only protect you if there is a real, identified person on the other end when something goes wrong. Verification is not a compliance checkbox bolted on for regulators; it is the thing that makes the escrow promise enforceable.
The state machine is the product. A request becomes an offer, becomes an accepted deal, becomes a purchase, becomes a carried item, becomes a delivery, becomes a release of funds. Every transition can fail, and each failure needs a defined answer: what happens to the money, what each side sees, who is told what and when. The unhappy paths outnumber the happy one, and they are where the product either holds together or falls apart.
Ordinary app bugs are annoying. Bugs that touch money are a different category, because you usually cannot fix them by shipping a patch.
A double-charge is not resolved by correcting the code — the money has already moved, and now there is a human on the other end who is angry and correct to be. A payout that fires twice is gone. A state transition that releases escrow early cannot be un-released by a hotfix.
This changes how you write things. Every money-moving operation has to be idempotent, because networks retry and users tap twice and both of those are normal. State transitions have to be atomic, because a partial write in the middle of a payment is worse than a failure. And ledgers have to be append-only — the record of what happened is not something you edit, because the moment it is editable it stops being evidence.
None of this is exotic. All of it is the difference between a bug you fix and a bug you refund.
Everything above applies to any escrow marketplace. Crossing a border adds problems that are not software problems at all, and the engineering has to absorb them anyway.
Prices move while a deal is open, because currencies move. Customs and duty are real costs that land on somebody, and the product has to be unambiguous about who before anyone commits, not after. Items that are perfectly ordinary in one country are restricted in another. Trips get cancelled, delayed, or rerouted, and a delivery network built from other people's travel plans inherits all the volatility of those plans.
The engineering lesson is that a design which assumes stability will spend the rest of its life being patched. Timelines slip, prices change, routes vanish. Building for that from the start is much cheaper than discovering it across fourteen versions.
The title suggests architecture diagrams. The reality is that owning it end to end means the boundary between "engineering problem" and "business problem" does not exist.
When a delivery goes wrong, the question is not only what the code did. It is what the policy should be, who absorbs the cost, what both users are told, and what changes so it happens less often. That is one conversation, not four, and being the person who understands both the state machine and the commercial consequence is most of the job.
The other half is choosing what not to build. Every marketplace has an infinite backlog because both sides always want more, and the sides want different things. Fourteen versions in, the decisions I am most glad about are almost all subtractions.
Flutter for both apps, so one codebase covers iOS and Android on each side — which matters enormously when "shipping a feature" already means shipping two products. Firebase for the backend. The choice that has aged best is not a technology but a principle: keep as much behaviour as possible changeable without an app release, because you cannot hotfix your way out of a marketplace problem when app review sits between you and your users.
---
The thing I would tell someone starting one of these: you are not building software that connects buyers and sellers. You are building the reason two strangers are willing to take a risk on each other, and every technical decision either strengthens that or quietly erodes it. Escrow, verification, and an honest state machine are not features on a roadmap. They are the product, and everything else is how it is delivered.
Shpper is at shpper.com, and there is a longer case study on this site.