Skip to documentation
BPBitcrowd Pay
DocsQuickstart

Server-side M-PESA payments

A clear path from request to verified result.

Use this five-minute path to initiate an M-PESA payment, keep your credentials on the server, and reconcile the terminal result.

Five-minute path

  1. Create a Sandbox API key in the owner dashboard and keep it on your server.
  2. Send POST /api/v1/payments/stk with a unique Idempotency-Key.
  3. Treat HTTP 202 as accepted for processing, then store the returned payment reference.
  4. Poll the status endpoint or verify signed webhook events until a terminal result.
  5. On an uncertain response, reuse the same idempotency key; do not create a new attempt.

Authentication

Call payment endpoints only from a trusted server. Use a placeholder such as bcp_test_REPLACE_ME while integrating. Never place keys in browser code, logs, or generated client snippets.

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.

Asynchronous payment status

A 202 Accepted response means initiation was accepted. It does not mean the customer paid. Reconcile through GET /api/v1/payments/{payment_id} or a verified webhook before marking an order paid.

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.

Safe retries and errors

Keep one stable key per logical attempt. Respect Retry-After on 429 responses and back off on 503. An ambiguous payment remains pending until reconciliation or owner instruction.

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 Push
  • GET /api/v1/paymentsSearch payments
  • GET /api/v1/payments/{payment_id}Get a payment
  • GET /api/public/v1/payment-links/{slug}Resolve an active hosted payment link
  • POST /api/public/v1/payment-links/{slug}/payments/stkInitiate an STK Push from an active payment link
  • GET /api/public/v1/payment-links/{slug}/payments/{payment_id}Poll a payment created through a hosted link
  • POST /api/public/v1/waitlistSubmit a coming-soon waitlist request