Mpesa B2C(Business to Customer) API

Mpesa B2C API is a gateway for disbursing funds to customers through Mpesa Bulk Payment account.

The Safaricom Business to Client API is useful when you want to make payments to Mpesa users from your company’s Mpesa account.

Using the Mpesa Portal for the payments is rather cumbersome and furthermore, you can automate the process of making payments to customers depending on your business logic.

In this guide, we will focus on setting up Mpesa B2C API using PHP.

Prerequisites

  1. An account from Mpesa developer portal
  2. An active bulk payment account from Mpesa(application is free, but you will require your business certificate, PIN of directors, a single leaf of cancelled cheque book and a photo or utility bill to prove existence of your business.)
  3. A domain name (e.g. www.example.com). This will help you create call back URL to notify your system when your transaction is finalized on the Mpesa side.
  4. A hosting plan (Bluehost is recommended)
  5. Knowledge of MySQL database
  6. Knowledge of PHP scripting Language

Step 1: Signing up with Mpesa Developer Portal

First, create an account at Mpesa Developer Portal If you don’t have an account. You will be required to supply your names, email address, Mobile number and other details.

Step 2: Create a B2C App(Business to Customer/Client)

Once your account is ready, sign in on the developer portal and create a B2C API. To do this, click on My APPs at the top Menu. Then click Add a new APP

On the next screen, enter the name of your app and check the box written “MPesa Sandbox For MPesa Sanbox B2B, B2C And C2B APIs”.

Your Mpesa B2C app will be created and approved within seconds and you will get a consumer secret and a consumer key.

Step 3: Making Payments to a Customer

Once you have the consumer key and consumer secret, you can start making requests to the Mpesa B2C end point using the PHP code below:

<?php

$shortcode="YOUR_SHORT_CODE_HERE";
$initiatorname="USER_NAME_FROM_MPESA_PORTAL_HERE";
$initiatorpassword="YOUR_PASSWORD_HERE";


$consumerkey    ="YOUR_CONSUMER_SECRET_HERE";
$consumersecret ="YOUR_CONSUMER_SECRET_HERE";


$commandid='BusinessPayment';
$recipient="25472XXXXXXX";
$amount="100";
$remarks='TEST BUSINESS DISBURSAL';
$occassion='JANUARY 2019';


$QueueTimeOutURL="YOUR_QUEUE_TIMEOUT_URL_HERE";
$ResultURL="YOUR_RESULT_URLHERE";



/*The below are production URLS CHANGE TO SANDBOX IF YOU HAVEN'T GONE LIVE */

$authorizationurl='https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials';
$paymentrequesturl="https://api.safaricom.co.ke/mpesa/b2c/v1/paymentrequest";



/* FIRST WE AUTHENTICATE OUR API CALL */

/* BEGIN AUTHENTICATION TO GET ACCESS TOKEN*/

  // Request headers
  $headers = array(	
    'Content-Type: application/json; charset=utf-8'
  );

  // Request
  $ch = curl_init($authorizationurl);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  //curl_setopt($ch, CURLOPT_HEADER, TRUE); // Includes the header in the output
  curl_setopt($ch, CURLOPT_HEADER, FALSE); // excludes the header in the output

  curl_setopt($ch, CURLOPT_USERPWD, $consumerkey . ":" . $consumersecret); // HTTP Basic Authentication
  $result = curl_exec($ch);	

if(curl_errno($ch)){
    echo 'Request Error:' . curl_error($ch);
    exit();
}



$result = json_decode($result);

$access_token=$result->access_token;

curl_close($ch);


/* END AUTHENTICATION */



