USSD

This page describes how to collect (NGN) payments via USSD.

Rave allows you collect payments from your customers offline using USSD. With USSD payments, you call our APIs to create a charge, then give your customer instructions to complete the payment on her/his mobile phone. Once payment is completed we notify you on your webhook.

This section show you how to accept payments using the USSD method.

Summary for accepting USSD payments.

  1. Sign-up for a rave account here (if you haven't already).

  2. Set up a webhook to get notified on payments, to see more on webhooks visit the webhook section.

  3. Handle the charge via the API (as shown below) or using the inline JS. (When trying to accept payment on a website you can use our inline JS method and pass currency as NGN. Once this is done, the option for USSD payments will show up if it has been set up on your dashboard.)

  4. After getting a response for the transaction call the verification endpoint to confirm the final status of the transaction.

📘

Available banks

At the moment, banks available for USSD payments (and their numeric codes) are:

  1. Access Bank -- 044
  2. Ecobank -- 050
  3. Fidelity Bank -- 070
  4. First Bank of Nigeria -- 011
  5. First City Monument Bank (FCMB) -- 214
  6. GTBank -- 058
  7. Heritage Bank -- 030
  8. Keystone Bank -- 082
  9. Stanbic IBTC Bank -- 221
  10. Sterling Bank -- 232
  11. Union Bank -- 032
  12. United Bank for Africa -- 033
  13. Unity Bank -- 215
  14. VFD Microfinance Bank -- 090110
  15. Wema Bank -- 035
  16. Zenith Bank -- 057

Charge Step 1: Encrypt your payload.

{
"PBFPubKey": "FLWPUBK-4e581ebf8372cd691203b27227e2e3b8-X",
"accountbank": "058", // Bank numeric code
"currency": "NGN",
"country": "NG",
"amount": "100",
"email": "[email protected]",
"phonenumber": "0902620185",
"firstname": "temi",
"lastname": "desola",
"IP": "355426087298442",
"is_ussd": 1,
"payment_type": "USSD",
"txRef": "MC-" + Date.now(),// your unique merchant reference
"orderRef": "MC_" + Date.now(),
"meta": [{metaname: "flightID", metavalue: "123949494DC"}],
"device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}

Parameter Definition

ParameterRequiredDescription
PBFPubKeyTrueThis is a unique key generated for each button created on Rave’s dashboard. It starts with a prefix FLWPUBK and ends with suffix X.
currencyTrue
(expected value: NGN)
This is the specified currency to charge in.
countrytrue
(Expected value: NG)
This is the pair country for the transaction with respect to the currency. See a list of Multicurrency support here Multicurrency Payments ]
payment_typefalse
(expected value: USSD)
This specifies that the payment method being used is for mobile money payments
amountTrueThis is the amount to be charged it is passed as - (“amount”:"100"). N.B. Amount should not be less than 100.
emailTrueThis is the email address of the customer.
phonenumberFalseThis is the phone number of the customer.
firstnameFalseThis is the first name of the card holder or the customer.
lastnameFalseThis is the last name of the card holder or the customer.
IPFalseIP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction.
txRefTrueThis is a unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction.
orderRefTrueUnique ref for the mobilemoney transaction to be provided by the merchant.
is_ussdTrue

(expected value: 1)
This identifies that a mobile money transaction is being carried out.
device_fingerprintFalseThis is the fingerprint for the device being used. It can be generated using a library on whatever platform is being used.

Sample encryption

<?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 payViaUssd(){ // set up a function to test card payment.
    
    error_reporting(E_ALL);
    ini_set('display_errors',1);
    
    $data = array('PBFPubKey' => 'FLWPUBK-e634d14d9ded04eaf05d5b63a0a06d2f-X',
    'currency' => 'NGN',
    'amount' => '30',
    'country' => 'NG',
    'firstname' => 'Edward',
    'lastname' => 'Kisane',
    'email' => '[email protected]',
    'IP' => '103.238.105.185',
    'txRef' => 'MXX-ASC-4578',
    'orderRef' => 'MXX-ASC-90929',
    'is_ussd' => 1,
    'payment_type' => 'USSD'
    'device_fingerprint' => '69e6b7f0sb72037aa8428b70fbe03986c');
    
    $SecKey = 'FLWSECK-bb971402072265fb156e90a3578fe5e6-X';
    
    $key = getKey($SecKey); 
    
    $dataReq = json_encode($data);
    
    $post_enc = encrypt3Des( $dataReq, $key );

    var_dump($dataReq);
    
    $postdata = array(
     'PBFPubKey' => 'FLWPUBK-e634d14d9ded04eaf05d5b63a0a06d2f-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);
}

payViaUssd();

Step 2: Call the charge endpoint with your encrypted data

https://api.ravepay.co/flwv3-pug/getpaidx/api/charge

Method: POST

$postdata = array(
     'PBFPubKey' => 'FLWPUBK-e634d14d9ded04eaf05d5b63a0a06d2f-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);
}

payViaUssd();

What happens after I call the charge endpoint?

When you call the charge endpoint you would receive a JSON response with a note field which contains a USSD code for your customer to dial to complete their payment (response shown below).

🚧

Note

  • Developers are expected to display the code returned as data.data.note to their users. This code once dialled starts the USSD session on the user's mobile device.

  • For Guaranty trust bank, after your customer dials the USSD code, they are required to specify the payment code as shared in response.

Then we call your webhook once the transaction has been completed with a successful response.

{
    "status": "success",
    "message": "V-COMP",
    "data": {
        "data": {
            "amount": "100.00",
            "type": "paymentcode",
            "redirect": false,
            "note": "*889*767*4744#",
            "transaction_date": "2021-08-02T14:49:57.912",
            "transaction_reference": "URF_1627912196813_5531035",
            "flw_reference": "TFYP286291627912197664",
            "redirect_url": null,
            "payment_code": "4744",
            "type_data": "4744",
            "meta_data": null
        },
        "response_code": "02",
        "response_message": "Transaction in progress"
    }
}
{
    "status": "success",
    "message": "V-COMP",
    "data": {
        "data": {
            "amount": "100.00",
            "type": "paymentcode",
            "redirect": false,
            "note": "*737*51*000#|0366",
            "transaction_date": "2019-10-25T16:45:40.002",
            "transaction_reference": "URF_1572018338712_3796135",
            "flw_reference": "FLW189001572018339809",
            "redirect_url": null,
            "payment_code": "0366",
            "type_data": "0366",
            "meta_data": null
        },
        "response_code": "02",
        "response_message": "Transaction in progress"
    }
}
{
  "id": 126090,
  "txRef": "rave-checkout-1523183226335",
  "flwRef": "FLW393751572018402410",
  "orderRef": URF_1572018401297_2169035,
  "paymentPlan": null,
  "createdAt": "2019-10-25T15:46:41.000Z",
  "amount": 2000,
  "charged_amount": 2000,
  "status": "successful",
  "IP": "197.149.95.62",
  "currency": "NGN",
  "customer": {
    "id": 22823,
    "phone": "0902620185",
    "fullName": "Temi Desola",
    "customertoken": null,
    "email": "[email protected]",
    "createdAt": "2018-04-08T10:28:01.000Z",
    "updatedAt": "2018-04-08T10:28:01.000Z",
    "deletedAt": null,
    "AccountId": 134
  },
  "entity": {
    "id": "NO-ENTITY"
  }
}

📘

Handling USSD transactions

When the hook response comes for a USSD money transaction, you can call the Transaction verification endpoint to get more details on the transaction.

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.

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 sample code of how to implement server side validation 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":"MC-sample001","SECKEY":"FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"}'
<?php 

$result = array();

$postdata =  array( 
  'txref' => 'rave-checkout-1523183226335',
  '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-checkout-1523183226335", 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": 2382914,
        "txref": "MC-sample001",
        "flwref": "TFYP286291627912197664",
        "devicefingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
        "cycle": "one-time",
        "amount": 100,
        "currency": "NGN",
        "chargedamount": 100,
        "appfee": 32.1,
        "merchantfee": 0,
        "merchantbearsfee": 1,
        "chargecode": "00",
        "chargemessage": "Transaction in progress",
        "authmodel": "USSD",
        "ip": "::ffff:127.0.0.1",
        "narration": "Flutterwave Developers",
        "status": "successful",
        "vbvcode": "N/A",
        "vbvmessage": "N/A",
        "authurl": "NO-URL",
        "acctcode": "00",
        "acctmessage": "Transaction Successful",
        "paymenttype": "ussd",
        "paymentid": "N/A",
        "fraudstatus": "ok",
        "chargetype": "normal",
        "createdday": 1,
        "createddayname": "MONDAY",
        "createdweek": 31,
        "createdmonth": 7,
        "createdmonthname": "AUGUST",
        "createdquarter": 3,
        "createdyear": 2021,
        "createdyearisleap": false,
        "createddayispublicholiday": 0,
        "createdhour": 13,
        "createdminute": 49,
        "createdpmam": "pm",
        "created": "2021-08-02T13:49:56.000Z",
        "customerid": 1355595,
        "custphone": "0902620185",
        "custnetworkprovider": "AIRTEL",
        "custname": "Flutterwave Developers",
        "custemail": "[email protected]",
        "custemailprovider": "COMPANY EMAIL",
        "custcreated": "2021-08-02T13:49:56.000Z",
        "accountid": 20937,
        "acctbusinessname": "Flutterwave Developers",
        "acctcontactperson": "Flutterwave Developer",
        "acctcountry": "NG",
        "acctbearsfeeattransactiontime": 1,
        "acctparent": 2410,
        "acctvpcmerchant": "N/A",
        "acctalias": null,
        "acctisliveapproved": 0,
        "orderref": "URF_1627912196813_5531035",
        "paymentplan": null,
        "paymentpage": null,
        "raveref": null,
        "amountsettledforthistransaction": 67.9,
        "meta": [
            {
                "id": 201298993,
                "metaname": "flightID",
                "metavalue": "123949494DC",
                "createdAt": "2021-08-02T13:49:56.000Z",
                "updatedAt": "2021-08-02T13:49:56.000Z",
                "deletedAt": null,
                "getpaidTransactionId": 2382914
            }
        ]
    }
}