Daraja SDK Logomark
Daraja SDK
Payments & Checkout

M-Pesa Express

The Lipa na M-Pesa Online / STK Push API, plus its companion query endpoint, for merchant-initiated prompts to a customer's phone.

Safaricom's name for this is Lipa na M-Pesa Online, more commonly referred to (including by Safaricom's own portal navigation) as M-Pesa Express or STK Push. It is a merchant-initiated C2B transaction: instead of the customer dialing into a Pay Bill themselves, the merchant triggers a payment prompt directly on the customer's phone.

M-Pesa Express Simulate (STK Push)

Endpoint

https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest

How it works

  1. The merchant sends a request with the transaction details.
  2. Daraja validates the request and acknowledges it.
  3. A prompt is pushed to the customer's M-PESA-registered phone.
  4. The customer enters their M-PESA PIN to authorize.
  5. M-PESA validates the PIN, debits the customer, and credits the merchant.
  6. The result is posted to the merchant's CallBackURL.
  7. The customer receives an SMS confirmation.

Prerequisites

  • Daraja account and sandbox app, with Consumer Key and Consumer Secret.
  • A passkey to encrypt the Password field. On sandbox this is provided on the Test Credentials page; in production it's emailed after go-live.
  • For go-live: a live Pay Bill/Till with Business Admin/Manager operators created.

Request

{
  "BusinessShortCode": 174379,
  "Password": "MTc0Mzc5YmZiMjc5ZjlhYTliZGJjZjE1OGU5N2RkNzFhNDY3Y2QyZTBjODkzMDU5YjEwZjc4ZTZiNzJhZGExZWQyYzkxOTIwMjEwNjI4MDkyNDA4",
  "Timestamp": "20210628092408",
  "TransactionType": "CustomerPayBillOnline",
  "Amount": "1",
  "PartyA": "254722000000",
  "PartyB": "174379",
  "PhoneNumber": "254722111111",
  "CallBackURL": "https://mydomain.com/path",
  "AccountReference": "accountref",
  "TransactionDesc": "txndesc"
}
FieldDescriptionTypeSample value
BusinessShortCodeThe Pay Bill/Till the payment goes to.Numeric174379
Passwordbase64.encode(Shortcode + Passkey + Timestamp).String(base64 string)
TimestampYYYYMMDDHHmmss, must match what was encoded into Password.Timestamp20210628092408
TransactionTypeCustomerPayBillOnline for Pay Bill, CustomerBuyGoodsOnline for Till.StringCustomerPayBillOnline
AmountTransaction amount.Numeric10
PartyAThe paying phone number, 2547XXXXXXXX.Numeric254722000000
PartyBThe receiving organization (usually equal to BusinessShortCode).Numeric174379
PhoneNumberNumber the USSD prompt is sent to; usually the same as PartyA.Numeric254722111111
CallBackURLWhere the result is posted.URLhttps://mydomain.com/path
AccountReferenceShown to the customer in the prompt. Up to 12 characters.Alphanumericaccountref
TransactionDescOptional. Up to 13 characters.Stringtxndesc

Safaricom's documentation states every field except TransactionDesc is mandatory.

Response

{
  "MerchantRequestID": "2654-4b64-97ff-b827b542881d3130",
  "CheckoutRequestID": "ws_CO_1007202409152617172396192",
  "ResponseCode": "0",
  "ResponseDescription": "Success. Request accepted for processing",
  "CustomerMessage": "Success. Request accepted for processing"
}

Callback payload

