Building Khoomi: Multi-Vendor Order Architecture
Earlier in this series I covered shop architecture. This one is about how orders work when a customer buys from multiple shops in one checkout.
On Khoomi, a single cart can contain items from different sellers. At checkout, that becomes one payment but multiple fulfillment flows. Shop A might ship tomorrow while Shop B takes a week. The order system needs to handle this gracefully.
The Parent-Child Model
An order has embedded shop orders. One document, multiple fulfillment units:
type Order struct {
ID primitive.ObjectID `bson:"_id"`
OrderNumber string `bson:"order_number"`
CustomerID primitive.ObjectID `bson:"customer_id"`
CustomerEmail string `bson:"customer_email"`
ShopOrders []ShopOrder `bson:"shop_orders"` // Embedded
Pricing OrderPricing `bson:"pricing"`
ShippingAddress UserAddressExcerpt `bson:"shipping_address"`
Status OrderStatus `bson:"status"` // Overall
PaymentStatus string `bson:"payment_status"`
CreatedAt time.Time `bson:"created_at"`
PaidAt *time.Time `bson:"paid_at,omitempty"`
CancelledAt *time.Time `bson:"cancelled_at,omitempty"`
}
type ShopOrder struct {
OrderID primitive.ObjectID `bson:"order_id"`
ShopID primitive.ObjectID `bson:"shop_id"`
ShopName string `bson:"shop_name"`
Items []OrderItem `bson:"items"`
Subtotal int64 `bson:"subtotal"` // in kobo
ShippingCost int64 `bson:"shipping_cost"` // in kobo
ShopTotal int64 `bson:"shop_total"` // in kobo
ShopOrderStatus OrderStatus `bson:"shop_order_status"` // Independent
TrackingNumber string `bson:"tracking_number,omitempty"`
SellerPayout SellerPayout `bson:"seller_payout"`
Refund *ShopOrderRefund `bson:"refund,omitempty"` // Created on cancellation
}
The parent Order tracks payment. Each ShopOrder tracks its own fulfillment. When Shop A marks its portion as shipped, Shop B’s status doesn’t budge — it stays exactly where it was.
Why embed instead of separate collections? Atomic reads. Fetching an order for display is one query, not a join. The customer sees everything at once: their items, each shop’s status, the overall payment state.
The trade-off? Updating a single shop’s status requires updating the entire order document. MongoDB’s positional operator ($[elem]) handles this efficiently, but it’s still a larger write than updating a separate document.
Atomic Checkout
Creating an order touches multiple collections. Inventory must be reserved. The cart must be cleared. The order must be inserted. All or nothing:
func (s *orderService) CreateOrderFromCart(ctx context.Context, userID ObjectID, req CreateOrderRequest) (*Order, error) {
// Build order from cart items (grouping by shop)
order := buildOrderFromCart(...)
callback := func(sessCtx mongo.SessionContext) (any, error) {
// Reserve inventory for each item
for _, shop := range order.ShopOrders {
for _, item := range shop.Items {
filter := bson.M{
"_id": item.ListingID,
"inventory.quantity": bson.M{"$gte": item.Quantity},
}
update := bson.M{
"$inc": bson.M{"inventory.quantity": -item.Quantity},
}
result, err := listingColl.UpdateOne(sessCtx, filter, update)
if err != nil {
return nil, err
}
if result.ModifiedCount == 0 {
return nil, fmt.Errorf("insufficient inventory for '%s'", item.Title)
}
}
}
_, err := orderColl.InsertOne(sessCtx, order)
if err != nil {
return nil, err
}
_, err = s.cart.ClearCartItems(sessCtx, userID)
if err != nil {
return nil, err
}
return order, nil
}
result, err := database.ExecuteTransaction(ctx, s.db.MongoClient, callback)
if err != nil {
return nil, err
}
return result.(*Order), nil
}
The filter "inventory.quantity": bson.M{"$gte": item.Quantity} is critical. It ensures stock exists before decrementing. If two customers checkout simultaneously and only one item remains, exactly one succeeds. The other gets a clear error.
The snippet hides the rest of the unit of work. The same transaction records coupon usage (so a code can’t be drained twice) and enqueues stock-change events for analytics and low-stock alerts. After it commits, the order triggers an outbox dispatch and a cache purge. Same shape — reserve, insert, clear — but the transaction is the whole checkout, not just three writes.
Why reserve at checkout instead of cart add? I covered this decision in the listing article. The short version: carts get abandoned constantly, and reserving at checkout means no expiry timers and no holds to babysit.
Independent Fulfillment
Each shop order has its own lifecycle:
PENDING → PAID → PROCESSING → SHIPPED → DELIVERED
↓
CANCELLED
When a shop updates their status, only the order belonging to their shop gets updated:
func (s *orderService) UpdateShopOrderStatus(ctx context.Context, params UpdateShopOrderParams) error {
currentStatus, err := extractCurrentShopOrderStatus(snapshot, params.ShopID)
if err != nil {
return err
}
// Include current status in the filter: two workers can't both win
filter := bson.M{
"_id": params.OrderID,
"shop_orders": bson.M{
"$elemMatch": bson.M{
"shop_id": params.ShopID,
"shop_order_status": currentStatus,
},
},
}
callback := func(sessCtx context.Context) (any, error) {
result, err := os.db.Coll.Orders.UpdateOne(sessCtx, filter,
bson.M{"$set": buildShopOrderStatusSetFields(params.Status, now, params)})
if err != nil {
return nil, err
}
if result.ModifiedCount == 0 {
return nil, errors.New("shop order status has already changed, please retry")
}
updatedOrder, err := os.GetOrderByID(sessCtx, params.OrderID)
if err != nil {
return nil, err
}
if params.Status == OrderStatusDelivered {
// Release this shop's earnings to its wallet
err := s.wallet.ReleaseEarningsInTx(sessCtx, params.ShopID, params.OrderID)
if err != nil {
return nil, err
}
}
// Derived parent status: when every shop order reaches the same
// status, the parent follows automatically.
if allShopOrdersInStatus(updatedOrder, params.Status) {
_, err = os.db.Coll.Orders.UpdateOne(sessCtx,
bson.M{"_id": params.OrderID},
bson.M{"$set": buildMainOrderStatusSetFields(params.Status, now)})
if err != nil {
return nil, err
}
}
return nil, nil
}
_, err := database.ExecuteTransaction(ctx, s.db.MongoClient, callback)
return err
}
The transitions themselves are validated before the write — paid→processing/shipped/cancelled, processing→shipped/cancelled, shipped→delivered. And because the filter carries the current status, two concurrent requests can’t double-advance a shop order; one of them always loses. When Shop B marks a delivered order, only Shop B’s earnings are released and its status moves.
The interesting bit is the last step: the parent status is derived. Whenever every shop order reaches the same status, the empty parent follows automatically. All shops delivered → the order is delivered. Shop A shipped while Shop B is still processing → the parent keeps its previous label, because there’s no honest single answer for “processing with one shipped component.”
Why not separate order documents per shop? The customer paid once. They expect one order number, one receipt, one tracking page. If I split orders into separate documents per shop, I’d need to reassemble them for every customer-facing view. Extra queries, extra complexity.
Escrow via Wallet
Sellers don’t get paid immediately. The money sits in escrow until delivery:
// Package wallet manages seller earnings on Khoomi.
//
// The wallet has two balances:
// - Pending: from orders not yet delivered (cannot withdraw)
// - Available: from delivered orders (can withdraw)
//
// Money Flow:
// 1. CreditPending: Payment received -> +pending, +total_earnings
// 2. ReleaseEarnings: Order delivered -> -pending, +available
// 3. ProcessWithdrawal: Seller withdraws -> -available, +total_withdrawn
When payment succeeds, the seller’s pending balance increases:
func (s *walletService) CreditPendingBalance(ctx context.Context, shopID, orderID ObjectID, amount int64) error {
callback := func(sessCtx mongo.SessionContext) (any, error) {
// Check idempotency - already credited for this order?
count, _ := s.db.Coll.WalletTransactions.CountDocuments(sessCtx, bson.M{
"shop_id": shopID,
"order_id": orderID,
"type": TxTypeOrderEarning,
})
if count > 0 {
// Already credited
return nil, nil
}
tx := WalletTransaction{
ShopID: shopID,
Type: TxTypeOrderEarning,
Amount: amount,
OrderID: &orderID,
// ...
}
_, err := s.db.Coll.WalletTransactions.InsertOne(sessCtx, tx)
if err != nil {
return nil, err
}
update := bson.M{
"$inc": bson.M{
"pending_balance": amount,
"total_earnings": amount,
},
}
_, err = s.db.Coll.Wallets.UpdateOne(sessCtx, bson.M{"shop_id": shopID}, update)
return nil, err
}
_, err := database.ExecuteTransaction(ctx, s.db.MongoClient, callback)
return err
}
When the order is delivered, pending becomes available:
func (s *walletService) ReleaseEarnings(ctx context.Context, shopID, orderID ObjectID) error {
callback := func(sessCtx mongo.SessionContext) (any, error) {
// Find the pending transaction
var pendingTx WalletTransaction
err := s.db.Coll.WalletTransactions.FindOne(sessCtx, bson.M{
"shop_id": shopID,
"order_id": orderID,
"type": TxTypeOrderEarning,
}).Decode(&pendingTx)
if err != nil {
return nil, ErrPendingEarningsNotFound
}
amount := pendingTx.Amount
releaseTx := WalletTransaction{
ShopID: shopID,
Type: TxTypeEarningReleased,
Amount: amount,
OrderID: &orderID,
Description: "Earnings released - order delivered",
}
_, err = s.db.Coll.WalletTransactions.InsertOne(sessCtx, releaseTx)
if err != nil {
return nil, err
}
// Move from pending to available
walletUpdate := bson.M{
"$inc": bson.M{
"pending_balance": -amount,
"available_balance": amount,
},
}
_, err = s.db.Coll.Wallets.UpdateOne(sessCtx, bson.M{"shop_id": shopID}, walletUpdate)
return nil, err
}
_, err := database.ExecuteTransaction(ctx, s.db.MongoClient, callback)
return err
}
Why escrow? Buyer protection. If a seller never ships or order status is “paid”, the money can be refunded. If the item arrives damaged, there’s a dispute window before the seller can withdraw.
The trade-off? Sellers wait for their money. Cash flow suffers. But for a marketplace with unknown sellers, trust requires holding funds until delivery is confirmed.
The settlement split
The wallet view hides the rest of the ledger. At payment time, the settlement engine recomputes a three-pot split for each shop order and verifies it against what was stamped at checkout:
- Seller earnings — credited to the wallet’s pending balance. This is the only slice that ever enters the wallet.
- Shipping escrow — locks the full carrier cost in a separate account, released to the carrier on fulfillment (or clawed back on refund). If a promo funded the shipping, a
ShippingFundedByflag records who covers the gap. - Platform fees — credited to the platform account in the same transaction.
So “release earnings on delivery” means: the seller’s slice moves pending → available, and the shipping escrow pays the carrier. Two locks, two releases, one delivery event.
Partial Cancellation
One shop can cancel their order without affecting others:
func (s *orderService) CancelOrderByShop(ctx context.Context, orderID, shopID ObjectID, reason string) error {
order, _ := s.GetOrderByID(ctx, orderID)
// Find this shop's order
var shopOrder *ShopOrder
for i := range order.ShopOrders {
if order.ShopOrders[i].ShopID == shopID {
shopOrder = &order.ShopOrders[i]
break
}
}
needsRefund := order.Status == OrderStatusPaid || order.Status == OrderStatusProcessing
callback := func(sessCtx mongo.SessionContext) (any, error) {
updateFields := bson.M{
"shop_orders.$[elem].shop_order_status": OrderStatusCancelled,
"shop_orders.$[elem].updated_at": time.Now(),
}
// Create refund record if order was paid
if needsRefund {
refund := ShopOrderRefund{
Status: RefundStatusPending,
Amount: shopOrder.ShopTotal,
InitiatedBy: RefundByShop,
Reason: reason,
RequestedAt: time.Now(),
RetryCount: 0,
}
updateFields["shop_orders.$[elem].refund"] = refund
}
update := bson.M{"$set": updateFields}
arrayFilters := options.Update().SetArrayFilters(options.ArrayFilters{
Filters: []any{bson.M{"elem.shop_id": shopID}},
})
_, err := s.db.Coll.Orders.UpdateOne(sessCtx, bson.M{"_id": orderID}, update, arrayFilters)
if err != nil {
return nil, err
}
// Restore inventory
for _, item := range shopOrder.Items {
inventoryUpdate := bson.M{
"$inc": bson.M{"inventory.quantity": item.Quantity},
}
_, _ = s.db.Coll.Listings.UpdateOne(sessCtx, bson.M{"_id": item.ListingID}, inventoryUpdate)
}
// Check if ALL shops are now cancelled
updatedOrder, _ := s.GetOrderByID(sessCtx, orderID)
allCancelled := true
for _, so := range updatedOrder.ShopOrders {
if so.ShopOrderStatus != OrderStatusCancelled {
allCancelled = false
break
}
}
// If all cancelled, cancel the whole order
if allCancelled {
mainUpdate := bson.M{
"$set": bson.M{
"status": OrderStatusCancelled,
"payment_status": "refund_pending",
"cancelled_at": time.Now(),
},
}
_, err = s.db.Coll.Orders.UpdateOne(sessCtx, bson.M{"_id": orderID}, mainUpdate)
}
return nil, err
}
_, err := database.ExecuteTransaction(ctx, s.db.MongoClient, callback)
return err
}
The $[elem] array filter lets me target a specific shop order by its shop_id. If Shop A cancels but Shop B is still fulfilling, the parent order stays active. Only Shop A’s portion shows as cancelled.
Why allow partial cancellation? Stock issues happen. A seller might realize they can’t fulfill an item after accepting the order. Cancelling the entire order (including items from other shops that are ready to ship) would punish buyers and other sellers for one shop’s mistake.
The real method goes through a cancel plan, not raw field edits. The plan answers a few questions up front: can this shop cancel at all, may it cancel from shipped (only before the carrier confirms pickup), does inventory need restoring, which shops get refund records, and what should the parent become if every shop cancels. After the transaction, applied coupons for the cancelled branch get rolled back, and the refund records the plan creates carry the reverse-split amounts — seller earnings clawback, shipping-escrow debit, and platform-fee clawback are tracked separately, not rolled into one number.
When a paid order is cancelled, it also creates a refund record:
type ShopOrderRefund struct {
Status RefundStatus `bson:"status"` // pending/processing/completed/failed
Amount int64 `bson:"amount"` // in kobo
InitiatedBy RefundInitiator `bson:"initiated_by"` // customer/shop/system
Reason string `bson:"reason,omitempty"`
RequestedAt time.Time `bson:"requested_at"`
ProcessedAt *time.Time `bson:"processed_at,omitempty"`
CompletedAt *time.Time `bson:"completed_at,omitempty"`
Reference string `bson:"reference,omitempty"`
FailureReason string `bson:"failure_reason,omitempty"`
RetryCount int `bson:"retry_count"`
LastRetryAt *time.Time `bson:"last_retry_at,omitempty"`
// Reverse settlement split: each pot clawed back separately.
Scope RefundScopeName `bson:"refund_scope,omitempty"`
WalletDeductAmount int64 `bson:"wallet_deduct_amount,omitempty"` // seller earnings
EscrowDebitAmount int64 `bson:"escrow_debit_amount,omitempty"` // shipping escrow
PlatformDebitAmount int64 `bson:"platform_debit_amount,omitempty"` // fee clawback
}
Automated Refund Processing
Refunds run on a scheduler, not inline with cancellation:
func (j *RefundsJob) Run(ctx context.Context) {
orders, _ := j.orderService.GetPendingRefunds(ctx)
for _, order := range orders {
for _, shopOrder := range order.ShopOrders {
if shopOrder.Refund == nil || shopOrder.Refund.Status != RefundStatusPending {
continue
}
// Max 5 retries before marking as failed
if shopOrder.Refund.RetryCount >= MaxRefundRetries {
j.orderService.MarkRefundAsFailed(ctx, order.ID, shopOrder.ShopID,
"Maximum retry attempts exceeded")
continue
}
j.orderService.ProcessShopOrderRefund(ctx, order.ID, shopOrder.ShopID)
}
}
}
Why async? Payment provider APIs fail. Network timeouts happen. If I processed refunds inline with cancellation, a Paystack outage would block the entire cancellation flow. With a scheduler, the cancellation succeeds immediately. The customer sees “cancelled” right away, and the actual money movement retries in the background until it works.
Processing a refund deducts from the seller’s pending balance:
func (s *orderService) ProcessShopOrderRefund(ctx context.Context, orderID, shopID ObjectID) error {
// Mark as processing (with retry count increment)
updateProcessing := bson.M{
"$set": bson.M{
"shop_orders.$.refund.status": RefundStatusProcessing,
"shop_orders.$.refund.processed_at": time.Now(),
},
"$inc": bson.M{
"shop_orders.$.refund.retry_count": 1,
},
}
s.db.Coll.Orders.UpdateOne(ctx, filter, updateProcessing)
// Deduct from seller wallet
err := s.wallet.DeductForRefund(ctx, shopID, orderID, shopOrder.Refund.Amount)
if err != nil {
// Revert to pending for retry
revertUpdate := bson.M{
"$set": bson.M{
"shop_orders.$.refund.status": RefundStatusPending,
"shop_orders.$.refund.failure_reason": fmt.Sprintf("Wallet deduction failed: %v", err),
},
}
s.db.Coll.Orders.UpdateOne(ctx, ...)
return err
}
completeUpdate := bson.M{
"$set": bson.M{
"shop_orders.$.refund.status": RefundStatusCompleted,
"shop_orders.$.refund.completed_at": time.Now(),
},
}
s.db.Coll.Orders.UpdateOne(ctx, ...)
return nil
}
The wallet deduction reverses the original credit:
func (s *walletService) DeductForRefund(ctx context.Context, shopID, orderID ObjectID, amount int64) error {
callback := func(sessCtx mongo.SessionContext) (any, error) {
// Idempotency check
count, _ := s.db.Coll.WalletTransactions.CountDocuments(sessCtx, bson.M{
"shop_id": shopID,
"order_id": orderID,
"type": TxTypeRefundDeduction,
})
if count > 0 {
return nil, ErrRefundAlreadyProcessed
}
// Verify sufficient pending balance
var w SellerWallet
s.db.Coll.Wallets.FindOne(sessCtx, bson.M{"shop_id": shopID}).Decode(&w)
if w.PendingBalance < amount {
return nil, ErrInsufficientPendingBalance
}
tx := WalletTransaction{
ShopID: shopID,
Type: TxTypeRefundDeduction,
Amount: amount,
OrderID: &orderID,
Description: "Refund deduction for cancelled order",
}
s.db.Coll.WalletTransactions.InsertOne(sessCtx, tx)
// Deduct from pending and total earnings
walletUpdate := bson.M{
"$inc": bson.M{
"pending_balance": -amount,
"total_earnings": -amount,
},
}
s.db.Coll.Wallets.UpdateOne(sessCtx, bson.M{"shop_id": shopID}, walletUpdate)
return nil, nil
}
_, err := database.ExecuteTransaction(ctx, s.db.MongoClient, callback)
return err
}
The refund deducts from pending_balance and total_earnings — the seller never actually earned this money, it’s going back to the customer. But the deduction is only the seller earnings slice. The same refund also debits the shipping escrow and claws back the platform fees, each pot tracked separately in the refund record. The buyer-facing refund amount is the sum; the bookkeeping keeps the parts.

