View as Markdown

Payments

Customers choose at checkout:

Method How it works
M-Pesa prompt An STK push to their phone; they enter their PIN.
Card / other A secure checkout page (card, M-Pesa, bank) in WhatsApp's browser.
Pay on delivery Cash or M-Pesa when the rider arrives — on the shop's own terms (below).

At the door, the rider can send an M-Pesa prompt to the customer's phone from their WhatsApp, and hands over once it's paid — no cash to carry. Or they record cash, which shows as owed by that rider until the shop marks it handed in.

A payment is only ever marked paid after we ask the provider directly; webhooks and redirects are just nudges. Money that arrives for a cancelled order, or twice, raises order.refund_needed.

Payment terms (the shop decides)

Each shop sets its own terms in the dashboard (Settings → Payment terms). Kibanda applies exactly those and nothing else:

Setting Effect
Pay on delivery: on / off Off = every delivery is paid in full (M-Pesa or card) before it leaves. Pickup orders can still pay at the counter.
Rules (each optional) Orders above KES X · customers with fewer than N delivered orders · customers with N or more refusals.
When a rule matches Full payment first, or a deposit first — the delivery fee or a percentage — with the rest on delivery.
Per customer Your shop's rules (default) · Always pays before delivery · Trusted: may always pay on delivery. Set on the Customers page or with payment_terms on the customer.
Per order The shop can ask for payment first on any confirmed order that hasn't left — full or the deposit — from WhatsApp (the order card), the dashboard, POST /v1/orders/{id}/require_payment, or the MCP require_payment tool.

How it plays out:

The shop's current terms are on GET /v1/shop as payment_terms.

Bring your own

By default payments go through Kibanda's provider. A shop can connect its own account (dashboard → Payments) so money goes straight to them:

Account M-Pesa prompts Card page You need
M-Pesa till (Buy Goods) ✓ A Daraja app with Lipa Na M-Pesa Online: consumer key/secret, passkey, the store number and the till
M-Pesa paybill ✓ The same, for the paybill
Paystack ✓ ✓ Secret key (sk_live_…); set the webhook URL we show you
Flutterwave ✓ ✓ Secret key (FLWSECK-…)

Credentials are checked with the provider before they're used, encrypted at rest (AES-256-GCM, bound to the account), and never shown again. Each payment records which account took it. If a shop's account stops working, new payments fall back to Kibanda's, so customers can always pay.

M-Pesa direct (Daraja): Safaricom calls back to a unique, unguessable URL per payment, and we still confirm with Safaricom's STK query before marking it paid, so a forged callback can't mark an order paid. The receipt number (e.g. TJK3H2K9LP) is saved on the payment.