M-PESA prompt checkout
Your customer types their number and gets the M-PESA prompt (STK push). They type their PIN on their own phone.
One API to send M-PESA prompts, hear the moment money lands, and track every shilling. Start in test mode today; go live after a quick business check.
# Ask the customer to pay KES 2,450 curl https://api.darajapay.co.ke/v1/payments \ -H "Authorization: Bearer sk_test_••••••••" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-1042" \ -d '{ "amount": 2450, "phone": "254712345512", "reference": "ORDER-1042", "callback_url": "https://yoursite.co.ke/webhook" }'
{ "event": "payment.completed", "data": { "id": "pay_8Kq2Xr91", "object": "payment", "mode": "test", "amount": 2450, "status": "completed", "mpesa_receipt": "TST4K2…", "reference": "ORDER-1042" } }
From a one-person Instagram shop to a big online store. Use the API, or no code at all.
Your customer types their number and gets the M-PESA prompt (STK push). They type their PIN on their own phone.
We call your server the second a payment succeeds or fails. Signed so you know it's us, and retried if your server doesn't answer.
Look up any payment by its id or list your latest ones through the API. A dashboard comes next.
No website? Make a link, send it on WhatsApp or Instagram, and get paid. No code.
Send customers to a DarajaPay page with your name and logo. We handle the payment and send them back.
Send money to suppliers, staff or customers' M-PESA (B2C).
Hold a payment until the buyer confirms delivery, then release it to the seller, using the same protected flow as Buy & Sell Safe through the API. The money would be held by our licensed partner, never in a DarajaPay wallet.
PlannedWooCommerce plugin · Android and iOS kits · Node.js, PHP and Python libraries
Start with your own Till or Paybill. Collecting through DarajaPay comes later.
Customers pay straight into your M-PESA Till or Paybill. DarajaPay never holds your money: we send the prompts, track every payment and tell your system.
No Till yet? We would collect for you and send your money to your bank or M-PESA every day.
Plain HTTPS and JSON. Send your secret key as a Bearer token. Base address: https://api.darajapay.co.ke/v1
POST /v1/payments with amount (whole shillings), phone (07… or 2547…), reference (your order number, up to 40 characters) and, if you like, description and callback_url. The customer gets an M-PESA prompt and the reply has status: "prompt_sent".
Send an Idempotency-Key header so a retry never charges twice.
# Ask the customer to pay KES 2,450 curl https://api.darajapay.co.ke/v1/payments \ -H "Authorization: Bearer sk_test_YOUR_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-1042" \ -d '{ "amount": 2450, "phone": "254712345512", "reference": "ORDER-1042", "callback_url": "https://yoursite.co.ke/webhook" }'
curl https://api.darajapay.co.ke/v1/payments/pay_8Kq2Xr91 \ -H "Authorization: Bearer sk_test_YOUR_KEY" # Your latest payments (limit 1 to 100) curl "https://api.darajapay.co.ke/v1/payments?limit=10" \ -H "Authorization: Bearer sk_test_YOUR_KEY"
We POST to your callback_url with the event payment.completed, payment.failed or payment.cancelled. If your server doesn't answer with a 2xx, we try again after 10 seconds and after a minute.
Every webhook carries a DarajaPay-Signature header: t=<time>,v1=<HMAC-SHA256> of <time>.<raw body>, signed with your webhook secret. Check it before you trust the event.
{ "event": "payment.completed", "data": { "id": "pay_8Kq2Xr91", "object": "payment", "mode": "test", "amount": 2450, "status": "completed", "mpesa_receipt": "TST4K2…", "reference": "ORDER-1042" } }
// Node.js: check the DarajaPay-Signature header import { createHmac, timingSafeEqual } from "node:crypto"; function fromDarajaPay(rawBody, header, secret) { const sig = Object.fromEntries( header.split(",").map((p) => p.split("=")), ); const mine = createHmac("sha256", secret) .update(sig.t + "." + rawBody) .digest("hex"); const age = Date.now() / 1000 - Number(sig.t); return ( Math.abs(age) < 300 && mine.length === (sig.v1 || "").length && timingSafeEqual(Buffer.from(mine), Buffer.from(sig.v1)) ); }
Test keys (sk_test_…) never send a real prompt. Use any Kenyan number: one ending 000 is cancelled, one ending 111 fails, anything else completes about 3 seconds later with a fake receipt like TST4K2….
Errors come back as { error: { code, message } } with a plain-English message. Live keys aren't issued yet: they come after approval, once your Till or Paybill is connected.
Takes about 5 minutes. Have these ready:
Yes. Ask Daraja first for quick answers about the API.