Skip to main content

Checkout ReqSample Senarials

JikoXpress — Checkout Session API Scenarios

GoodVersion catches.1.0 Let| meApril address2026 each:| 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

TypeDescription
customerNameIMMEDIATE Pay now, right at this terminal
tagSLIPGenerate slip, customer goes to cashier
TABPay later, dine-in rounds

Session Statuses

StatusDescription
PENDING_PAYMENTWaiting for payment — USSD webhook or cashier confirmation
CONFIRMEDPayment done, order created
EXPIREDSlip timed out, inventory released
CANCELLEDManually cancelled, inventory released
FAILEDPayment failed, inventory released

Payment Methods

MethodDescription
CASHPhysical money
MOBILE_MONEYUSSD — M-Pesa, Tigo, Airtel
CARDVisa, Mastercard
PLATFORM_WALLETJikoXpress app wallet
KITCHEN_WALLETKitchen issued prepaid balance
KITCHEN_CHANNELKitchen configured channel — Lipa Voda, HaloPesa etc

IDs Resolved Server Side

These are never passed in requestyes,resolved flexiblefrom label,context:

notnecessarily

Rounds

whenround, cashier picks existing open bill from bills list, then creates new session linked to that bill. So we need billIdifnullbill,
FieldResolved From
staffIdAuthenticated staff session token
accountIdAuthenticated JikoXpress user token
Kitchen (POS/Kiosk)Staff session — staff belongs to a name.

kitchen
Kitchen adding(App adirect) Passed in request
Kitchen means(App newcart) Derived iffrom providedcart meansitems addgrouped roundby kitchen

Payment Split Model

All payments are a list. Single payment = list of one. Amounts must sum to existingsession bill.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
    }
  ]
}

Tab ID — yes we need billIdkitchenCustomerId inrequired requestonly for rounds.KITCHEN_WALLET
kitchenPaymentMethodId required only for KITCHEN_CHANNEL

Let

me
redo

Scenarios

all
samples clean:

1. POS — Pay NowNow, Cash

Staff takes order at counter. Customer pays cash immediately.

{
  "kitchenId": "uuid",
  "sessionType": "IMMEDIATE",
  "channel": "POS",
  "fulfillmentType": "PICKUP",
  "staffId": "uuid",
  "paymentMethod": "CASH",
  "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

2.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.

{
  "kitchenId": "uuid",
  "sessionType": "SLIP",
  "channel": "POS",
  "fulfillmentType": "PICKUP",
  "staffId": "uuid",
  "tag": "John",
  "orderInstructions": null,
  "items": [
    {"menuId": "uuid", "quantity": 1, "itemInstructions": "no salt"}
  ]
}

3.No payments — cashier will collect when customer brings slip


6. POS — TabTab, Round 1 (RoundNew 1,Bill)

Dine-in customer. Staff opens new bill)tab/bill for the table.

{
  "kitchenId": "uuid",
  "sessionType": "TAB",
  "channel": "POS",
  "fulfillmentType": "DINE_IN",
  "staffId": "uuid",
  "tableNumber": "05",
  "tag": "Family ya Mbeya",
  "billId": null,
  "items": [
    {"menuId": "uuid", "quantity": 2, "itemInstructions": null},
    {"menuId": "uuid", "quantity": 1, "itemInstructions": null}"extra spicy"}
  ]
}

4.No billId — server creates new KitchenBillsEntity automatically
No payments — customer pays at end


7. POS — TabTab, Round 2 (RoundExisting 2,Bill)

Same table orders more. Cashier picks existing bill)open bill, adds new round.

{
  "kitchenId": "uuid",
  "sessionType": "TAB",
  "channel": "POS",
  "fulfillmentType": "DINE_IN",
  "staffId": "uuid",
  "tableNumber": "05",
  "billId": "existing-bill-uuid",
  "items": [
    {"menuId": "uuid", "quantity": 2, "itemInstructions": null}
  ]
}

8. Kiosk Machine — Cash Slip

Customer self-orders on kiosk machine. Chooses to pay cash. Slip prints. Customer walks to cashier.

{
  "kitchenId": "uuid",
  "sessionType": "SLIP",
  "channel": "KIOSK",
  "fulfillmentType": "PICKUP",
  "items": [
    {"menuId": "uuid", "quantity": 2, "itemInstructions": null},
    {"menuId": "uuid", "quantity": 1, "itemInstructions": null}
  ]
}

6.kitchenId derived 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.

{
  "kitchenId": "uuid",
  "sessionType": "IMMEDIATE",
  "channel": "KIOSK",
  "fulfillmentType": "PICKUP",
  "paymentMethod": "MOBILE_MONEY",
  "items": [
    {"menuId": "uuid", "quantity": 1, "itemInstructions": null}
  ],
  "payments": [
    {
      "paymentMethod": "MOBILE_MONEY",
      "amount": 5000
    }
  ]
}

7.Session stays PENDING_PAYMENT until USSD webhook confirms


10. Table QR — Pay on Phone

Customer scans QR at table, orders, pays via USSD on their own phone.

{
  "kitchenId": "uuid",
  "sessionType": "IMMEDIATE",
  "channel": "TABLE_QR",
  "fulfillmentType": "DINE_IN",
  "tableNumber": "05",
  "paymentMethod": "MOBILE_MONEY",
  "items": [
    {"menuId": "uuid", "quantity": 1, "itemInstructions": null}
  ],
  "payments": [
    {
      "paymentMethod": "MOBILE_MONEY",
      "amount": 5000
    }
  ]
}

8.kitchenId embedded 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.

{
  "kitchenId": "uuid",
  "sessionType": "SLIP",
  "channel": "TABLE_QR",
  "fulfillmentType": "DINE_IN",
  "tableNumber": "05",
  "items": [
    {"menuId": "uuid", "quantity": 1, "itemInstructions": null}
  ]
}

9.


12. App — FromCheckout 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",
  "paymentMethod": "MOBILE_MONEY",
  "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
    }
  ]
}

10.accountId WhatsAppfrom auth token
kitchenId derived from cart itemsDelivery
one session created per kitchen
fromCart: true — no items needed in request


13. App — Direct Buy, No Cart

App user buys directly from a kitchen without going through cart.

{
  "kitchenId": "uuid",
  "sessionType": "IMMEDIATE",
  "channel": "WHATSAPP"APP",
  "fulfillmentType": "DELIVERY",
  "paymentMethod"kitchenId": "MOBILE_MONEY"uuid",
  "customerPhone"fromCart": false,
  "items": [
    {"menuId": "+255700000000"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
kitchenId required 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
    }
  ]
}

AllkitchenId 10from scenarios covered. What do u thinkchatbotanythingeach off?kitchen has their own WhatsApp number
customerPhone identifies customer — no JikoXpress account needed
No accountId — 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