NAV Navbar

 

Introduction

Welcome to Safaricom APIs! In this release, we have exposed API endpoints for accessing M-Pesa services; we have M-Pesa API endpoints for B2B, B2C, and C2B. Our APIs are built on REST; data entities are represented as HTTP resources and are accessed using HTTP verbs, majorly GET and POST. API request parameters and responses - including errors - are encoded in JSON. Our APIs response status codes and error codes comply with HTTP status codes as defined in RFC 2616. You can invoke our API endpoints using REST clients like Postman or SoapUI and command line tools like cUrl and Node.js.

To get you started, we have provided code samples in curl, Ruby, PHP, Python, NodeJS, and Java! Sample JSON responses for each APIs request have also been availed. You can view the code examples to the right, and you can switch between the programming languages with the tabs on the top right.

Getting Started

Developer Sign-up

To use our APIs or invoke an API endpoint, you will need to register for an account. Available accounts are individual and company accounts. Company accounts are for users with an existing M-PESA shortcodes or billing accounts with Safaricom and wish to test our APIs with an intention to develop a production app; users intending to apply for an M-Pesa shortcode can also register with a company account.

An account activation link will be sent in an email to the email address you used in registering for a developer account; the activation link expires within 24 hours of it being sent, and you will need to register for another account. The account activation link enables you to activate your developer account and set a password for your account.

Creating a Sandbox App

The first time you log in into your developer account, after account activation, the sandbox apps page opens from which you can create an app by clicking

add an app.

Creating an app involves setting an app name and choosing the API products for which you will want to use. API products are a business package of the available APIs and the rules for access built around them.

App Dashboard

Once you have created an app, a dashboard from which you can view details about the app(s) you have created. Clicking on an app reveals tabs with more information about the app; the tabs are Keys, Products, Details, Edit and Delete.

Keys

The app keys of an app are the consumer key and consumer secret. The Keys tab shows the app keys, the date they were issued and their expiry period. Sandbox keys do not expire.

Products

A product is an encapsulation of an API(s) with access rules and business rules around their use. The use case of an app the developer is building should be guided by an API product. Access rules involve the traffic generated by the app.

Details

The details tab gives you a summary of the details about your app and the terms and conditions for accessing the sandbox.

Analytics

From the analytics tab, you can gauge how you can gauge how your app is performing over a period of time - day, week(s) or month(s). We provide app analytics for throughput, maximum response time, minimum response time, endpoint response time, message count and error count

Edit

Should you need to change details about your app like, its name or API products, then you can do so in the Edit tab.

Delete

This delete tab, destroys your app and removes it from your dashboard.

Authentication

To authenticate your app and get an OAuth access token, use this code. An access token expires in 3600 seconds or 1 hour

# With cUrl, you can just pass the correct header with each request
curl -X GET https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials  \
  -H "Accept: application/json" \
  -u "YOUR_APP_CONSUMER_KEY:YOUR_APP_CONSUMER_SECRET"

require 'net/http'
require 'net/https'
require 'uri'

uri = URI('https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials')

Net::HTTP.start(uri.host, uri.port,
  :use_ssl => uri.scheme == 'https',
  :verify_mode => OpenSSL::SSL::VERIFY_NONE)

do |http|

  request = Net::HTTP::Get.new uri.request_uri
  request.basic_auth 'YOUR_APP_CONSUMER_KEY', 'YOUR_APP_CONSUMER_SECRET'

  response = http.request request # Net::HTTPResponse object

  puts response
  puts response.body
end
import requests
from requests.auth import HTTPBasicAuth

consumer_key = "YOUR_APP_CONSUMER_KEY"
consumer_secret = "YOUR_APP_CONSUMER_SECRET"
api_URL = "https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials"

r = requests.get(api_URL, auth=HTTPBasicAuth(consumer_key, consumer_secret))

print (r.text)
var request = require('request'),
  consumer_key = "YOUR_APP_CONSUMER_KEY",
  consumer_secret = "YOUR_APP_CONSUMER_SECRET",
  url = "https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials"
  auth = "Basic " + new Buffer(consumer_key + ":" + consumer_secret).toString("base64");

  request(
    {
      url : url,
      headers : {
        "Authorization" : auth
      }
    },
    function (error, response, body) {
      // TODO: Use the body object to extract OAuth access token
    }
  )
<?php
$url = 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials';

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
$credentials = base64_encode('YOUR_APP_CONSUMER_KEY:YOUR_APP_CONSUMER_SECRET');
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Authorization: Basic '.$credentials)); //setting a custom header
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);

$curl_response = curl_exec($curl);

echo json_decode($curl_response);

?>
// Use base64 to encode the username and password.
String app_key = "YOUR_CONSUMER_KEY";
String app_secret = "YOUR_CONSUMER_SECRET";
String appKeySecret = app_key + ":" + app_secret;
byte[] bytes = usernameAndPassword.getBytes("ISO-8859-1");
String auth = Base64.encode(bytes);

OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
  .url("https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials")
  .get()
  .addHeader("authorization", "Basic " + auth)
  .addHeader("cache-control", "no-cache")
  .build();

Response response = client.newCall(request).execute();
{
  "access_token": "SGWcJPtNtYNPGm6uSYR9yPYrAI3Bm",
  "expires_in": "3599"
}

To make an API call, you will need to authenticate your app. We have provided an OAuth API for you to generate an access token, we support client_credentials grant type. To authorize your API call to the OAuth API, you will need a Basic Auth over HTTPS authorization token. The Basic Auth string is a base64 encoded string of your app’s client key and client secret. We have provided a means to obtain the Basic Auth string for your sandbox apps; while you are in the OAuth API’s sandbox. Click on ‘HTTP Basic Set Credentials’ button.

HTTP Basic Set Credentials

In the pop-up menu that appears, enter your consumer key and consumer secret. You have an option to save these credentials for 30 days and therefore, you do not need to set them every time you need an access token

Set Credentials Menu

HTTP Header Parameters

With an OAuth 2.0 Access Token, an application can now invoke our APIs by including the access token in the HTTP header. Our APIs currently only supports application/json content type.

Parameter Description
Authorization OAuth 2.0 Access Token. Bearer keyword followed by a space and the OAuth 2.0 Access Token. Bearer <Access-Token>
Content-Type Only application/json content type is supported.

Security Credentials

// Classes and Libraries to import
import java.security.MessageDigest;
import java.security.Security;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.crypto.Cipher;
import java.math.BigInteger;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import java.io ByteArrayOutputStream;

