Skip to main content

JikoXpress Financial System Architecture

Version 2.0 | April 2026 | QBIT SPARK CO LIMITED Refined architecture based on full design sessions — all scenarios, edge cases, and decisions captured.


Table of Contents

  1. Overview & Core Principles
  2. Platform Context
  3. Two Financial Worlds
  4. Treasury Design
  5. Wallet System
  6. Chart of Accounts
  7. Money Splitting
  8. Escrow System
  9. Payment Channels
  10. Full Money Journey — All Scenarios
  11. Subscription Payments
  12. Settlements & Payouts
  13. Refunds & Cancellations
  14. Admin Withdrawal
  15. PSP Reconciliation
  16. Reporting
  17. Safety Rules & Integrity Checks
  18. Decision Log

1. Overview & Core Principles

JikoXpress financial system is a unified internal accounting engine — a proper ledger that tracks every single money movement on the platform, regardless of amount (even TZS 0.00), channel, or purpose.

Core Principles

Principle Description
Every event is a transaction Cash, mobile money, wallet, kitchen wallet, TZS 0 promo orders — all recorded
Double entry accounting Every debit has a credit. Books always balance
Single source of truth Internal ledger is authoritative — not the external PSP
Dynamic money splitting Splits are passed as parameters, not hardcoded
Escrow is a flag Any transaction can be held — decided at runtime by the caller
Everyone has one wallet Same JikoXpress account can be customer, kitchen owner, and rider — one wallet
Counter orders stay outside POS/cash/kitchen custom payments never enter the treasury pool
Platform earns on online only Service fee applies only to App and WhatsApp orders

2. Platform Context

JikoXpress Pro connects customers with local chefs and restaurants (like Toasty for East Africa). The platform has:

  • 7 sales channels: POS, Kiosk, Table QR, Mobile App, WhatsApp, Drive-Through, Direct Counter
  • 4 subscription tiers: STARTER, GROWING, PROFESSIONAL, ENTERPRISE
  • Multiple fulfillment types: Dine-in, Pickup, Delivery, Drive-Through
  • East Africa first: Built for M-Pesa, Tigo Pesa, Airtel Money via Selcom, Azampay

Who Are the Actors?

Every person on the platform has one account and one wallet — regardless of their role:

Same JikoXpress account can be:
├── Customer       — orders food via App/WhatsApp
├── Kitchen owner  — runs a kitchen on the platform
└── Rider          — delivers orders for kitchens

One person. One wallet. All roles.


3. Two Financial Worlds

Not all money flows through JikoXpress. There are two separate worlds:

┌──────────────────────────────────────────────────────┐
│              WORLD 1 — JikoXpress Pool                │
│                                                        │
│  App orders, WhatsApp orders                          │
│  Money flows through our PSP accounts                 │
│  Treasury tracks everything                           │
│  Platform earns service fee here                      │
└──────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────┐
│         WORLD 2 — Kitchen's Own World                 │
│                                                        │
│  POS cash, card, kitchen custom payments              │
│  Money goes directly to kitchen's physical till       │
│  or kitchen's own mobile money account                │
│  Treasury never sees this money                       │
│  Platform earns ZERO here (covered by subscription)  │
└──────────────────────────────────────────────────────┘

Which Channels Belong to Which World?

Channel World Service Fee Treasury Involvement
Mobile App JikoXpress Pool Yes Full
WhatsApp JikoXpress Pool Yes Full
POS — Cash Kitchen's World No None
POS — Card Kitchen's World No None
POS — Kitchen Wallet Kitchen's World No None
POS — Kitchen Custom (Lipa Voda etc) Kitchen's World No None
Kiosk — Cash Kitchen's World No None
Table QR — Cash Kitchen's World No None
Drive-Through — Cash Kitchen's World No None

The rule: Platform only earns a service fee when it brings the customer. Counter/automation channels are covered by the kitchen's subscription fee — not per-order cuts.


4. Treasury Design

The treasury is the central bank of JikoXpress. It is a proper ledger using double entry accounting — every money movement has two sides (debit and credit), books always balance.

What Double Entry Means

Money never appears or disappears — it always moves from somewhere to somewhere.

Example: Customer pays TZS 10,000 via Mpesa

Money arrives at Selcom (we now have it):
DEBIT  ASSET_PSP_SELCOM     10,000

