This page describes how to accept payment using Mpesa
Rave currently allows merchants use two (2) payment methods in Kenya (card and Mpesa).
We would show you how to accept payments using mpesa.
Pre-requisites for accepting payments in Kenya.
-
Sign-up for an account here .
-
set up a webhook to get notified on payments, to see more on webhooks visit the webhook section.
-
When trying to accept payment on a website you can use our inline js method and pass
currency
asKES
andcountry
asKE
, once you do this, the options for card and mpesa would come up. -
when accepting payments using our APIs please see how to accept mobile money payments via our APIs, see instructions on doing that on this page.
-
After getting a response for the transaction call the verification endpoint to confirm the final status of the transaction.
Step 1: Encrypt your payload.
{
"PBFPubKey": "FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",
"currency": "KES",
"country": "KE",
"amount": "100",
"phonenumber": "0926420185",
"email": "user@exampe",
"firstname": "jsksk",
"lastname": "ioeoe",
"IP": "40.14.290",
"narration": "funds payment",
"txRef": "jw-222",
"meta": [{metaname: "extra info", metavalue: "a pie"}],
"device_fingerprint": "89191918hgdgdg99191", //(optional)
"payment_type": "mpesa",
"is_mpesa": "1",
"is_mpesa_lipa": 1
}
Parameter Definition
Parameters | Required | Description |
---|---|---|
currency | Trueexpected value: KES | This is the currency to charge the customer in. |
PBFPubKey | True | This is a unique key generated for each button created on Rave’s dashboard. It starts with a prefix FLWPUBK and ends with suffix X . |
country | Trueexpected value: KE | This is the pair country for the transaction with respect to the currency. See a list of Multicurrency support here Multicurrency Payments ] |
amount | True | This is the amount to be charged it is passed as - (“amount”:10). |
phonenumber | True | This must be a valid Mpesa mobile number on test and live environment. |
email | True | This is the email address of the customer. |
firstname | False | This is the first name of the card holder or the customer. |
lastname | False | This is the last name of the card holder or the customer. |
IP | False | IP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction. |
narration | False | This is the narration passed in the customer debit. |
txRef | True | This is the unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction |
meta | Falsee.g. meta: [{metaname: "extra info", metavalue: "a pie"}] | Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the payment in a structured format. |
payment_type | Trueexpected value: mpesa | This specifies that the payment method being used is for mpesa payments |
is_mpesa | Trueexpected value: 1 | This identifies that an mpesa transaction is being carried out. |
is_mpesa_lipa | Trueexpected value: 1 | This identifies that an mpesa transaction is being carried out. |
Sample Encryption
To see how to encrypt in other languages visit the Rave Encryption section.
<?php
function getKey($seckey){
$hashedkey = md5($seckey);
$hashedkeylast12 = substr($hashedkey, -12);
$seckeyadjusted = str_replace("FLWSECK-", "", $seckey);
$seckeyadjustedfirst12 = substr($seckeyadjusted, 0, 12);
$encryptionkey = $seckeyadjustedfirst12.$hashedkeylast12;
return $encryptionkey;
}
function encrypt3Des($data, $key)
{
$encData = openssl_encrypt($data, 'DES-EDE3', $key, OPENSSL_RAW_DATA);
return base64_encode($encData);
}
function encryptMpesa(){ // set up a function to test card payment.
error_reporting(E_ALL);
ini_set('display_errors',1);
$data = array('PBFPubKey' => 'FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X',
'currency' => 'KES',
'country' => 'KE',
'payment_type' => 'mpesa',
'amount' => '30',
'phonenumber' => '054709929300',
'firstname' => 'Paul',
'lastname' => 'Kagaru',
'narration' => 'mpesa/12394484',
'email' => '[email protected]',
'IP' => '103.238.105.185',
'txRef' => 'MXX-ASC-4578',
'is_mpesa' => 1,
'device_fingerprint' => '69e6b7f0sb72037aa8428b70fbe03986c');
$SecKey = 'FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X';
$key = getKey($SecKey);
$dataReq = json_encode($data);
$post_enc = encrypt3Des( $dataReq, $key );
var_dump($post_enc);
}
encryptMpesa();
This step above would generate an encrypted string
which you can now send as a request to the /charge
endpoint.
Step 2: Initiate your payment.
https://api.ravepay.co/flwv3-pug/getpaidx/api/charge
Method: POST
<?php
$postdata = array(
'PBFPubKey' => 'FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X',
'client' => $post_enc,
'alg' => '3DES-24');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.ravepay.co/flwv3-pug/getpaidx/api/charge");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postdata)); //Post Fields
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 200);
curl_setopt($ch, CURLOPT_TIMEOUT, 200);
$headers = array('Content-Type: application/json');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$request = curl_exec($ch);
if ($request) {
$result = json_decode($request, true);
echo "<pre>";
print_r($result);
}else{
if(curl_error($ch))
{
echo 'error:' . curl_error($ch);
}
}
curl_close($ch);
}
payviampesa();
What happens after I call the charge endpoint ?
- A pop up is shown to the customer prompting them to enter their Mpesa pin and choosing to decline or accept the payment. Once that is completed by the customer we send a request to your hook endpoint.
{
"id": 130438,
"txRef": "rave-1902008383",
"flwRef": "ws_CO_15042018193205498_1998935614_1884_1523809926391",
"orderRef": "1998935614_1884_1523809926391",
"paymentPlan": null,
"createdAt": "2018-04-15T16:32:06.000Z",
"amount": 2000,
"charged_amount": 2028,
"status": "successful",
"IP": "41.86.149.34",
"currency": "KES",
"customer": {
"id": 23858,
"phone": "254791498442",
"fullName": "Anonymous customer",
"customertoken": null,
"email": "[email protected]",
"createdAt": "2018-04-15T16:32:05.000Z",
"updatedAt": "2018-04-15T16:32:05.000Z",
"deletedAt": null,
"AccountId": 1884
},
"entity": {
"id": "NO-ENTITY"
}
}
{
"status": "success",
"message": "V-COMP",
"data": {
"cycle": "one-time",
"merchantbearsfee": 0,
"status": "pending",
"vbvrespmessage": "N/A",
"authurl": "N/A",
"vbvrespcode": "N/A",
"paymentId": "N/A",
"charge_type": "normal",
"is_live": 0,
"id": 693982,
"txRef": "MC-1520540972797",
"redirectUrl": "http://127.0.0",
"amount": "10",
"charged_amount": "10.00",
"authModelUsed": "VBVSECURECODE",
"flwRef": "N/A",
"orderRef": "5014803035",
"currency": "KES",
"device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
"customerId": 437498,
"paymentType": "mpesa",
"narration": "FLW-PBF MPESA Transaction ",
"IP": "::ffff:10.142.180.61",
"fraud_status": "ok",
"AccountId": 48,
"merchantfee": 0,
"updatedAt": "2018-03-09T11:06:21.000Z",
"createdAt": "2018-03-09T11:06:21.000Z",
"business_number": "637747"
}
}
Great you are almost done, now you need to verify the transaction before giving value for this transaction.
Step 3: Verify the payment.
After charging a customer successfully, you need to verify that the payment was successful with Rave before giving value to your customer on your website.
Although the Rave inline already verifies the payment from the client-side, we strongly recommend you still do a server-side verification to be double sure no foul play occurred during the payment flow.
Below are the important things to check for when validating the payment:
Verify the transaction reference.
Verify the data.status
of the transaction to be successful
.
Verify the currency to be the expected currency
Most importantly validate the amount paid to be equal to or at least greater than the amount of the value to be given.
Below is a sample snippet to show how to implement server-side verification in different programming languages:
curl --request POST \
--url https://api.ravepay.co/flwv3-pug/getpaidx/api/v2/verify \
--header 'content-type: application/json' \
--data '{"txref":"rave-1902008383","SECKEY":"FLWSECK-24ca3da9d6f342610790b4103d9f5a1e-X"}'
<?php
$result = array();
$postdata = array(
'txref' => 'rave-1902008383',
'SECKEY' => 'FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X'
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"https://api.ravepay.co/flwv3-pug/getpaidx/api/v2/verify");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,json_encode($postdata)); //Post Fields
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$headers = [
'Content-Type: application/json',
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$request = curl_exec ($ch);
$err = curl_error($ch);
if($err){
// there was an error contacting rave
die('Curl returned error: ' . $err);
}
curl_close ($ch);
$result = json_decode($request, true);
if('error' == $result->status){
// there was an error from the API
die('API returned error: ' . $result->message);
}
if('successful' == $result->data->status && '00' == $result->data->chargecode){
// transaction was successful...
// please check other things like whether you already gave value for this ref
// If the amount and currency matches the expected amount and currency etc.
// if the email matches the customer who owns the product etc
// Give value
}
//Endpoint to verify transaction
private final String VERIFY_ENDPOINT = "https://api.ravepay.co/flwv3-pug/getpaidx/api/v2/verify";
/**
*
* Method to
*
* @param paymententity - <b>paymententity - set as a constant with default value as 1</b>
* @param txref - <b>txref - is the unique payment reference generated by the merchant.</b>
* @param secret - <b>secret - is the merchant secret key</b>
* @return
* @throws UnirestException
*/
public JSONObject verify(String flwRef, String secret, double amount, int paymententity) throws UnirestException, Exception {
// This packages the payload
JSONObject data = new JSONObject();
data.put("txref", txref);
data.put("SECKEY", secret)
// end of payload
// This sends the request to server with payload
HttpResponse<JsonNode> response = Unirest.post(VERIFY_ENDPOINT)
.header("Content-Type", "application/json")
.body(data)
.asJson();
// This get the response from payload
JsonNode jsonNode = response.getBody();
// This get the json object from payload
JSONObject responseObject = jsonNode.getObject();
// check of no object is returned
if(responseObject == null)
throw new Exception("No response from server");
// This get status from returned payload
String status = responseObject.optString("status", null);
// this ensures that status is not null
if(status == null)
throw new Exception("Transaction status unknown");
// This confirms the transaction exist on rave
if(!"success".equalsIgnoreCase(status)){
String message = responseObject.optString("message", null);
throw new Exception(message);
}
data = responseObject.getJSONObject("data");
// This get the amount stored on server
double actualAmount = data.getDouble("amount");
// This validates that the amount stored on client is same returned
if(actualAmount != amount)
throw new Exception("Amount does not match");
// now you can give value for payment.
}
var data = new {txref = "rave-1902008383", SECKEY = "FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"};
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var responseMessage = client.PostAsJsonAsync("https://api.ravepay.co/flwv3-pug/getpaidx/api/v2/verify", data).Result;
var responseStr = responseMessage.Content.ReadAsStringAsync().Result;
var response = Newtonsoft.Json.JsonConvert.DeserializeObject<ResponseData>(responseStr);
if (response.data.status == "successful" && response.data.amount == amount && response.data.chargecode == "00")
{
System.Console.WriteLine("Payment Successful then give value");
}
When you successfully verify a completed payment see sample response below:
{
"status": "success",
"message": "Tx Fetched",
"data": {
"txid": 130438,
"txref": "rave-1902008383",
"flwref": "ws_CO_15042018193205498_1998935614_1884_1523809926391",
"devicefingerprint": "9f04df238ec44e48babdd6f02bf3dfec",
"cycle": "one-time",
"amount": 2000,
"currency": "KES",
"chargedamount": 2028,
"appfee": 0,
"merchantfee": 0,
"merchantbearsfee": 0,
"chargecode": "00",
"chargemessage": "Successful, pending customer validation",
"authmodel": "VBVSECURECODE",
"ip": "41.86.149.34",
"narration": "FLW-PBF MPESA Transaction ",
"status": "successful",
"vbvcode": "N/A",
"vbvmessage": "N/A",
"authurl": "N/A",
"acctcode": "00",
"acctmessage": "The service request has been accepted successsfully",
"paymenttype": "mpesa",
"paymentid": "N/A",
"fraudstatus": "ok",
"chargetype": "normal",
"createdday": 0,
"createddayname": "SUNDAY",
"createdweek": 15,
"createdmonth": 3,
"createdmonthname": "APRIL",
"createdquarter": 2,
"createdyear": 2018,
"createdyearisleap": false,
"createddayispublicholiday": 0,
"createdhour": 16,
"createdminute": 32,
"createdpmam": "pm",
"created": "2018-04-15T16:32:06.000Z",
"customerid": 23858,
"custphone": "254791498442",
"custnetworkprovider": "UNKNOWN PROVIDER",
"custname": "Anonymous customer",
"custemail": "[email protected]",
"custemailprovider": "YAHOO MAIL",
"custcreated": "2018-04-15T16:32:05.000Z",
"accountid": 1884,
"acctbusinessname": "Jumanji",
"acctcontactperson": "Desola Ade",
"acctcountry": "NG",
"acctbearsfeeattransactiontime": 0,
"acctparent": 1,
"acctvpcmerchant": "N/A",
"acctalias": null,
"acctisliveapproved": 0,
"orderref": "1998935614_1884_1523809926391",
"paymentplan": null,
"paymentpage": null,
"raveref": null,
"amountsettledforthistransaction": 2028,
"meta": [
{
"id": 26353,
"metaname": "flightID",
"metavalue": "AP1234",
"createdAt": "2018-04-15T16:32:10.000Z",
"updatedAt": "2018-04-15T16:32:10.000Z",
"deletedAt": null,
"getpaidTransactionId": 130438
}
]
}
}