// Function to encrypt the initiator credentials
public static String encryptInitiatorPassword(String securityCertificate, String password) {
    String encryptedPassword = "YOUR_INITIATOR_PASSWORD";
    try {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        byte[] input = password.getBytes();

        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
        FileInputStream fin = new FileInputStream(new File(securityCertificate));
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate certificate = (X509Certificate) cf.generateCertificate(fin);
        PublicKey pk = certificate.getPublicKey();
        cipher.init(Cipher.ENCRYPT_MODE, pk);

        byte[] cipherText = cipher.doFinal(input);

        // Convert the resulting encrypted byte array into a string using base64 encoding
        encryptedPassword = Base64.encode(cipherText);

    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(PasswordUtil.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchProviderException ex) {
        Logger.getLogger(PasswordUtil.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchPaddingException ex) {
        Logger.getLogger(PasswordUtil.class.getName()).log(Level.SEVERE, null, ex);
    } catch (FileNotFoundException ex) {
        Logger.getLogger(PasswordUtil.class.getName()).log(Level.SEVERE, null, ex);
    } catch (CertificateException ex) {
        Logger.getLogger(PasswordUtil.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidKeyException ex) {
        Logger.getLogger(PasswordUtil.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalBlockSizeException ex) {
        Logger.getLogger(PasswordUtil.class.getName()).log(Level.SEVERE, null, ex);
    } catch (BadPaddingException ex) {
        Logger.getLogger(PasswordUtil.class.getName()).log(Level.SEVERE, null, ex);
    }

    return encryptedPassword;
}
<?php
$publicKey = "PATH_TO_CERTICATE";
//$plaintext = "Safaricom132!";
$plaintext = "YOUR_PASSWORD";

openssl_public_encrypt($plaintext, $encrypted, $publicKey, OPENSSL_PKCS1_PADDING);

echo base64_encode($encrypted);

?>

M-Pesa Core authenticates a transaction by decrypting the security credentials. Security credentials are generated by encrypting the base64 encoded initiator password with M-Pesa’s public key, a X509 certificate.

The algorithm for generating security credentials is as follows:

  • Write the unencrypted password into a byte array.

  • Encrypt the array with the M-Pesa public key certificate. Use the RSA algorithm, and use PKCS #1.5 padding (not OAEP), and add the result to the encrypted stream.

  • Convert the resulting encrypted byte array into a string using base64 encoding. The resulting base64 encoded string is the security credential.

Command IDs

Command ID Description
TransactionReversal Reversal for an erroneous C2B transaction.
SalaryPayment Used to send money from an employer to employees e.g. salaries
BusinessPayment Used to send money from business to customer e.g. refunds
PromotionPayment Used to send money when promotions take place e.g. raffle winners
AccountBalance Used to check the balance in a paybill/buy goods account (includes utility, MMF, Merchant, Charges paid account).
CustomerPayBillOnline Used to simulate a transaction taking place in the case of C2B Simulate Transaction or to initiate a transaction on behalf of the customer (STK Push).
TransactionStatusQuery Used to query the details of a transaction.
CheckIdentity Similar to STK push, uses M-Pesa PIN as a service.
BusinessPayBill Sending funds from one paybill to another paybill
BusinessBuyGoods sending funds from buy goods to another buy goods.
DisburseFundsToBusiness Transfer of funds from utility to MMF account.
BusinessToBusinessTransfer Transferring funds from one paybills MMF to another paybills MMF account.
BusinessTransferFromMMFToUtility Transferring funds from paybills MMF to another paybills utility account.

Identifier Types

Identifier types - both sender and receiver - identify an M-Pesa transaction’s sending and receiving party as either a shortcode, a till number or a MSISDN (phone number). There are three identifier types that can be used with M-Pesa APIs.

Identifier Identity
1 MSISDN
2 Till Number
4 Shortcode

Test Credentials

To facilitate testing of sandbox apps, we have provided test credentials consisting of:

  • Test Shortcodes
  • Initiator Name
  • Security Credential
  • Test MSISDN
  • Lipa Na M-Pesa Online Shortcode
  • Lipa Na M-Pesa Online Passkey

There are two shortcodes in the test credentials page - Shortcode 1 and Shortcode 2. The initiator name and security credentials are associated with Shortcode 1. For M-Pesa APIs that have ‘PartyA’ in their request parameters, use Shortcode 1; Shortcode 2 will be used for ‘PartyB’.

The test credentials page has a utility to generate an M-Pesa security credential. You use the utility by entering the string alongside ‘Security Credential’ with no preceding and trailing spaces and then clicking on ‘Generate Credentials’ button. You will then get an M-Pesa security credential for use in invoking an API that requires a security credential in its request parameters.

The ‘Lipa Na M-Pesa Online Shortcode’ is a production shortcode. On the sandbox, API calls using Lipa Na M-Pesa Online Payment API can only be made to this shortcode and money will be transacted. Auto-reversal of funds received on this shortcode will be done every night at 23:59 HRS EAT (midnight).

The ‘Lipa Na M-Pesa Online Passkey’ is used to create a password for use when making a Lipa Na M-Pesa Online Payment API call. The password is generated by base64 encoding the passkey, shortcode and timestamp, the resultant string from this encoding is the password.

Test credentials expire after three days, after which you will be issued with fresh test credentials. Lipa Na M-Pesa Online Shortcode and Lipa Na M-Pesa Passkey do not expire.

Test Cases

The developer will download test cases when they are creating a sandbox app. The test cases are in an Excel spreadsheet which the developer will fill with results from the API calls they make for each test scenarios.

Once the test case scenarios have been duly filled, the developer will upload the filled Excel spreadsheet during the ‘Go Live’ process. Our support team will then review the test cases and then either approve or reject the production app.

Testing on local host

M-Pesa APIs are asynchronous, responses to API requests are posted by the API Gateway to the client that made the request. To receive these responses, the client should have HTTP Post listeners. This is achieved by deploying an HTTP listener on a publicly accessible server. To test on local host, an http tunnelling client will be required.

Ngrok and LocalTunnel are examples of http tunnelling clients that you could use to get a public IP and make your local host accessible over the internet.

Going Live

Having played with the APIs on the sandbox and filled the test cases for an API(s) that you would like to move into production with, the developer will initiate the ‘Go Live’ process.

To go live with an M-Pesa API, the developer will need to prove ownership or authorization to use an M-Pesa shortcode. M-Pesa Shortcodes are managed on the M-Pesa Web Portal where owners of the shortcodes can add users and assign roles. When a request to verify ownership or authorization to use an M-Pesa shortcode is received, an OTP - one time password - is sent to the user with a Business Manager and Business Administrator role and a notification is sent to users with a Business Manager role.

For non M-Pesa APIs, the developer will provide a valid identification document known to Safaricom for example a National ID or passport used to register as a Safaricom subscriber.

Step by Step Go Live Guide

1. Upload test cases

From the menu bar, click on ‘Go Live’

go live

Upload a filled out test cases (Excel spreadsheet) and click ‘Upload’, once uploaded click on the check box as consent to the ‘Terms and Conditions’. Click on ‘Next’.

upload test cases

2. Select a verification type

Verification can be by a ‘Shortcode’ or a ‘Document ID’.

verification

To verify an M-Pesa Shortcode - input your ‘organisation short code’, ‘organisation name’ and ‘M-Pesa User Name’.

The ‘M-Pesa User Name’ is user name used on the M-Pesa Web Portal for the Business Administrator or Business Manager or Business Operator roles within the organisation in M-Pesa.

verification_shortcode

To verify a ‘Document ID’, select a ‘Document Type’, input the ‘Document ID’ and ‘Organisation Name’.

The ‘Document Type’ used are any document submitted to Safaricom while subscribing to any Safaricom services, for example, registering for an M-Pesa account or Safaricom MSISDN.

This verification type is for non M-Pesa APIs.

verification_document_type

Click ‘Verify’

3. OTP Confirmation

A verification will be done and a six digit passcode, OTP - one time password, will be sent via SMS.

If the verification type was an M-Pesa Shortcode, the OTP will be sent to MSISDN belonging to the M-Pesa Business Administrator or Manager or Operator. An SMS notification will also be sent to other users within the M-Pesa organisation notifying them of the intention to integrate their shortcode to M-Pesa APIs.

If verification type was a Document ID, the OTP will be sent to the MSISDN associated with the document.

otp_confirmation

The developer will select the API Products they will be integrating to, select only the API Products for which you have filled test case scenarios for in the filled test cases Excel spreadsheet uploaded at step 1. The API Products will be presented depending on the verification type used. For M-Pesa shortcode verification type, the API products displayed will depend on the M-Pesa product type configured on M-Pesa for that shortcode.

For ‘Document ID’ verification method, non M-Pesa API Products will be displayed.

Click ‘Submit’ once you have selected the API Product(s) and input the OTP.

4. Production URLs and Credentials

Your application to ‘Go Live’ will be reviewed for approval by our support teams. The approval process involves a review of the uploaded test cases ensuring that all test scenarios were successfully tested and the actual results filled for the test scenarios are appropriate.

production

To switch to your production app page, click on the switch adjacent to your email address and select the organisation name you submitted earlier during the verification process. Your production app will be automatically be assigned a name and will have a ‘pending’ tag.

Once your production app is approved, you will receive an email notification of the production urls for the APIs in the approved API Products. The production app will be tagged ‘approved’ and its consumer key and consumer secret can now be used.

Inviting Developers

Once you have an approved production app, you can now invite other developers to help you to develop your app. You can only invite developers who also have developer accounts on Daraja.

To invite a developer(s):

1. Switch to your production profile by clicking the switch to your right adjacent to your email address.

2. From the drop down menu that appears, click on ‘Manage Companies’.

3. The ‘Manage Companies’ page will open. The page will have a list of companies that you have created or have been invited to.

For companies you have created, your role will be ‘Owner’ and ‘Monetization Administrator’. You can invite developers to your company by providing the email address they used in registering for a developer account. They will then get an email with an invitation link and join your company by consenting to the invitation.

Once a developer has been invited to your company, they will have a ‘Developer’ role by default. You can give a developer ‘Company Administrator’ role. A developer with the ‘Company Administrator’ role can invite other developers, assign and revoke roles to other developers and remove developers.

If you have been invited to a company, you have access to the production apps of that company. You can then make API calls using the consumer key and consumer secret of any production app associated with the company you have been invited to.

Only the owner of a company can edit or delete a production app(s) of their company. Invited developers, even those with a ‘Company Administrator’ role, cannot edit or delete a production app(s).

M-Pesa APIs

B2C API

curl -X POST --header "Authorization: Bearer <Access-Token>" --header "Content-Type: application/json" -d "{
  \"InitiatorName\":\" \",
  \"SecurityCredential\":\" \",
  \"CommandID\":\" \",
  \"Amount\":\" \",
  \"PartyA\":\" \",
  \"PartyB\":\" \",
  \"Remarks\":\" \",
  \"QueueTimeOutURL\":\"http://your_timeout_url\",
  \"ResultURL\":\"http://your_result_url\",
  \"Occasion\":\" \"
}" "https://sandbox.safaricom.co.ke/mpesa/b2c/v1/paymentrequest"

require 'net/http'
require 'net/https'
require 'uri'

uri = URI('https://sandbox.safaricom.co.ke/mpesa/b2c/v1/paymentrequest')

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(uri)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request["authorization"] = 'Bearer <Access-Token>'
request.body = "{\"InitiatorName\":\" \",
  \"SecurityCredential\":\" \",
  \"CommandID\":\" \",
  \"Amount\":\" \",
  \"PartyA\":\" \",
  \"PartyB\":\" \",
  \"Remarks\":\" \",
  \"QueueTimeOutURL\":\"http://your_timeout_url\",
  \"ResultURL\":\"http://your_result_url\",
  \"Occasion\":\" \"}"

response = http.request(request)
puts response.read_body
import requests

access_token = "Access-Token"
api_url = "https://sandbox.safaricom.co.ke/mpesa/b2c/v1/paymentrequest"
headers = { "Authorization": "Bearer %s" % access_token }
request = {
  "InitiatorName": " ",
  "SecurityCredential":" ",
  "CommandID": " ",
  "Amount": " ",
  "PartyA": " ",
  "PartyB": " ",
  "Remarks": " ",
  "QueueTimeOutURL": "http://your_timeout_url",
  "ResultURL": "http://your_result_url",
  "Occasion": " "
}

response = requests.post(api_url, json = request, headers=headers)

print (response.text)
var request = require('request'),
  oauth_token = "YOUR_ACCESS_TOKEN",
  url = "https://sandbox.safaricom.co.ke/mpesa/b2c/v1/paymentrequest"
  auth = "Bearer " + oauth_token;

  request(
    {
      method: 'POST'
      url : url,
      headers : {
        "Authorization" : auth
      },
    json : {
      "InitiatorName": " ",
      "SecurityCredential":" ",
      "CommandID": " ",
      "Amount": " ",
      "PartyA": " ",
      "PartyB": " ",
      "Remarks": " ",
      "QueueTimeOutURL": "http://your_timeout_url",
      "ResultURL": "http://your_result_url",
      "Occasion": " "
    }
  },
    function (error, response, body) {
      // TODO: Use the body object to extract the response
      console.log(body)
    }
  )
<?php
$url = 'https://sandbox.safaricom.co.ke/mpesa/b2c/v1/paymentrequest';

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Authorization:Bearer ACCESS_TOKEN')); //setting custom header


$curl_post_data = array(
  //Fill in the request parameters with valid values
  'InitiatorName' => ' ',
  'SecurityCredential' => ' ',
  'CommandID' => ' ',
  'Amount' => ' ',
  'PartyA' => ' ',
  'PartyB' => ' ',
  'Remarks' => ' ',
  'QueueTimeOutURL' => 'http://your_timeout_url',
  'ResultURL' => 'http://your_result_url',
  'Occasion' => ' '
);

$data_string = json_encode($curl_post_data);

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);

$curl_response = curl_exec($curl);
print_r($curl_response);

echo $curl_response;
?>
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"InitiatorName\":\" \",
  \"SecurityCredential\":\" \",
  \"CommandID\":\" \",
  \"Amount\":\" \",
  \"PartyA\":\" \",
  \"PartyB\":\" \",
  \"Remarks\":\" \",
  \"QueueTimeOutURL\":\"http://your_timeout_url\",
  \"ResultURL\":\"http://your_result_url\",
  \"Occasion\":\" \"}");
Request request = new Request.Builder()
  .url("https://sandbox.safaricom.co.ke/mpesa/b2c/v1/paymentrequest")
  .post(body)
  .addHeader("authorization", "Bearer YOUR_OAUTH_TOKEN")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
// Sample M-Pesa Core response received on the callback url.
{
    "Result":{
    "ResultType":0,
    "ResultCode":0,
    "ResultDesc":"The service request has been accepted successfully.",
    "OriginatorConversationID":"19455-424535-1",
    "ConversationID":"AG_20170717_00006be9c8b5cc46abb6",
    "TransactionID":"LGH3197RIB",
    "ResultParameters":{
      "ResultParameter":[
        {
          "Key":"TransactionReceipt",
          "Value":"LGH3197RIB"
        },
        {
          "Key":"TransactionAmount",
          "Value":8000
        },
        {
          "Key":"B2CWorkingAccountAvailableFunds",
          "Value":150000
        },
        {
          "Key":"B2CUtilityAccountAvailableFunds",
          "Value":133568
        },
        {
          "Key":"TransactionCompletedDateTime",
          "Value":"17.07.2017 10:54:57"
        },
        {
          "Key":"ReceiverPartyPublicName",
          "Value":"254708374149 - John Doe"
        },
        {
          "Key":"B2CChargesPaidAccountAvailableFunds",
          "Value":0
        },
        {
          "Key":"B2CRecipientIsRegisteredCustomer",
          "Value":"Y"
        }
      ]
    },
    "ReferenceData":{
      "ReferenceItem":{
        "Key":"QueueTimeoutURL",
        "Value":"https://internalsandbox.safaricom.co.ke/mpesa/b2cresults/v1/submit"
      }
    }
  }
}

This API enables Business to Customer (B2C) transactions between a company and customers who are the end-users of its products or services. Use of this API requires a valid and verified B2C M-Pesa Short code.

B2C Resource URL

POST https://sandbox.safaricom.co.ke/mpesa/b2c/v1/paymentrequest

B2C Query Parameters

Parameter Description
InitiatorName This is the credential/username used to authenticate the transaction request.
SecurityCredential Base64 encoded string of the B2C short code and password, which is encrypted using M-Pesa public key and validates the transaction on M-Pesa Core system.
CommandID Unique command for each transaction type e.g. SalaryPayment, BusinessPayment, PromotionPayment
Amount The amount being transacted
PartyA Organization’s shortcode initiating the transaction.
PartyB Phone number receiving the transaction
Remarks Comments that are sent along with the transaction.
QueueTimeOutURL The timeout end-point that receives a timeout response.
ResultURL The end-point that receives the response of the transaction
Occasion Optional

B2B API

curl -X POST --header "Authorization: Bearer <Access-Token>" --header "Content-Type: application/json" -d "{
  \"Initiator\": \" \",
  \"SecurityCredential\": \" \",
  \"CommandID\": \" \",
  \"SenderIdentifierType\": \" \",
  \"RecieverIdentifierType\": \" \",
  \"Amount\": \" \",
  \"PartyA\": \" \",
  \"PartyB\": \" \",
  \"AccountReference\": \" \",
  \"Remarks\": \" \",
  \"QueueTimeOutURL\": \"http://your_timeout_url\",
  \"ResultURL\": \"http://your_result_url\"
}" "https://sandbox.safaricom.co.ke/mpesa/b2b/v1/paymentrequest"

