Biashara ERP Enterprise Suite
← All guides

POS payment integrations (Kenya)

Cash, M-Pesa STK, Buy Goods, Paybill, C2B, and branch-scoped payment acceptors at checkout.

POS payment integrations (Kenya)

See also: PAYMENT_METHODS_ARCHITECTURE.md for acceptor model, branch scoping, and tenant walkthroughs.

Payment methods at checkout

Tile Backend codes Flow
Cash cash Immediate sale
M-Pesa mpesa, mpesa_paybill_manual, mpesa_buy_goods_manual, mpesa_paybill_stk, mpesa_buy_goods_stk, mpesa_c2b, kcb_stk, equity_stk Manual, STK, or C2B wait
Card visa Terminal approval reference
Bank bank_transfer Transfer reference
Other other Free-text reference

M-Pesa family methods show sub-chips — one per enabled payment acceptor in settings. Checkout is branch-aware: only acceptors where branch_id is empty or matches the current store appear.

Each sale can record mpesa_channel_id plus snapshot columns (mpesa_channel_paybill, mpesa_channel_till, mpesa_channel_account) for receipts and reconciliation.

Owner configuration

Settings → Integrations → POS payment settings (/pos/payments/settings)

The page has five independent sections, each with its own Save bar. Saving one section does not overwrite others.

Section Saves
Manual M-Pesa Global defaults: txn code mandatory, verify-on-phone, legacy paybill/till fallbacks
POS channels Payment acceptors — bank paybill, manual till/paybill, STK/C2B chips; branch scope; active/inactive
Daraja Sandbox/production env, training mode, credentials, test STK
KCB Buni Same pattern
Equity Jenga Same pattern

Typical tenant journeys

Kiosk — Equity 247247 only (no STK):

  1. Open POS channels → quick-add Equity Paybill 247247 (or add Bank paybill row).
  2. Enter account ref (6 digits), set Active, save channels only.
  3. Disable unused STK rows; skip Daraja/KCB/Equity sections entirely.
  4. At POS, cashier picks the Equity chip → hint shows paybill + account → enter SMS code after customer pays.

Multi-branch — different tills per outlet:

  1. Add one manual_buy_goods row per till; set Branch on each row.
  2. Save channels only. Checkout at Branch A shows Branch A tills; Branch B shows Branch B tills.

Petrol — many pumps, one branch:

  1. Add many manual_buy_goods rows (same branch or org-wide) with distinct till_number and labels (e.g. "Pump 3").
  2. Each enabled row appears as its own sub-chip at checkout.

Section API

PATCH /pos/payments/settings/sections/{section}
section ∈ manual | channels | daraja | kcb | equity

Daraja (tenant provision)

Register callback URLs (per organization slug):

  • STK: https://{host}/webhooks/{slug}/mpesa/stk-callback
  • C2B: https://{host}/webhooks/{slug}/mpesa/c2b/confirmation

Async checkout flow

  1. Cashier selects M-Pesa method and acceptor sub-chip (e.g. STK Paybill, Equity 247247, Pump 2 till).
  2. Checkout sends mpesa_channel_id with the cart (hidden field + JS sync).
  3. Complete sale (STK/C2B) calls POST /pos/checkout/payments/initiate with cart + mpesa_channel_id.
  4. STK pushes to the customer phone (or C2B shows account reference from the selected acceptor).
  5. Frontend polls GET /pos/checkout/payments/intents/{id} until paid.
  6. Daraja/KCB webhook marks PaymentIntent paid; optional auto-fulfillment creates the sale.
  7. When the webhook marks the intent paid, the backend auto-posts the sale (stock, events, parked-cart clear). The POS polls until checkout_complete is true, then redirects to receipt — no second Complete sale tap.
  8. If auto-post did not run (e.g. stock error), the cashier can still submit with payment_intent_id as a fallback.

Simulation vs sandbox STK testing

Two different modes — do not confuse them:

Mode Setting POS behaviour
Sandbox STK (real test push) Active environment = Sandbox, Training mode off, sandbox credentials saved Calls Daraja/Buni/Jenga sandbox API — customer phone gets STK prompt
Production STK (live) Active environment = Production, production credentials saved, training mode off Calls production API — real money
Offline training Training mode on No API call; checkout auto-completes via simulate-paid (cashier drills only)

Sandbox test flow (Daraja example)

  1. Integrations → POS payment setup → Daraja
  2. Set Active environment at POS = Sandbox
  3. Leave Training mode off
  4. Enter Daraja sandbox credentials (consumer key, secret, shortcode e.g. 174379, passkey from developer.safaricom.co.ke)
  5. Register STK callback URL in Daraja portal (copy from settings page)
  6. Save settings
  7. Optional — Test STK from settings: enter a mobile number in Send test STK push (KES 1) and tap Send test push — no sale is created
  8. Or at POS: add items → choose STK Paybill/Till → enter sandbox test number (e.g. 254708374149) → Complete sale
  9. Phone receives Daraja sandbox STK prompt

Same pattern for KCB Buni (sandbox client ID/secret from Buni portal) and Equity Jenga (sandbox API key/secret).

Test STK from payment settings

Each rail (Daraja, KCB, Equity) has a Send test STK push panel on /pos/payments/settings:

  • Sends KES 1 via the active environment’s saved credentials (POST /pos/payments/settings/test-stk)
  • Requires Training mode off and credentials saved for the selected environment
  • Does not create a PaymentIntent or sale — use POS checkout for full async flow testing
  • Production environment shows a confirmation dialog before sending
  • Training mode and Active environment toggles update the panel immediately (no save required for enable/disable state)

Callbacks still need a reachable webhook URL (staging/ngrok) if you want to verify the provider callback path end-to-end.

Go-live

  1. Enter production credentials in the production credential block (keep sandbox keys for reference)
  2. Set Active environment at POS = Production
  3. Ensure Training mode is off
  4. Register production callback URLs with the provider

Legacy single credential blobs are migrated automatically into the sandbox bucket on read.

Simulation (offline training only)

Set Training mode on a rail, or POS_MPESA_SIMULATE=true globally. Initiate creates an intent without calling the provider; dev/tests use POST .../intents/{id}/simulate-paid.

Plugins

  • DarajaApp\Services\Pos\Payments\Gateways\DarajaStkGateway
  • KCB BuniBuniKcbStkGateway (credentials when available)
  • Equity JengaJengaEquityStkGateway (credentials when available)

Ready to run your business on one platform?

14-day trial on entry tier · CRM & mass SMS · Industry-specific modules · Your own workspace subdomain