/* BEGIN PAYMENT REQUEST IF WE GOT ACCESS TOKEN*





if(!$access_token)

{

  echo " Invalid access token ". print_r($result);

}

else


{



$publicKey = "-----BEGIN CERTIFICATE-----
MIIGkzCCBXugAwIBAgIKXfBp5gAAAD+hNjANBgkqhkiG9w0BAQsFADBbMRMwEQYK
CZImiZPyLGQBGRYDbmV0MRkwFwYKCZImiZPyLGQBGRYJc2FmYXJpY29tMSkwJwYD
VQQDEyBTYWZhcmljb20gSW50ZXJuYWwgSXNzdWluZyBDQSAwMjAeFw0xNzA0MjUx
NjA3MjRaFw0xODAzMjExMzIwMTNaMIGNMQswCQYDVQQGEwJLRTEQMA4GA1UECBMH
TmFpcm9iaTEQMA4GA1UEBxMHTmFpcm9iaTEaMBgGA1UEChMRU2FmYXJpY29tIExp
bWl0ZWQxEzARBgNVBAsTClRlY2hub2xvZ3kxKTAnBgNVBAMTIGFwaWdlZS5hcGlj
YWxsZXIuc2FmYXJpY29tLmNvLmtlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAoknIb5Tm1hxOVdFsOejAs6veAai32Zv442BLuOGkFKUeCUM2s0K8XEsU
t6BP25rQGNlTCTEqfdtRrym6bt5k0fTDscf0yMCoYzaxTh1mejg8rPO6bD8MJB0c
FWRUeLEyWjMeEPsYVSJFv7T58IdAn7/RhkrpBl1dT7SmIZfNVkIlD35+Cxgab+u7
+c7dHh6mWguEEoE3NbV7Xjl60zbD/Buvmu6i9EYz+27jNVPI6pRXHvp+ajIzTSsi
eD8Ztz1eoC9mphErasAGpMbR1sba9bM6hjw4tyTWnJDz7RdQQmnsW1NfFdYdK0qD
RKUX7SG6rQkBqVhndFve4SDFRq6wvQIDAQABo4IDJDCCAyAwHQYDVR0OBBYEFG2w
ycrgEBPFzPUZVjh8KoJ3EpuyMB8GA1UdIwQYMBaAFOsy1E9+YJo6mCBjug1evuh5
TtUkMIIBOwYDVR0fBIIBMjCCAS4wggEqoIIBJqCCASKGgdZsZGFwOi8vL0NOPVNh
ZmFyaWNvbSUyMEludGVybmFsJTIwSXNzdWluZyUyMENBJTIwMDIsQ049U1ZEVDNJ
U1NDQTAxLENOPUNEUCxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2
aWNlcyxDTj1Db25maWd1cmF0aW9uLERDPXNhZmFyaWNvbSxEQz1uZXQ/Y2VydGlm
aWNhdGVSZXZvY2F0aW9uTGlzdD9iYXNlP29iamVjdENsYXNzPWNSTERpc3RyaWJ1
dGlvblBvaW50hkdodHRwOi8vY3JsLnNhZmFyaWNvbS5jby5rZS9TYWZhcmljb20l
MjBJbnRlcm5hbCUyMElzc3VpbmclMjBDQSUyMDAyLmNybDCCAQkGCCsGAQUFBwEB
BIH8MIH5MIHJBggrBgEFBQcwAoaBvGxkYXA6Ly8vQ049U2FmYXJpY29tJTIwSW50
ZXJuYWwlMjBJc3N1aW5nJTIwQ0ElMjAwMixDTj1BSUEsQ049UHVibGljJTIwS2V5
JTIwU2VydmljZXMsQ049U2VydmljZXMsQ049Q29uZmlndXJhdGlvbixEQz1zYWZh
cmljb20sREM9bmV0P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0
aWZpY2F0aW9uQXV0aG9yaXR5MCsGCCsGAQUFBzABhh9odHRwOi8vY3JsLnNhZmFy
aWNvbS5jby5rZS9vY3NwMAsGA1UdDwQEAwIFoDA9BgkrBgEEAYI3FQcEMDAuBiYr
BgEEAYI3FQiHz4xWhMLEA4XphTaE3tENhqCICGeGwcdsg7m5awIBZAIBDDAdBgNV
HSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwJwYJKwYBBAGCNxUKBBowGDAKBggr
BgEFBQcDAjAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAQEAC/hWx7KTwSYr
x2SOyyHNLTRmCnCJmqxA/Q+IzpW1mGtw4Sb/8jdsoWrDiYLxoKGkgkvmQmB2J3zU
ngzJIM2EeU921vbjLqX9sLWStZbNC2Udk5HEecdpe1AN/ltIoE09ntglUNINyCmf
zChs2maF0Rd/y5hGnMM9bX9ub0sqrkzL3ihfmv4vkXNxYR8k246ZZ8tjQEVsKehE
dqAmj8WYkYdWIHQlkKFP9ba0RJv7aBKb8/KP+qZ5hJip0I5Ey6JJ3wlEWRWUYUKh
gYoPHrJ92ToadnFCCpOlLKWc0xVxANofy6fqreOVboPO0qTAYpoXakmgeRNLUiar
0ah6M/q/KA==
-----END CERTIFICATE-----
";


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

//GENERATE SECURITY CREDENTIAL USING THE PUBLIC KEY ABOVE

$securitycredential=base64_encode($encrypted);

/* MAKE PAYMENT REQUEST */


$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $paymentrequesturl);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Authorization:Bearer '.$access_token)); 