require 'net/http'
require 'net/https'
require 'uri'

uri = URI('https://sandbox.safaricom.co.ke/mpesa/b2b/v1/paymentrequest')

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(uri)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request["authorization"] = 'Bearer <Access-Token>'
request.body = "{\"Initiator\": \" \",
      \"SecurityCredential\": \" \",
      \"CommandID\": \" \",
      \"SenderIdentifierType\": \" \",
      \"RecieverIdentifierType\": \" \",
      \"Amount\": \" \",
      \"PartyA\": \" \",
      \"PartyB\": \" \",
      \"AccountReference\": \" \",
      \"Remarks\": \" \",
      \"QueueTimeOutURL\": \"http://your_timeout_url\",
      \"ResultURL\": \"http://your_result_url\"}"

response = http.request(request)
puts response.read_body
import requests

access_token = "Access-Token"
api_url = "https://sandbox.safaricom.co.ke/mpesa/b2b/v1/paymentrequest"
headers = { "Authorization": "Bearer %s" % access_token }
request = {
  "Initiator": " ",
  "SecurityCredential": " ",
  "CommandID": " ",
  "SenderIdentifierType": " ",
  "RecieverIdentifierType": " ",
  "Amount": " ",
  "PartyA": " ",
  "PartyB": " ",
  "AccountReference": " ",
  "Remarks": " ",
  "QueueTimeOutURL": "http://your_timeout_url",
  "ResultURL": "http://your_result_url"
}

response = requests.post(api_url, json = request, headers=headers)

print (response.text)
var request = require('request'),
  oauth_token = "YOUR_ACCESS_TOKEN",
  url = "https://sandbox.safaricom.co.ke/mpesa/b2b/v1/paymentrequest"
  auth = "Bearer " + oauth_token;

  request(
    {
      method: 'POST'
      url : url,
      headers : {
        "Authorization" : auth
      },
    json : {
      "Initiator": " ",
      "SecurityCredential": " ",
      "CommandID": " ",
      "SenderIdentifierType": " ",
      "RecieverIdentifierType": " ",
      "Amount": " ",
      "PartyA": " ",
      "PartyB": " ",
      "AccountReference": " ",
      "Remarks": " ",
      "QueueTimeOutURL": "http://your_timeout_url",
      "ResultURL": "http://your_result_url"
    }
  },
    function (error, response, body) {
      // TODO: Use the body object to extract the response
      console.log(body)
    }
  )
<?php
$url = 'https://sandbox.safaricom.co.ke/mpesa/b2b/v1/paymentrequest';

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Authorization:Bearer ACCESS_TOKEN')); //setting custom header


$curl_post_data = array(
  //Fill in the request parameters with valid values
  'Initiator' => ' ',
  'SecurityCredential' => ' ',
  'CommandID' => ' ',
  'SenderIdentifierType' => ' ',
  'RecieverIdentifierType' => ' ',
  'Amount' => ' ',
  'PartyA' => ' ',
  'PartyB' => ' ',
  'AccountReference' => ' ',
  'Remarks' => ' ',
  'QueueTimeOutURL' => 'http://your_timeout_url',
  'ResultURL' => 'http://your_result_url'
);

$data_string = json_encode($curl_post_data);

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);

$curl_response = curl_exec($curl);
print_r($curl_response);

echo $curl_response;
?>
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"Initiator\": \" \",
      \"SecurityCredential\": \" \",
      \"CommandID\": \" \",
      \"SenderIdentifierType\": \" \",
      \"RecieverIdentifierType\": \" \",
      \"Amount\": \" \",
      \"PartyA\": \" \",
      \"PartyB\": \" \",
      \"AccountReference\": \" \",
      \"Remarks\": \" \",
      \"QueueTimeOutURL\": \"http://your_timeout_url\",
      \"ResultURL\": \"http://your_result_url\"}");