Money is split:
CREDIT LIABILITY_WALLETS      9,000  (kitchen's cut — we owe them)
CREDIT REVENUE_SERVICE_FEE    1,000  (we earned this)

Total debits = Total credits = TZS 10,000 ✅

Treasury Buckets — What the Money Is

At any moment, every shilling in the treasury is tagged to a bucket:

ASSET_PSP_SELCOM      — physical money sitting at Selcom
ASSET_PSP_AZAMPAY     — physical money sitting at Azampay
ASSET_ESCROW          — held money, belongs to nobody yet

LIABILITY_WALLETS     — belongs to ALL users (customers, kitchen owners, riders)
                        this is their money — we owe it back on demand
LIABILITY_SETTLEMENTS — payout requests being processed, on the way out

REVENUE_SERVICE_FEE      — platform's cut from online orders (OURS)
REVENUE_SUBSCRIPTION     — monthly/yearly kitchen fees (OURS)

EXPENSE_REFUNDS       — money returned to customers

The Golden Safety Rule

ASSET_PSP_SELCOM + ASSET_PSP_AZAMPAY + ASSET_ESCROW
>=
LIABILITY_WALLETS + LIABILITY_SETTLEMENTS

If this breaks → crisis alert immediately. Platform is spending money it doesn't own.


5. Wallet System

User Wallet (Personal)

Every registered JikoXpress user gets one wallet automatically on account creation.

  • One wallet per person, regardless of role
  • Used to: top up, pay orders, receive earnings (kitchen revenue, delivery fees), withdraw to Mpesa
  • Lives under LIABILITY_WALLETS in treasury — it's the user's money, not the platform's

Kitchen Wallet (Kitchen-Level)

A completely separate system — not the platform wallet.

  • Issued by the kitchen to their own loyal customers
  • Kitchen manages it independently (deposits, deductions, balance)
  • Has its own ledger — kitchen's books, not platform's
  • Platform only records "paid via KITCHEN_WALLET" in the transaction — no treasury movement

Wallet vs Treasury

Level What It Sees
Treasury LIABILITY_WALLETS = TZS 5,000,000 (total of all wallets combined)
Wallet level Kibuti = TZS 45,000 / Mama Lishe = TZS 230,000 / John rider = TZS 80,000

Treasury sees the total obligation. Individual wallets explain the breakdown.

Wallet Transaction Ledger

Every wallet movement creates a record with:

  • type — TOPUP, ORDER_PAYMENT, ORDER_EARNING, DELIVERY_EARNING, WITHDRAWAL, SUBSCRIPTION_PAYMENT
  • direction — CREDIT (in) or DEBIT (out)
  • amount
  • balanceBefore and balanceAfter — full audit trail, history reconstructable at any point
  • referenceId — order ID, payout ID etc

6. Chart of Accounts

ASSETS  (what platform has / controls)
├── ASSET_PSP_SELCOM          Physical money at Selcom
├── ASSET_PSP_AZAMPAY         Physical money at Azampay
└── ASSET_ESCROW              Held money — no owner yet

LIABILITIES  (what platform owes — never touch without permission)
├── LIABILITY_WALLETS         All user wallets combined
└── LIABILITY_SETTLEMENTS     Payout requests being processed

REVENUE  (platform's own earnings)
├── REVENUE_SERVICE_FEE       Cut from App/WhatsApp orders
└── REVENUE_SUBSCRIPTION      Kitchen monthly/yearly fees

EXPENSE  (money platform spent)
└── EXPENSE_REFUNDS           Refunds sent back to customers

EQUITY  (platform net worth)
├── EQUITY_CAPITAL            Admin deposits / investments into platform
└── EQUITY_RETAINED_EARNINGS  Accumulated platform profits

P&L Formula

Platform Net Profit = REVENUE_SERVICE_FEE + REVENUE_SUBSCRIPTION - EXPENSE_REFUNDS

Admin can only withdraw from Net Profit — never from ASSETS covering LIABILITIES

7. Money Splitting

When an online order is paid, the total is split into buckets dynamically. The caller (checkout service) passes the split parameters — the financial service just executes. No business logic in the financial service.

Split Buckets per Online Order

Split Type Goes To Notes
Menu revenue Kitchen owner's wallet Main food items
Packaging fee Kitchen owner's wallet Kitchen sells packaging too
Delivery fee Rider's wallet Only on delivery orders
Service fee REVENUE_SERVICE_FEE App/WhatsApp only

Service Fee — Configurable Refundability

Controlled by a flag in the platform config:

service.fee.refundable = true   → service fee goes into escrow with the rest
                                  on cancel → full refund including service fee

service.fee.refundable = false  → service fee goes to REVENUE immediately on confirmation
                                  on cancel → only kitchen/rider money refunded
                                  platform keeps its cut

Early stage → set true to build customer trust. Mature stage → flip to false to protect platform revenue. No code change needed — one config change.


8. Escrow System

Escrow is a dynamic flag, not a hardcoded rule. The checkout service decides whether to hold money based on the order context, and passes this to the financial service.

How It Works

Every transaction has:

  • holdInEscrow: true/false
  • escrowReleaseCondition: DELIVERY_CONFIRMED | PICKUP_CODE_CONFIRMED | null

Financial service does exactly what it's told. It doesn't know WHY — the checkout layer knows that.

Default Business Rules (Set by Checkout, Not Financial Service)

Fulfillment Type Payment Channel Hold in Escrow? Release Condition
Delivery Mobile Money Yes DELIVERY_CONFIRMED
Delivery Platform Wallet Yes DELIVERY_CONFIRMED
Pickup Mobile Money Yes PICKUP_CODE_CONFIRMED
Pickup Platform Wallet Yes PICKUP_CODE_CONFIRMED
Dine-in Any online No Immediate
Any Cash No (cash is physical) Immediate

Rules can change without touching the financial service — just update the checkout layer logic.


9. Payment Channels

Channel Hits Treasury? External PSP API? Escrow Possible? Service Fee?
CASH No No No No
KITCHEN_CUSTOM (Lipa Voda etc) No No No No
KITCHEN_WALLET No No No No
MOBILE_MONEY (USSD) Yes Yes (Selcom/Azampay) Yes If online channel
PLATFORM_WALLET Yes (internal) No Yes If online channel
CARD Yes Yes (card gateway) Yes If online channel

10. Full Money Journey — All Scenarios

Chapter 1 — Counter Orders (World 2 — No Treasury Involvement)


Scenario 1.1 — POS Cash, Any Fulfillment

Customer pays TZS 10,000 cash at counter

→ Order recorded ✅
→ Transaction logged (channel: CASH) ✅
→ Receipt generated ✅
→ Money physically in kitchen till 💰
→ Treasury: nothing moves ❌
→ Platform earns: TZS 0 (covered by subscription)

Kitchen sales report shows: CASH += TZS 10,000

Scenario 1.2 — POS Kitchen Custom Payment (Lipa Voda, HaloPesa)

Customer pays TZS 15,000 via Lipa Voda at counter

→ Order recorded ✅
→ Transaction logged (channel: KITCHEN_CUSTOM) ✅
→ Money goes to kitchen's own Lipa Voda account 💰
→ Treasury: nothing moves ❌
→ Platform earns: TZS 0

Kitchen sales report shows: KITCHEN_CUSTOM += TZS 15,000

Scenario 1.3 — POS Kitchen Wallet Payment

Loyal customer has TZS 20,000 in kitchen wallet
Orders food TZS 8,000, pays from kitchen wallet

→ Order recorded ✅
→ Transaction logged (channel: KITCHEN_WALLET) ✅
→ Kitchen wallet balance -= TZS 8,000 (kitchen manages independently)
→ Treasury: nothing moves ❌
→ Platform earns: TZS 0

Kitchen sales report shows: KITCHEN_WALLET += TZS 8,000

Scenario 1.4 — Kiosk, Table QR, Drive-Through (Cash)

Same as above — counter level, no treasury, no service fee. Order recorded, money stays in kitchen's physical world.


Chapter 2 — Online Orders (World 1 — Treasury Involved)


Scenario 2.1 — App Order, Pickup, Mobile Money (USSD)

Total: TZS 12,000
  Menu:         TZS 10,500
  Packaging:    TZS   500
  Service fee:  TZS 1,000

STEP 1 — Payment initiated:
  Checkout session created, inventory held
  USSD prompt sent to customer via Selcom API
  Customer confirms on phone
  Selcom webhook fires

STEP 2 — Money arrives, held in escrow (pickup not yet confirmed):
  DEBIT  ASSET_PSP_SELCOM    12,000
  CREDIT ASSET_ESCROW        12,000

STEP 3 — Order confirmed, kitchen notified, kitchen prepares

STEP 4 — Customer arrives, shows pickup code, confirmed:
  DEBIT  ASSET_ESCROW              12,000
  CREDIT LIABILITY_WALLETS          11,000  (kitchen owner)
  CREDIT REVENUE_SERVICE_FEE         1,000  (platform)

  Kitchen wallet += TZS 11,000 ✅
  Platform revenue += TZS 1,000 ✅

Scenario 2.2 — App Order, Delivery, Mobile Money

Total: TZS 18,000
  Menu:          TZS 12,000
  Packaging:     TZS  1,000
  Delivery fee:  TZS  4,000
  Service fee:   TZS  1,000

STEP 1 — Money arrives, full amount held in escrow:
  DEBIT  ASSET_PSP_SELCOM    18,000
  CREDIT ASSET_ESCROW        18,000

STEP 2 — Order confirmed, kitchen prepares, rider picks up

STEP 3 — Delivery confirmed:
  DEBIT  ASSET_ESCROW              18,000
  CREDIT LIABILITY_WALLETS         12,000  (kitchen — menu + packaging)
  CREDIT LIABILITY_WALLETS          4,000  (rider — delivery fee)
  CREDIT REVENUE_SERVICE_FEE        1,000  (platform)

  Kitchen wallet += TZS 12,000 ✅
  Rider wallet   += TZS 4,000 ✅
  Platform       += TZS 1,000 ✅

Scenario 2.3 — App Order, Delivery, JikoXpress Wallet Payment

Customer has TZS 30,000 in JikoXpress wallet
Pays TZS 18,000 from wallet — NO PSP API call, purely internal

STEP 1 — Wallet debited, goes to escrow:
  DEBIT  LIABILITY_WALLETS     18,000  (customer wallet decreases)
  CREDIT ASSET_ESCROW          18,000  (held)

STEP 2 — Delivery confirmed, escrow releases:
  DEBIT  ASSET_ESCROW              18,000
  CREDIT LIABILITY_WALLETS         12,000  (kitchen)
  CREDIT LIABILITY_WALLETS          4,000  (rider)
  CREDIT REVENUE_SERVICE_FEE        1,000  (platform)

  No money entered or left the pool — purely internal redistribution ✅

Scenario 2.4 — App Order, Dine-in, Mobile Money

Dine-in — no escrow needed (no delivery/pickup risk)

  DEBIT  ASSET_PSP_SELCOM     11,000
  CREDIT LIABILITY_WALLETS     10,000  (kitchen)
  CREDIT REVENUE_SERVICE_FEE    1,000  (platform)

Immediate split. No escrow. ✅

Scenario 2.5 — WhatsApp Order, Pickup, Mobile Money

Same flow as Scenario 2.1. Channel recorded as WHATSAPP. Service fee applies. Escrow holds until pickup code confirmed.


Scenario 2.6 — Split Payment (Multiple Methods)

Order total: TZS 20,000
  Customer pays TZS 10,000 from wallet
  Customer pays TZS 10,000 via USSD

Two TransactionEntities — both linked to same order, both held in escrow

  DEBIT  LIABILITY_WALLETS     10,000  (wallet portion)
  DEBIT  ASSET_PSP_SELCOM      10,000  (USSD portion)
  CREDIT ASSET_ESCROW          20,000  (total held)

On release → same split logic, total amount split proportionally ✅

Scenario 2.7 — Platform Offer / Promo Applied

Customer orders TZS 10,000 via App
Platform covers delivery fee TZS 2,000 (free delivery offer)
Customer pays TZS 8,000

Two TransactionEntities:
  1. Customer: TZS 8,000 via USSD → escrow
  2. Platform: TZS 2,000 subsidy (paidBy = PLATFORM) → escrow

On delivery confirmed:
  DEBIT  ASSET_ESCROW              10,000
  CREDIT LIABILITY_WALLETS          8,000  (kitchen)
  CREDIT LIABILITY_WALLETS          2,000  (rider — full fee covered)
  CREDIT REVENUE_SERVICE_FEE          500  (platform still earns cut on food amount)

  PlatformOfferUsageEntity created — budget tracked
  If budgetUsed >= budget → offer auto-deactivated ✅

Scenario 2.8 — TZS 0.00 Free Order (Full Promo)

Platform gives customer completely free order

TransactionEntity still created:
  amount: 0.00
  channel: PLATFORM
  holdInEscrow: false

Still recorded. Kitchen notified. Order flows normally.
No money moves. Event is captured. ✅

Chapter 3 — Wallet Operations


Scenario 3.1 — User Tops Up Wallet

Kibuti tops up TZS 50,000 via Mpesa

  DEBIT  ASSET_PSP_SELCOM      50,000
  CREDIT LIABILITY_WALLETS     50,000  (belongs to Kibuti)

Kibuti wallet += TZS 50,000 ✅

Scenario 3.2 — Anyone Requests Payout to Mpesa

Kitchen owner (or rider, or customer) withdraws TZS 30,000

STEP 1 — Money earmarked:
  DEBIT  LIABILITY_WALLETS      30,000
  CREDIT LIABILITY_SETTLEMENTS  30,000  (being processed)

STEP 2 — JikoXpress calls Selcom API, sends to Mpesa:
  DEBIT  LIABILITY_SETTLEMENTS  30,000
  CREDIT ASSET_PSP_SELCOM       30,000  (money leaves)

Money arrives on Mpesa ✅
Same flow for customers, kitchen owners, riders

Scenario 3.3 — Payout Fails

Selcom API returns failure (wrong number, system down etc)

  DEBIT  LIABILITY_SETTLEMENTS  30,000  (earmark reversed)
  CREDIT LIABILITY_WALLETS      30,000  (money back in wallet)

User notified. They can retry. No money lost. ✅

11. Subscription Payments

Kitchen owners pay monthly or yearly. They choose how to pay.


Scenario 11.1 — Subscription via Wallet

Mama Lishe pays TZS 15,000/month from wallet

Pure internal — no PSP:
  DEBIT  LIABILITY_WALLETS      15,000
  CREDIT REVENUE_SUBSCRIPTION   15,000

Kitchen wallet -= TZS 15,000
Platform revenue += TZS 15,000 ✅

Scenario 11.2 — Subscription via Mobile Money

  DEBIT  ASSET_PSP_SELCOM       15,000
  CREDIT REVENUE_SUBSCRIPTION   15,000

Platform revenue += TZS 15,000 ✅

Renewal Logic

On renewal date:
1. Check if owner chose wallet AND has sufficient balance
2. If yes → auto deduct (Scenario 11.1)
3. If no balance OR chose mobile money → send payment prompt
4. If payment fails → grace period (configurable, e.g. 3 days)
5. If still unpaid after grace → kitchen suspended

12. Settlements & Payouts

Any user can request a payout at any time — customers, kitchen owners, riders. Same mechanism for all.

Rules

  1. Amount cannot exceed available wallet balance
  2. Minimum payout amount configurable in platform settings
  3. Processing time depends on external PSP
  4. Failed payouts → money returns to wallet automatically
  5. Full audit trail — who requested, when, destination, result

13. Refunds & Cancellations


Scenario 13.1 — Order Cancelled, Money in Escrow, service.fee.refundable=true

TZS 18,000 in escrow, full refund including service fee

  DEBIT  ASSET_ESCROW          18,000
  CREDIT ASSET_PSP_SELCOM      18,000

Selcom API → refund to customer Mpesa
Platform earns nothing ✅

Scenario 13.2 — Order Cancelled, service.fee.refundable=false

Platform keeps service fee

  DEBIT  ASSET_ESCROW           18,000
  CREDIT ASSET_PSP_SELCOM       17,000  (customer refund minus fee)
  CREDIT REVENUE_SERVICE_FEE     1,000  (platform keeps)

Scenario 13.3 — Order Cancelled, Paid via Wallet

  DEBIT  ASSET_ESCROW          18,000
  CREDIT LIABILITY_WALLETS     18,000  (back to customer wallet)

No PSP call. Pure internal. Instant refund to wallet ✅

Scenario 13.4 — Post-Completion Refund (Dispute)

Order delivered, escrow already released, kitchen already paid
Rare — dispute/complaint scenario. Manual admin action required.

  DEBIT  LIABILITY_WALLETS      10,000  (kitchen wallet reduced)
  DEBIT  EXPENSE_REFUNDS        10,000
  CREDIT ASSET_PSP_SELCOM       10,000  (refund to customer)

Full audit trail. SUPER_ADMIN approval required.

14. Admin Withdrawal

Platform accumulates profit from service fees and subscriptions. Admin can withdraw profit only.

Rules

  1. Only SUPER_ADMIN can request
  2. Requires second SUPER_ADMIN approval
  3. Can only withdraw from net profit (REVENUE minus EXPENSE minus already withdrawn)
  4. Can never withdraw from escrow — belongs to customers
  5. Can never withdraw from LIABILITY_WALLETS — belongs to users
  6. System enforces hard limit — no override

Withdrawal Calculation

Available to withdraw =
  REVENUE_SERVICE_FEE
  + REVENUE_SUBSCRIPTION
  - EXPENSE_REFUNDS
  - previously_withdrawn

If requested amount <= available → allow ✅
If requested amount > available  → reject hard ❌

Journal Entry

  DEBIT  EQUITY_RETAINED_EARNINGS  500,000
  CREDIT ASSET_PSP_SELCOM          500,000

Selcom API → transfers to admin Mpesa ✅

15. PSP Reconciliation

The Rule

Selcom actual balance >= ASSET_PSP_SELCOM  ✅ (normal — other money may be there too)
Selcom actual balance <  ASSET_PSP_SELCOM  ❌ CRISIS — money is missing

The Selcom account may receive money from other business activities unrelated to JikoXpress. That is expected and normal. The check only validates that JikoXpress-tracked money is fully covered.

Daily Reconciliation Process

1. Pull Selcom transaction log for the day
2. Match each incoming payment to a TransactionEntity by reference/amount
3. Flag unmatched payments (other sources — expected, not a problem)
4. Flag any JikoXpress transaction with no matching Selcom record (problem)
5. Alert if ASSET_PSP_SELCOM > Selcom actual balance (crisis)

16. Reporting

Treasury Snapshot (Platform View)

WHAT WE HAVE:
  At Selcom:              ASSET_PSP_SELCOM balance
  At Azampay:             ASSET_PSP_AZAMPAY balance
  In escrow:              ASSET_ESCROW balance

WHAT WE OWE:
  To all users (wallets): LIABILITY_WALLETS balance
  Payouts in progress:    LIABILITY_SETTLEMENTS balance

WHAT WE EARNED:
  Service fees:           REVENUE_SERVICE_FEE balance
  Subscriptions:          REVENUE_SUBSCRIPTION balance
  Net profit:             REVENUE_SERVICE_FEE + REVENUE_SUBSCRIPTION - EXPENSE_REFUNDS

Kitchen Owner Sales Report

Reads from orders + wallet transactions — NOT treasury directly:

SALES REPORT — Mama Lishe Kitchen | April 2026
──────────────────────────────────────────────
CHANNEL BREAKDOWN:
  App orders         TZS 150,000    (in your JikoXpress wallet)
  WhatsApp orders    TZS  80,000    (in your JikoXpress wallet)
  Cash (POS)         TZS 200,000    (collected physically — in your till)
  Lipa Voda (POS)    TZS  50,000    (in your Lipa Voda account)

TOTAL SALES:         TZS 480,000
──────────────────────────────────────────────
JIKOXPRESS WALLET:
  Available balance  TZS 230,000    (ready to withdraw)

Individual Wallet Statement

MY WALLET — Kibuti | April 2026
──────────────────────────────────────────────────────
Current Balance: TZS 45,000
──────────────────────────────────────────────────────
DATE      DESCRIPTION                  IN        OUT   BALANCE
Apr 23    Top up via Mpesa         50,000           50,000
Apr 23    Order #47 — Mama Lishe             5,000  45,000
Apr 22    Order #31 earnings        8,500           53,500
Apr 21    Withdrawal to Mpesa               15,000  38,500
Apr 20    Subscription — April              15,000  53,500
──────────────────────────────────────────────────────

Same wallet, same statement format — whether customer, kitchen owner, or rider. Transaction type field enables per-role breakdown reports.

How Treasury Gets Breakdown by Role

Total rider earnings in wallets  =
  sum(WalletTransactionEntity where type = DELIVERY_EARNING and not withdrawn)

Total kitchen earnings in wallets =
  sum(WalletTransactionEntity where type = ORDER_EARNING and not withdrawn)

Treasury sees LIABILITY_WALLETS = TZS 5,000,000. Wallet transaction types explain the breakdown underneath it.


17. Safety Rules & Integrity Checks

Primary Safety Check (Runs Continuously)

ASSET_PSP_SELCOM + ASSET_PSP_AZAMPAY + ASSET_ESCROW
>=
LIABILITY_WALLETS + LIABILITY_SETTLEMENTS

If false → ALERT IMMEDIATELY 🚨

Immutability Rules

  • JournalEntryEntity — never updated, never deleted. Append-only ledger.
  • TransactionEntity — status only moves forward: PENDING → COMPLETED → REFUNDED. Never backwards.
  • WalletTransactionEntity — never deleted. balanceBefore and balanceAfter on every record. History reconstructable at any point in time.

Escrow Integrity Check

ASSET_ESCROW balance = sum of all EscrowEntity where status = HELD

If mismatch → discrepancy alert 🚨

Admin Withdrawal Hard Limit

Withdrawal amount <= net profit available

System rejects if exceeded. No override. No exceptions.

Access Control

Action Who
View treasury SUPER_ADMIN
Request payout Any user (own wallet only)
Approve admin withdrawal Second SUPER_ADMIN
View platform P&L SUPER_ADMIN
View own wallet Any user
Manual post-completion refund SUPER_ADMIN + approval
Create platform offers SUPER_ADMIN

18. TODO — Pending Design Decisions

These are confirmed features that need to be designed when we reach the relevant service/module.


TODO-001 — Kitchen Delivery Configuration

Where: Kitchen settings / Kitchen config entity What: Kitchen owner needs ability to configure their delivery model:

deliveryModel:
  PLATFORM_RIDER   — use JikoXpress riders (default)
  OWN_RIDER        — kitchen has own boda, delivers themselves
  NO_DELIVERY      — kitchen does not offer delivery

absorbDeliveryFee: true/false
  true  — kitchen covers delivery cost for customer (free delivery for customer)
  false — customer pays delivery fee (default)

deliveryCoverageRange: Integer (km)
  — only absorb delivery fee for orders within this range
  — e.g. 3 = free delivery within 3km, customer pays beyond that

Impact on splits:

  • PLATFORM_RIDER + absorbDeliveryFee=true → kitchen split reduced, rider still paid, customer sees free delivery
  • OWN_RIDER → no rider split, no DisbursementEntity for rider, kitchen keeps full amount
  • Checkout service reads kitchen config and builds splits accordingly
  • Financial service just executes whatever splits it receives — no business logic inside

19. Decision Log

Decision Choice Rationale
Tax handling Dropped for V1 Reduces complexity. Kitchen owner responsible for TRA compliance. Add later if legal pressure arises.
Escrow rule location Dynamic flag from checkout caller Financial service stays dumb — just executes. Business logic lives in checkout. Decoupled.
Service fee refundability Config flag in properties file One switch changes behavior platform-wide. No code change needed.
Separate wallets per role No — one wallet per person Same account = customer, owner, rider. No artificial separation by role.
Treasury breakdown per role Not in treasury — in wallet transaction types Treasury sees total obligation. Breakdown via WalletTransactionEntity.type queries.
Full accounting vs simple ledger Proper double-entry ledger, not QuickBooks Platform holds other people's money — can't skip proper tracking. Kept fit-for-purpose, not over-engineered.
PSP accounts in chart Yes — one per PSP Need to know where physical money sits. Enables clean reconciliation.
Counter orders in treasury No Cash/kitchen custom never touch JikoXpress PSP. Platform is just a tool. Covered by subscription.
Admin withdrawal restriction Revenue/profit only Can never touch escrow or user wallet liabilities. Non-negotiable safety rule.
Payout eligibility Any user Customers, kitchen owners, riders — same wallet system, same payout flow. Role irrelevant.
Subscription payment method User's choice Flexibility. Auto-deduct from wallet if chosen and balance sufficient. Fallback to mobile money prompt.
Kitchen wallet Completely separate from platform Kitchen manages independently. Platform records usage only. No treasury involvement.

QBIT SPARK CO LIMITED | JikoXpress Pro | April 2026 Internal financial architecture document — confidential