$curl_post_data = array(  
 
  'InitiatorName' => $initiatorname,
  'SecurityCredential' => $securitycredential,
  'CommandID' =>  $commandid,
  'Amount' =>  $amount,
  'PartyA' => $shortcode,
  'PartyB' =>  $recipient,  
  'Remarks' => $remarks,
  'QueueTimeOutURL' => $QueueTimeOutURL,
  'ResultURL' => $ResultURL,
  'Occassion' => $occassion
);

$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);


  $response = json_decode($curl_response);

  if(!isset($response->ConversationID))

  {
  echo "There was an error when submitting your request: $response->errorMessage";
  
  }

  else

  {
  echo "Success, $response->ResponseDescription ";
  }


}


?>

Step 4: Queue Timeout and Result URLs

The Mpesa B2C API Queue timeout URL is called if the transactions times out from the Mpesa side. Result URL is called once the transaction is finalized.

When you receive the status of the transaction on the results URL, you should analyze the response to see if the transaction succeeded or not.

A transaction may fail due to the following reasons:

  1. Incorrect security credentials
  2. Insufficient funds from the Mpesa B2C account. Please note the minimum amount you can load on the B2C account is Kshs. 5,000/=. But you should not worry, if you are just testing out theĀ  Mpesa Business to Customer API, you can load the amount and then send it back to your phone number
  3. Authorization error: This happens if the username that you have specified does not have the rights to initiate the Mpesa B2C transaction.

Sample Success Response from Mpesa B2C gateway

{  
   "Result":{  
      "ResultType":0,
      "ResultCode":0,
      "ResultDesc":"The service request is processed successfully.",
      "OriginatorConversationID":"11369-5305957-1",
      "ConversationID":"AG_20190103_00004ad7c21029e28510",
      "TransactionID":"NA30XPKKVCW",
      "ResultParameters":{  
         "ResultParameter":[  
            {  
               "Key":"TransactionAmount",
               "Value":100
            },
            {  
               "Key":"TransactionReceipt",
               "Value":"NA30XKHVCW"
            },
            {  
               "Key":"ReceiverPartyPublicName",
               "Value":"2547XXXXXXX - JOHN DOE"
            },
            {  
               "Key":"TransactionCompletedDateTime",
               "Value":"03.01.2019 17:48:32"
            },
            {  
               "Key":"B2CUtilityAccountAvailableFunds",
               "Value":4425.00
            },
            {  
               "Key":"B2CWorkingAccountAvailableFunds",
               "Value":0.00
            },
            {  
               "Key":"B2CRecipientIsRegisteredCustomer",
               "Value":"Y"
            },
            {  
               "Key":"B2CChargesPaidAccountAvailableFunds",
               "Value":0.00
            }
         ]
      },
      "ReferenceData":{  
         "ReferenceItem":{  
            "Key":"QueueTimeoutURL",
            "Value":"http:\/\/internalapi.safaricom.co.ke\/mpesa\/b2cresults\/v1\/submit"
         }
      }
   }
}

Sample Error Message from Mpesa B2C API Gateway- The Initiator Information is Invalid

{  
   "Result":{  
      "ResultType":0,
      "ResultCode":2001,
      "ResultDesc":"The initiator information is invalid.",
      "OriginatorConversationID":"7488-6256766-1",
      "ConversationID":"AG_20190104_00004ce9fc41b15aa227",
      "TransactionID":"NA43XZPDE3",
      "ReferenceData":{  
         "ReferenceItem":{  
            "Key":"QueueTimeoutURL",
            "Value":"http:\/\/internalapi.safaricom.co.ke\/mpesa\/b2cresults\/v1\/submit"
         }
      }
   }
}

Sample Safaricom Mpesa B2C API Insufficient Funds JSON Error

{  
   "Result":{  
      "ResultType":0,
      "ResultCode":1,
      "ResultDesc":"The balance is insufficient for the transaction.",
      "OriginatorConversationID":"18221-6293510-1",
      "ConversationID":"AG_20190104_00005b7d5130c96080a9",
      "TransactionID":"Ni417ZUTBX",
      "ReferenceData":{  
         "ReferenceItem":{  
            "Key":"QueueTimeoutURL",
            "Value":"http:\/\/internalapi.safaricom.co.ke\/mpesa\/b2cresults\/v1\/submit"
         }
      }
   }
}

In simple terms, if the ResultCode parameter is 0, then it means the transaction is completed and successful. Any other values means an error.

Conclusion

This guide should give you an overview of how you should develop a Safaricom Mpesa B2C API. While it is not a conclusive list of all the things that you might require, it will assist you to program your app.

Use the code given here at your own risk. Also, never save your Mpesa login portal password on your hosting files for security reasons.