Request request = new Request.Builder()
  .url("https://sandbox.safaricom.co.ke/mpesa/b2b/v1/paymentrequest")
  .post(body)
  .addHeader("authorization", "Bearer ACCESS_TOKEN")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
{
  "Result":{
    "ResultType":0,
    "ResultCode":0,
    "ResultDesc":"The service request has been accepted successfully.",
    "OriginatorConversationID":"8551-61996-3",
    "ConversationID":"AG_20170727_00006baee344f4ce0796",
    "TransactionID":"LGR519G2QV",
    "ResultParameters":{
      "ResultParameter":[
        {
          "Key":"InitiatorAccountCurrentBalance",
          "Value":"{ Amount={BasicAmount=46713.00, MinimumAmount=4671300, CurrencyCode=KES}}"
        },
        {
          "Key":"DebitAccountCurrentBalance",
          "Value":"{Amount={BasicAmount=46713.00, MinimumAmount=4671300, CurrencyCode=KES}}"
        },
        {
          "Key":"Amount",
          "Value":10
        },
        {
          "Key":"DebitPartyAffectedAccountBalance",
          "Value":"Working Account|KES|46713.00|46713.00|0.00|0.00"
        },
        {
          "Key":"TransCompletedTime",
          "Value":20170727102524
        },
        {
          "Key":"DebitPartyCharges",
          "Value":"Business Pay Bill Charge|KES|77.00"
        },
        {
          "Key":"ReceiverPartyPublicName",
          "Value":"603094 - Safaricom3117"
        },
        {
          "Key":"Currency",
          "Value":"KES"
        }
      ]
    },
    "ReferenceData":{
      "ReferenceItem":[
        {
          "Key":"BillReferenceNumber",
          "Value":"aaa"
        },
        {
          "Key":"QueueTimeoutURL",
          "Value":"https://internalsandbox.safaricom.co.ke/mpesa/b2bresults/v1/submit"
        },
        {
          "Key":"Occasion"
        }
      ]
    }
  }
}

This API enables Business to Business (B2B) transactions between a business and another business. Use of this API requires a valid and verified B2B M-Pesa short code for the business initiating the transaction and the both businesses involved in the transaction.

B2B - Resource URL

POST https://sandbox.safaricom.co.ke/mpesa/b2b/v1/paymentrequest

B2B - Request Parameters

Parameter Description
Initiator This is the credential/username used to authenticate the transaction request.
SecurityCredential Base64 encoded string of the B2B short code and password, which is encrypted using M-Pesa public key and validates the transaction on M-Pesa Core system.
CommandID Unique command for each transaction type, possible values are: BusinessPayBill, MerchantToMerchantTransfer, MerchantTransferFromMerchantToWorking, MerchantServicesMMFAccountTransfer, AgencyFloatAdvance
Amount The amount being transacted.
PartyA Organization’s short code initiating the transaction.
SenderIdentifier Type of organization sending the transaction.
PartyB Organization’s short code receiving the funds being transacted.
RecieverIdentifierType Type of organization receiving the funds being transacted.
Remarks Comments that are sent along with the transaction.
QueueTimeOutURL The path that stores information of time out transactions.it should be properly validated to make sure that it contains the port, URI and domain name or publicly available IP.
ResultURL The path that receives results from M-Pesa it should be properly validated to make sure that it contains the port, URI and domain name or publicly available IP.
AccountReference Account Reference mandatory for “BusinessPaybill” CommandID.

B2B Response Parameters

Parameter Description
ConversationID A unique numeric code generated by the M-Pesa system of the response to a request.
OriginatorConversationID A unique numeric code generated by the M-Pesa system of the request.
ResponseDescription A response message from the M-Pesa system accompanying the response to a request.

C2B API

This API enables Paybill and Buy Goods merchants to integrate to M-Pesa and receive real time payments notifications.

Register URL

curl -X POST --header "Authorization: Bearer <Access-Token>" --header "Content-Type: application/json" -d "{
    \"ShortCode\":\" \",
    \"ResponseType\":\" \",
    \"ConfirmationURL\":\"http://ip_address:port/confirmation\",
    \"ValidationURL\":\"http://ip_address:port/validation_url\"
}" "https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl"

require 'net/http'
require 'net/https'
require 'uri'

uri = URI('https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl')

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(url)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request["authorization"] = 'Bearer <Access-Token>'
request.body = "{\"ShortCode\":\"ShortCode\",
    \"ResponseType\":\"ResponseType\",
    \"ConfirmationURL\":\"http://ip_address:port/confirmation\",
    \"ValidationURL\":\"http://ip_address:port/validation_url\"}"

response = http.request(request)
puts response.read_body
import requests

access_token = "Access-Token"
api_url = "http://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl"
headers = {"Authorization": "Bearer %s" % access_token}
request = { "ShortCode": " ",
    "ResponseType": " ",
    "ConfirmationURL": "http://ip_address:port/confirmation",
    "ValidationURL": "http://ip_address:port/validation_url"}

response = requests.post(api_url, json = request, headers=headers)

print (response.text)
var request = require('request'),
  oauth_token = "YOUR_ACCESS_TOKEN",
  url = "https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl"
  auth = "Bearer " + oauth_token;

  request(
    {
      method: 'POST'
      url : url,
      headers : {
        "Authorization" : auth
      },
      json : {
        "ShortCode": " ",
        "ResponseType": " ",
        "ConfirmationURL": "http://ip_address:port/confirmation",
        "ValidationURL": "http://ip_address:port/validation_url"
      }
    },
    function (error, response, body) {
      // TODO: Use the body object to extract the 
      console.log(body)
    }
  )
<?php
$url = 'https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl';

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Authorization:Bearer ACCESS_TOKEN')); //setting custom header


$curl_post_data = array(
  //Fill in the request parameters with valid values
  'ShortCode' => ' ',
  'ResponseType' => ' ',
  'ConfirmationURL' => 'http://ip_address:port/confirmation',
  'ValidationURL' => 'http://ip_address:port/validation_url'
);

$data_string = json_encode($curl_post_data);

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);

$curl_response = curl_exec($curl);
print_r($curl_response);

echo $curl_response;
?>
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"ShortCode\":\"ShortCode\",
    \"ResponseType\":\"ResponseType\",
    \"ConfirmationURL\":\"http://ip_address:port/confirmation\",
    \"ValidationURL\":\"http://ip_address:port/validation_url\"}");
Request request = new Request.Builder()
  .url("https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl")
  .post(body)
  .addHeader("authorization", "Bearer ACCESS_TOKEN")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();

The C2B Register URL API registers the 3rd party’s confirmation and validation URLs to M-Pesa ; which then maps these URLs to the 3rd party shortcode. Whenever M-Pesa receives a transaction on the shortcode, M-Pesa triggers a validation request against the validation URL and the 3rd party system responds to M-Pesa with a validation response (either a success or an error code). The response expected is the success code the 3rd party

M-Pesa completes or cancels the transaction depending on the validation response it receives from the 3rd party system. A confirmation request of the transaction is then sent by M-Pesa through the confirmation URL back to the 3rd party which then should respond with a success acknowledging the confirmation.

C2B Register URL - Resource URL

POST https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl

C2B Register URL - Request Parameters

Parameter Description
ValidationURL Validation URL for the client.
ConfirmationURL Confirmation URL for the client.
ResponseType Default response type for timeout.
ShortCode The short code of the organization.

Register URL - Response Parameters

Parameter Description
ConversationID A unique numeric code generated by the M-Pesa system of the response to a request.
OriginatorConversationID A unique numeric code generated by the M-Pesa system of the request.
ResponseDescription A response message from the M-Pesa system accompanying the response to a request.

C2B Simulate Transaction

curl -X POST --header "Authorization: Bearer <Access-Token>" --header "Content-Type: application/json" -d "{
  \"ShortCode\":\" \",
  \"CommandID\":\"CustomerPayBillOnline\",
  \"Amount\":\" \",
  \"Msisdn\":\" \",
  \"BillRefNumber\":\" \"
}" "https://sandbox.safaricom.co.ke/mpesa/c2b/v1/simulate"

require 'net/http'
require 'net/https'
require 'uri'

uri = URI('https://sandbox.safaricom.co.ke/mpesa/c2b/v1/simulate')

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(url)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request["authorization"] = 'Bearer <Access-Token>'
request.body = "{ \"ShortCode\":\" \",
  \"CommandID\":\"CustomerPayBillOnline\",
  \"Amount\":\" \",
  \"Msisdn\":\" \",
  \"BillRefNumber\":\" \" }"

response = http.request(request)
puts response.read_body
import requests

access_token = "ACCESS_TOKEN"
api_url = "https://sandbox.safaricom.co.ke/mpesa/c2b/v1/simulate"
headers = {"Authorization": "Bearer %s" % access_token}
request = { "ShortCode":" ",
  "CommandID":"CustomerPayBillOnline",
  "Amount":" ",
  "Msisdn":" ",
  "BillRefNumber":" " }

response = requests.post(api_url, json = request, headers=headers)

print (response.text)
var request = require('request'),
  oauth_token = "YOUR_ACCESS_TOKEN",
  url = "https://sandbox.safaricom.co.ke/mpesa/c2b/v1/simulate"
  auth = "Bearer " + oauth_token;

  request(
    {
      method: 'POST'
      url : url,
      headers : {
        "Authorization" : auth
      },
      json : {
        //Fill in the request parameters with valid values
        "ShortCode":" ",
        "CommandID":"CustomerPayBillOnline",
        "Amount":" ",
        "Msisdn":" ",
        "BillRefNumber":" "
      }
    },
    function (error, response, body) {
      // TODO: Use the body object to extract the response
      console.log(body)
    }
  )
<?php
$url = 'https://sandbox.safaricom.co.ke/mpesa/c2b/v1/simulate';

  $curl = curl_init();
  curl_setopt($curl, CURLOPT_URL, $url);
  curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Authorization:Bearer ACCESS_TOKEN')); //setting custom header


  $curl_post_data = array(
          //Fill in the request parameters with valid values
         'ShortCode' => ' ',
         'CommandID' => 'CustomerPayBillOnline',
         'Amount' => ' ',
         'Msisdn' => ' ',
         'BillRefNumber' => '00000'
  );

  $data_string = json_encode($curl_post_data);

  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($curl, CURLOPT_POST, true);
  curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);

  $curl_response = curl_exec($curl);
  print_r($curl_response);

  echo $curl_response;
