Skip to main content
FyberPay integrates with Safaricom’s Daraja API to process M-Pesa payments for ISP subscriber billing. The platform supports three payment flows: STK Push (Lipa Na M-Pesa Online) for instant payment prompts, C2B (Customer to Business) for paybill payments, and Bill Manager for automated invoice delivery and recurring billing.

Supported Payment Flows

FlowUse CaseTrigger
STK PushOn-demand payment promptsSubscriber clicks “Pay Now” in portal
C2B (Paybill)Subscriber-initiated paybill paymentsSubscriber pays via M-Pesa menu
Bill ManagerAutomated invoices with SMS remindersInvoice created by billing cycle

Environment Endpoints

Base URL: https://sandbox.safaricom.co.ke
EndpointPath
OAuth Token/oauth/v1/generate
STK Push/mpesa/stkpush/v1/processrequest
STK Query/mpesa/stkpushquery/v1/query
C2B Register/mpesa/c2b/v2/registerurl
Transaction Status/mpesa/transactionstatus/v1/query
Bill Manager/v1/billmanager-invoice/...

Authentication: OAuth Token Flow

All Daraja API calls require a Bearer token obtained via OAuth 2.0 client credentials.
1

Obtain credentials from Daraja portal

Register your app at developer.safaricom.co.ke and get your Consumer Key and Consumer Secret.
2

Request an access token

FyberPay sends a GET request with Basic auth (base64-encoded ConsumerKey:ConsumerSecret):
curl -X GET \
  "https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials" \
  -H "Authorization: Basic $(echo -n 'ConsumerKey:ConsumerSecret' | base64)"
3

Token caching

FyberPay caches tokens in Redis for 290 seconds (~5 minutes). Tokens from Safaricom are valid for 3600 seconds (1 hour), but FyberPay refreshes early to avoid edge cases with clock drift. Each set of credentials (per-org or platform-level) gets its own cache key.

STK Push (Lipa Na M-Pesa Online)

STK Push sends a payment prompt directly to the subscriber’s phone. The subscriber enters their M-Pesa PIN to authorize the payment.

Request Format

{
  "BusinessShortCode": "174379",
  "Password": "base64(Shortcode + Passkey + Timestamp)",
  "Timestamp": "20260322143000",
  "TransactionType": "CustomerPayBillOnline",
  "Amount": 1500,
  "PartyA": "254712345678",
  "PartyB": "174379",
  "PhoneNumber": "254712345678",
  "CallBackURL": "https://api.fyberpay.com/webhooks/mpesa/stk",
  "AccountReference": "INV-2026-0042",
  "TransactionDesc": "Payment INV-2026-0042"
}
The Password field is computed as Base64(BusinessShortCode + Passkey + Timestamp). The Timestamp format is YYYYMMDDHHmmss in East Africa Time (UTC+3). FyberPay computes this automatically.
M-Pesa requires whole number amounts. FyberPay applies Math.ceil() to round up fractional amounts before sending.

Phone Number Normalization

FyberPay normalizes Kenyan phone numbers to the 254XXXXXXXXX format (no plus sign). The following inputs are all accepted:
InputNormalized
+254712345678254712345678
0712345678254712345678
254712345678254712345678

STK Push Response

{
  "MerchantRequestID": "29115-34620561-1",
  "CheckoutRequestID": "ws_CO_191220191020363925",
  "ResponseCode": "0",
  "ResponseDescription": "Success. Request accepted for processing",
  "CustomerMessage": "Success. Request accepted for processing"
}
FyberPay stores the CheckoutRequestID to correlate with the callback.

STK Push Callback

Safaricom sends the result to your CallBackURL:
{
  "Body": {
    "stkCallback": {
      "MerchantRequestID": "29115-34620561-1",
      "CheckoutRequestID": "ws_CO_191220191020363925",
      "ResultCode": 0,
      "ResultDesc": "The service request is processed successfully.",
      "CallbackMetadata": {
        "Item": [
          { "Name": "Amount", "Value": 1500 },
          { "Name": "MpesaReceiptNumber", "Value": "NLJ7RT61SV" },
          { "Name": "Balance" },
          { "Name": "TransactionDate", "Value": 20191219102115 },
          { "Name": "PhoneNumber", "Value": 254712345678 }
        ]
      }
    }
  }
}
FyberPay extracts MpesaReceiptNumber, Amount, and PhoneNumber from the CallbackMetadata.Item array.

STK Query (Status Check)

If no callback arrives, FyberPay can poll the transaction status:
{
  "BusinessShortCode": "174379",
  "Password": "base64(Shortcode + Passkey + Timestamp)",
  "Timestamp": "20260322143500",
  "CheckoutRequestID": "ws_CO_191220191020363925"
}
FyberPay maps the ResultCode to internal payment statuses:
ResultCodeFyberPay StatusMeaning
0SUCCESSPayment completed
1032CANCELLEDUser cancelled the request
1019PENDINGTransaction in progress
1025PENDINGTransaction timed out, may still complete
1037PENDINGTimeout waiting for user input
OtherFAILEDTransaction failed

