Server-side M-PESA payments
The API contract for payments that wait well.
Build against stable request, status, webhook, error, and retry semantics. The examples below use placeholders only.
STK Push
Send the phone number and amount from your trusted server. The request is asynchronous and idempotent; HTTP 202 means initiation was accepted, not payment success.
curl -X POST https://api.example.test/api/v1/payments/stk \
-H 'Authorization: Bearer bcp_test_REPLACE_ME' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: order_123_attempt_1' \
-d '{"phone_number":"2547XXXXXXX","amount":100,"external_reference":"order_123"}'Use these placeholders on your server; never put a key in browser code.
Payment status
Poll the payment resource with the returned identifier. Terminal states are SUCCESS, FAILED, CANCELLED, and TIMED_OUT.
Payment Links
Hosted Payment Links keep API keys server-side and use the same asynchronous status, callback, and webhook rules.
Signed webhooks
Read the raw request body before parsing JSON. Require the webhook ID, Unix-seconds timestamp, v1=<hex> signature, and secret version; reject timestamps outside 900 seconds. Compare v1=<hex(HMAC-SHA256(secret_for_version, timestamp + "." + raw_body))> in constant time, persist the event ID before fulfillment, and return 2xx only after durable acceptance.
Errors and rate limits
Use the documented error code as the machine decision and customerMessage as display copy. Respect 429 retry guidance and never replace an uncertain attempt with a new idempotency key.
Sandbox and Live
Keep Sandbox and Live credentials, destinations, callbacks, and acceptance checks distinct. Promotion is a separate owner release decision.
Public operation map
These are the public operations in the validated contract. Owner management and internal routes are intentionally excluded.
POST /api/v1/payments/stkInitiate an M-PESA STK PushGET /api/v1/paymentsSearch paymentsGET /api/v1/payments/{payment_id}Get a paymentGET /api/public/v1/payment-links/{slug}Resolve an active hosted payment linkPOST /api/public/v1/payment-links/{slug}/payments/stkInitiate an STK Push from an active payment linkGET /api/public/v1/payment-links/{slug}/payments/{payment_id}Poll a payment created through a hosted linkPOST /api/public/v1/waitlistSubmit a coming-soon waitlist request