?>
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"ShortCode\":\" \",
  \"CommandID\":\"CustomerPayBillOnline\",
  \"Amount\":\" \",
  \"Msisdn\":\" \",
  \"BillRefNumber\":\" \" }");
Request request = new Request.Builder()
  .url("https://sandbox.safaricom.co.ke/mpesa/c2b/v1/simulate")
  .post(body)
  .addHeader("authorization", "Bearer ACCESS_TOKEN")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
// Validation Response
{
  "TransactionType":"",
  "TransID":"LGR219G3EY",
  "TransTime":"20170727104247",
  "TransAmount":"10.00",
  "BusinessShortCode":"600134",
  "BillRefNumber":"xyz",
  "InvoiceNumber":"",
  "OrgAccountBalance":"",
  "ThirdPartyTransID":"",
  "MSISDN":"254708374149",
  "FirstName":"John",
  "MiddleName":"Doe",
  "LastName":""
}

//Confirmation Respose
{
  "TransactionType":"",
  "TransID":"LGR219G3EY",
  "TransTime":"20170727104247",
  "TransAmount":"10.00",
  "BusinessShortCode":"600134",
  "BillRefNumber":"xyz",
  "InvoiceNumber":"",
  "OrgAccountBalance":"49197.00",
  "ThirdPartyTransID":"1234567890",
  "MSISDN":"254708374149",
  "FirstName":"John",
  "MiddleName":"",
  "LastName":""
}

C2B Simulate Transaction - Resource URL

POST https://sandbox.safaricom.co.ke/mpesa/c2b/v1/simulate

C2B Simulate Transaction - Request Parameters

Parameter Description
CommandID Unique command for each transaction type.
Amount The amount been transacted.
MSISDN MSISDN (phone number) sending the transaction, start with country code without the plus(+) sign.
BillRefNumber Bill Reference Number (Optional).
ShortCode 6 digit M-Pesa Till Number or PayBill Number

C2B Simulate Transaction - Response Parameters

Parameter Description
ConversationID A unique numeric code generated by the M-Pesa system of the response to a request.
OriginatorConversationID A unique numeric code generated by the M-Pesa system of the request.
ResponseDescription A response message from the M-Pesa system accompanying the response to a request.

Account Balance API

curl -X POST --header "Authorization: Bearer <Access-Token>" --header "Content-Type: application/json" -d "{
    \"Initiator\":\"Initiator\",
    \"SecurityCredential\":\"security credential\",
    \"CommandID\":\"AccountBalance\",
    \"PartyA\":\"shortcode\",
    \"IdentifierType\":\"4\",
    \"Remarks\":\"Remarks\",
    \"QueueTimeOutURL\":\"https://ip_address:port/timeout_url\",
    \"ResultURL\":\"https://ip_address:port/result_url\"
}" "https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query"

require 'net/http'
require 'net/https'
require 'uri'

uri = URI('https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query')

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(uri)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request["authorization"] = 'Bearer <Access-Token>'
request.body = "{\"Initiator\":\" \",
    \"SecurityCredential\":\" \",
    \"CommandID\":\"AccountBalance\",
    \"PartyA\":\" \",
    \"IdentifierType\":\"4\",
    \"Remarks\":\" \",
    \"QueueTimeOutURL\":\"https://ip_address:port/timeout_url\",
    \"ResultURL\":\"https://ip_address:port/result_url\"}"

response = http.request(request)
puts response.read_body
import requests

access_token = "Access-Token"
api_url = "https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query"
headers = {"Authorization": "Bearer %s" % access_token}
request = { "Initiator":" ",
    "SecurityCredential":" ",
    "CommandID":"AccountBalance",
    "PartyA":"shortcode",
    "IdentifierType":"4",
    "Remarks":"Remarks",
    "QueueTimeOutURL":"https://ip_address:port/timeout_url",
    "ResultURL":"https://ip_address:port/result_url"
    }

response = requests.post(api_url, json = request, headers=headers)

print (response.text)
var request = require('request'),
  oauth_token = "YOUR_ACCESS_TOKEN",
  url = "https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query"
  auth = "Bearer " + oauth_token;

  request(
    {
      method: 'POST'
      url : url,
      headers : {
        "Authorization" : auth
      },
      json : {
        "Initiator":" ",
        "SecurityCredential":" ",
        "CommandID":"AccountBalance",
        "PartyA":" ",
        "IdentifierType":"4",
        "Remarks":" ",
        "QueueTimeOutURL":"https://ip_address:port/timeout_url",
        "ResultURL":"https://ip_address:port/result_url"
        }
    },
    function (error, response, body) {
      // TODO: Use the body object to extract the response
      console.log(body)
    }
  )
<?php
$url = 'https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query';

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Authorization:Bearer ACCESS_TOKEN')); //setting custom header


$curl_post_data = array(
  //Fill in the request parameters with valid values
  'CommandID' => ' ',
  'Initiator' => ' ',
  'SecurityCredential' => ' ',
  'CommandID' => 'AccountBalance',
  'PartyA' => ' ',
  'IdentifierType' => '4',
  'Remarks' => ' ',
  'QueueTimeOutURL' => 'https://ip_address:port/timeout_url',
  'ResultURL' => 'https://ip_address:port/result_url'
);

$data_string = json_encode($curl_post_data);

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);

$curl_response = curl_exec($curl);
print_r($curl_response);

echo $curl_response;
?>
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"Initiator\":\" \",
    \"SecurityCredential\":\" \",
    \"CommandID\":\"AccountBalance\",
    \"PartyA\":\" \",
    \"IdentifierType\":\"4\",
    \"Remarks\":\" \",
    \"QueueTimeOutURL\":\"https://ip_address:port/timeout_url\",
    \"ResultURL\":\"https://ip_address:port/result_url\"}");
Request request = new Request.Builder()
  .url("https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query")
  .post(body)
  .addHeader("authorization", "Bearer ACCESS_TOKEN")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
{
  "Result":{
    "ResultType":0,
    "ResultCode":0,
    "ResultDesc":"The service request has b een accepted successfully.",
    "OriginatorConversationID":"19464-802673-1",
    "ConversationID":"AG_20170728_0000589b6252f7f25488",
    "TransactionID":"LGS0000000",
    "ResultParameters":{
      "ResultParameter":[
        {
          "Key":"AccountBalance",
          "Value":"Working Account|KES|46713.00|46713.00|0.00|0.00&Float Account|KES|0.00|0.00|0.00|0.00&Utility Account|KES|49217.00|49217.00|0.00|0.00&Charges Paid Account|KES|-220.00|-220.00|0.00|0.00&Organization Settlement Account|KES|0.00|0.00|0.00|0.00"
        },
        {
          "Key":"BOCompletedTime",
          "Value":20170728095642
        }
      ]
    },
    "ReferenceData":{
      "ReferenceItem":{
        "Key":"QueueTimeoutURL",
        "Value":"https://internalsandbox.safaricom.co.ke/mpesa/abresults/v1/submit"
      }
    }
  }
}

The Account Balance API requests for the account balance of a shortcode.

Account Balance - Resource URL

POST https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query

Account Balance - Request Parameters

Parameter Description
Initiator This is the credential/username used to authenticate the transaction request.
SecurityCredential Base64 encoded string of the M-Pesa short code and password, which is encrypted using M-Pesa public key and validates the transaction on M-Pesa Core system.
CommandID A unique command passed to the M-Pesa system.
PartyB The shortcode of the organisation receiving the transaction.
ReceiverIdentifierType Type of the organisation receiving the transaction.
Remarks Comments that are sent along with the transaction.
QueueTimeOutURL The timeout end-point that receives a timeout message.
ResultURL The end-point that receives a successful transaction.
AccountType Organisation receiving the funds.

Reversal

curl -X POST --header "Authorization: Bearer <Access-Token>" --header "Content-Type: application/json" -d "{
  \"Initiator\":\" \",
  \"SecurityCredential\":\" \",
  \"CommandID\":\"TransactionReversal\",
  \"TransactionID\":\" \",
  \"Amount\":\" \",
  \"ReceiverParty\":\" \",
  \"RecieverIdentifierType\":\"4\",
  \"ResultURL\":\"https://ip_address:port/result_url\",
  \"QueueTimeOutURL\":\"https://ip_address:port/timeout_url\",
  \"Remarks\":\" \",
  \"Occasion\":\" \"
}" "https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request"

require 'net/http'
require 'net/https'
require 'uri'

uri = URI('https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request')

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(uri)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request["authorization"] = 'Bearer <Access-Token>'
request.body = "{\"Initiator\":\" \",
  \"SecurityCredential\":\" \",
  \"CommandID\":\"TransactionReversal\",
  \"TransactionID\":\" \",
  \"Amount\":\" \",
  \"ReceiverParty\":\" \",
  \"RecieverIdentifierType\":\"4\",
  \"ResultURL\":\"https://ip_address:port/result_url\",
  \"QueueTimeOutURL\":\"https://ip_address:port/timeout_url\",
  \"Remarks\":\" \",
  \"Occasion\":\" \"}"

response = http.request(request)
puts response.read_body
import requests

access_token = "Access-Token"
api_url = "https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request"
headers = {"Authorization": "Bearer %s" % access_token}
request = { "Initiator":" ",
  "SecurityCredential":" ",
  "CommandID":"TransactionReversal",
  "TransactionID":" ",
  "Amount":" ",
  "ReceiverParty":" ",
  "RecieverIdentifierType":"4",
  "ResultURL":"https://ip_address:port/result_url",
  "QueueTimeOutURL":"https://ip_address:port/timeout_url",
  "Remarks":" ",
  "Occasion":" "
  }

