Case study · B2B marketplace
Msane — one cart, many factories
A B2B manufacturing marketplace where a single checkout becomes several orders, and the parts must add up to the total exactly.
The problem
Msane connects buyers with factories, wholesalers and designers. A buyer fills one cart with products from several providers and pays once. Behind that single payment:
- each provider needs an order of their own,
- each order needs its share of the coupon, the fees and the VAT,
- stock must go down safely when two buyers check out together.
On top of the catalogue, custom and design orders are negotiated in chat with quotes, samples and installments, and factory representatives pick up orders on behalf of a factory.
My role
I was the only backend developer and wrote almost all of the project: authentication and onboarding, products, cart and checkout, the order lifecycles, returns and refunds, ratings, wallets, settlements and reports.
Architecture
- Controller
- FormRequest
- DTO
- Service
- Repository
- Resource
There are 48 repository interfaces. Their methods are named after what the business asks for, such as the pending orders a representative has not been assigned yet. They are not generic create, read, update and delete.
Key decisions
Splitting the cart without losing a halala. Checkout turns the cart into one order per provider. A coupon and a fixed fee apply to the whole cart, so each order gets a proportional share.
Group the cart lines by provider
Lock the product rows in ascending id order
Two checkouts with overlapping products take locks in the same order, so they cannot deadlock.
Reserve the stock
Share the coupon and the fixed fee between the orders
Proportionally, in the smallest currency unit.
Correct the rounding drift
Rounding each share can leave the sum off by one unit. The difference is assigned so the shares match the total exactly.
Create one order per provider with its financial breakdown
The breakdown is stored as a snapshot, so later price or VAT changes do not rewrite history.
A wallet that can hold money. The wallet I started from could only add and subtract. I rebuilt it as a ledger with row locks and three more operations: reserve, release and withdraw from the reserved amount. A settlement can hold a balance while it is being reviewed. Batch withdrawals take one lock and write one ledger row per order.
Deadlines that cancel themselves. An accepted offer must be paid within a time limit, and other steps have limits too. Each deadline is a delayed job that carries the timestamp it was created for. When it runs, it compares that timestamp with the order. If the order has moved on, the job exits. Nothing has to find and delete queued jobs.
First to accept wins. Several factory representatives can see the same open order. Acceptance takes a row lock, so only one of them gets it.
Outcome
- Delivered in about seven and a half weeks as the only backend developer.
- 25 feature test files with about 195 test methods, including three suites that walk a whole lifecycle from start to finish: a representative’s order, a return request and a design order.
- Each feature is documented with its API description.
What was left
Card payment was designed behind a strategy interface, with wallet payment working end to end. Connecting the live gateway was the next step when I moved on. I would also break up the two largest order services, which grew past a thousand lines each.