C2B (Customer to Business)

C2B handles payments where the subscriber initiates the transaction from their M-Pesa menu by sending money to the ISP’s paybill number.

URL Registration

FyberPay registers validation and confirmation URLs with Safaricom:
POST /mpesa/c2b/v2/registerurl
URL TypeEndpointPurpose
Validation/webhooks/mpesa/c2b/validationFyberPay validates the payment before Safaricom processes it
Confirmation/webhooks/mpesa/c2b/confirmationSafaricom confirms the completed payment

C2B Callback Payload

{
  "TransactionType": "Pay Bill",
  "TransID": "RKTQDM7W6S",
  "TransTime": "20260322143025",
  "TransAmount": "1500.00",
  "BusinessShortCode": "174379",
  "BillRefNumber": "INV-2026-0042",
  "InvoiceNumber": "",
  "OrgAccountBalance": "49197.00",
  "ThirdPartyTransID": "",
  "MSISDN": "254712345678",
  "FirstName": "JOHN",
  "MiddleName": "",
  "LastName": "DOE"
}
FyberPay parses the TransID as the receipt number, TransAmount as the payment amount, MSISDN as the phone number, and BillRefNumber as the account/invoice reference.

Bill Manager API

Bill Manager handles automated invoice delivery, SMS reminders, and payment reconciliation. It operates at the platform level (not per-org) using dedicated credentials.

One-Time Setup: Opt-In

Register your paybill with Safaricom Bill Manager:
{
  "shortcode": "174379",
  "email": "billing@fyberpay.com",
  "officialContact": "254700000000",
  "sendReminders": 1,
  "callbackurl": "https://api.fyberpay.com/webhooks/billmanager/payment"
}
The response includes an app_key that confirms successful registration.

Sending Invoices

Endpoint: POST /v1/billmanager-invoice/single-invoicingSends an invoice to a single subscriber. The subscriber receives an SMS with the amount, due date, and payment instructions.

Cancelling Invoices

POST /v1/billmanager-invoice/cancel-single-invoice
{
  "externalReference": "INV-2026-0042"
}
Returns 200 for successful cancellation or 409 if the invoice was already cancelled (both treated as success by FyberPay).

Payment Reconciliation

After processing a Bill Manager payment, FyberPay sends an acknowledgment back to Safaricom. This triggers an e-receipt SMS to the subscriber:
POST /v1/billmanager-invoice/reconciliation

Webhook Security

FyberPay validates incoming M-Pesa callbacks using IP allowlisting. Configure the DARAJA_ALLOWED_IPS environment variable with Safaricom’s callback IPs (comma-separated). If no allowlist is configured, all IPs are accepted (suitable for sandbox testing only).
Always configure DARAJA_ALLOWED_IPS in production. Safaricom publishes their callback IP ranges in the Daraja developer portal.

Error Codes Reference

Common M-Pesa STK Push error codes:
CodeDescription
0Success
1Insufficient funds
1019Transaction in progress
1025Timeout waiting for response
1032Request cancelled by user
1037Timeout waiting for user input
2001Wrong PIN entered
1001Unable to lock subscriber (subscriber busy)
1Balance insufficient
17Internal failure

Retry Behavior and Circuit Breaker

All Daraja API calls are wrapped in a circuit breaker (daraja circuit). If Safaricom’s API is consistently failing, the circuit opens and FyberPay stops sending requests for a cooldown period. This prevents cascade failures and protects against Safaricom downtime. For STK callbacks that fail to arrive:
  1. FyberPay queries the transaction status using the STK Query endpoint
  2. If the query also fails, the payment remains in PENDING status
  3. A background job retries pending payment status checks periodically

Per-Org vs Platform Credentials

FyberPay supports two credential models:
ModelUse CaseConfiguration
Platform credentialsSingle paybill for all ISPsSet via DARAJA_CONSUMER_KEY, DARAJA_CONSUMER_SECRET, DARAJA_SHORTCODE env vars
Per-org credentialsEach ISP has their own paybillConfigured in Settings > Payment Gateways per organization
Per-org credentials take priority. If an organization has no M-Pesa credentials configured, FyberPay falls back to platform-level credentials.

Required Environment Variables

# Daraja API credentials (platform-level fallback)
DARAJA_CONSUMER_KEY=your_consumer_key
DARAJA_CONSUMER_SECRET=your_consumer_secret
DARAJA_SHORTCODE=174379
DARAJA_PASSKEY=your_passkey
DARAJA_CALLBACK_URL=https://api.fyberpay.com/webhooks/mpesa/stk
DARAJA_ENV=sandbox  # or "production"
DARAJA_ALLOWED_IPS=196.201.214.200,196.201.214.206

# Bill Manager credentials (platform-level)
BILLMGR_CONSUMER_KEY=your_bm_consumer_key
BILLMGR_CONSUMER_SECRET=your_bm_consumer_secret
BILLMGR_SHORTCODE=174379
BILLMGR_EMAIL=billing@yourisp.com
BILLMGR_PHONE=254700000000

# Gateway mode
GATEWAY_MODE=live  # or "test" for sandbox