{
  "Body": {
    "stkCallback": {
      "MerchantRequestID": "29115-34620561-1",
      "CheckoutRequestID": "ws_CO_191220191020363925",
      "ResultCode": 0,
      "ResultDesc": "The service request is processed successfully.",
      "CallbackMetadata": {
        "Item": [
          { "Name": "Amount", "Value": 1.0 },
          { "Name": "MpesaReceiptNumber", "Value": "NLJ7RT61SV" },
          { "Name": "TransactionDate", "Value": 20191219102115 },
          { "Name": "PhoneNumber", "Value": 254708374149 }
        ]
      }
    }
  }
}
FieldDescriptionPresent when
MerchantRequestID / CheckoutRequestIDEchoed from the original request/response.Always
ResultCode0 for success; any other value is an error.Always
ResultDescHuman-readable status.Always
CallbackMetadata.ItemArray of {Name, Value} pairs: Amount, MpesaReceiptNumber, Balance, TransactionDate, PhoneNumber.Successful transactions only

Error codes

CodeMessageFix
400.002.02Bad Request, invalid fieldMatch the request payload exactly to the documentation.
404.001.03Invalid Access TokenRegenerate the token; confirm the correct Consumer Key/Secret pair is in use.
404.001.01Resource not foundCheck the endpoint URL.
405.001Method Not AllowedUse POST.
500.001.1001Merchant does not existUse the short code assigned at Go Live as BusinessShortCode.
500.001.1001Wrong credentialsPassword is invalid, doesn't match BusinessShortCode, or the Timestamp used to build it doesn't match the one in the body.
500.001.1001Unable to lock subscriber, a transaction is already in processAn existing session for that subscriber is still open. Wait at least a minute before retrying.
500.003.02System is busyRetry shortly.
500.003.1001Internal Server ErrorConfirm the request matches the documentation.
500.003.02Spike Arrest ViolationSending too many requests too fast.
500.003.03Quota ViolationSending too many requests overall.

Defect in the source material

Safaricom's error table lists code 500.003.02 twice, once for "System is busy" and again for "Spike Arrest Violation." Both entries are reproduced as documented; there is no way to tell from Safaricom's own material which one actually fires for a given failure.

M-Pesa Express Query

Checks the status of an already-submitted STK Push.

Endpoint

https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query

Request

{
  "BusinessShortCode": "174379",
  "Password": "MTc0Mzc5YmZiMjc5TliZGJjZjE1OGU5N2RkNzFhNDY3Y2QyZTBjODkzMDU5YjEwZjc4ZTZiNzJhZGExZWQyYzkxOTIwMTYwMjE2MTY1NjI3",
  "Timestamp": "20160216165627",
  "CheckoutRequestID": "ws_CO_260520211133524545"
}
FieldDescriptionSample value
BusinessShortCodeSame short code used on the original push.654321
PasswordSame encoding as the push request, base64.encode(Shortcode+Passkey+Timestamp).(base64 string)
TimestampYYYYMMDDHHmmss.20160216165627
CheckoutRequestIDReturned by the original push request.ws_CO_DMZ_123212312_2342347678234

Response

{
  "ResponseCode": "0",
  "ResponseDescription": "The service request has been accepted successfully",
  "MerchantRequestID": "22205-34066-1",
  "CheckoutRequestID": "ws_CO_13012021093521236557",
  "ResultCode": "0",
  "ResultDesc": "The service request is processed successfully."
}

ResultDesc 1032 maps to "Request canceled by the user"; 0 maps to success. Beyond those two, Safaricom's query documentation doesn't enumerate the full result code list separately, since it overlaps with the push callback's codes above.

Testing

Daraja Simulator: create a test app with the M-Pesa Express (Lipa na M-Pesa) product attached; the simulator auto-fills credentials and test data.

Postman: token from https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials, then call the push or query endpoint above.

Go Live

See Organization Portal & Go Live for creating the admin/manager operators. On the portal's Go Live tab, submit your live short code, organization name (Safaricom asks that special characters be stripped from it), and M-PESA admin/manager username (case-sensitive). Production endpoints and production Consumer Key/Secret follow by email.

Till numbers with STK Push

Safaricom documents that a Till Number works with this API too. Set TransactionType to CustomerBuyGoodsOnline, PartyB to the Till Number, and BusinessShortCode to whichever short code was used at Go Live (often the head office/store number rather than the Till itself).

FAQs

On this page