response = requests.post(api_url, json = request, headers=headers)

print (response.text)
var request = require('request'),
  oauth_token = "YOUR_ACCESS_TOKEN",
  url = "https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request"
  auth = "Bearer " + oauth_token;

  request(
    {
      method: 'POST'
      url : url,
      headers : {
        "Authorization" : auth
      },
      json : {
        "Initiator":" ",
        "SecurityCredential":" ",
        "CommandID":"TransactionReversal",
        "TransactionID":" ",
        "Amount":" ",
        "ReceiverParty":" ",
        "RecieverIdentifierType":"4",
        "ResultURL":"https://ip_address:port/result_url",
        "QueueTimeOutURL":"https://ip_address:port/timeout_url",
        "Remarks":" ",
        "Occasion":" "
        }
    },
    function (error, response, body) {
      // TODO: Use the body object to extract the response
      console.log(body)
    }
  )
<?php
$url = 'https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request';

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Authorization:Bearer ACCESS_TOKEN')); //setting custom header


$curl_post_data = array(
  //Fill in the request parameters with valid values
  'CommandID' => ' ',
  'Initiator' => ' ',
  'SecurityCredential' => ' ',
  'CommandID' => 'TransactionReversal',
  'TransactionID' => ' ',
  'Amount' => ' ',
  'ReceiverParty' => ' ',
  'RecieverIdentifierType' => '4',
  'ResultURL' => 'https://ip_address:port/result_url',
  'QueueTimeOutURL' => 'https://ip_address:port/timeout_url',
  'Remarks' => ' ',
  'Occasion' => ' '
);

$data_string = json_encode($curl_post_data);

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);

$curl_response = curl_exec($curl);
print_r($curl_response);

echo $curl_response;
?>
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"Initiator\":\" \",
  \"SecurityCredential\":\" \",
  \"CommandID\":\"TransactionReversal\",
  \"TransactionID\":\" \",
  \"Amount\":\" \",
  \"ReceiverParty\":\" \",
  \"RecieverIdentifierType\":\"4\",
  \"ResultURL\":\"https://ip_address:port/result_url\",
  \"QueueTimeOutURL\":\"https://ip_address:port/timeout_url\",
  \"Remarks\":\" \",
  \"Occasion\":\" \"}");
Request request = new Request.Builder()
  .url("https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request")
  .post(body)
  .addHeader("authorization", "Bearer ACCESS_TOKEN")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
{
  "Result":{
    "ResultType":0,
    "ResultCode":0,
    "ResultDesc":"The service request has been accepted successfully.",
    "OriginatorConversationID":"10819-695089-1",
    "ConversationID":"AG_20170727_00004efadacd98a01d15",
    "TransactionID":"LGR019G3J2",
    "ReferenceData":{
      "ReferenceItem":{
        "Key":"QueueTimeoutURL",
        "Value":"https://internalsandbox.safaricom.co.ke/mpesa/reversalresults/v1/submit"
      }
    }
  }
}

Reverses a B2B, B2C or C2B M-Pesa transaction.

Reversal Resource URL

POST https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request

Reversal Request Parameters

Parameter Description
Initiator This is the credential/username used to authenticate the transaction request.
SecurityCredential Base64 encoded string of the M-Pesa short code and password, which is encrypted using M-Pesa public key and validates the transaction on M-Pesa Core system.
CommandID Unique command for each transaction type, possible values are: TransactionReversal.
PartyA Organization/MSISDN sending the transaction.
RecieverIdentifierType Type of organization receiving the transaction.
Remarks Comments that are sent along with the transaction.
QueueTimeOutURL The path that stores information of time out transaction.
ResultURL The path that stores information of transaction.
TransactionID Organization Receiving the funds.
Occasion Optional.

Lipa na M-Pesa Online Payment

curl -X POST --header "Authorization: Bearer <Access-Token>" --header "Content-Type: application/json" -d "{
  \"BusinessShortCode\": \" \",
  \"Password\": \" \",
  \"Timestamp\": \" \",
  \"TransactionType\": \"CustomerPayBillOnline\",
  \"Amount\": \" \",
  \"PartyA\": \" \",
  \"PartyB\": \" \",
  \"PhoneNumber\": \" \",
  \"CallBackURL\": \"https://ip_address:port/callback\",
  \"AccountReference\": \" \",
  \"TransactionDesc\": \" \"
}" "https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest"

require 'net/http'
require 'net/https'
require 'uri'

uri = URI('https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest')

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(uri)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request["authorization"] = 'Bearer <Access-Token>'
request.body = "{\"BusinessShortCode\": \" \",
  \"Password\": \" \",
  \"Timestamp\": \" \",
  \"TransactionType\": \"CustomerPayBillOnline\",
  \"Amount\": \" \",
  \"PartyA\": \" \",
  \"PartyB\": \" \",
  \"PhoneNumber\": \" \",
  \"CallBackURL\": \"https://ip_address:port/callback\",
  \"AccountReference\": \" \",
  \"TransactionDesc\": \" \"}"

response = http.request(request)
puts response.read_body
import requests

access_token = "Access-Token"
api_url = "https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest"
headers = { "Authorization": "Bearer %s" % access_token }
request = {
  "BusinessShortCode": " ",
  "Password": " ",
  "Timestamp": " ",
  "TransactionType": "CustomerPayBillOnline",
  "Amount": " ",
  "PartyA": " ",
  "PartyB": " ",
  "PhoneNumber": " ",
  "CallBackURL": "https://ip_address:port/callback",
  "AccountReference": " ",
  "TransactionDesc": " "
}

response = requests.post(api_url, json = request, headers=headers)

print (response.text)
var request = require('request'),
  oauth_token = "YOUR_ACCESS_TOKEN",
  url = "https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest"
  auth = "Bearer " + oauth_token;

  request(
    {
      method: 'POST'
      url : url,
      headers : {
        "Authorization" : auth
      },
    json : {
      "BusinessShortCode": " ",
      "Password": " ",
      "Timestamp": " ",
      "TransactionType": "CustomerPayBillOnline",
      "Amount": " ",
      "PartyA": " ",
      "PartyB": " ",
      "PhoneNumber": " ",
      "CallBackURL": "https://ip_address:port/callback",
      "AccountReference": " ",
      "TransactionDesc": " "
    }
  },
    function (error, response, body) {
      // TODO: Use the body object to extract the response
      console.log(body)
    }
  )
<?php
$url = 'https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest';

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Authorization:Bearer ACCESS_TOKEN')); //setting custom header


$curl_post_data = array(
  //Fill in the request parameters with valid values
  'BusinessShortCode' => ' ',
  'Password' => ' ',
  'Timestamp' => ' ',
  'TransactionType' => 'CustomerPayBillOnline',
  'Amount"' => ' ',
  'PartyA' => ' ',
  'PartyB' => ' ',
  'PhoneNumber' => ' ',
  'CallBackURL' => 'https://ip_address:port/callback',
  'AccountReference' => ' ',
  'TransactionDesc' => ' '
);

$data_string = json_encode($curl_post_data);

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);

$curl_response = curl_exec($curl);
print_r($curl_response);

echo $curl_response;
?>
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"BusinessShortCode\": \" \",
  \"Password\": \" \",
  \"Timestamp\": \" \",
  \"TransactionType\": \"CustomerPayBillOnline\",
  \"Amount\": \" \",
  \"PartyA\": \" \",
  \"PartyB\": \" \",
  \"PhoneNumber\": \" \",
  \"CallBackURL\": \"https://ip_address:port/callback\",
  \"AccountReference\": \" \",
  \"TransactionDesc\": \" \"}");
Request request = new Request.Builder()
  .url("https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest")
  .post(body)
  .addHeader("authorization", "Bearer ACCESS_TOKEN")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
// A cancelled request
{
  "Body":{
    "stkCallback":{
      "MerchantRequestID":"8555-67195-1",
      "CheckoutRequestID":"ws_CO_27072017151044001",
      "ResultCode":1032,
      "ResultDesc":"[STK_CB - ]Request cancelled by user"
    }
  }
}

// An accepted request
{
  "Body":{
    "stkCallback":{
      "MerchantRequestID":"19465-780693-1",
      "CheckoutRequestID":"ws_CO_27072017154747416",
      "ResultCode":0,
      "ResultDesc":"The service request is processed successfully.",
      "CallbackMetadata":{
        "Item":[
          {
            "Name":"Amount",
            "Value":1
          },
          {
            "Name":"MpesaReceiptNumber",
            "Value":"LGR7OWQX0R"
          },
          {
            "Name":"Balance"
          },
          {
            "Name":"TransactionDate",
            "Value":20170727154800
          },
          {
            "Name":"PhoneNumber",
            "Value":254721566839
          }
        ]
      }
    }
  }
}

Lipa na M-Pesa Online Payment API is used to initiate a M-Pesa transaction on behalf of a customer using STK Push. This is the same technique mySafaricom App uses whenever the app is used to make payments.

Lipa na M-Pesa Online Payment - Resource URL

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

Lipa na M-Pesa Online Payment - Request Parameters

Parameter Description
BusinessShortCode The organization shortcode used to receive the transaction.
Password The password for encrypting the request. This is generated by base64 encoding BusinessShortcode, Passkey and Timestamp.
Timestamp The timestamp of the transaction in the format yyyymmddhhiiss.
TransactionType The transaction type to be used for this request. Only CustomerPayBillOnline is supported.
Amount The amount to be transacted.
PartyA The MSISDN sending the funds.
PartyB The organization shortcode receiving the funds
PhoneNumber The MSISDN sending the funds.
CallBackURL The url to where responses from M-Pesa will be sent to.
AccountReference Used with M-Pesa PayBills.
TransactionDesc A description of the transaction.

