M-Pesa Checkout API
Collect M-Pesa payments on any website. No plugin needed: one signup, one API call.
1. Create an account
Sign up at https://pay.vunaonline.co.ke/api/signup. You get a Merchant ID, an API key and a webhook secret.
2. Create a payment
curl -X POST https://pay.vunaonline.co.ke/api/v1/payments \
-H "Authorization: Bearer mk_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 250, "reference": "ORDER-1024", "phone": "0712345678"}'
If you pass phone, the customer gets the M-Pesa prompt immediately (server-to-server).
Without a phone, send the customer to the returned checkout_url: a hosted page where they type their number.
Response:
{
"id": "e3f9c2a81b7d4f6a9c05",
"status": "sent", // created | sent | paid | failed
"amount": 250,
"checkout_url": "https://pay.vunaonline.co.ke/pay/e3f9c2a81b7d4f6a9c05"
}
3. Check status
curl https://pay.vunaonline.co.ke/api/v1/payments/e3f9c2a81b7d4f6a9c05 \
-H "Authorization: Bearer mk_YOUR_API_KEY"
{
"id": "e3f9c2a81b7d4f6a9c05",
"status": "paid",
"amount": 250,
"receipt": "SLK7X2M9QP",
"paid_at": "2026-10-16T14:03:11.000Z"
}
4. Webhook (instant notification)
You never send anything. The moment a payment completes, our servers POST a notification to you at the webhook URL you saved in your dashboard. Your end only listens.
This is what WE send to YOUR webhook URL:
POST https://your-site.com/your-webhook
Content-Type: application/json
X-MK-Signature: hex hmac-sha256 of the raw body, keyed with your webhook secret
{"id":"e3f9c2a81b7d4f6a9c05","status":"paid","amount":250,
"reference":"ORDER-1024","receipt":"SLK7X2M9QP","paid_at":"..."}
The webhook is only a heads-up: the customer's money has already settled to your Till or bank account before it arrives, and if your server misses it we retry up to 10 times. You can always confirm a payment yourself with the status endpoint or your dashboard statement.
Verify the signature (Node.js):
const crypto = require("crypto");
const sig = crypto.createHmac("sha256", process.env.WEBHOOK_SECRET)
.update(rawBody).digest("hex");
if (sig === req.headers["x-mk-signature"]) { /* genuine */ }
PHP:
$sig = hash_hmac("sha256", file_get_contents("php://input"), $WEBHOOK_SECRET);
if ($sig === $_SERVER["HTTP_X_MK_SIGNATURE"]) { /* genuine */ }
Errors
401 invalid_api_key bad or missing API key
400 amount_invalid amount must be a whole number of KES, 1 to 1,000,000
400 phone_invalid use 07XXXXXXXX or 2547XXXXXXXX
404 payment_not_found no payment with that id
Merchant dashboard
Log in here with your email and password to see transactions, set your webhook URL and test webhooks.
Notes
Payments settle directly to the M-Pesa Till or bank account you register in your dashboard — your money is never held by anyone. Test with small amounts first. Questions: lipaonlinepayment@gmail.com.