Checkout ReqSample Senarials
JikoXpress — Checkout Session API Scenarios
Version 1.0 | April 2026 | QBIT SPARK CO LIMITED
Reference guide for all checkout session creation scenarios
Overview
A checkout session is a temporary stepping stone to an order. Once confirmed, the session creates an order and its job is done. Sessions are never refunded — orders are.
Session Types
| Type | Description |
|---|---|
IMMEDIATE |
Pay now, right at this terminal |
SLIP |
Generate slip, customer goes to cashier |
TAB |
Pay later, dine-in rounds |
Session Statuses
| Status | Description |
|---|---|
PENDING_PAYMENT |
Waiting for payment — USSD webhook or cashier confirmation |
CONFIRMED |
Payment done, order created |
EXPIRED |
Slip timed out, inventory released |
CANCELLED |
Manually cancelled, inventory released |
FAILED |
Payment failed, inventory released |
Payment Methods
| Method | Description |
|---|---|
CASH |
Physical money |
MOBILE_MONEY |
USSD — M-Pesa, Tigo, Airtel |
CARD |
Visa, Mastercard |
PLATFORM_WALLET |
JikoXpress app wallet |
KITCHEN_WALLET |
Kitchen issued prepaid balance |
KITCHEN_CHANNEL |
Kitchen configured channel — Lipa Voda, HaloPesa etc |
Channel & Fulfillment Matrix
| Channel | DINE_IN | PICKUP | DELIVERY | DRIVE_THROUGH |
|---|---|---|---|---|
POS |
✅ | ✅ | ❌ | ✅ |
KIOSK |
✅ | ✅ | ❌ | ❌ |
TABLE_QR |
✅ | ❌ | ❌ | ❌ |
APP |
❌ | ✅ | ✅ | ❌ |
WHATSAPP |
❌ | ✅ | ✅ | ❌ |
Server rejects any combination not in this matrix with a clear error message.
POS — Dine In, Pay Now
Customer sits at table, pays immediately — no tab opened.
{
"sessionType": "IMMEDIATE",
"channel": "POS",
"fulfillmentType": "DINE_IN",
"tableNumber": "05",
"tag": "John",
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": null}
],
"payments": [
{
"paymentMethod": "CASH",
"amount": 5000
}
]
}
POS — Drive Through, Pay Now
Customer in lane, staff takes order at window, pays immediately.
{
"sessionType": "IMMEDIATE",
"channel": "POS",
"fulfillmentType": "DRIVE_THROUGH",
"laneNumber": "1",
"tag": "Red Toyota",
"items": [
{"menuId": "uuid", "quantity": 2, "itemInstructions": null},
{"menuId": "uuid", "quantity": 1, "itemInstructions": "no onions"}
],
"payments": [
{
"paymentMethod": "CASH",
"amount": 8000
}
]
}
POS — Drive Through, Generate Slip
Customer in lane, staff takes order, generates slip. Customer drives to payment window.
{
"sessionType": "SLIP",
"channel": "POS",
"fulfillmentType": "DRIVE_THROUGH",
"laneNumber": "1",
"tag": "Blue Corolla",
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": null}
]
}
Kiosk — Dine In, Cash Slip
Customer at kiosk inside restaurant, orders for their table, pays cash at counter.
{
"sessionType": "SLIP",
"channel": "KIOSK",
"fulfillmentType": "DINE_IN",
"tableNumber": "03",
"items": [
{"menuId": "uuid", "quantity": 2, "itemInstructions": null}
]
}
Kiosk — Dine In, Mobile Money
Customer at kiosk, orders for their table, pays USSD immediately.
{
"sessionType": "IMMEDIATE",
"channel": "KIOSK",
"fulfillmentType": "DINE_IN",
"tableNumber": "03",
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": null}
],
"payments": [
{
"paymentMethod": "MOBILE_MONEY",
"amount": 5000
}
]
}
App — Pickup, Platform Wallet
App user orders pickup, pays with JikoXpress platform wallet.
{
"sessionType": "IMMEDIATE",
"channel": "APP",
"fulfillmentType": "PICKUP",
"kitchenId": "uuid",
"fromCart": false,
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": null}
],
"payments": [
{
"paymentMethod": "PLATFORM_WALLET",
"amount": 5000
}
]
}
WhatsApp — Pickup
Customer orders via WhatsApp, will collect food themselves.
{
"sessionType": "IMMEDIATE",
"channel": "WHATSAPP",
"fulfillmentType": "PICKUP",
"kitchenId": "uuid",
"customerPhone": "+255700000000",
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": null}
],
"payments": [
{
"paymentMethod": "MOBILE_MONEY",
"amount": 5000
}
]
}
IDs Resolved Server Side
These are never passed in request — resolved from context:
| Field | Resolved From |
|---|---|
staffId |
Authenticated staff session token |
accountId |
Authenticated JikoXpress user token |
| Kitchen (POS/Kiosk) | Staff session — staff belongs to a kitchen |
| Kitchen (App direct) | Passed in request |
| Kitchen (App cart) | Derived from cart items grouped by kitchen |
Payment Split Model
All payments are a list. Single payment = list of one. Amounts must sum to session total.
{
"payments": [
{
"paymentMethod": "KITCHEN_WALLET",
"kitchenCustomerId": "uuid",
"amount": 3000
},
{
"paymentMethod": "KITCHEN_CHANNEL",
"kitchenPaymentMethodId": "uuid",
"amount": 1500
},
{
"paymentMethod": "MOBILE_MONEY",
"amount": 1000
},
{
"paymentMethod": "CASH",
"amount": 500
},
{
"paymentMethod": "CARD",
"amount": 500
},
{
"paymentMethod": "PLATFORM_WALLET",
"amount": 500
}
]
}
kitchenCustomerIdrequired only forKITCHEN_WALLET
kitchenPaymentMethodIdrequired only forKITCHEN_CHANNEL
Scenarios
1. POS — Pay Now, Cash
Staff takes order at counter. Customer pays cash immediately.
{
"sessionType": "IMMEDIATE",
"channel": "POS",
"fulfillmentType": "PICKUP",
"tag": "John",
"orderInstructions": null,
"items": [
{"menuId": "uuid", "quantity": 2, "itemInstructions": null},
{"menuId": "uuid", "quantity": 1, "itemInstructions": "no salt"}
],
"payments": [
{
"paymentMethod": "CASH",
"amount": 5000
}
]
}
2. POS — Pay Now, Split Payment
Customer pays part cash, part mobile money.
{
"sessionType": "IMMEDIATE",
"channel": "POS",
"fulfillmentType": "PICKUP",
"tag": "Sarah",
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": null}
],
"payments": [
{
"paymentMethod": "CASH",
"amount": 2000
},
{
"paymentMethod": "MOBILE_MONEY",
"amount": 3000
}
]
}
3. POS — Pay Now, Kitchen Wallet
Loyal customer pays using their prepaid kitchen wallet.
{
"sessionType": "IMMEDIATE",
"channel": "POS",
"fulfillmentType": "PICKUP",
"tag": "Mama Fatuma",
"items": [
{"menuId": "uuid", "quantity": 2, "itemInstructions": null}
],
"payments": [
{
"paymentMethod": "KITCHEN_WALLET",
"kitchenCustomerId": "uuid",
"amount": 5000
}
]
}
4. POS — Pay Now, Kitchen Channel (Lipa Voda)
Customer pays via kitchen configured channel e.g. Vodacom Lipa.
{
"sessionType": "IMMEDIATE",
"channel": "POS",
"fulfillmentType": "PICKUP",
"tag": "James",
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": null}
],
"payments": [
{
"paymentMethod": "KITCHEN_CHANNEL",
"kitchenPaymentMethodId": "uuid",
"amount": 5000
}
]
}
5. POS — Generate Slip (Human Kiosk)
Staff takes order, generates slip. Customer walks to cashier to pay.
{
"sessionType": "SLIP",
"channel": "POS",
"fulfillmentType": "PICKUP",
"tag": "John",
"orderInstructions": null,
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": "no salt"}
]
}
No
payments— cashier will collect when customer brings slip
6. POS — Tab, Round 1 (New Bill)
Dine-in customer. Staff opens new tab/bill for the table.
{
"sessionType": "TAB",
"channel": "POS",
"fulfillmentType": "DINE_IN",
"tableNumber": "05",
"tag": "Family ya Mbeya",
"items": [
{"menuId": "uuid", "quantity": 2, "itemInstructions": null},
{"menuId": "uuid", "quantity": 1, "itemInstructions": "extra spicy"}
]
}
No
billId— server creates newKitchenBillsEntityautomatically
Nopayments— customer pays at end
7. POS — Tab, Round 2 (Existing Bill)
Same table orders more. Cashier picks existing open bill, adds new round.
{
"sessionType": "TAB",
"channel": "POS",
"fulfillmentType": "DINE_IN",
"tableNumber": "05",
"billId": "existing-bill-uuid",
"items": [
{"menuId": "uuid", "quantity": 2, "itemInstructions": null}
]
}
billIdlinks this round to the existing open bill
New kitchen ticket fires for these items only
8. Kiosk Machine — Cash Slip
Customer self-orders on kiosk machine. Chooses to pay cash. Slip prints. Customer walks to cashier.
{
"sessionType": "SLIP",
"channel": "KIOSK",
"fulfillmentType": "PICKUP",
"items": [
{"menuId": "uuid", "quantity": 2, "itemInstructions": null},
{"menuId": "uuid", "quantity": 1, "itemInstructions": null}
]
}
kitchenIdderived from kiosk device registration
Slip expires after configured time (default 15 min)
9. Kiosk Machine — Mobile Money (Immediate)
Customer self-orders on kiosk, pays via USSD immediately.
{
"sessionType": "IMMEDIATE",
"channel": "KIOSK",
"fulfillmentType": "PICKUP",
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": null}
],
"payments": [
{
"paymentMethod": "MOBILE_MONEY",
"amount": 5000
}
]
}
Session stays
PENDING_PAYMENTuntil USSD webhook confirms
10. Table QR — Pay on Phone
Customer scans QR at table, orders, pays via USSD on their own phone.
{
"sessionType": "IMMEDIATE",
"channel": "TABLE_QR",
"fulfillmentType": "DINE_IN",
"tableNumber": "05",
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": null}
],
"payments": [
{
"paymentMethod": "MOBILE_MONEY",
"amount": 5000
}
]
}
kitchenIdembedded in QR code, resolved server side
Needs cashier approval before going to kitchen
11. Table QR — Pay at Cashier (Slip)
Customer scans QR, orders, chooses to pay cash at counter.
{
"sessionType": "SLIP",
"channel": "TABLE_QR",
"fulfillmentType": "DINE_IN",
"tableNumber": "05",
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": null}
]
}
12. App — Checkout from Cart, Delivery
App user checks out all items from their cart. One payment, orders split per kitchen automatically.
{
"sessionType": "IMMEDIATE",
"channel": "APP",
"fulfillmentType": "DELIVERY",
"fromCart": true,
"deliveryAddress": {
"street": "Lumumba St",
"area": "Mbeya",
"city": "Mbeya",
"notes": "Green gate",
"latitude": -8.9,
"longitude": 33.4
},
"payments": [
{
"paymentMethod": "MOBILE_MONEY",
"amount": 15000
}
]
}
accountIdfrom auth token
kitchenIdderived from cart items — one session created per kitchen
fromCart: true— noitemsneeded in request
13. App — Direct Buy, No Cart
App user buys directly from a kitchen without going through cart.
{
"sessionType": "IMMEDIATE",
"channel": "APP",
"fulfillmentType": "DELIVERY",
"kitchenId": "uuid",
"fromCart": false,
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": null}
],
"deliveryAddress": {
"street": "Lumumba St",
"area": "Mbeya",
"city": "Mbeya",
"notes": "Green gate",
"latitude": -8.9,
"longitude": 33.4
},
"payments": [
{
"paymentMethod": "PLATFORM_WALLET",
"amount": 5000
}
]
}
fromCart: false— items passed directly in request
kitchenIdrequired since not derived from cart
14. WhatsApp — Delivery
Customer orders via WhatsApp chatbot. Not a JikoXpress user — identified by phone number only.
{
"sessionType": "IMMEDIATE",
"channel": "WHATSAPP",
"fulfillmentType": "DELIVERY",
"kitchenId": "uuid",
"customerPhone": "+255700000000",
"items": [
{"menuId": "uuid", "quantity": 1, "itemInstructions": null},
{"menuId": "uuid", "quantity": 2, "itemInstructions": "extra sauce"}
],
"deliveryAddress": {
"street": "Lumumba St",
"area": "Mbeya",
"city": "Mbeya",
"notes": "Green gate",
"latitude": -8.9,
"longitude": 33.4
},
"payments": [
{
"paymentMethod": "MOBILE_MONEY",
"amount": 8000
}
]
}
kitchenIdfrom chatbot — each kitchen has their own WhatsApp number
customerPhoneidentifies customer — no JikoXpress account needed
NoaccountId— WhatsApp customer is anonymous to platform
Edge Cases
Kitchen Wallet — Insufficient Balance, Split with Cash
Customer's kitchen wallet has 3000 but total is 5000. Pays difference with cash.
{
"sessionType": "IMMEDIATE",
"channel": "POS",
"fulfillmentType": "PICKUP",
"items": [
{"menuId": "uuid", "quantity": 2, "itemInstructions": null}
],
"payments": [
{
"paymentMethod": "KITCHEN_WALLET",
"kitchenCustomerId": "uuid",
"amount": 3000
},
{
"paymentMethod": "CASH",
"amount": 2000
}
]
}
Three-Way Split
Customer splits across cash, kitchen channel, and mobile money.
{
"sessionType": "IMMEDIATE",
"channel": "POS",
"fulfillmentType": "PICKUP",
"items": [
{"menuId": "uuid", "quantity": 3, "itemInstructions": null}
],
"payments": [
{
"paymentMethod": "CASH",
"amount": 2000
},
{
"paymentMethod": "KITCHEN_CHANNEL",
"kitchenPaymentMethodId": "uuid",
"amount": 2000
},
{
"paymentMethod": "MOBILE_MONEY",
"amount": 1000
}
]
}
Tab — Pay Bill with Split
Customer finishes eating, pays open tab with card and cash.
This happens at bill close time, not session creation.
Separate endpoint:POST /bills/{billId}/pay
{
"payments": [
{
"paymentMethod": "CARD",
"amount": 10000
},
{
"paymentMethod": "CASH",
"amount": 5000
}
]
}
Cashier Confirms Slip Payment
Customer arrives at counter with slip. Cashier scans barcode or finds slip by number. Confirms cash collected. Order created and sent to kitchen.
Endpoint:
POST /checkout-sessions/{sessionId}/confirm-slip
{
"payments": [
{
"paymentMethod": "CASH",
"amount": 5000
}
]
}
Cashier can also split payment at this point — customer may pay slip with mobile money instead of cash
Cashier Confirms Slip — Split Payment
Customer brought slip but wants to pay part cash, part mobile money.
Endpoint:
POST /checkout-sessions/{sessionId}/confirm-slip
{
"payments": [
{
"paymentMethod": "CASH",
"amount": 3000
},
{
"paymentMethod": "MOBILE_MONEY",
"amount": 2000
}
]
}
After confirmation → session
CONFIRMED→ order created → kitchen notified → inventory deducted
QBIT SPARK CO LIMITED | JikoXpress | April 2026
Internal development reference — confidential