The trade-off? If the seller somehow withdrew before delivery (which shouldn’t happen, since pending balance can’t be withdrawn), the deduction fails. After 5 retries, the refund is marked as failed, and I get an alert to handle it manually.
Cleanup Expired Orders
Pending orders that never get paid should release their inventory:
// GetPendingOrdersForCleanup finds them; CancelExpiredOrder cancels one.
func (s *orderService) CancelExpiredOrder(ctx context.Context, orderID ObjectID) error {
expiredTime := time.Now().Add(-24 * time.Hour)
var order models.Order
callback := func(sessCtx mongo.SessionContext) (any, error) {
// Cancel the order (guarded: already-cancelled or paid won't match)
update := bson.M{
"$set": bson.M{
"status": OrderStatusCancelled,
"payment_status": "expired",
"internal_note": "Auto-cancelled: Payment not received within expiration period",
"cancelled_at": time.Now(),
"shop_orders.$[].shop_order_status": OrderStatusCancelled,
},
}
result, err := s.db.Coll.Orders.UpdateOne(sessCtx,
bson.M{"_id": orderID, "status": OrderStatusPending}, update)
if result.ModifiedCount == 0 {
// Already cancelled or paid — nothing to do
return nil, nil
}
// Restore all inventory
order, _ = s.GetOrderByID(sessCtx, orderID)
for _, shop := range order.ShopOrders {
for _, item := range shop.Items {
inventoryUpdate := bson.M{
"$inc": bson.M{"inventory.quantity": item.Quantity},
}
_, _ = s.db.Coll.Listings.UpdateOne(sessCtx, bson.M{"_id": item.ListingID}, inventoryUpdate)
}
}
return nil, nil
}
}
_, err := os.db.WithTransaction(ctx, callback)
return err
}
A PendingOrdersJob runs every six hours and publishes an expiration event that a consumer acts on. Orders expire after 24 hours of unpaid pending — and a reminder fires at the 12-hour mark so the customer can still pay before the cutoff. That window gives customers enough time to complete bank transfers (which can take hours in Nigeria), but not long enough to hold inventory hostage indefinitely.
The handler is careful not to kill a paid order that looks pending. It re-syncs each order’s payment status first (VerifyAndUpdatePaidOrder): if the payment actually landed, the order gets marked paid and skipped. Only truly unpaid orders are cancelled. And the "status": OrderStatusPending inside the update filter keeps the whole thing idempotent — if payment came through between the scan and the update, the filter simply won’t match.
Each cancellation runs in its own transaction: flip the status, restore inventory, enqueue the expired-order event, and after the commit, roll back any applied coupons.
Seller Payout Calculation
Each shop order tracks exactly what the seller receives:
type SellerPayout struct {
Amount int64 `bson:"amount"` // Customer paid (kobo)
PlatformFee int64 `bson:"platform_fee"` // Khoomi's cut (kobo)
TransactionFee int64 `bson:"transaction_fee"` // Payment processor (kobo)
NetAmount int64 `bson:"net_amount"` // Seller receives (kobo)
PayoutStatus string `bson:"payout_status"`
}
Calculated at checkout:
shopTotal := shopSubtotal + buyerFacingShipping + handlingFee - shopDiscount
// Fees carve off the item subtotal, not the shop total
platformFee := shopSubtotal * PLATFORM_FEE_RATE / PERCENT_DIVISOR / 100
transactionFee := shopSubtotal * TRANSACTION_FEE_RATE / PERCENT_DIVISOR / 100
netAmount := shopTotal - platformFee - transactionFee
Stamped at checkout so a seller viewing an incoming order sees roughly what they’ll get — “You’ll get ₦54,000 from this ₦60,000 order.” But the authoritative number comes later: at payment, the settlement engine recomputes the three-pot split (earnings, shipping escrow, fees) and asserts it matches the stamped breakdown before crediting the wallet. The NetAmount above is display-only; the wallet is credited the settlement’s earnings slice.
Why calculate at checkout instead of delivery? Transparency. No surprises when withdrawal time comes.
The trade-off? Fee changes don’t apply retroactively. If I lower the platform fee tomorrow, orders placed today still use today’s rate. For accounting consistency, that’s actually what I want.
What I’d Do Differently
The parent order status — done. This used to be a wishlist item: Order.Status mixed meanings whenever shops diverged. The code now derives the parent status — when every shop order reaches the same status, the parent follows. The genuinely mixed case (one shipped, one processing) still has no single label, and that’s correct: there isn’t one.
Refund settlement coupling. The reverse split (wallet, escrow, platform fees) is correct now, but it lives spread across three services and a hand-written invariant check that fails closed on mismatch. If the money model evolves again, that’s the file I expect to touch. I’d like the split rules in one place with property tests over the invariants.
Refund status visibility — mostly solved. Refund requests now live in their own collection, so support’s “all failed refunds this week” query is a normal indexed query instead of a scan. The remaining smell is that the processing state still lives embedded on the shop order — a reasonable place for single-order views, but it means the scheduler and the request collection must agree on whose refund is whose.
The Pattern
Same philosophy as the earlier posts: optimize for the common case.
- Most orders have 1-2 shops → embed, don’t normalize
- Most checkouts succeed → reserve at checkout, not cart add
- Most shops fulfill successfully → escrow by default, release on delivery
- Most cancellations are partial → per-shop status, not all-or-nothing
- Most refunds succeed on first try → async processing with retry, not inline
The multi-vendor order system handles the 90% case elegantly. The 10% (complex disputes, multi-shop returns, refunds that fail after 5 retries) requires me to step in manually. At Khoomi’s current scale, that’s maybe one or two cases a week. Acceptable.
Next: Wallets and seller withdrawals.
—Samuel
Edits:
- 2026-08-19: Brought the post in line with the current code: the checkout transaction also records coupon usage and stock events; “independent fulfillment” now shows the concurrency guard and the derived parent status (the old “what I’d do differently” item, now done); added the settlement split — seller earnings, shipping escrow, and platform fees are kept in separate pots and refunds claw back each one; partial cancellation runs through a cancel plan with a carrier-pickup guard; expired orders are handled by a six-hour scheduler with a 12-hour reminder and a payment re-check first; fees are computed on the item subtotal and
net_amountis display-only, since the wallet is credited the settlement’s earnings slice.