Lipa na M-Pesa Online Payment - Response Parameters

Parameter Description
MerchantRequestID Merchant Request ID
CheckoutRequestID Check out Request ID
ResponseCode Response Code
ResultDesc Result Desc
ResponseDescription Response Description message
ResultCode Result Code

Lipa na M-Pesa Online Query Request

curl -X POST --header "Authorization: Bearer <Access-Token>" --header "Content-Type: application/json" -d "{
  \"BusinessShortCode\":\" \" ,
  \"Password\":\" \",
  \"Timestamp\":\" \",
  \"CheckoutRequestID\":\" \"
}" "https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query"

require 'net/http'
require 'net/https'
require 'uri'

uri = URI('https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query')

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(uri)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request["authorization"] = 'Bearer <Access-Token>'
request.body = "{\"BusinessShortCode\":\" \" ,
        \"Password\":\" \",
        \"Timestamp\":\" \",
        \"CheckoutRequestID\":\" \"}"

response = http.request(request)
puts response.read_body
import requests

access_token = "Access-Token"
api_url = "https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query"
headers = {"Authorization": "Bearer %s" % access_token}
request = { "BusinessShortCode": " " ,
        "Password": " ",
        "Timestamp": " ",
        "CheckoutRequestID": " "
  }

response = requests.post(api_url, json = request, headers=headers)

print (response.text)
var request = require('request'),
  oauth_token = "YOUR_ACCESS_TOKEN",
  url = "https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query"
  auth = "Bearer " + oauth_token;

  request(
    {
      method: 'POST'
      url : url,
      headers : {
        "Authorization" : auth
      },
      json : {
        "BusinessShortCode": " " ,
        "Password": " ",
        "Timestamp": " ",
        "CheckoutRequestID": " "
        }
    },
    function (error, response, body) {
      // TODO: Use the body object to extract the response
      console.log(body)
    }
  )
<?php
$url = 'https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query';

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Authorization:Bearer ACCESS_TOKEN')); //setting custom header


$curl_post_data = array(
  //Fill in the request parameters with valid values
  'BusinessShortCode' => ' ',
  'Password' => ' ',
  'Timestamp' => ' ',
  'CheckoutRequestID' => ' '
);

$data_string = json_encode($curl_post_data);

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);

$curl_response = curl_exec($curl);
print_r($curl_response);

echo $curl_response;
?>
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"BusinessShortCode\":\" \" ,
        \"Password\":\" \",
        \"Timestamp\":\" \",
        \"CheckoutRequestID\":\" \"}");
Request request = new Request.Builder()
  .url("https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query")
  .post(body)
  .addHeader("authorization", "Bearer ACCESS_TOKEN")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
{
  "ResponseCode":"0",
  "ResponseDescription":"The service request has been accepted successfully",
  "MerchantRequestID":"8555-67195-1",
  "CheckoutRequestID":"ws_CO_27072017151044001",
  "ResultCode":"1032",
  "ResultDesc":"[STK_CB - ]Request cancelled by user"
}

Lipa na M-Pesa Online Query Request - Resource URL

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

Lipa na M-Pesa Online Query Request - Request Parameters

Parameter Description
BusinessShortCode Business Short Code
Password Password
Timestamp Timestamp
CheckoutRequestID Checkout RequestID

Lipa na M-Pesa Online Query Request - Response Parameters

Parameter Description
MerchantRequestID Merchant Request ID
CheckoutRequestID Check out Request ID
ResponseCode Response Code
ResultDesc Result Desc
ResponseDescription Response Description message
ResultCode Result Code

Transaction Status

curl -X POST --header "Authorization: Bearer <Access-Token>" --header "Content-Type: application/json" -d "{
  \"Initiator\": \" \",
  \"SecurityCredential\": \" \",
  \"CommandID\":\"TransactionStatusQuery\",
  \"TransactionID\": \" \",
  \"PartyA\": \" \",
  \"IdentifierType\":\"1\",
  \"ResultURL\":\"https://ip_address:port/result_url\",
  \"QueueTimeOutURL\":\"https://ip_address:port/timeout_url\",
  \"Remarks\": \" \",
  \"Occasion\": \" \"
}" "https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query"

require 'net/http'
require 'net/https'
require 'uri'

uri = URI('https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query')

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(uri)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request["authorization"] = 'Bearer <Access-Token>'
request.body = "{\"Initiator\": \" \",
  \"SecurityCredential\": \" \",
  \"CommandID\":\"TransactionStatusQuery\",
  \"TransactionID\": \" \",
  \"PartyA\": \" \",
  \"IdentifierType\":\"1\",
  \"ResultURL\":\"https://ip_address:port/result_url\",
  \"QueueTimeOutURL\":\"https://ip_address:port/timeout_url\",
  \"Remarks\": \" \",
  \"Occasion\": \" \"}"

response = http.request(request)
puts response.read_body
import requests

access_token = "Access-Token"
api_url = "https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query"
headers = { "Authorization": "Bearer %s" % access_token }
request = {
  "Initiator":" ",
  "SecurityCredential":" ",
  "CommandID":"TransactionStatusQuery",
  "TransactionID":" ",
  "PartyA":" ",
  "IdentifierType":"1",
  "ResultURL":"https://ip_address:port/result_url",
  "QueueTimeOutURL":"https://ip_address:port/timeout_url",
  "Remarks":" ",
  "Occasion":" "
}

response = requests.post(api_url, json = request, headers=headers)

print (response.text)
var request = require('request'),
  oauth_token = "YOUR_ACCESS_TOKEN",
  url = "https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query"
  auth = "Bearer " + oauth_token;

  request(
    {
      method: 'POST'
      url : url,
      headers : {
        "Authorization" : auth
      },
    json : {
      "Initiator":" ",
      "SecurityCredential":" ",
      "CommandID":"TransactionStatusQuery",
      "TransactionID":" ",
      "PartyA":" ",
      "IdentifierType":"1",
      "ResultURL":"https://ip_address:port/result_url",
      "QueueTimeOutURL":"https://ip_address:port/timeout_url",
      "Remarks":" ",
      "Occasion":" "
    }
  },
    function (error, response, body) {
      // TODO: Use the body object to extract the response
      console.log(body)
    }
  )
<?php
$url = 'https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query';

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Authorization:Bearer ACCESS_TOKEN')); //setting custom header


$curl_post_data = array(
  //Fill in the request parameters with valid values
  'Initiator' => ' ',
  'SecurityCredential' => ' ',
  'CommandID' => 'TransactionStatusQuery',
  'TransactionID' => ' ',
  'PartyA' => ' ',
  'IdentifierType' => '1',
  'ResultURL' => 'https://ip_address:port/result_url',
  'QueueTimeOutURL' => 'https://ip_address:port/timeout_url',
  'Remarks' => ' ',
  'Occasion' => ' '
);

$data_string = json_encode($curl_post_data);

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);

$curl_response = curl_exec($curl);
print_r($curl_response);

echo $curl_response;
?>
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"Initiator\": \" \",
  \"SecurityCredential\": \" \",
  \"CommandID\":\"TransactionStatusQuery\",
  \"TransactionID\": \" \",
  \"PartyA\": \" \",
  \"IdentifierType\":\"1\",
  \"ResultURL\":\"https://ip_address:port/result_url\",
  \"QueueTimeOutURL\":\"https://ip_address:port/timeout_url\",
  \"Remarks\": \" \",
  \"Occasion\": \" \"}");
Request request = new Request.Builder()
  .url("https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query")
  .post(body)
  .addHeader("authorization", "Bearer ACCESS_TOKEN")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
{
  "Result":{
    "ResultType":0,
    "ResultCode":0,
    "ResultDesc":"The service request has been accepted successfully.",
    "OriginatorConversationID":"10816-694520-2",
    "ConversationID":"AG_20170727_000059c52529a8e080bd",
    "TransactionID":"LGR0000000",
    "ResultParameters":{
      "ResultParameter":[
        {
          "Key":"ReceiptNo",
          "Value":"LGR919G2AV"
        },
        {
          "Key":"Conversation ID",
          "Value":"AG_20170727_00004492b1b6d0078fbe"
        },
        {
          "Key":"FinalisedTime",
          "Value":20170727101415
        },
        {
          "Key":"Amount",
          "Value":10
        },
        {
          "Key":"TransactionStatus",
          "Value":"Completed"
        },
        {
          "Key":"ReasonType",
          "Value":"Salary Payment via API"
        },
        {
          "Key":"TransactionReason"
        },
        {
          "Key":"DebitPartyCharges",
          "Value":"Fee For B2C Payment|KES|33.00"
        },
        {
          "Key":"DebitAccountType",
          "Value":"Utility Account"
        },
        {
          "Key":"InitiatedTime",
          "Value":20170727101415
        },
        {
          "Key":"Originator Conversation ID",
          "Value":"19455-773836-1"
        },
        {
          "Key":"CreditPartyName",
          "Value":"254708374149 - John Doe"
        },
        {
          "Key":"DebitPartyName",
          "Value":"600134 - Safaricom157"
        }
      ]
    },
    "ReferenceData":{
      "ReferenceItem":{
        "Key":"Occasion",
        "Value":"aaaa"
      }
    }
  }
}

Transaction Status API checks the status of a B2B, B2C and C2B APIs transactions.

Transaction Status Resource URL

POST https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query

Transaction Status Request Parameters

