Integrate M-Pesa STK Push payments into any website with a single API call.
The E-Payments API lets you initiate M-Pesa STK Push payments, query transaction status, and list transactions. All requests are authenticated via an API key in the X-API-Key header.
https://yourdomain.com/api/v1STK Push
Trigger M-Pesa payments
Webhooks
Real-time event notifications
Sandbox
Test without going live
All /api/v1/* endpoints require an API key. Generate keys in your API Keys dashboard.
fetch('https://yourapp.com/api/v1/payment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'mpesa_test_xxxxxxxxxxxxxxxxxxxxxxxx'
}
})Sandbox keys
mpesa_test_...Production keys
mpesa_live_.../api/v1/* from your own backend, then have your backend talk to your frontend. A key leaked in client-side code can be used by anyone to trigger payments against your account.Sends an STK Push prompt to the customer's phone. They enter their M-Pesa PIN to confirm.
/api/v1/paymentTrigger an STK Push to a customer{
"phoneNumber": "0712345678", // Required — Kenyan format or 254...
"amount": 500, // Required — integer, min 1 KES
"accountReference": "ORDER-123", // Optional — max 12 chars, shown in M-Pesa
"description": "Payment for order", // Optional
"metadata": { "orderId": "abc" } // Optional — stored with transaction
}{
"success": true,
"transactionId": "3f2e8a1b-...",
"checkoutRequestId": "ws_CO_...",
"status": "pending",
"message": "Payment request sent to customer's phone"
}/api/v1/transaction/:id or use webhooks for the final status./api/v1/transaction/:idGet the current status of a transaction{
"transaction": {
"id": "3f2e8a1b-...",
"status": "completed", // pending | completed | failed | cancelled
"amount": "500.00",
"phone_number": "254712345678",
"mpesa_receipt_number": "RKL23X...",
"account_reference": "ORDER-123",
"created_at": "2024-01-15T10:30:00Z"
}
}/api/v1/transactions?page=1&limit=20&status=completedPaginated list of all transactions{
"transactions": [ { "id": "...", "status": "completed", "amount": "500.00", ... } ],
"pagination": { "page": 1, "limit": 20, "total": 145, "pages": 8 }
}Query Parameters
pagePage number (default: 1)limitResults per page, max 100 (default: 20)statusFilter: pending | completed | failed | cancelledConfigure endpoints in your dashboard. E-Payments sends a signed POST when these events occur.
payment.completedpayment.failedpayment.pendingorder.createdorder.paidorder.status_changedinvoice.paidrefund.completedcustomer.created{
"event": "payment.completed",
"timestamp": "2024-01-15T10:32:00Z",
"data": {
"id": "3f2e8a1b-...",
"status": "completed",
"amount": "500.00",
"phone_number": "254712345678",
"mpesa_receipt_number": "RKL23X..."
}
}Verify the X-MPesa-Signature header:
const crypto = require('crypto');
app.post('/webhooks/mpesa', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-mpesa-signature'];
const expected = crypto.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(req.body).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
return res.status(401).send('Unauthorized');
}
const { event, data } = JSON.parse(req.body);
// Handle: 'payment.completed' | 'payment.failed' | 'payment.pending' |
// 'order.created' | 'order.paid' | 'order.status_changed' |
// 'invoice.paid' | 'refund.completed' | 'customer.created'
// 'data' is the raw order/invoice/refund/customer/transaction row.
res.sendStatus(200);
});Add M-Pesa as a checkout option in any Shopify store with our drop-in script.
<!-- In checkout.liquid or order confirmation -->
<script>
window.EPaymentsConfig = {
apiKey: 'mpesa_test_your_api_key',
apiUrl: 'https://yourapp.com/api/v1/payment',
onSuccess: function(tx) { console.log('Paid:', tx.transactionId); },
onError: function(err) { console.error('Failed:', err); }
};
</script>
<script src="https://yourapp.com/shopify-integration.js"></script>
<!-- Place anywhere on your page -->
<button id="mpesa-pay-btn"
data-amount="{{ cart.total_price | divided_by: 100 }}"
data-reference="{{ order.name }}">
Pay with M-Pesa
</button>Already using window.MpesaSaaSConfig? It still works — the script reads either name, so existing installs don't need to change anything.