Parameter Description
CommandID Unique command for each transaction type, possible values are: TransactionStatusQuery.
ShortCode Organization /MSISDN sending the transaction.
IdentifierType Type of organization receiving the transaction
Remarks Comments that are sent along with the transaction.
Initiator The name of Initiator to initiating the request.
SecurityCredential Base64 encoded string of the M-Pesa short code and password, which is encrypted using M-Pesa public key and validates the transaction on M-Pesa Core system.
QueueTimeOutURL The path that stores information of time out transaction.
ResultURL The path that stores information of transaction.
TransactionID Organization Receiving the funds.
Occasion Optional.

Errors

Safaricom APIs are built to comply with HTTP Status codes. The following are error codes that will be returned whenever there are errors in a request. Server errors are rare but do occur whenever there are connectivity issues.

Error Code Meaning
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
405 Method Not Allowed
406 Not Acceptable – You requested a format that isn’t json
429 Too Many Requests – You’re requesting too many kittens! Slow down!
500 Internal Server Error – We had a problem with our server. Try again later.
503 Service Unavailable – We’re temporarily offline for maintenance. Please try again later.

Creating a HTTP Server Listener

# import the Flask Framework
from flask import Flask,jsonify, make_response, request

app = Flask(__name__)

# Create the context (endpoint/URL) which will be triggered when the request
# hits the above specified port. This will resolve to a URL like
# 'http://address:port/context'. E.g. the context below would
# resolve to 'http://127.0.0.1:80/mpesa/b2c/v1' on the local computer. Then
# the Handler will handle the request received via the given URL.

# You may create a separate URL for every endpoint you need

@app.route('/mpesa/b2c/v1', methods = ["POST"])
def listenB2c():
    #save the data
    request_data = request.data

    #Perform your processing here e.g. print it out...
    print(request_data)

    # Prepare the response, assuming no errors have occurred. Any response
    # other than a 0 (zero) for the 'ResultCode' during Validation only means
    # an error occurred and the transaction is cancelled
    message = {
        "ResultCode": 0,
        "ResultDesc": "The service was accepted successfully",
        "ThirdPartyTransID": "1234567890"
    };

    # Send the response back to the server
    return jsonify({'message': message}), 200

# Change this part to reflect the API you are testing
@app.route('/mpesa/b2b/v1')
def listenB2b():
    request_data = request.data
    print(request_data)
    message = {
        "ResultCode": 0,
        "ResultDesc": "The service was accepted successfully",
        "ThirdPartyTransID": "1234567890"
    };

    return jsonify({'message': message}), 200

if __name__ == '__main__':
    app.run(debug=True)

var prettyjson = require('prettyjson');
var options = {
  noColor: true
};

var express = require('express'),
    bodyParser = require('body-parser'),
    app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// B2C ResultURL - /api/v1/b2c/result
app.post('/b2c/result', function(req, res) {
  console.log('-----------B2C CALLBACK------------');
  console.log(prettyjson.render(req.body, options));
  console.log('-----------------------');

  var message = {
    "ResponseCode": "00000000",
    "ResponseDesc": "success"
  };

  res.json(message);
});

// B2C QueueTimeoutURL - /api/v1/b2c/timeout
app.post('/b2c/timeout', function(req, res) {
  console.log('-----------B2C TIMEOUT------------');
  console.log(prettyjson.render(req.body, options));
  console.log('-----------------------');

  var message = {
    "ResponseCode": "00000000",
    "ResponseDesc": "success"
  };

  res.json(message);
});

// C2B ValidationURL - /api/v1/c2b/validation
app.post('/validation', function(req, res) {
  console.log('-----------C2B VALIDATION REQUEST-----------');
  console.log(prettyjson.render(req.body, options));
  console.log('-----------------------');

  var message = {
    "ResultCode": 0,
    "ResultDesc": "Success",
    "ThirdPartyTransID": "1234567890"
  };

  res.json(message);
});

// C2B ConfirmationURL - /api/v1/c2b/confirmation
app.post('/confirmation', function(req, res) {
  console.log('-----------C2B CONFIRMATION REQUEST------------');
  console.log(prettyjson.render(req.body, options));
  console.log('-----------------------');

  var message = {
    "ResultCode": 0,
    "ResultDesc": "Success"
  };

  res.json(message);
});

// B2B ResultURL - /api/v1/b2b/result
app.post('/b2b/result', function(req, res) {
  console.log('-----------B2B CALLBACK------------');
  console.log(prettyjson.render(req.body, options));
  console.log('-----------------------');

  var message = {
    "ResponseCode": "00000000",
    "ResponseDesc": "success"
  };

  res.json(message);
});


// B2B QueueTimeoutURL - /api/v1/b2b/timeout
app.post('/b2b/timeout', function(req, res) {
  console.log('-----------B2B TIMEOUT------------');
  console.log(prettyjson.render(req.body, options));
  console.log('-----------------------');

  var message = {
    "ResponseCode": "00000000",
    "ResponseDesc": "success"
  };

  res.json(message);
});

console.log("Server listening on port: 8310")
app.listen(8310);

<?php
    $postData = = file_get_contents('php://input');
    //perform your processing here, e.g. log to file....
    $file = fopen("log.txt", "w"); //url fopen should be allowed for this to occur
    if(fwrite($file, $postData) === FALSE)
    {
        fwrite("Error: no data written");
    }

    fwrite("\r\n");
    fclose($file);

    echo '{"ResultCode": 0, "ResultDesc": "The service was accepted successfully", "ThirdPartyTransID": "1234567890"}';
?>
//Requires the following libraries. Maven repositories given below
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import org.json.simple.JSONObject;

public class Main{
    private HttpServer server;
    public Main()    {
    try {
            //Define the port over which the listener will accept requests
            int port = 8080;

            server = HttpServer.create(new InetSocketAddress(port), 0);
            /**
             * Create the context (endpoint/URL) which will be triggered when the request
             * hits the above specified port. This will resolve to a URL like
             * 'http://address:port/context'. E.g. the context below would
             * resolve to 'http://127.0.0.1:80/confirm' on the local computer. Then
             * the Handler will handle the request received via the given URL.
             *
             * You may create a separate context for every endpoint you need
            */
            server.createContext("/confirm", new ConfirmHandler());

            server.setExecutor(null);

            //start the server
            server.start();
            System.out.println("Server started");
        }
        catch (Exception ex){
            ex.printStackTrace();
        }
    }

    /**
     * Class to handle incoming requests based on specified Contexts, you can
     * create a class for each separate context/URL
     */
    public class ConfirmHandler implements HttpHandler{
        @Override
        public void handle(HttpExchange he) throws IOException{
            /**
             * handle the request which comes through the '/confirm' context
             */
            System.out.println("Request received");

            /**
             * Buffer and store the response in a string
             */
            BufferedReader br = new BufferedReader(new InputStreamReader(he.getRequestBody(), "UTF-8"));
            String line = "";
            StringBuilder buffer = new StringBuilder();
            while((line = br.readLine()) != null)
            {
                buffer.append(line);
            }

            /**
             * Once buffered, you can perform any other processing
             * you need on the buffered response e.g. print out the response...
             */
            System.out.println("Res: " + buffer.toString());

            /**
             * Prepare the response, assuming no errors have occurred. Any response
             * other than a 0 (zero) for the 'ResultCode' during Validation means an
             * error occurred and the transaction is cancelled
             */
            JSONObject obj = new JSONObject();
            obj.put("ResultCode", 0);
            obj.put("ResultDesc", "The service was accepted successfully");
            obj.put("ThirdPartyTransID", "1234567890");

            /**
             * Respond to the server appropriately
             */
            String res = obj.toJSONString();
            he.sendResponseHeaders(200, res.length());
            OutputStream os = he.getResponseBody();
            os.write(res.getBytes("UTF-8"));
            os.close();
        }
    }

    public static void main(String args[]){
        Main main = new Main();
    }
}

/**
 * Maven Repositories:
 *
 * <dependency>
 * <groupId>com.sun.net.httpserver</groupId>
 * <artifactId>http</artifactId>
 * <version>20070405</version>
 * </dependency>
 *
 * <dependency>
 * <groupId>com.googlecode.json-simple</groupId>
 * <artifactId>json-simple</artifactId>
 * <version>1.1.1</version>
 * </dependency>
 *
 * <repositories>
 * <repository>
 * <id>reficio</id>
 * <url>http://repo.reficio.org/maven/</url>
 * </repository>
 * </repositories>
 *
 * More examples here: https://www.codeproject.com/Tips/1040097/Create-a-Simple-Web-Server-in-Java-HTTP-Server
 */
}

M-Pesa APIs are asynchronous. When a valid M-Pesa API request is received by the API Gateway, it is sent to M-Pesa where it is added to a queue. M-Pesa then processes the requests in the queue and sends a response to the API Gateway which then forwards the response to the URL registered in the CallBackURL or ResultURL request parameter. Whenever M-Pesa receives more requests than the queue can handle, M-Pesa responds by rejecting any more requests and the API Gateway sends a queue timeout response to the URL registered in the QueueTimeOutURL request parameter.

To receive responses, either M-Pesa results or queue timeouts, an HTTP listener will be needed. The listener should be deployed to a server that can receive traffic over the internet. On local host, use an http tunnelling client like ngrok or localtunnel to get a public IP that will enable your local host to receive traffic over the internet. Sample json responses that are received on callback urls or queue timeout urls are provided in the ‘Json Response’ tab on the top right corner for each API. Sample http listeners are also provided on the right in Python, NodeJS, PHP and Java.

The HTTP listner should deploy POST methods for receiving M-Pesa responses on CallBackURL or ResultURL and for receiving queue timeouts on QueueTimeOutURL.