The Rave By Flutterwave Developer Documentation

We have put together comprehensive guidelines and documentation to help you get right into integrating any of our products quickly. You can also get support when you need help!

Get Started    Discussions
Suggest Edits

Introduction

This page gives an introduction to using Rave's API's

 

Rave's API are HTTP based RESTful APIs. API request and response format are in JSON.

Suggest Edits

API Request

This describes the request format for Rave APIs

 

To construct a REST API request, combine these components:

Component
Description

The HTTP method

  • GET. Requests data from a resource.

  • POST. Submits data to a resource to process.

  • PUT. Updates a resource.

  • PATCH. Partially updates a resource.

  • DELETE. Deletes a resource.

The URL to the API service

-Sandbox.https://ravesandboxapi.flutterwave.com

  • Live. https://api.ravepay.co

The URI to the resource

The resource to query, submit data to, update, or delete. For example, flwv3-pug/getpaidx/api/validatecharge.

HTTP request headers

Includes the Content-Type header with the value application/json.

A JSON request body

Required for most GET, POST, PUT, and PATCH calls.

This sample request that verifies a completed transactions.

curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/verify \
  --data '{"flw_ref":"FLW-MOCK-6f52518a2ecca2b6b090f9593eb390ce","normalize":"1","SECKEY":"FLWPUBK-e634d14d9ded04eaf05d5b63a0a06d2f-X"}'

HTTP Request Headers

The following table describes the commonly used HTTP request headers:

Header
Description

Accept

The response format. Required for operations with a response body. The syntax is:

Accept: application/<format>

Where format is json.

Content-Type

The request format. Required for operations with a request body. The syntax is:

Content-Type: application/<format>
Suggest Edits

API Response

This describes the Response format for Rave APIs

 

Rave API calls return HTTP status codes in the response headers. API calls also return JSON response bodies that include information about the resource.

HTTP Status Codes

Each REST API request returns a success or error HTTP status code.

Success

In the response headers, Rave returns these HTTP status codes for successful requests:

Status code
Description

200 OK

The request succeeded.

Error

In the responses for failed requests, Rave returns HTTP 400 status codes.

RAVE API errors can be grouped into three main categories. The validation errors, rave errors and provider errors.
They are usually returned in this format with the HTTP 400 status code:

{
    "status": "error",
    "message": "Merchant not enabled for API Disburse",
    "data": null
}

With Data Object.

{
  "status": "error",
  "message": "No transaction found",
  "data": {
    "code": "NO TX",
    "message": "No transaction found"
  }
}

You can see more on Rave errors here

HTTP status code
Typical error code and error message
Cause

400 Bad Request

VV_TX. TRANSACTION ALREADY VERIFIED

The transaction was already verified with the same reference.

404 Not Found

NO TX. The specified resource does not exist.

The server did not find anything that matches the request URI. Either the URI is incorrect or the resource is not available. For example, no data exists in the database at that key.

500 Internal Server Error

An internal server error has occurred.

A system or application error occurred. Although the client appears to provide a correct request, something unexpected occurred on the server.

503 Service Unavailable

Service Unavailable.

The server cannot handle the request for a service due to temporary maintenance.

Suggest Edits

Rave Encryption

This page describes how to encrypt charge requests when calling Rave's direct API's

 

Rave ensures complete security by using 3DES military grade encryption. When performing a card/account charge and your request is sent from your server, you will need to use 3DES encryption and our getKey function to generate an encryption key.

Using your secret key in your application

Always store your secret key in an environment variable, don't expose it in your code, you would most likely commit it, and it would be exposed to the public. You can add it in your code using an environment variable.

Rave encryption is broken down into two parts,

  1. the getKey() function; this helps generate the encryption key to be used by hashing the key (Secret Key) using an md5 algorithm then picking a substring(last 12 digits) of the the hashed key.

On the other end, you would strip the original key of it's prefix (FLWSECK-), and get the substring (first 12 digits), combine them to make create the encryption key.

  1. Second part is the encryption function itself, which uses the 3DES algorithm method. The data object to be passed along with the key is the payment request data in String format. Example is given below.
// this is the getKey function that generates an encryption Key for you by passing your Secret Key as a parameter.
function getKey(seckey){
    var md5 = require('md5');
    var keymd5 = md5(seckey);
    var keymd5last12 = keymd5.substr(-12);

    var seckeyadjusted = seckey.replace('FLWSECK-', '');
    var seckeyadjustedfirst12 = seckeyadjusted.substr(0, 12);

    return seckeyadjustedfirst12 + keymd5last12;
}

// This is the encryption function that encrypts your payload by passing the stringified format and your encryption Key.
function encrypt(key, text)
{
    var CryptoJS = require('crypto-js');
    var forge    = require('node-forge');
    var utf8     = require('utf8');
    var cipher   = forge.cipher.createCipher('3DES-ECB', forge.util.createBuffer(key));
    cipher.start({iv:''});
    cipher.update(forge.util.createBuffer(text, 'utf-8'));
    cipher.finish();
    var encrypted = cipher.output;
    return ( forge.util.encode64(encrypted.getBytes()) );
}


/**** THIS ENCRYPTION SECTION IS FOR FRONT END ECRYPTION***/

// Encryption can also be done at the front end using `RSA Encryption`:

function getPublicKey(){
  // write function to generate Public Key here using RSA Encryption
  // see cryptico docs on how to do that.
}
chargeDataGlobal = { "// Enter your payload here" };
var newdata = {PBFPubKey:chargeDataGlobal.PBFPubKey, client:cryptico.encrypt(JSON.stringify(chargeDataGlobal),getPublicKey()).cipher};
<?php

// this is the getKey function that generates an encryption Key for you by passing your Secret Key as a parameter.
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);
 }
import base64
from Crypto.Cipher import DES3
import hashlib

"""this is the getKey function that generates an encryption Key for you by passing your Secret Key as a parameter."""

def getKey():
  seckey = "FLWSECK-6b32914d4d60c10d0ef72bdad734134a-X"
  hashedseckey = hashlib.md5(seckey.encode("utf-8")).hexdigest()
  hashedseckeylast12 = hashedseckey[-12:]
  seckeyadjusted = seckey.replace('FLWSECK-', '')
  seckeyadjustedfirst12 = seckeyadjusted[:12]
  return seckeyadjustedfirst12 + hashedseckeylast12

 """This is the encryption function that encrypts your payload by passing the text and your encryption Key."""

def encryptData(key, plainText):
  blockSize = 8
  padDiff = blockSize - (len(plainText) % blockSize)
  cipher = DES3.new(key, DES3.MODE_ECB)
  plainText = "{}{}".format(plainText, "".join(chr(padDiff) * padDiff))
  encrypted = base64.b64encode(cipher.encrypt(plainText))
  return encrypted
public class TripleDES{
  private String key;

// Method to turn bytes in hex
  public static String toHexStr(byte[] bytes){

       StringBuilder builder = new StringBuilder();

       for(int i = 0; i < bytes.length; i++ ){
           builder.append(String.format("%02x", bytes[i]));
       }

       return builder.toString();
   }

   // this is the getKey function that generates an encryption Key for you by passing your Secret Key as a parameter.

   public static String getKey(String seedKey) {
        try {
            MessageDigest md = MessageDigest.getInstance("md5");
            byte[] hashedString = md.digest(seedKey.getBytes("utf-8"));
            byte[] subHashString = toHexStr(Arrays.copyOfRange(hashedString, hashedString.length - 12, hashedString.length)).getBytes("utf-8");
            String subSeedKey = seedKey.replace("FLWSECK-", "");
            subSeedKey = subSeedKey.substring(0, 12);
            byte[] combineArray = new byte[24];
            System.arraycopy(subSeedKey.getBytes(), 0, combineArray, 0, 12);
            System.arraycopy(subHashString, subHashString.length - 12, combineArray, 12, 12);
            return new String(combineArray);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getGlobal().log(Level.SEVERE, null, ex);
        } catch (UnsupportedEncodingException ex) {
            Logger.getGlobal().log(Level.SEVERE, null, ex);
        }
        return null;
    }

    // This is the encryption function that encrypts your payload by passing the stringified format and your encryption Key.

public static String encryptData(String message, String _encryptionKey)  {
        try {
            final byte[] digestOfPassword = _encryptionKey.getBytes("utf-8");
            final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);

            final SecretKey key = new SecretKeySpec( keyBytes , "DESede");
            final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            final byte[] plainTextBytes = message.getBytes("utf-8");
            final byte[] cipherText = cipher.doFinal(plainTextBytes);
            return Base64.getEncoder().encodeToString(cipherText);

        } catch (Exception e) {

            e.printStackTrace();
            return "";
        }
}
// Authored by Tade samson find link to github here: https://github.com/TadeSamson/RavePaymentDataEncryption


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;

namespace EncryptionService
{
    public class RavePaymentDataEncryption: IPaymentDataEncryption
    {


        /// <summary>
        /// Gets an encryption key from rave secret key.
        /// </summary>
        /// <param name="secretKey">The secret key generated from your rave dashboard</param>
        /// <returns>a string value encrypted</returns>
        public string GetEncryptionKey(string secretKey)
        {

            //MD5 is the hash algorithm expected by rave to generate encryption key
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            //MD5CryptoServiceProvider works with bytes so a conversion of plain secretKey to it bytes equivalent is required.
            //UTF8Encoding.UTF8.GetBytes(secretKey) can also be used.
            byte[] secretKeyBytes = ASCIIEncoding.UTF8.GetBytes(secretKey);

            
            byte[] hashedSecret = md5.ComputeHash(secretKeyBytes,0,secretKeyBytes.Length);
            byte[] hashedSecretLast12Bytes=new byte[12];
            Array.Copy(hashedSecret, hashedSecret.Length - 12, hashedSecretLast12Bytes, 0, 12);
            String hashedSecretLast12HexString = BitConverter.ToString(hashedSecretLast12Bytes);
            hashedSecretLast12HexString = hashedSecretLast12HexString.ToLower().Replace("-", "");
            String secretKeyFirst12 = secretKey.Replace("FLWSECK-", "").Substring(0,12);
            byte[] hashedSecretLast12HexBytes = ASCIIEncoding.UTF8.GetBytes(hashedSecretLast12HexString);
            byte[] secretFirst12Bytes = ASCIIEncoding.UTF8.GetBytes(secretKeyFirst12);
            byte[] combineKey = new byte[24];
            Array.Copy(secretFirst12Bytes, 0, combineKey, 0, secretFirst12Bytes.Length);
            Array.Copy(hashedSecretLast12HexBytes, hashedSecretLast12HexBytes.Length-12, combineKey, 12, 12);
            return  ASCIIEncoding.UTF8.GetString(combineKey);
        }

      // This is the encryption function that encrypts your payload by passing the stringified format and your encryption Key.
        public string EncryptData(string encryptionKey, string data)
        {
            TripleDES des = new TripleDESCryptoServiceProvider();
            des.Mode = CipherMode.ECB;
            des.Padding = PaddingMode.PKCS7;
            des.Key = ASCIIEncoding.UTF8.GetBytes(encryptionKey);
            ICryptoTransform cryptoTransform = des.CreateEncryptor();
            byte[] dataBytes=ASCIIEncoding.UTF8.GetBytes(data);
            byte[] encryptedDataBytes= cryptoTransform.TransformFinalBlock(dataBytes, 0, dataBytes.Length);
            des.Clear();
            return Convert.ToBase64String(encryptedDataBytes);
        }

        public string DecryptData(string encryptedData,string encryptionKey)
        {
            TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
            des.Key = ASCIIEncoding.UTF8.GetBytes(encryptionKey);
            des.Mode = CipherMode.ECB;
            des.Padding = PaddingMode.PKCS7;
            ICryptoTransform cryptoTransform = des.CreateDecryptor();
            byte[] EncryptDataBytes=Convert.FromBase64String(encryptedData);
            byte[] plainDataBytes= cryptoTransform.TransformFinalBlock(EncryptDataBytes, 0, EncryptDataBytes.Length);
            des.Clear();
            return ASCIIEncoding.UTF8.GetString(plainDataBytes);

        }
    }
}
The `getEncryptedData` method needs to be called first to clean the secret key, all other methods are used inside it.

private static String encrypt(String data, String key) throws Exception {
        byte[] keyBytes = key.getBytes(UTF_8);
        SecretKeySpec skey = new SecretKeySpec(keyBytes, ALGORITHM);
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);

        cipher.init(Cipher.ENCRYPT_MODE, skey);
        byte[] plainTextBytes = data.getBytes(UTF_8);
        byte[] buf = cipher.doFinal(plainTextBytes);
        return Base64.encodeToString(buf, Base64.DEFAULT);

    }

    private static String getMd5(String md5) throws Exception {
        MessageDigest md = MessageDigest.getInstance(MD5);
        byte[] array = md.digest(md5.getBytes(CHARSET_NAME));
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < array.length; ++i) {
            sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString();
    }

    public static String getEncryptedData(String unEncryptedString, String secret) {
        try {
            // hash the secret
            String md5Hash = getMd5(secret);
            String cleanSecret = secret.replace(TARGET, "");
            int hashLength = md5Hash.length();
            String encryptionKey = cleanSecret.substring(0, 12).concat(md5Hash.substring(hashLength - 12, hashLength));

            return encrypt(unEncryptedString, encryptionKey);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
// Encrypting the secret key

func MD5(string: String) -> Data? {
    guard let messageData = string.data(using:String.Encoding.utf8) else { return nil }
    var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH))
    
    _ = digestData.withUnsafeMutableBytes {digestBytes in
        messageData.withUnsafeBytes {messageBytes in
            CC_MD5(messageBytes, CC_LONG(messageData.count), digestBytes)
        }
    }
    
    return digestData
}
func getEncryptionKey(_ secretKey:String)->String {
    let md5Data = MD5(string:secretKey)
    let md5Hex =  md5Data!.map { String(format: "%02hhx", $0) }.joined()
    
    var secretKeyHex = ""
    
    if secretKey.contains("FLWSECK-") {
        secretKeyHex = secretKey.replacingOccurrences(of: "FLWSECK-", with: "")
    }
    if secretKey.contains("-X") {
        secretKeyHex = secretKeyHex.replacingOccurrences(of: "-X", with: "")
    }
    
    let index = secretKeyHex.index(secretKeyHex.startIndex, offsetBy: 12)
    let first12 = secretKeyHex.substring(to: index)
    
    let last12 = md5Hex.substring(from:md5Hex.index(md5Hex.endIndex, offsetBy: -12))
    return first12 + last12
    
}

// Encrypting the payload

static func encrypt(string:String, key:String) -> NSData? {
        
        let keyData: NSData! = (key as NSString).data(using: String.Encoding.utf8.rawValue) as NSData!
        let keyBytes         = keyData.bytes
        
        let data: NSData! = (string as NSString).data(using: String.Encoding.utf8.rawValue) as NSData!
        let dataLength    = UInt(data.length)
        let dataBytes     = data.bytes
        
        let cryptData    = NSMutableData(length: Int(dataLength) + kCCBlockSize3DES)!
        let cryptPointer = cryptData.mutableBytes
        let cryptLength  = size_t(cryptData.length)
        
        let keyLength              = size_t(kCCKeySize3DES)
        let operation: CCOperation = UInt32(kCCEncrypt)
        let algoritm:  CCAlgorithm = UInt32(kCCAlgorithm3DES)
        let options:   CCOptions   = UInt32(kCCOptionPKCS7Padding+kCCOptionECBMode)
        
        var numBytesEncrypted :Int = 0
        
        let cryptStatus = CCCrypt(operation,
                                  algoritm,
                                  options,
                                  keyBytes, keyLength,
                                  nil,
                                  dataBytes, Int(dataLength),
                                  cryptPointer, cryptLength,
                                  &numBytesEncrypted)
        if UInt32(cryptStatus) == UInt32(kCCSuccess) {
            let _: Int = numBytesEncrypted
            cryptData.length = Int(numBytesEncrypted)
            return cryptData
        } else {
            print("Error: \(cryptStatus)")
        }
        return nil
    }

// USAGE

let secret = getEncryptionKey("FLWSECK-045c2e4312c965eefc9c7c62f0a4762c-X")
            let data =  encrypt(string: payloadJson, key:secret)

Encryption Examples

<?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 payviacard(){ // set up a function to test card payment.
    
    error_reporting(E_ALL);
    ini_set('display_errors',1);
    
    $data = array('PBFPubKey' => 'FLWPUBK-e634d14d9ded04eaf05d5b63a0a06d2f-X',
    'cardno' => '5438898014560229',
    'currency' => 'NGN',
    'country' => 'NG',
    'cvv' => '789',
    'amount' => '300',
    'expiryyear' => '19',
    'expirymonth' => '09',
    'suggested_auth' => 'pin',
    'pin' => '3310',
    'email' => 'tester@flutter.co',
    'IP' => '103.238.105.185',
    'txRef' => 'MXX-ASC-4578',
    '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://ravesandboxapi.flutterwave.com/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);
}

payviacard();
Suggest Edits

Rave Parameters

Describes all parameters that can be passed using rave's APIs

 

Card Charge Parameters

Parameter
Required
Description

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.

cardno

True

This is the number on the cardholders card. E.g. 5399 6701 2349 0229.

cvv

True

This is the 3-digit number at the back of the card.

expirymonth

True

This is the month of card expiration as written on a cardholder’s card. Format is ‘MM’.

expiryyear

True

This is the year of card expiration on as written on a cardholder’s card. Format is ‘YY’.

currency

False
(Defaults to NGN)

This is the specified currency to charge the card in.

country

False
(Defaults to NG)

This is the pair country for the transaction with respect to the currency. See a list of Multicurrency support here Multicurrency Payments ]

pin

False
(Used only for transactions that require PIN)

This is the pin issued to the customer for his card.

suggested_auth

False
(Default value: PIN)

this is an identifier to show that the suggested authentication model is being used.

amount

True

This is the amount to be charged from card it is passed as - (“amount”:10).

email

True

This is the email address of the customer.

phonenumber

True

This is the phone number of the customer.

firstname

True

This is the first name of the card holder or the customer.

lastname

True

This is the last name of the card holder or the customer.

IP

True

IP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction.

txRef

True

This is the unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction

device_fingerprint

False

This is the fingerpringt for the device being used. It can be generated using a library on whatever platform is being used.

charge_type

False
(Used for preauthorised transactions)

This identifies that you are making a preauthorised payment request call to the charge endpoint by passing the value preauth. It should be built with your payment request only when carrying out a preauthorised transaction.

Account Charge Parameters

Account charge for Nigeria

This only applies to Nigeria

Parameter
Required
Description

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.

accountnumber

True

This is the account number of the customer associated with a valid bank account.

accountbank

True

This is a bank code that represents the bank account to be debited. To get a list of banks in Nigeria, call the List of Banks API

currency

False
(Defaults to NGN)

This is the specified currency to charge the account in.

country

False
(Defaults to NG)

This is the pair country for the transaction with respect to the currency. See a list of Multicurrency support here Multicurrency Payments ]

amount

False

This is the amount to be charged from the account it is passed as - (“amount”:10).

email

True

This is the email address of the customer.

phonenumber

True

This is the phone number of the customer.

firstname

false

This is the first name of the account holder or the customer.

lastname

false

This is the last name of the account holder or the customer.

IP

True

IP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction.

txRef

True

This is the unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction.

payment_type

True
(Expected value: account)

This specifies that the payment method being used is for account payments

device_fingerprint

False

This is the fingerpringt for the device being used. It can be generated using a library on whatever platform is being used.

Recurring Billing Parameters

Parameter
Required
Description

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.

cardno

True

This is the number on the cardholders card. E.g. 5399 6701 2349 0229.

cvv

True

This is the 3-digit number at the back of the card.

expirymonth

True

This is the month of card expiration as written on a cardholder’s card. Format is ‘MM’.

expiryyear

True

This is the year of card expiration on as written on a cardholder’s card. Format is ‘YY’.

currency

False
(Defaults to NGN)

This is the specified currency to charge the card in.

country

False
(Defaults to NG)

This is the pair country for the transaction with respect to the currency. See a list of Multicurrency support here Multicurrency Payments ]

pin

False
(Used only for transactions that require PIN)

This is the pin issued to the customer for his card.

suggested_auth

False
(Default value: PIN)

this is an identifier to show that the suggested authentication model is being used.

amount

True

This is the amount to be charged from card it is passed as - (“amount”:10).

email

True

This is the email address of the customer.

phonenumber

False

This is the phone number 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

True

IP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction.

txRef

True

This is the unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction.

device_fingerprint

False

This is the fingerpringt for the device being used. It can be generated using a library on whatever platform is being used.

charge_type

True

This helps identify the frequency of the recurring debit it can be set to the follow possible values:

recurring-daily, recurring-weekly, recurring-monthly, recurring-quarterly, recurring-bianually, recurring-anually

payment_plan

True

This is the payment plan ID to use for the recurring payment, you can see how to create payment plans here

 

Rave authenticates your API requests using your account’s API keys. If you do not include your key when making an API request, or use one that is incorrect or outdated, Rave returns an error.

All API requests exist in either test or live mode, and one mode cannot be manipulated by data in the other. To get your test api keys you need to sign up here, and to get your live api keys sign up here

There are also two types of API keys: publishable and secret.

  • Publishable API keys are meant solely to identify your account with Rave, they aren’t secret. In other words, they can safely be published in places like your Rave JavaScript code, or in an Android or iPhone app. Publishable keys only have the power to create a transaction.

  • Secret API keys should be kept confidential and only stored on your own servers. Your account’s secret API key can perform any API request to Rave without restriction.

Obtaining your API Keys

Your API keys are available in the Dashboard by navigating to Settings - > API.

Only use your test API keys for testing and development.

This ensures you don't accidentally modify your live customers or transactions.

If you cannot see your API keys in the Dashboard, this means you do not have access to them. Contact your Rave account’s owner and ask to be added to their team as an admin.

Test and live modes

The test and live modes function almost identically, with a few necessary differences:

Suggest Edits

Webhooks

This page describes what webhooks are and how to configure them.

 

What is a Webhook

A WebHook is an HTTP callback: an HTTP POST that occurs when something happens; a simple event-notification via HTTP POST. A web application implementing WebHooks will POST a message to a URL when certain things happen.

Rave sends webhooks events that notify your application any time a payment event happens on your account. This is very useful for events - like getting paid via mobile money or USSD where the transaction is completed outside your application- Recurring billing where an api call is not needed for subsequent billings.

In Rave you can setup webhooks that would let us notify you anytime events- A user on a subscription is charged, a customer completes a payment, we update a pending payment to failed or successful- happen in your account.

When to use webhooks

Webhooks can be used for all kinds of payment methods, card, account, USSD, Mpesa, and Ghana Mobile money.

If you use Rave to accept alternate payment methods like USSD, Mpesa, and Ghana mobile money, it is best practice to use webhooks so that your integration can be notified about changes the status of the payment once it is completed. This is because these payment methods are asynchronous and responses only come once the customer has completed the payment on their device.

You might also use webhooks to:

  • Update a customer's membership record in your database when a subscription payment succeeds.

  • Email a customer when a subscription payment fails.

  • Update your database when the status of a pending payment is updated to successful or failed

NB: Not in all cases would you be able to rely completely on webhooks to get notified, an example is if your server is experiencing a downtime and your hook endpoints are affected, some customers might still be transacting independently of that and the hook call triggered would fail because your server was unreachable.

In such cases we advice that developers set up a requery service that goes to poll for the transaction status at regular intervals e.g. every hour using the Xrequery Transaction verification endpoint, till a successful or failed response is returned.

Sample Transaction Payload

On Rave, Webhooks can be configured for transactions. When a transaction is completed, a POST HTTP request is sent to the URL you have configured. The HTTP payload will contain

{
  "id": 126122,
  "txRef": "rave-pos-121775237991",
  "flwRef": "FLW-MOCK-72d0b2d66273fad0bb32fdea9f0fa298",
  "orderRef": "URF_1523185223111_833935",
  "paymentPlan": null,
  "createdAt": "2018-04-08T11:00:23.000Z",
  "amount": 1000,
  "charged_amount": 1000,
  "status": "successful",
  "IP": "197.149.95.62",
  "currency": "NGN",
  "customer": {
    "id": 22836,
    "phone": null,
    "fullName": "Anonymous customer",
    "customertoken": null,
    "email": "salesmode@ravepay.co",
    "createdAt": "2018-04-08T11:00:22.000Z",
    "updatedAt": "2018-04-08T11:00:22.000Z",
    "deletedAt": null,
    "AccountId": 134
  },
  "entity": {
    "card6": "539983",
    "card_last4": "8381"
  }
}
{
  "id": 125837,
  "txRef": "rave-pos-272519815315",
  "flwRef": "FLWACHMOCK-1523118279396",
  "orderRef": "URF_1523118277202_7343035",
  "paymentPlan": null,
  "createdAt": "2018-04-07T16:24:37.000Z",
  "amount": 200,
  "charged_amount": 200,
  "status": "successful",
  "IP": "197.149.95.62",
  "currency": "NGN",
  "customer": {
    "id": 5766,
    "phone": "N/A",
    "fullName": "Anonymous customer",
    "customertoken": null,
    "email": "salesmode@ravepay.co",
    "createdAt": "2017-10-16T10:03:19.000Z",
    "updatedAt": "2017-10-16T10:03:19.000Z",
    "deletedAt": null,
    "AccountId": 134
  },
  "entity": {
    "account_number": "0690000037",
    "first_name": "Dele Moruf",
    "last_name": "Quadri"
  }
}
{
  "id": 126090,
  "txRef": "rave-checkout-1523183226335",
  "flwRef": "flwm3s4m0c1523183355860",
  "orderRef": null,
  "paymentPlan": null,
  "createdAt": "2018-04-08T10:29:15.000Z",
  "amount": 2000,
  "charged_amount": 2000,
  "status": "successful",
  "IP": "197.149.95.62",
  "currency": "GHS",
  "customer": {
    "id": 22823,
    "phone": "0578922930",
    "fullName": "Anonymous Customer",
    "customertoken": null,
    "email": "user@example.com",
    "createdAt": "2018-04-08T10:28:01.000Z",
    "updatedAt": "2018-04-08T10:28:01.000Z",
    "deletedAt": null,
    "AccountId": 134
  },
  "entity": {
    "id": "NO-ENTITY"
  }
}
{
  "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": "user@ymail.com",
    "createdAt": "2018-04-15T16:32:05.000Z",
    "updatedAt": "2018-04-15T16:32:05.000Z",
    "deletedAt": null,
    "AccountId": 1884
  },
  "entity": {
    "id": "NO-ENTITY"
  }
}

How to setup webhooks on your dashboard.

Login to you Rave dashboard then click on *settings* , on the setting page navigate to webhooks to add a webhook.

Login to you Rave dashboard then click on settings , on the setting page navigate to webhooks to add a webhook.

Once on the webhook page, click the input text to add your webhook and use the save action button to save it.

Once on the webhook page, click the input text to add your webhook and use the save action button to save it.

Receiving a webhook notification

Creating a webhook endpoint on your server is no different from creating any page on your website. With PHP, you might create a new .php file on your server; with a framework like Laravel, Flask, Sinatra, you would add a new route with the desired webhook URL.

Webhook data is sent as form-urlencoded by default but you can configure on your webhook settings page on the dashboard to send the request as JSON instead.

Checking webhook signatures

You can use a secret hash to verify that your received requests were sent by rave.

<?php

// Retrieve the request's body
$body = @file_get_contents("php://input");

// retrieve the signature sent in the reques header's.
$signature = (isset($_SERVER['HTTP_VERIF_HASH']) ? $_SERVER['HTTP_VERIF_HASH'] : '');

/* It is a good idea to log all events received. Add code *
 * here to log the signature and body to db or file       */

if (!$signature) {
    // only a post with rave signature header gets our attention
    exit();
}

// Store the same signature on your server as an env variable and check against what was sent in the headers
$local_signature = getenv('SECRET_HASH');

// confirm the event's signature
if( $signature !== $local_signature ){
  // silently forget this ever happened
  exit();
}

http_response_code(200); // PHP 5.4 or greater
// parse event (which is json string) as object
// Give value to your customer but don't give any output
// Remember that this is a call from rave's servers and 
// Your customer is not seeing the response here at all
$response = json_decode($body);
if ($response->body->status == 'successful') {
    # code...
    // TIP: you may still verify the transaction
    		// before giving value.
}
exit();

// This example uses Express to receive webhooks
const app = require("express")();


app.post("/my/webhook/url", function(request, response) {
  /* It is a good idea to log all events received. Add code *
 * here to log the signature and body to db or file       */
  
  // retrieve the signature from the header
  var hash = req.headers["HTTP_VERIF_HASH"];
  
  if(!hash) {
  	// discard the request,only a post with rave signature header gets our attention 
  }
  
  // Get signature stored as env variable on your server
  const secret_hash = process.env.MY_HASH;
  
  // check if signatures match
  
  if(hash !== secret_hash) {
   // silently exit, or check that you are passing the write hash on your server.
  }
  
  // Retrieve the request's body
  var request_json = JSON.parse(request.body);

  // Give value to your customer but don't give any output
// Remember that this is a call from rave's servers and 
// Your customer is not seeing the response here at all

  response.send(200);
});
require "json"

# Using Sinatra
post "/my/webhook/url" do
  # Retrieve the request's body
  request_json = request.body.read

  # Do something with request_json

  status 200
end
mport json
from django.http import HttpResponse

# Using Django
def my_webhook_view(request):
  # Retrieve the request's body
  request_json = request.body

  # Do something with request_json

  return HttpResponse(status=200)
// Using Spark framework (http://sparkjava.com)
public Object handle(Request request, Response response) {
  // Retrieve the request's body and parse it as JSON
  Request requestJson = APIResource.GSON.fromJson(request.body(), Event.class);

  // Do something with eventJson

  response.status(200);
  return "";
}
using System;
using System.IO;
using Microsoft.AspNetCore.Mvc;

namespace workspace.Controllers
{
    [Route("api/[controller]")]
    public class RaveWebHook : Controller
    {
        [HttpPost]
        public void Index() {
          	
            var Ravejson = new StreamReader(HttpContext.Request.Body).ReadToEnd();
            

            // Do something with Ravejson
        }
    }
}

Verifying Webhook signature(secret hash)

Rave returns the secret hash configured in your settings, in the request headers as verif-hash you can store the same secret hash as an environment variable and check if it is the same value sent in the verif-hash property before processing the webhook request. If they are not the same you can discard the request.

Receiving webhooks with a CSRF-protected server

When using Rails, Django, or any other web framework, your site might automatically check that every POST request contains a CSRF token. This is an important security feature that protects you and your users from cross-site request forgery).

However, this security measure might also prevent your site from processing webhooks sent by rave. If so, you might need to exempt the webhooks route from CSRF protection. See how to do that below

import json

# Webhooks are always sent as HTTP POST requests, so we want to ensure
# that only POST requests will reach your webhook view. We can do that by
# decorating `webhook()` with `require_POST`.
#
# Then to ensure that the webhook view can receive webhooks, we need
# also need to decorate `webhook()` with `csrf_exempt`.
@require_POST
@csrf_exempt
def webhook(request):
  # Process webhook data in `request.body`
class RaveController < ApplicationController
  # If your controller accepts requests other than Rave webhooks,
  # you'll probably want to use `protect_from_forgery` to add CSRF
  # protection for your application. But don't forget to exempt
  # your webhook route!
  protect_from_forgery :except => :webhook

  def webhook
    # Process webhook data in `params`
  end
end

Responding to a webhook request

To acknowledge receipt of a webhook, your endpoint should return a 2xx HTTP status code. All response codes outside this range, including 3xx codes, will indicate to Rave that you did not receive the webhook. This does mean that a URL redirection or a "Not Modified" response will be treated as a failure. Rave will ignore any other information returned in the request headers or request body.

If your endpoint does not successfully receive a webhook for any reason, webhooks would not be retried, though you can query for the status using the Verify Payment endpoint to reconcile your data with any missed events.

Best practices

If your webhook script performs complex logic, or makes network calls, it's possible that the script would time out before rave sees its complete execution. For that reason, you might want to have your webhook endpoint immediately acknowledge receipt by returning a 2xx HTTP status code, and then perform the rest of its duties.

Webhook endpoints might occasionally receive the same event more than once. We advise you to guard against duplicated event receipts by making your event processing idempotent. One way of doing this is logging the events you've processed, and then checking if the status has changed before processing the identical event. Additionally, we recommend verifying webhook signatures to confirm that received events are being sent from rave.

Suggest Edits

Card Payments

This page shows how to perform card payments using Rave's APIs

 

Rave allows you charge local (card's issued in Nigeria) and International cards using our APIs. When charging cards with Rave you have to take into account authentication models this is primarily how the user who is meant to pay authenticates the transaction e.g. using a one time pin (OTP), a card internet PIN (i-Pin) or an address verification system (AVS).

Rave automatically determines the authentication model to be used by the card and sends a response requiring you pass the needed parameter for that authentication model.

The guide below would show you how to charge cards on rave using our APIs.

Pre-requisites for accepting card payments on rave.

  1. Sign-up for a test account here, and for a live account here .

  2. You can use Webhooks to get notified of transactions or use the response returned by the endpoint.

  3. For all card payments, you would need to implement three steps to the transactions, Initiate payment, Validate payment, Verify completed payment.

Step 1: Collect the card details from the customer

You can use a custom form to collect the card details from the customer including extra information needed, see a sample of the card details to collect from the customer.

{
  "PBFPubKey": "FLWPUBK-4e581ebf8372cd691203b27227e2e3b8-X",
  "cardno": "5438898014560229",
  "cvv": "890",
  "expirymonth": "09",
  "expiryyear": "19",
  "currency": "NGN",
  "country": "NG",
  "amount": "10",
  "email": "user@gmail.com",
  "phonenumber": "0902620185",
  "firstname": "temi",
  "lastname": "desola",
  "IP": "355426087298442",
  "txRef": "MC-" + Date.now(),// your unique merchant reference
  "meta": [{metaname: "flightID", metavalue: "123949494DC"}],
  "redirect_url": "https://rave-webhook.herokuapp.com/receivepayment",
  "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}
{
  "PBFPubKey": "FLWPUBK-4e581ebf8372cd691203b27227e2e3b8-X",
  "cardno": "5438898014560229",
  "cvv": "890",
  "expirymonth": "09",
  "expiryyear": "19",
  "currency": "NGN",
  "country": "NG",
  "amount": "10",
  "email": "user@gmail.com",
  "phonenumber": "0902620185",
  "firstname": "temi",
  "lastname": "desola",
  "subaccount": [
    {
      "id": "RS_D87A9EE339AE28BFA2AE86041C6DE70E",
      "transaction_split_ratio": "2"
    },
    {
      "id": "RS_344DD49DB5D471EF565C897ECD67CD95",
      "transaction_split_ratio": "3"
    },
    {
      "id": "RS_839AC07C3450A65004A0E11B83E22CA9",
      "transaction_split_ratio": "5"
    }
  ],
  "IP": "355426087298442",
  "txRef": "MC-",
  "meta": [
    {
      "metaname": "flightID",
      "metavalue": "123949494DC"
    }
  ],
  "IP": "355426087298442",
  "txRef": "MC-" + Date.now(),// your unique merchant reference
  "redirect_url": "https://rave-webhook.herokuapp.com/receivepayment",
  "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}

Parameter Description

Parameter
Required
Description

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.

cardno

true

This is the number on the cardholders card. E.g. 5399 6701 2349 0229.

cvv

true

Card security code. This is 3/4 digit code at the back of the customers card, used for web payments.

expirymonth

true

Two-digit number representing the card's expiration month.

expiryyear

true

Two- digit number representing the card's expiration year.

currency

false
defaults to NGN

This is the specified currency to charge the card in.

country

false
defaults to NG

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 from card it is passed as - (“amount”:10).

email

true

This is the email address of the customer.

phonenumber

false

This is the phone number 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

true

IP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction.

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

false

Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format.

subaccounts

false

his is an array of objects containing the subaccount IDs to split the payment into.

redirect_url

false

3DSecure only

This is a url you provide, we redirect to it after the customer completes payment and append the response to it as query parameters.

device_fingerprint

false

This is the fingerpringt for the device being used. It can be generated using a library on whatever platform is being used.

charge_type

false

expected value: preauth

This identifies that you are making a Preauthorised payment request call to the charge endpoint by passing the value preauth. It should be built with your payment request only when carrying out a preauthorised transaction.

Collecting Card details securely using

Step 2: Encrypt the card details.

To see how to encrypt using any of our encryption function copy the sample request below and visit the Rave Encryption section.

Step 3: Initiate your payment.

After encryption, the next step is to initiate your payment using the encrypted string by sending a request to the /charge endpoint. See how to do that below.

Sandbox Endpoint: https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge

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

Sample Request:

Copy the curl request and make a request in your terminal to see how it works!

curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge \
  --header 'content-type: application/json' \
  --data '{"PBFPubKey":"FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X","client":"VodhvFFsni0CBeieHPq9HTuG5lbNPgmD5rbEw6Uxb0TD9eD9B3VM5uZ1B5lC3thQMbPypNBCAYxaW2W21VnGuznMPf1G1digW0sHjuO6BGLGbzkwv12rmgNelv19ECSaKfyJmWOSPBvQifHMXZz2M35WuZpE2oD78Be54Xz7vUy3b6MkxrFc+d5gTnuiluBcSDSmnpj/d1ovlo5bix3PeuMUtIYzGFE/RK/EcIYyfYnpL26VFT1aEn5d/iOPyHecqFYVhCMwzV0E6j0uBtT/DMWg+Bi4O1VHej2EBxxKcmwu9rTYvsFf81AtOKZazJEKOea9Xn7mx0J/QpcP2kEf3asWrUqNUgvacl8y8IyaS4jGtU7fCcrIreHttSekpT/16rc45sC428zQy6OfSLoJDA4D2Ww+TEYnMWRNhzuBDHJ9wJTfHmgQcipiD/r7cQyLAzyllfhXsHWFIv3R+ECgrrvxpYMe2lVQ5d+DdTO2pC1MyhkOscNBp7dUwoEGfU7nxY/UGoRWV5WSAg9nFYELS2F4gfvWVkbP07Q+ap11GYUbuZFTMmfULbK/3j//q+9eElWS+E2m6mY4upgehIat8qIGsvGLKR3kagL4wQPZlBMD/S8eiQ8sUD+ngFS8T0XfZUXC5m6IMQdZ7Bfz0mAT2w==","alg":"3DES-24"}'
{
  "PBFPubKey": "FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",
  "client": "VodhvFFsni0CBeieHPq9HTuG5lbNPgmD5rbEw6Uxb0TD9eD9B3VM5uZ1B5lC3thQMbPypNBCAYxaW2W21VnGuznMPf1G1digW0sHjuO6BGLGbzkwv12rmgNelv19ECSaKfyJmWOSPBvQifHMXZz2M35WuZpE2oD78Be54Xz7vUy3b6MkxrFc+d5gTnuiluBcSDSmnpj/d1ovlo5bix3PeuMUtIYzGFE/RK/EcIYyfYnpL26VFT1aEn5d/iOPyHecqFYVhCMwzV0E6j0uBtT/DMWg+Bi4O1VHej2EBxxKcmwu9rTYvsFf81AtOKZazJEKOea9Xn7mx0J/QpcP2kEf3asWrUqNUgvacl8y8IyaS4jGtU7fCcrIreHttSekpT/16rc45sC428zQy6OfSLoJDA4D2Ww+TEYnMWRNhzuBDHJ9wJTfHmgQcipiD/r7cQyLAzyllfhXsHWFIv3R+ECgrrvxpYMe2lVQ5d+DdTO2pC1MyhkOscNBp7dUwoEGfU7nxY/UGoRWV5WSAg9nFYELS2F4gfvWVkbP07Q+ap11GYUbuZFTMmfULbK/3j//q+9eElWS+E2m6mY4upgehIat8qIGsvGLKR3kagL4wQPZlBMD/S8eiQ8sUD+ngFS8T0XfZUXC5m6IMQdZ7Bfz0mAT2w==",
  "alg": "3DES-24"
}
  • client: This is the encrypted request parameters.

  • PBFPubKey: This is your merchant public key.

  • alg: must always be passed as 3DES-24

When you initiate the payment you would get a response based on the card that was sent to Rave, we explain the response you would get for each card type and what you need to do next.

Using a Local Mastercard/verve i.e. card issued in Nigeria

When using a local mastercard/Verve card, we suggest that you charge the card using the customers PIN, the suggested auth is returned after initiating payment, you would get a response that looks like this:

{
  "status": "success",
  "message": "AUTH_SUGGESTION",
  "data": {
    "suggested_auth": "PIN"
  }
}

When you get this response you are to add pin and suggested_auth to your payload again, re-encrypt it and Initiate the payment again. See an example of the new request to send to the Initiate the payment.

{
  "PBFPubKey": "FLWPUBK-4e581ebf8372cd691203b27227e2e3b8-X",
  "cardno": "5438898014560229",
  "cvv": "890",
  "expirymonth": "09",
  "expiryyear": "19",
  "currency": "NGN",
  "pin": "3310",
  "country": "NG",
  "amount": "10",
  "email": "desola.ade1@gmail.com",
  "suggested_auth": "PIN",
  "phonenumber": "0902620185",
  "firstname": "temi",
  "lastname": "desola",
  "IP": "355426087298442",
  "txRef": "MC-" + Date.now(),
  "redirect_url": "https://rave-webhook.herokuapp.com/receivepayment",
  "meta": [{metaname: "flightID", metavalue: "123949494DC"}],
  "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}

Using AVS (Address verification system) to charge an international card.

When using an international card that uses the AVS system, we detect this automatically and suggest using the AVS authmodel. The suggested authmodel is returned after initiating payment with the card details, see a sample response below.

{
  "status": "success",
  "message": "AUTH_SUGGESTION",
  "data": {
    "suggested_auth": "NOAUTH_INTERNATIONAL"
  }
}
{
  "status": "success",
  "message": "AUTH_SUGGESTION",
  "data": {
    "suggested_auth": "AVS_VBVSECURECODE"
  }
}

When you get this response you are to add the cards billing address details and suggested_auth to your payload again, re-encrypt it and Initiate the payment again. See an example of the new request to send to the Initiate the payment.

{
  "PBFPubKey": "FLWPUBK-4e581ebf8372cd691203b27227e2e3b8-X",
  "cardno": "4556052704172643",
  "cvv": "828",
  "expirymonth": "09",
  "expiryyear": "19",
  "currency": "USD",
  "country": "NG",
  "amount": "10",
  "email": "user@gmail.com",
  "phonenumber": "0902620185",
  "firstname": "temi",
  "lastname": "desola",
  "IP": "355426087298442",
  "txRef": "MC-" + Date.now(),
  "meta": [{metaname: "flightID", metavalue: "123949494DC"}],
  "suggested_auth": "AVS_VBVSECURECODE"/ "NOAUTH_INTERNATIONAL",
  "billingzip": "07205",
  "billingcity": "Hillside",
  "billingaddress": "470 Mundet PI",
  "billingstate": "NJ",
  "billingcountry": "US",
  "redirect_url": "https://rave-webhook.herokuapp.com/receivepayment",
  "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}

The billing details of the card include, billingzip, billingcity, billingaddress, billingstate, billingcountry.

  • billingzip: This is the zip code or postal card registered with the card, customers can easily find this on their bank statement.

  • billingcity: This is the city registered with the card, it makes up part of the address the customer used for their card. Customers can easily find this on their bank statement.

  • billingaddress: This is the house/building address registered with the card. Customers can easily find this on their bank statement.

  • billingstate: This is the state registered with the card. Customers can easily find this on their bank statement.

  • billingcountry: This is the country registered with the card. Customers can easily find this on their bank statement.

Handling AVS_VBVSECURECODE & 3DSecure Transactions

When the suggested auth is AVS_VBVSECURECODE it means the payment requires that the billing address of the card is sent, and after the Initiate payment step the validation step would happen using 3DSecure authentication.

What you need to do after receiving the initial payment response is load the authUrl returned in an iFrame and allow the customer validate the transaction, once that is completed we would call your redirect _url and append the response as query parameters.

3Dsecure Transactions.

Billing address details are not required for strictly 3DSecure transactions.

Final response to expect from the Initiate payment call.

{
  "status": "success",
  "message": "V-COMP",
  "data": {
    "id": 12945,
    "txRef": "MC-7663-YU",
    "orderRef": "URF_1501241395442_2906135",
    "flwRef": "FLW-MOCK-9deabfa86935b9f0805ae276d49ad079",
    "redirectUrl": "http://127.0.0",
    "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
    "settlement_token": null,
    "cycle": "one-time",
    "amount": 10,
    "charged_amount": 10,
    "appfee": 0,
    "merchantfee": 0,
    "merchantbearsfee": 0,
    "chargeResponseCode": "02",
    "chargeResponseMessage": "Success-Pending-otp-validation",
    "authModelUsed": "PIN",
    "currency": "NGN",
    "IP": "::ffff:127.0.0.1",
    "narration": "FLW-PBF CARD Transaction ",
    "status": "success-pending-validation",
    "vbvrespmessage": "Approved. Successful",
    "authurl": "N/A",
    "vbvrespcode": "00",
    "acctvalrespmsg": null,
    "acctvalrespcode": null,
    "paymentType": "card",
    "paymentId": "2",
    "fraud_status": "ok",
    "charge_type": "normal",
    "is_live": 0,
    "createdAt": "2017-07-28T11:29:55.000Z",
    "updatedAt": "2017-07-28T11:29:56.000Z",
    "deletedAt": null,
    "customerId": 168,
    "AccountId": 134,
    "customer": {
      "id": 168,
      "phone": null,
      "fullName": "demi adeola",
      "customertoken": null,
      "email": "tester@flutter.co",
      "createdAt": "2017-02-25T12:20:22.000Z",
      "updatedAt": "2017-02-25T12:20:22.000Z",
      "deletedAt": null,
      "AccountId": 134
    },
    "customercandosubsequentnoauth": true
  }
}
https://rave-webhook.herokuapp.com/newregistration?resp=%7B%22name%22%3A%22opop%22%2C%22data%22%3A%7B%22status%22%3A%22success%22%2C%22message%22%3A%22V-COMP%22%2C%22data%22%3A%7B%22id%22%3A301782%2C%22txRef%22%3A%22rave-checkout-1541080175862%22%2C%22orderRef%22%3A%22URF_1541080202653_2603435%22%2C%22flwRef%22%3A%22FLWACHMOCK-1541080203844%22%2C%22redirectUrl%22%3A%22https%3A%2F%2Frave-webhook.herokuapp.com%2Fnewregistration%22%2C%22device_fingerprint%22%3A%22532b4e9fa7695279392f4780b9868b9b%22%2C%22settlement_token%22%3Anull%2C%22cycle%22%3A%22one-time%22%2C%22amount%22%3A60%2C%22charged_amount%22%3A60%2C%22appfee%22%3A0.9%2C%22merchantfee%22%3A0%2C%22merchantbearsfee%22%3A1%2C%22chargeResponseCode%22%3A%2200%22%2C%22raveRef%22%3A%22RV31541080202194E1A956BA48%22%2C%22chargeResponseMessage%22%3A%22Approved.+Successful.%22%2C%22authModelUsed%22%3A%22AUTH%22%2C%22currency%22%3A%22NGN%22%2C%22IP%22%3A%22197.149.95.62%22%2C%22narration%22%3A%22Synergy+Group%22%2C%22status%22%3A%22successful%22%2C%22modalauditid%22%3A%2276d43165b4e49f5ce5e71736298e109d%22%2C%22vbvrespmessage%22%3A%22N%2FA%22%2C%22authurl%22%3A%22NO-URL%22%2C%22vbvrespcode%22%3A%22N%2FA%22%2C%22acctvalrespmsg%22%3Anull%2C%22acctvalrespcode%22%3Anull%2C%22paymentType%22%3A%22account%22%2C%22paymentPlan%22%3Anull%2C%22paymentPage%22%3Anull%2C%22paymentId%22%3A%22478%22%2C%22fraud_status%22%3A%22ok%22%2C%22charge_type%22%3A%22normal%22%2C%22is_live%22%3A0%2C%22createdAt%22%3A%222018-11-01T13%3A50%3A02.000Z%22%2C%22updatedAt%22%3A%222018-11-01T13%3A50%3A03.000Z%22%2C%22deletedAt%22%3Anull%2C%22customerId%22%3A58159%2C%22AccountId%22%3A134%2C%22customer%22%3A%7B%22id%22%3A58159%2C%22phone%22%3A%22N%2FA%22%2C%22fullName%22%3A%22Anonymous+customer%22%2C%22customertoken%22%3Anull%2C%22email%22%3A%22cchizie26%40gmail.com%22%2C%22createdAt%22%3A%222018-10-24T16%3A58%3A21.000Z%22%2C%22updatedAt%22%3A%222018-10-24T16%3A58%3A21.000Z%22%2C%22deletedAt%22%3Anull%2C%22AccountId%22%3A134%7D%2C%22validateInstructions%22%3A%7B%22valparams%22%3A%5B%5D%2C%22instruction%22%3A%22%22%7D%2C%22validateInstruction%22%3A%22Please+dial+*901*4*1%23+to+get+your+OTP.+Enter+the+OTP+gotten+in+the+field+below%22%7D%7D%2C%22tx%22%3A%7B%22id%22%3A301782%2C%22txRef%22%3A%22rave-checkout-1541080175862%22%2C%22orderRef%22%3A%22URF_1541080202653_2603435%22%2C%22flwRef%22%3A%22FLWACHMOCK-1541080203844%22%2C%22redirectUrl%22%3A%22https%3A%2F%2Frave-webhook.herokuapp.com%2Fnewregistration%22%2C%22device_fingerprint%22%3A%22532b4e9fa7695279392f4780b9868b9b%22%2C%22settlement_token%22%3Anull%2C%22cycle%22%3A%22one-time%22%2C%22amount%22%3A60%2C%22charged_amount%22%3A60%2C%22appfee%22%3A0.9%2C%22merchantfee%22%3A0%2C%22merchantbearsfee%22%3A1%2C%22chargeResponseCode%22%3A%2200%22%2C%22raveRef%22%3A%22RV31541080202194E1A956BA48%22%2C%22chargeResponseMessage%22%3A%22Approved.+Successful.%22%2C%22authModelUsed%22%3A%22AUTH%22%2C%22currency%22%3A%22NGN%22%2C%22IP%22%3A%22197.149.95.62%22%2C%22narration%22%3A%22Synergy+Group%22%2C%22status%22%3A%22successful%22%2C%22modalauditid%22%3A%2276d43165b4e49f5ce5e71736298e109d%22%2C%22vbvrespmessage%22%3A%22N%2FA%22%2C%22authurl%22%3A%22NO-URL%22%2C%22vbvrespcode%22%3A%22N%2FA%22%2C%22acctvalrespmsg%22%3Anull%2C%22acctvalrespcode%22%3Anull%2C%22paymentType%22%3A%22account%22%2C%22paymentPlan%22%3Anull%2C%22paymentPage%22%3Anull%2C%22paymentId%22%3A%22478%22%2C%22fraud_status%22%3A%22ok%22%2C%22charge_type%22%3A%22normal%22%2C%22is_live%22%3A0%2C%22createdAt%22%3A%222018-11-01T13%3A50%3A02.000Z%22%2C%22updatedAt%22%3A%222018-11-01T13%3A50%3A03.000Z%22%2C%22deletedAt%22%3Anull%2C%22customerId%22%3A58159%2C%22AccountId%22%3A134%2C%22customer%22%3A%7B%22id%22%3A58159%2C%22phone%22%3A%22N%2FA%22%2C%22fullName%22%3A%22Anonymous+customer%22%2C%22customertoken%22%3Anull%2C%22email%22%3A%22cchizie26%40gmail.com%22%2C%22createdAt%22%3A%222018-10-24T16%3A58%3A21.000Z%22%2C%22updatedAt%22%3A%222018-10-24T16%3A58%3A21.000Z%22%2C%22deletedAt%22%3Anull%2C%22AccountId%22%3A134%7D%2C%22validateInstructions%22%3A%7B%22valparams%22%3A%5B%5D%2C%22instruction%22%3A%22%22%7D%2C%22validateInstruction%22%3A%22Please+dial+*901*4*1%23+to+get+your+OTP.+Enter+the+OTP+gotten+in+the+field+below%22%7D%2C%22success%22%3Atrue%7D

Some of the important responses you need to check are broken down below:

  • data.chargeResponseCode: This is the response code of the transaction, it typically tells you when a transaction is successful with a response code 00 or when the transaction requires validation 02.

  • data.chargeResponseMessage: This is the response message and it can be shown to the customer to show what needs to be done next.

  • data.authModelUsed: This shows you the authentication model used for the transaction, it can also help you decide internally what steps to take after the payment is initiated, e.g. if the value is PIN the customer would be required to submit their otp based on the message returned in chargeResponseMessage or if the value is VBVSECURECODE you would be required to load the authurl returned in the response in an iframe.

  • data.authurl: This is used for authenticating the customer in a VBVSECURECODE transaction, you need to load it in an iFrame if returned to you

  • data.paymentType: This shows you the payment instrument used i.e. if the customer used a card, account or ussd to complete the payment.

Step 4: Validate the payment.

After initiating the payment you would need to validate the transaction, validation is like authentication, essentially the customer is required to validate that he is the customer with the correct permissions to carry out the payment.

When validating transactions you need to take into account the authModelUsed and the chargeResponseMessage returned.

Scenario 1:

When

  • authModelUsed: PIN you would be asked to validate the transaction by asking the customer for the otp sent to their registered(with the bank account) mobile number.

  • chargeResponseMessage: You need to show this to the customer, it would come with the instructions needed to complete validation.

Scenario 2:

When

  • authModelUsed: VBVSECURECODE you would be asked to validate by loading the authurl returned in an iframe, the customer would see a page with their bank's branding asking them to validate the transaction, once this is completed we would call your redirect_url and append the response as query parameters.

NB: When validating a transaction with VBVSECURECODE as the auth model there would be no use of the /validatecharge endpoint.

To Validate a transaction, see how below:

Sandbox Endpoint: https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/validatecharge

Sample Request:

curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/validatecharge \
  --header 'content-type: application/json' \
  --data '{"PBFPubKey":"FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X","transaction_reference":"FLW-MOCK-ce3654ac725278c4e2b7700c3af1fab8","otp":12345}'
{
  "PBFPubKey": "FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",
  "transaction_reference": "FLW-MOCK-744927fe5cae22fddef891d1ee14ac7b", 
  "otp": "181971713"
}
  • PBFPubKey: This is your merchant public key.

  • transaction_reference: This is the flwRef returned in the Initiate payment response.

  • otp: This is the one time pin inputed by the customer.

When you validate a payment you would get a response that looks like responses below:

{
  "status": "success",
  "message": "Charge Complete",
  "data": {
    "data": {
      "responsecode": "00",
      "responsemessage": "successful"
    },
    "tx": {
      "id": 12935,
      "txRef": "Ghshsh",
      "orderRef": "URF_1501241077083_3844735",
      "flwRef": "FLW-MOCK-a71d1de9130a1e221720ef52456943e5",
      "redirectUrl": "http://127.0.0",
      "device_fingerprint": "N/A",
      "settlement_token": null,
      "cycle": "one-time",
      "amount": 1000,
      "charged_amount": 1000,
      "appfee": 0,
      "merchantfee": 0,
      "merchantbearsfee": 0,
      "chargeResponseCode": "00",
      "chargeResponseMessage": "Success-Pending-otp-validation",
      "authModelUsed": "PIN",
      "currency": "NGN",
      "IP": "::ffff:127.0.0.1",
      "narration": "FLW-PBF CARD Transaction ",
      "status": "successful",
      "vbvrespmessage": "successful",
      "authurl": "http://flw-pms-dev.eu-west-1.elasticbeanstalk.com/mockvbvpage?ref=FLW-MOCK-a71d1de9130a1e221720ef52456943e5&code=00&message=Approved. Successful",
      "vbvrespcode": "00",
      "acctvalrespmsg": null,
      "acctvalrespcode": null,
      "paymentType": "card",
      "paymentId": "2",
      "fraud_status": "ok",
      "charge_type": "normal",
      "is_live": 0,
      "createdAt": "2017-07-28T11:24:37.000Z",
      "updatedAt": "2017-07-28T13:42:20.000Z",
      "deletedAt": null,
      "customerId": 85,
      "AccountId": 134,
      "customer": {
        "id": 85,
        "phone": null,
        "fullName": "Anonymous customer",
        "customertoken": null,
        "email": "user@example.com",
        "createdAt": "2017-01-24T08:09:05.000Z",
        "updatedAt": "2017-01-24T08:09:05.000Z",
        "deletedAt": null,
        "AccountId": 134
      },
      "chargeToken": {
        "user_token": "1b7d7",
        "embed_token": "flw-t0-fcebba188b33ecc6a3dca944a638e28f-m03k"
      }
    }
  }
}

Once you get this response you need to the last step to complete the payment; Payment verification.

Implement Webhooks

It is advised all merchants use webhooks to get automatic updates on their transactions. To setup webhooks please visit the Webhooks ] section.

Step 5: Verify the payment.

After charging a card 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 chargecode returned in the response to be 00.

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://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify \
  --header 'content-type: application/json' \
  --data '{"txref":"MC-1520443531487","SECKEY":"FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"}'
<?php 

$result = array();

$postdata =  array( 
  'txref' => 'OH-AAED44',
  'SECKEY' => 'FLWSECK-bb971402072265fb156e90a3578fe5e6-X'
  );

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"https://ravesandboxapi.flutterwave.com/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://ravesandboxapi.flutterwave.com/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 = "OH-AAED44", SECKEY = "FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"};
            var client = new HttpClient();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var responseMessage = client.PostAsJsonAsync("https://ravesandboxapi.flutterwave.com/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": 161196,
    "txref": "MC-1520443531487",
    "flwref": "FLW-MOCK-6404ffd177d60ff20b6ddf92d01dab84",
    "devicefingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
    "cycle": "one-time",
    "amount": 100,
    "currency": "NGN",
    "chargedamount": 100,
    "appfee": 0,
    "merchantfee": 0,
    "merchantbearsfee": 1,
    "chargecode": "02",
    "chargemessage": "Please enter the OTP sent to your mobile number 080****** and email te**@rave**.com",
    "authmodel": "PIN",
    "ip": "::ffff:10.150.200.23",
    "narration": "CARD Transaction ",
    "status": "success-pending-validation",
    "vbvcode": "00",
    "vbvmessage": "Approved. Successful",
    "authurl": "N/A",
    "acctcode": null,
    "acctmessage": null,
    "paymenttype": "card",
    "paymentid": "878",
    "fraudstatus": "ok",
    "chargetype": "normal",
    "createdday": 1,
    "createddayname": "MONDAY",
    "createdweek": 23,
    "createdmonth": 5,
    "createdmonthname": "JUNE",
    "createdquarter": 2,
    "createdyear": 2018,
    "createdyearisleap": false,
    "createddayispublicholiday": 0,
    "createdhour": 7,
    "createdminute": 9,
    "createdpmam": "am",
    "created": "2018-06-04T07:09:42.000Z",
    "customerid": 17573,
    "custphone": "09026420185",
    "custnetworkprovider": "AIRTEL",
    "custname": "temi desola",
    "custemail": "desola.ade1@gmail.com",
    "custemailprovider": "GMAIL",
    "custcreated": "2018-02-27T09:55:51.000Z",
    "accountid": 134,
    "acctbusinessname": "Synergy Group",
    "acctcontactperson": "Desola Ade",
    "acctcountry": "NG",
    "acctbearsfeeattransactiontime": 1,
    "acctparent": 1,
    "acctvpcmerchant": "N/A",
    "acctalias": "temi",
    "acctisliveapproved": 0,
    "orderref": "URF_1528096182393_8660935",
    "paymentplan": null,
    "paymentpage": null,
    "raveref": "RV3152809618136771B0698BF4",
    "card": {
      "expirymonth": "09",
      "expiryyear": "19",
      "cardBIN": "539983",
      "last4digits": "8381",
      "brand": "GUARANTY TRUST BANK DEBITSTANDARD",
      "card_tokens": [
        {
          "embedtoken": "flw-t1nf-f22c700a552802803e89732bdf808b33-m03k",
          "shortcode": "e0371",
          "expiry": "9999999999999"
        }
      ],
      "type": "MASTERCARD",
      "life_time_token": "flw-t1nf-f22c700a552802803e89732bdf808b33-m03k"
    },
    "meta": []
  }
}

Save a card after charging so user's don't have to enter card details again

Awesome now you are done charging a card, you can always save the card for future charges using our Save a card APIs.

Suggest Edits

Handling Timeouts Via API

 

When using our APIs, and you experience a timeout, rave has built a system for you to handle this gracefully. Please see below on how to do that.

NB: Timeouts happen when a request takes to long to provide respond or you get a 5xx error calling the endpoints, typically our window before we return a timeout response is 25 Seconds

Sample response when there is a timeout:

{
  "status": "error",
  "message": "No Message",
  "data": {
    "code": "FLW_ERR",
    "message": "No Message",
    "err_tx": {
      "id": 60335,
      "chargeResponseCode": "NR",
      "chargeResponseMessage": "No Message",
      "status": "failed",
      "merchantbearsfee": 1,
      "appfee": 96.25,
      "merchantfee": 0,
      "charged_amount": 4500.00
    }
  }
}

What do you need to do when you get this ?

You need to ensure you start passing a query string use_polling=1 in your charge and validate/validatecharge endpoint like this:

https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge?use_polling=1

What happens when you pass the query params ?

You get a response that looks like this:

{
  "status": "success",
  "message": "LONG-REQ",
  "data": {
    "ping_url": "http://localhost:9329/flwv3-pug/getpaidx/api/requests/RCORE_CHREQ_529B140C1A06B38AD413",
    "message": "The request is taking too long. Please poll the ping_url to get response",
    "REQUEST": "RCORE_CHREQ_529B140C1A06B38AD413",
    "wait": 7468
  }
}

Okay I just got that response what does it allow me do ?

The response expects that you POLL the ping_url returned at your own intervals to get a success or failure response.

NB: "wait": 7468, is the advised wait time before you should start polling. Could be used in setTimeout or interval or delay time for cron based systems.

I have started polling what response should I expect ?

Once we have a success or failure response we return a response that looks like this:

{
  "status": "success",
  "message": "REQSPONSE",
  "data": {
    "id": 61,
    "reqid": "RCORE_CHREQ_17F0B216FD488F229EA0",
    "status": "completed",
    "response": "{\"code\":\"FLW_ERR\",\"message\":\"Sorry that account number is invalid. Please check and try again\",\"err_tx\":{\"id\":83146,\"flwRef\":\"ACHG-1511212902957\",\"chargeResponseCode\":\"RR\",\"chargeResponseMessage\":\"Sorry that account number is invalid. Please check and try again\",\"status\":\"failed\",\"merchantbearsfee\":0,\"appfee\":\"10.0736\",\"merchantfee\":\"0\",\"charged_amount\":\"330.07\"},\"is_err\":1}",
    "createdAt": "2017-11-20T21:21:44.000Z",
    "updatedAt": "2017-11-20T21:21:57.000Z",
    "deletedAt": null,
    "response_parsed": {
      "code": "FLW_ERR",
      "message": "Sorry that account number is invalid. Please check and try again",
      "err_tx": {
        "id": 83146,
        "flwRef": "ACHG-1511212902957",
        "chargeResponseCode": "RR",
        "chargeResponseMessage": "Sorry that account number is invalid. Please check and try again",
        "status": "failed",
        "merchantbearsfee": 0,
        "appfee": "10.0736",
        "merchantfee": "0",
        "charged_amount": "330.07"
      },
      "is_err": 1
    }
  }
}

If we still don't have a success or failure response we return a response that looks like this:

{
  "status": "success",
  "message": "REQSPONSE",
  "data": {
    "id": 61,
    "reqid": "RCORE_CHREQ_17F0B216FD488F229EA0",
    "status": "pending",
    "response": null,
    "createdAt": "2017-11-20T21:21:44.000Z",
    "updatedAt": "2017-11-20T21:21:44.000Z",
    "deletedAt": null,
    "response_parsed": null
  }
}

Why should I do this

This helps you handle timeouts gracefully. Timeouts happen in payment systems because of the deep level of connections within systems and in some cases dependence on outer systems. Imagine a timeout happens when your customer is trying to pay, we send you the response above and you can do this:

  • Cut the session and inform them you would update the status while you poll at the back.

This would mean you have prevented your customers from experiencing cases where they are debited but value not given because your system did not plan for such cases.

  • And immediately you get the status you can notify your customer, building trust and handling timeouts gracefully.

Do I still need to requery when I get the final response from the ping url ?

Yes you do.

Suggest Edits

Bank Account payments

This page shows you how to charge bank accounts on Rave.

 

Rave allows you charge local bank accounts in Nigeria, your customer's can input their account number, an OTP is sent to their phone and the charge is authorised.

The guide below would show you how to charge cards on rave using our APIs.

Pre-requisites for accepting account payments on rave.

  1. Sign-up for a test account here, and for a live account here .

  2. You can use webhooks to get notified on transactions using the webhook guide, or use the response returned by the endpoint.

  3. For all account payments you would need to implement three steps to the transactions, Initiate payment , Validate payment, Verify completed payment.

Step 1: Collect the bank details from the customer

You can use a custom form to collect the bank account details from the customer including extra information needed, see a sample of the bank account details to collect from the customer.

{
  "PBFPubKey": "FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",
  "accountbank": "232",// get the bank code from the bank list endpoint.
  "accountnumber": "0061333471",
  "currency": "NGN",
  "payment_type": "account",
  "country": "NG",
  "amount": "10",
  "email": "desola.ade1@gmail.com",
  "passcode": "09101989",//customer Date of birth this is required for Zenith bank account payment.
  "bvn": "12345678901",
  "phonenumber": "0902620185",
  "firstname": "temi",
  "lastname": "desola",
  "IP": "355426087298442",
  "txRef": "MC-0292920", // merchant unique reference
  "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}
{
  "PBFPubKey": "FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",
  "accountbank": "232",// get the bank code from the bank list endpoint.
  "accountnumber": "0061333471",
  "currency": "NGN",
  "payment_type": "account",
  "country": "NG",
  "amount": "10",
  "email": "desola.ade1@gmail.com",
  "passcode": "09101989",//customer Date of birth this is required for Zenith bank account payment.
  "phonenumber": "0902620185",
  "firstname": "temi",
  "lastname": "desola",
  "subaccount": [
    {
      "id": "RS_D87A9EE339AE28BFA2AE86041C6DE70E",
      "transaction_split_ratio": "2"
    },
    {
      "id": "RS_344DD49DB5D471EF565C897ECD67CD95",
      "transaction_split_ratio": "3"
    },
    {
      "id": "RS_839AC07C3450A65004A0E11B83E22CA9",
      "transaction_split_ratio": "5"
    }
  ],
  "IP": "355426087298442",
  "txRef": "MC-0292920", // merchant unique reference
  "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}
  • passcode: This is required for Zenith bank account payments, you are required to collect the customer's date of birth and pass it in this format DDMMYYYY.

Parameter Description

Parameters
Required
Description

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.

accountbank

True

This represents the bank to be debited. To get a list of banks in Nigeria, call the List of Banks API

accountnumber

True

This is the account number of the customer associated with a valid bank account.

currency

False
(Defaults to NGN)

This is the specified currency to charge the account in.

country

False
(Defaults to NG)

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 from the account it is passed as - (“amount”:10).

email

True

This is the email address of the customer.

passcode

False
(Required for Zenith bank)

This is required for Zenith bank account payments, you are required to collect the customer's date of birth and pass it in this format DDMMYYYY.

phonenumber

True

This is the phone number of the customer.

firstname

False

This is the first name of the account holder or the customer.

lastname

False

This is the last name of the account holder or the customer.

IP

True

IP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction.

txRef

True

This is the unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction.

payment_type

True
(Expected value: account)

This specifies that the payment method being used is for account payments

bvn

True
(Required for UBA account payment option)

This is the customer's bvn number.

device_fingerprint

False

This is the fingerpringt for the device being used. It can be generated using a library on whatever platform is being used.

redirect_url

false
(Required for GTB & First bank account payments.)

This is a url you provide, we redirect to it after the customer completes payment and append the response to it as query parameters.

Get a list of available banks to show to your customers

Make use of the List of Banks API to show your customers what banks are available for this payment method.

Step 2: Encrypt your request

To see how to encrypt using any of our encryption function copy the sample request below and visit the Rave Encryption section.

Step 3: Initiate your payment.

After encryption the next step is to initiate your payment using the encrypted string by sending a request to the /charge endpoint. See how to do that below.

Sandbox Endpoint: https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge

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

Sample Request:

curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge \
  --data '{"PBFPubKey":"FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X","client":"VodhvFFsni0CBeieHPq9HTuG5lbNPgmD5rbEw6Uxb0TD9eD9B3VM5uZ1B5lC3thQMbPypNBCAYwSybPo9pNJUIXSNhgdzsqG8eEggSJhWYv+4HToZxWamqsWrUqNUgvaCws3iPTAJOy0fPuHI53dSaMbq/EeHnGrdosfSuAGvm/L6joVVb6e7vyZ4bJl9bJyT73INhSN5glUAvHElup+AOYVoyQiQ1gN7PmW6I0DrUiiC1GSq87zk8rt7Xv31OBja7Ib+znEHBfcI/Ii36HbQF2MunOy2oAteyWIbr3cTyUuyERroRKL769f3NMxUQw5iQ39LU0KgmP2XvgMQONcuiPJWlJ9LzG8ngqCZNFGQ5yIvYrUiiufPowa7A8sAgaoIQQMt0OWGijfpJ4CeAA9/s1Bv03ZhhX2","alg":"3DES-24"}'
{
  "PBFPubKey": "FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",
  "client": "VodhvFFsni0CBeieHPq9HTuG5lbNPgmD5rbEw6Uxb0TD9eD9B3VM5uZ1B5lC3thQMbPypNBCAYwSybPo9pNJUIXSNhgdzsqG8eEggSJhWYv+4HToZxWamqsWrUqNUgvaCws3iPTAJOy0fPuHI53dSaMbq/EeHnGrdosfSuAGvm/L6joVVb6e7vyZ4bJl9bJyT73INhSN5glUAvHElup+AOYVoyQiQ1gN7PmW6I0DrUiiC1GSq87zk8rt7Xv31OBja7Ib+znEHBfcI/Ii36HbQF2MunOy2oAteyWIbr3cTyUuyERroRKL769f3NMxUQw5iQ39LU0KgmP2XvgMQONcuiPJWlJ9LzG8ngqCZNFGQ5yIvYrUiiufPowa7A8sAgaoIQQMt0OWGijfpJ4CeAA9/s1Bv03ZhhX2",
  "alg": "3DES-24"
}
  • client: This is the encrypted request parameters.

  • PBFPubKey: This is your merchant public key.

  • alg: must always be passed as 3DES-24

When you initiate the payment you would get a response that looks like responses below:

{
  "status": "success",
  "message": "V-COMP",
  "data": {
    "id": 13644,
    "txRef": "MC-7663-YU",
    "orderRef": null,
    "flwRef": "ACHG-1501266637278",
    "redirectUrl": "http://127.0.0",
    "device_fingerprint": "N/A",
    "settlement_token": null,
    "cycle": "one-time",
    "amount": 1000,
    "charged_amount": 1000,
    "appfee": 0,
    "merchantfee": 0,
    "merchantbearsfee": 0,
    "chargeResponseCode": "02",
    "chargeResponseMessage": "Pending OTP validation",
    "authModelUsed": "AUTH",
    "currency": "NGN",
    "IP": "::ffff:127.0.0.1",
    "narration": "Synergy Group",
    "status": "success-pending-validation",
    "vbvrespmessage": "N/A",
    "authurl": "NO-URL",
    "vbvrespcode": "N/A",
    "acctvalrespmsg": null,
    "acctvalrespcode": null,
    "paymentType": "account",
    "paymentId": "16",
    "fraud_status": "ok",
    "charge_type": "normal",
    "is_live": 0,
    "createdAt": "2017-07-28T18:30:37.000Z",
    "updatedAt": "2017-07-28T18:30:48.000Z",
    "deletedAt": null,
    "customerId": 85,
    "AccountId": 134,
    "customer": {
      "id": 85,
      "phone": null,
      "fullName": "Anonymous customer",
      "customertoken": null,
      "email": "user@example.com",
      "createdAt": "2017-01-24T08:09:05.000Z",
      "updatedAt": "2017-01-24T08:09:05.000Z",
      "deletedAt": null,
      "AccountId": 134
    },
    "validateInstructions": {
      "valparams": [
        "OTP"
      ],
      "instruction": "Please validate with the OTP sent to your mobile or email"
    }
  }
}

Some of the important responses you need to check are broken down below:

  • data.status: The status object inside the data object is the right status to check for, possible values are successful, failed, pending, success-pending-validation and pending-validation.

  • data.chargeResponseCode: This is the response code of the transaction, it typically tells you when a transaction is successful with a response code 00 or when the transaction requires validation 02.

  • data.validateInstructions: This object contains the instructions you are meant to show to the customer so they know the next step to take, it typically tells them how to validate the transaction.

  • data.paymentType: This shows you the payment instrument used i.e. if the customer used a card, account or ussd to complete the payment.

Collecting account payments using GTB & First Bank account payment.

When using GTB or First Bank for account payment collection, you would need to redirect the user to a new window for them to log in to their internet banking profile and complete the transaction.

After the transaction is completed we would redirect back to the redirect_url you provided in the initial charge request. Below is a sample of the initial charge request to send for GTB account payments.

When using the GTB account payment option the amount passed needs to be greater than NGN 100.

{
  "PBFPubKey": "FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",
  "accountbank": "058",// get the bank code from the bank list endpoint.
  "accountnumber": "0000000",
  "currency": "NGN",
  "payment_type": "account",
  "country": "NG",
  "amount": "100", // amount must be greater than NGN100
  "email": "desola.ade1@gmail.com",
  "phonenumber": "0902620185",
  "firstname": "temi",
  "lastname": "desola",
  "IP": "355426087298442",
  "redirect_url": "https://rave-webhook.herokuapp.com/receivepayment",
  "txRef": "MC-0292920", // merchant unique reference
  "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}

When using GTB or First Bank account payment the validate payment step happens on the Internet banking page of the customer.

After the initial charge request, the response would contain an authUrl, this is the link to redirect the customer too so they can complete their payment.

Step 4: Validate the payment.

After initiating the payment you would need to validate the transaction, validation is like authentication, essentially the customer is required to validate that he is the customer with the correct permissions to carry out the payment.

To Validate a transaction, see how below:

Sandbox Endpoint: https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/validate

Sample Request:

url --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/validate \
  --data '{"PBFPubKey":"FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X","transactionreference":"ACHG-1501266637278","otp":"12345"}'
{
  "PBFPubKey": "FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",
  "transactionreference": "ACHG-1501266637278", 
  "otp": "12345"
}
  • PBFPubKey: This is your merchant public key.

  • transactionreference: This is the flwRef returned in the Initiate payment response.

  • otp: This is the one time pin inputed by the customer.

When you validate a payment you would get a response that looks like response below:

{
  "status": "success",
  "message": "Charge Complete",
  "data": {
    "id": 13644,
    "txRef": "MC-7663-YU",
    "orderRef": null,
    "flwRef": "ACHG-1501266637278",
    "redirectUrl": "http://127.0.0",
    "device_fingerprint": "N/A",
    "settlement_token": null,
    "cycle": "one-time",
    "amount": 1000,
    "charged_amount": 1000,
    "appfee": 0,
    "merchantfee": 0,
    "merchantbearsfee": 0,
    "chargeResponseCode": "00",
    "chargeResponseMessage": "Pending OTP validation",
    "authModelUsed": "AUTH",
    "currency": "NGN",
    "IP": "::ffff:127.0.0.1",
    "narration": "Synergy Group",
    "status": "successful",
    "vbvrespmessage": "N/A",
    "authurl": "NO-URL",
    "vbvrespcode": "N/A",
    "acctvalrespmsg": "Approved Or Completed Successfully",
    "acctvalrespcode": "00",
    "paymentType": "account",
    "paymentId": "16",
    "fraud_status": "ok",
    "charge_type": "normal",
    "is_live": 0,
    "createdAt": "2017-07-28T18:30:37.000Z",
    "updatedAt": "2017-07-28T18:33:10.000Z",
    "deletedAt": null,
    "customerId": 85,
    "AccountId": 134,
    "customer": {
      "id": 85,
      "phone": null,
      "fullName": "Anonymous customer",
      "customertoken": null,
      "email": "user@example.com",
      "createdAt": "2017-01-24T08:09:05.000Z",
      "updatedAt": "2017-01-24T08:09:05.000Z",
      "deletedAt": null,
      "AccountId": 134
    }
  }
}
{
  "status": "error",
  "message": "Transaction already complete",
  "data": {
    "code": "SYSERR",
    "message": "Transaction already complete"
  }
}
{
  "status": "error",
  "message": "otp is required",
  "data": {}
}

Implement Webhooks

It is advised all merchants use webhooks to get automatic updates on their transactions. To set up webhooks please visit the Webhooks section.

Step 5: Verify the payment.

After charging an account 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 chargecode returned in the response to be 00.

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://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify \
  --header 'content-type: application/json' \
  --data '{"txref":"MC-09182829","SECKEY":"FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"}'
<?php 

$result = array();

$postdata =  array( 
  'txref' => 'MC-09182829',
  'SECKEY' => 'FLWSECK-bb971402072265fb156e90a3578fe5e6-X'
  );

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"https://ravesandboxapi.flutterwave.com/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://ravesandboxapi.flutterwave.com/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 = "OH-AAED44", SECKEY = "FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"};
            var client = new HttpClient();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var responseMessage = client.PostAsJsonAsync("https://ravesandboxapi.flutterwave.com/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": 144534,
    "txref": "MC-09182829",
    "flwref": "ACHG-1525942912853",
    "devicefingerprint": "N/A",
    "cycle": "one-time",
    "amount": 100,
    "currency": "NGN",
    "chargedamount": 100,
    "appfee": 0,
    "merchantfee": 0,
    "merchantbearsfee": 1,
    "chargecode": "02",
    "chargemessage": "Pending OTP validation",
    "authmodel": "AUTH",
    "ip": "::ffff:10.102.148.117",
    "narration": "Synergy Group",
    "status": "success-pending-validation",
    "vbvcode": "N/A",
    "vbvmessage": "N/A",
    "authurl": "NO-URL",
    "acctcode": null,
    "acctmessage": null,
    "paymenttype": "account",
    "paymentid": "2",
    "fraudstatus": "ok",
    "chargetype": "normal",
    "createdday": 4,
    "createddayname": "THURSDAY",
    "createdweek": 19,
    "createdmonth": 4,
    "createdmonthname": "MAY",
    "createdquarter": 2,
    "createdyear": 2018,
    "createdyearisleap": false,
    "createddayispublicholiday": 0,
    "createdhour": 9,
    "createdminute": 1,
    "createdpmam": "am",
    "created": "2018-05-10T09:01:52.000Z",
    "customerid": 24728,
    "custphone": "09090838390",
    "custnetworkprovider": "ETISALAT",
    "custname": "yemi alade",
    "custemail": "user@example.com",
    "custemailprovider": "COMPANY EMAIL",
    "custcreated": "2018-04-21T11:37:43.000Z",
    "accountid": 134,
    "acctbusinessname": "Synergy Group",
    "acctcontactperson": "Desola Ade",
    "acctcountry": "NG",
    "acctbearsfeeattransactiontime": 1,
    "acctparent": 1,
    "acctvpcmerchant": "N/A",
    "acctalias": "temi",
    "acctisliveapproved": 0,
    "orderref": "URF_1525942912124_3844735",
    "paymentplan": null,
    "paymentpage": null,
    "raveref": "RV31525942911654CA881BAE82",
    "account": {
      "id": 2,
      "account_number": "0690000031",
      "account_bank": "044",
      "first_name": "NO-NAME",
      "last_name": "NO-LNAME",
      "account_is_blacklisted": 0,
      "createdAt": "2016-12-31T04:09:24.000Z",
      "updatedAt": "2018-06-04T09:20:10.000Z",
      "deletedAt": null,
      "account_token": {
        "token": "flw-t0e1bb79f967612fc1-k3n-mock"
      }
    },
    "meta": []
  }
}

Save an account after charging so user's don't have to enter account details again

Accounts that can be saved

Only Access bank and Sterling bank accounts can be saved, support for more banks are coming soon.

Awesome now you are done charging an account, you can always save the account for future charges using our Save account APIs.

Suggest Edits

USSD Payments

This page describes how to accept payments using 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.

Rave currently has USSD options available for Zenith bank and Guaranty Trust bank customers.

The guide below would show you how to charge your customers with USSD on rave using our APIs.

Testing USSD Payments Successfuly

USSD payments can only be tested using Live API Keys and credentials, you would also need a live account and phone number enabled for USSD payments by the bank.

Pre-requisites for accepting USSD payments on rave.

  1. Sign-up for a live account here .

  2. You can use Webhooks to get notified on transactions, or use the response returned by the endpoint.

  3. For all USSD payments you would need to implement three steps to the transactions, Initiate payment , Receive webhook response, Verify completed payment.

Step 1: Encrypt your request

To see how to encrypt using any of our encryption function copy the sample request below and visit the Rave Encryption section.

{
  "PBFPubKey": "FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",
  "accountbank": "058",
  "accountnumber": "00000",// always use this default for GTB
  "currency": "NGN",
  "payment_type": "ussd",
  "country": "NG",
  "amount": "10",
  "email": "desola.ade1@gmail.com",
  "phonenumber": "0902620185",
  "firstname": "temi",
  "lastname": "desola",
  "IP": "355426087298442",
  "txRef": "MC-" + Date.now(), // merchant unique reference
  "orderRef": "MC_" + Date.now(),
  "is_ussd": "true",
  "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}
{
  "PBFPubKey": "FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",
  "accountbank": "057",
  "accountnumber": "0691008392",//collect the customers account number for Zenith
  "currency": "NGN",
  "payment_type": "ussd",
  "country": "NG",
  "amount": "10",
  "email": "desola.ade1@gmail.com",
  "phonenumber": "0902620185", 
  "firstname": "temi",
  "lastname": "desola",
  "IP": "355426087298442",
  "txRef": "MC-" + Date.now(), // merchant unique reference
  "orderRef": "MC_" + Date.now(),
  "is_ussd": "true",
  "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}

Parameter Description

Parameter
Required
Description

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.

accountbank

True

This represents the bank to be debited. To get a list of banks in Nigeria, call the List of Banks API

accountnumber

True
(For GTB expected value is: 00000, For Zenith bank pass the customer's bank account number)

This is the account number of the customer associated with a valid bank account.

When a customer is paying with Zenith bank, they need to be registered for the USSD service.

currency

True
USSD can only be charged in NGN

This is the specified currency to charge the account in.

country

False
Defaults to NGN

This is the pair country for the transaction with respect to the currency.

amount

True
minimum amount is N10

This is the amount to be charged from the account it is passed as - (“amount”:10).

email

True

This is the email address of the customer.

phonenumber

True

This is the phone number of the customer, linked with their bank account and used for USSD registration at the bank.

firstname

False

This is the first name of the account holder or the customer.

lastname

False

This is the last name of the account holder or the customer.

IP

True

IP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction.

txRef

True

This is the unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction.

orderRef

True

This is the unique order reference for the USSD transaction.

is_ussd

True
must be a truthy value e.g. 1 or true

This indicates that a USSD transaction is being carried out.

device_fingerprint

False

This is the fingerpringt for the device being used. It can be generated using a library on whatever platform is being used.

Step 2: Initiate your payment.

After encryption the next step is to initiate your payment using the encrypted string by sending a request to the /charge endpoint. See how to do that below.

Sandbox Endpoint: https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge

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

Sample Request:

curl --request POST \
  --url https://api.ravepay.co/flwv3-pug/getpaidx/api/charge \
  --header 'content-type: application/json' \
  --data '{"client":"Hqj0LP5HNRNIthnOLqYjBwTIPizUp4PdiP7ZAKwqVz2RYtx5I0tcWurEVI40yXW9vyXa6Xxr9vhbmsTD8wE5B5ETm8i0H3s9zBFUiwosSmVsm+dh/NvVYzBIKaTvYW8qfmycnhf0WqFGCvNe95ArrW4p+QYDy7IpVui9f892l1IurQMyRZAZlYoW2L/6iX3WB0FYmm6bA8RFfqRoVNEJuI7STziZE/DdjjTKGwbcQO+QnnBv8Pp0rWwRNEwrb4RHYogfR06WsoGW0Vpl3EGlzMC0N/VudyGMFzkIrYdNE3DXjY9BcyXy1JYVU94lbYJXIouYfO/t5X2rCS6z0Ded8P2UIF4AJPFYDdytPJxwOUSt7DoU03S4AE3cl/9mdfsx0Di42KVIDfUwDTTf5cdl3i5S+V6Gcb8Mi8VnUyINsiW7FqnifgGSeAqxrV3+gaVS0YbLUWrRMhqIAE8pvdpLRq2SnO6wzhRVOO8JiMvCtGgNj7tSkgn/AJ7Lt+RAhdovMYmwhr4yXfKm7QCwkq5bP0mcaH/L+X7n6vS3k+XS4usbPgBdTuz3+Tsl/qhhDMRQDhOWYEgFSIOMZ9tBFiRD3VXiKRYQzq68QSG9VYbT8K2/W8NVH+1igCIEJxjsFHmcqkTmxeoxr7s=","PBFPubKey":"FLWPUBK-4e581ebf8372cd691203b27227e2e3b8-X","alg":"3DES-24"}'
{
  "client": "Hqj0LP5HNRNIthnOLqYjBwTIPizUp4PdiP7ZAKwqVz2RYtx5I0tcWurEVI40yXW9vyXa6Xxr9vhbmsTD8wE5B5ETm8i0H3s9zBFUiwosSmVsm+dh/NvVYzBIKaTvYW8qfmycnhf0WqFGCvNe95ArrW4p+QYDy7IpVui9f892l1IurQMyRZAZlYoW2L/6iX3WB0FYmm6bA8RFfqRoVNEJuI7STziZE/DdjjTKGwbcQO+QnnBv8Pp0rWwRNEwrb4RHYogfR06WsoGW0Vpl3EGlzMC0N/VudyGMFzkIrYdNE3DXjY9BcyXy1JYVU94lbYJXIouYfO/t5X2rCS6z0Ded8P2UIF4AJPFYDdytPJxwOUSt7DoU03S4AE3cl/9mdfsx0Di42KVIDfUwDTTf5cdl3i5S+V6Gcb8Mi8VnUyINsiW7FqnifgGSeAqxrV3+gaVS0YbLUWrRMhqIAE8pvdpLRq2SnO6wzhRVOO8JiMvCtGgNj7tSkgn/AJ7Lt+RAhdovMYmwhr4yXfKm7QCwkq5bP0mcaH/L+X7n6vS3k+XS4usbPgBdTuz3+Tsl/qhhDMRQDhOWYEgFSIOMZ9tBFiRD3VXiKRYQzq68QSG9VYbT8K2/W8NVH+1igCIEJxjsFHmcqkTmxeoxr7s=",
  "PBFPubKey": "FLWPUBK-4e581ebf8372cd691203b27227e2e3b8-X",
  "alg": "3DES-24"
}
  • client: This is the encrypted request parameters.

  • PBFPubKey: This is your merchant public key.

  • alg: must always be passed as 3DES-24

When you initiate the payment you would get a response that looks like responses below:

{
  "status": "success",
  "message": "V-COMP",
  "data": {
    "id": 872026,
    "txRef": "MC-1522657714136",
    "orderRef": "MC_1522657714136",
    "flwRef": "19730",
    "redirectUrl": "http://127.0.0",
    "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
    "settlement_token": null,
    "cycle": "one-time",
    "amount": 10,
    "charged_amount": 10,
    "appfee": 0.125,
    "merchantfee": 0,
    "merchantbearsfee": 1,
    "chargeResponseCode": "02",
    "raveRef": null,
    "chargeResponseMessage": "Pending payment validation",
    "authModelUsed": "USSD",
    "currency": "NGN",
    "IP": "::ffff:10.165.112.254",
    "narration": "Raver",
    "status": "success-pending-validation",
    "vbvrespmessage": "N/A",
    "authurl": "NO-URL",
    "vbvrespcode": "N/A",
    "acctvalrespmsg": null,
    "acctvalrespcode": null,
    "paymentType": "ussd",
    "paymentPlan": null,
    "paymentPage": null,
    "paymentId": "10479",
    "fraud_status": "ok",
    "charge_type": "normal",
    "is_live": 0,
    "createdAt": "2018-04-02T08:30:11.000Z",
    "updatedAt": "2018-04-02T08:30:12.000Z",
    "deletedAt": null,
    "customerId": 556576,
    "AccountId": 48,
    "customer": {
      "id": 556576,
      "phone": "09026420185",
      "fullName": "temi desola",
      "customertoken": null,
      "email": "user@example.com",
      "createdAt": "2018-04-02T08:28:00.000Z",
      "updatedAt": "2018-04-02T08:28:00.000Z",
      "deletedAt": null,
      "AccountId": 48
    },
    "validateInstructions": "Pending payment validation"
  }
}
{
  "status": "success",
  "message": "V-COMP",
  "data": {
    "id": 875722,
    "txRef": "rave-pos-147999568555",
    "orderRef": "129574",
    "flwRef": "19846",
    "redirectUrl": "http://127.0.0",
    "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
    "settlement_token": null,
    "cycle": "one-time",
    "amount": 10,
    "charged_amount": 10.13,
    "appfee": 0.125,
    "merchantfee": 0,
    "merchantbearsfee": 0,
    "chargeResponseCode": "02",
    "raveRef": null,
    "chargeResponseMessage": "Kindly dial *966*6*035883*orderid# on your mobile to complete the transaction",
    "authModelUsed": "USSD",
    "currency": "NGN",
    "IP": "197.149.95.62",
    "narration": "Raver",
    "status": "success-pending-validation",
    "vbvrespmessage": "N/A",
    "authurl": "NO-URL",
    "vbvrespcode": "N/A",
    "acctvalrespmsg": null,
    "acctvalrespcode": null,
    "paymentType": "ussd",
    "paymentPlan": null,
    "paymentPage": null,
    "paymentId": "17822",
    "fraud_status": "ok",
    "charge_type": "normal",
    "is_live": 0,
    "createdAt": "2018-04-02T18:47:13.000Z",
    "updatedAt": "2018-04-02T18:47:15.000Z",
    "deletedAt": null,
    "customerId": 555880,
    "AccountId": 48,
    "customer": {
      "id": 555880,
      "phone": "09092928282",
      "fullName": "Anonymous Customer",
      "customertoken": null,
      "email": "salesmode@ravepay.co",
      "createdAt": "2018-04-01T23:31:32.000Z",
      "updatedAt": "2018-04-01T23:31:32.000Z",
      "deletedAt": null,
      "AccountId": 48
    },
    "validateInstructions": "Kindly dial *966*6*035883*129574# on your mobile to complete the transaction"
  }
}

Some of the important responses you care about are broken down below:

  • chargeResponseCode: This is the response code of the transaction, it typically tells you when a transaction is successful with a response code 00 or when the transaction requires validation 02.

  • validateInstructions: This object contains the instructions you are meant to show to the customer so they know the next step to take, it typically tells them how to validate the transaction.

  • paymentType: This shows you the payment instrument used i.e. if the customer used a card, account or ussd to complete the payment.

  • charged_amount: This is the amount that would be displayed as part of the USSD string, you would need to round up this amount, e.g. if your charged_amount is returned as 20.29 round it up to 21

  • flwRef: You are to show this to the customer, as a Transaction reference, when they dial the USSD code on their screen, the prompt would ask them to input this reference.

  • validateInstructions: Display what is returned here to the customer when customer is paying with Zenith USSD.

  • *737*50*charged_amount*159#: When using GTB You need to display this as the USSD string the customer would dial. See a sample image below;

The `flwRef` is shown as Transaction reference.

The flwRef is shown as Transaction reference.

Step 3: Receive webhook response

Rave allows you configure Webhooks so you can get notified when actions like payment have been completed, it is a seamless way to handle offline transactions. Webhooks are returned in application/x-www-form-urlencoded, but you can configure to receive your responses in JSON .

Visit the Webhooks section to see more details on implementing and receiving webhooks.

id=873293&txRef=rave-pos-380128206115&flwRef=19778&orderRef=568192&paymentPlan=&createdAt=2018-04-02T11%3A38%3A31.000Z&amount=10&charged_amount=10.13&status=successful&IP=197.149.95.62&currency=NGN&customer%5Bid%5D=557049&customer%5Bphone%5D=08166018693&customer%5BfullName%5D=Anonymous%20Customer&customer%5Bcustomertoken%5D=&customer%5Bemail%5D=salesmode%40ravepay.co&customer%5BcreatedAt%5D=2018-04-02T10%3A45%3A31.000Z&customer%5BupdatedAt%5D=2018-04-02T10%3A45%3A31.000Z&customer%5BdeletedAt%5D=&customer%5BAccountId%5D=48&entity%5Baccount_number%5D=1938833&entity%5Bfirst_name%5D=Joshua&entity%5Blast_name%5D=Akeredolu

You can convert this response to JSON once received to better make use of it.

Step 4: Verify the payment.

After charging a customer via USSD successfully, you need to verify that the payment was successful with Rave before giving value to your customer.

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 chargecode returned in the response to be 00.

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://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify \
  --header 'content-type: application/json' \
  --data '{"txref":"<Your live reference>","SECKEY":"<Your live secret key>"}'

When you successfully verify a completed payment see sample response below:

{
    "status": "success",
    "message": "Tx Fetched",
    "data": {
        "txid": 873293,
        "txref": "rave-pos-380128206115",
        "flwref": "19778",
        "devicefingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
        "cycle": "one-time",
        "amount": 10,
        "currency": "NGN",
        "chargedamount": 10.13,
        "appfee": 0.125,
        "merchantfee": 0,
        "merchantbearsfee": 0,
        "chargecode": "00",
        "chargemessage": "Pending payment validation",
        "authmodel": "USSD",
        "ip": "197.149.95.62",
        "narration": "Raver",
        "status": "successful",
        "vbvcode": "N/A",
        "vbvmessage": "N/A",
        "authurl": "NO-URL",
        "acctcode": "00",
        "acctmessage": "Successful",
        "paymenttype": "ussd",
        "paymentid": "9584",
        "fraudstatus": "ok",
        "chargetype": "normal",
        "createdday": 1,
        "createddayname": "MONDAY",
        "createdweek": 14,
        "createdmonth": 3,
        "createdmonthname": "APRIL",
        "createdquarter": 2,
        "createdyear": 2018,
        "createdyearisleap": false,
        "createddayispublicholiday": 0,
        "createdhour": 11,
        "createdminute": 38,
        "createdpmam": "am",
        "created": "2018-04-02T11:38:31.000Z",
        "customerid": 557049,
        "custphone": "08166009393",
        "custnetworkprovider": "MTN",
        "custname": "Anonymous Customer",
        "custemail": "salesmode@ravepay.co",
        "custemailprovider": "COMPANY EMAIL",
        "custcreated": "2018-04-02T10:45:31.000Z",
        "accountid": 48,
        "acctbusinessname": "Raver",
        "acctcontactperson": "Desola Adesina",
        "acctcountry": "NG",
        "acctbearsfeeattransactiontime": 0,
        "acctparent": 1,
        "acctvpcmerchant": "N/A",
        "acctalias": "raver",
        "acctisliveapproved": 0,
        "orderref": "568192",
        "paymentplan": null,
        "paymentpage": null,
        "raveref": null,
        "amountsettledforthistransaction": 10.005
    }
}
Suggest Edits

Ghana mobile money

This page describes how to collect payments via Ghana mobile money.

 

Rave currently allows merchants use two (2) payment methods in Ghana (card and mobilemoney).

We would show you how to accept payments using the mobile money method.

Pre-requisites for accepting payments in Ghana.

  1. Sign-up for a test account here, and for a live account here .

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

  3. When trying to accept payment on a website you can use our inline js method and pass currency as GHS and country as GH, once you do this, the options for card and mobile money would come up.

  4. 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.

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

Step 1: Encrypt your payload.

Vodafone.

You need to display instructions for your customers to dial a USSD short code to retrieve their voucher code, they would need to pass in the voucher code before they click on pay. See a example here.

{
  "PBFPubKey": "FLWPUBK-4e581ebf8372cd691203b27227e2e3b8-X",
  "currency": "GHS",
  "payment_type": "mobilemoneygh",
  "country": "GH",
  "amount": "50",
  "email": "user@example.com",
  "phonenumber": "054709929220",
  "network": "MTN",
  "firstname": "temi",
  "lastname": "desola",
  "voucher": "128373", // only needed for Vodafone users.
  "IP": "355426087298442",
  "txRef": "MC-" + Date.now(),
  "orderRef": "MC_" + Date.now(),
  "is_mobile_money_gh": 1,
  "redirect_url": "https://rave-webhook.herokuapp.com/receivepayment",
  "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}

Parameter Definition

Parameter
Required
Description

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.

currency

True
(expected value: GHS)

This is the specified currency to charge in.

country

True

(expected value: GH)

This is the pair country for the transaction with respect to the currency. See a list of Multicurrency support here Multicurrency Payments ]

payment-type

True

(expected value: mobilemoneygh)

This specifies that the payment method being used is for mobile money payments

amount

True

This is the amount to be charged it is passed as - (“amount”:10).

Minimum amount for GHS is 1 GHS

network

True

(possible values: MTN, VODAFONE, TIGO)

This is the customer's mobile money network provider.

email

True

This is the email address of the customer.

phonenumber

True

This is the phone number linked to the customer's mobile money account.

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

True

IP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction.

txRef

True

This is a unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction.

orderRef

True

Unique ref for the mobilemoney transaction to be provided by the merchant.

voucher

True (only for Vodafone cash)

This is the voucher code generated by the customer. It is meant to be passed in the initial charge request.

is_mobile_money_gh

True

(expected value: 1)

This identifies that a mobile money transaction is being carried out.

device_fingerprint

False

This 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 payviamobilemoneygh(){ // set up a function to test card payment.
    
    error_reporting(E_ALL);
    ini_set('display_errors',1);
    
    $data = array('PBFPubKey' => 'FLWPUBK-e634d14d9ded04eaf05d5b63a0a06d2f-X',
    'currency' => 'GHS',
    'country' => 'GH',
    'payment_type' => 'mobilemoneygh',
    'amount' => '30',
    'phonenumber' => '054709929300',
    'firstname' => 'Eddy',
    'lastname' => 'Kwame',
    'network' => 'MTN',
    'email' => 'tester@flutter.co',
    'IP' => '103.238.105.185',
    'txRef' => 'MXX-ASC-4578',
    'orderRef' => 'MXX-ASC-90929',
    'is_mobile_money_gh' => 1,
    '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);
}

payviamobilemoneygh();

Step 2: Call the charge endpoint with your encrypted data

Sandbox Endpoint: https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge

Live endpoint: 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);
}

payviamobilemoneygh();

What happens after I call the charge endpoint?

When you call the charge endpoint you would need to display a prompt to the user depending on the mobile network in use, we give a detailed descript on what to show for each network below:

MTN

  1. Dial *170#
  2. Choose option: 7) Wallet
  3. Choose option: 3) My Approvals
  4. Enter your MOMO pin to retrieve your pending approval list
  5. Choose a pending transaction
  6. Choose option 1 to approve
  7. Tap button to continue

Tigo

  1. Dial 5015# to approve your transaction.
  2. Select the transaction to approve and click on send.
  3. Select YES to confirm your payment.

Vodafone

  1. Dial *110# to generate your transaction voucher.
  2. Select option) 6 to generate the voucher.
  3. Enter your PIN in next prompt.
  4. Input the voucher generated in the payment modal.

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

Handling Mobile money transactions

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

However, we advise merchants implement manual re-query into the system for edge cases and to deal with any failure point that might occur.

An example is when a customer says they have been debited for a transaction but the transaction returned an error or timeout from the processor. With a manual re-query, you can get the updated status at any time and give value to the customer.

{
  "id": 126090,
  "txRef": "rave-checkout-1523183226335",
  "flwRef": "flwm3s4m0c1523183355860",
  "orderRef": null,
  "paymentPlan": null,
  "createdAt": "2018-04-08T10:29:15.000Z",
  "amount": 2000,
  "charged_amount": 2000,
  "status": "successful",
  "IP": "197.149.95.62",
  "currency": "GHS",
  "customer": {
    "id": 22823,
    "phone": "0578922930",
    "fullName": "Anonymous Customer",
    "customertoken": null,
    "email": "user@example.com",
    "createdAt": "2018-04-08T10:28:01.000Z",
    "updatedAt": "2018-04-08T10:28:01.000Z",
    "deletedAt": null,
    "AccountId": 134
  },
  "entity": {
    "id": "NO-ENTITY"
  }
}
{
  "status": "success",
  "message": "V-COMP",
  "data": {
    "id": 690055,
    "txRef": "MC-1520528216374",
    "orderRef": null,
    "flwRef": "FLWMM1522085245161",
    "redirectUrl": "http://127.0.0",
    "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
    "settlement_token": null,
    "cycle": "one-time",
    "amount": 3,
    "charged_amount": 3,
    "appfee": 0.037500000000000006,
    "merchantfee": 0,
    "merchantbearsfee": 1,
    "chargeResponseCode": "02",
    "raveRef": null,
    "chargeResponseMessage": "pending charge processing",
    "authModelUsed": "MOBILEMONEY",
    "currency": "GHS",
    "IP": "::ffff:10.5.186.67",
    "narration": "Raver",
    "status": "success-pending-validation",
    "vbvrespmessage": "N/A",
    "authurl": "NO-URL",
    "vbvrespcode": "N/A",
    "acctvalrespmsg": null,
    "acctvalrespcode": null,
    "paymentType": "mobilemoneygh",
    "paymentPlan": null,
    "paymentPage": null,
    "paymentId": "N/A",
    "fraud_status": "ok",
    "charge_type": "normal",
    "is_live": 0,
    "createdAt": "2018-03-08T16:57:23.000Z",
    "updatedAt": "2018-03-08T16:57:26.000Z",
    "deletedAt": null,
    "customerId": 437599,
    "AccountId": 48,
    "customer": {
      "id": 437599,
      "phone": "5475309092",
      "fullName": "temi desola",
      "customertoken": null,
      "email": "user@example.com",
      "createdAt": "2018-03-08T16:57:23.000Z",
      "updatedAt": "2018-03-08T16:57:23.000Z",
      "deletedAt": null,
      "AccountId": 48
    },
    "validateInstructions": "pending charge processing"
  }
}

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 chargecode returned in the response to be 00.

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://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify \
  --header 'content-type: application/json' \
  --data '{"txref":"rave-checkout-1523183226335","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://ravesandboxapi.flutterwave.com/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://ravesandboxapi.flutterwave.com/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://ravesandboxapi.flutterwave.com/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": 126088,
      "txref": "rave-checkout-1523183226335",
      "flwref": "flwm3s4m0c1523183281767",
      "devicefingerprint": "9f04df238ec44e48babdd6f02bf3dfec",
      "cycle": "one-time",
      "amount": 2000,
      "currency": "GHS",
      "chargedamount": 2000,
      "appfee": 0,
      "merchantfee": 0,
      "merchantbearsfee": 1,
      "chargecode": "00",
      "chargemessage": "Pending Payment Validation",
      "authmodel": "MOBILEMONEY",
      "ip": "197.149.95.62",
      "narration": "Synergy Group",
      "status": "successful",
      "vbvcode": "N/A",
      "vbvmessage": "N/A",
      "authurl": "NO-URL",
      "acctcode": "00",
      "acctmessage": "Approved",
      "paymenttype": "mobilemoneygh",
      "paymentid": "N/A",
      "fraudstatus": "ok",
      "chargetype": "normal",
      "createdday": 0,
      "createddayname": "SUNDAY",
      "createdweek": 14,
      "createdmonth": 3,
      "createdmonthname": "APRIL",
      "createdquarter": 2,
      "createdyear": 2018,
      "createdyearisleap": false,
      "createddayispublicholiday": 0,
      "createdhour": 10,
      "createdminute": 28,
      "createdpmam": "am",
      "created": "2018-04-08T10:28:01.000Z",
      "customerid": 22823,
      "custphone": "0578922930",
      "custnetworkprovider": "UNKNOWN PROVIDER",
      "custname": "Anonymous Customer",
      "custemail": "user@example.com",
      "custemailprovider": "COMPANY EMAIL",
      "custcreated": "2018-04-08T10:28:01.000Z",
      "accountid": 134,
      "acctbusinessname": "Synergy Group",
      "acctcontactperson": "Desola Ade",
      "acctcountry": "NG",
      "acctbearsfeeattransactiontime": 1,
      "acctparent": 1,
      "acctvpcmerchant": "N/A",
      "acctalias": "temi",
      "acctisliveapproved": 0,
      "orderref": null,
      "paymentplan": null,
      "paymentpage": null,
      "raveref": null,
      "amountsettledforthistransaction": 2000
    }
}
Suggest Edits

Mpesa

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.

  1. Sign-up for a test account here, and for a live account here .

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

  3. When trying to accept payment on a website you can use our inline js method and pass currency as KES and country as KE, once you do this, the options for card and mpesa would come up.

  4. 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.

  5. 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

True
expected 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

True
expected 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

True

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

False
e.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

True
expected value: mpesa

This specifies that the payment method being used is for mpesa payments

is_mpesa

True
expected value: 1

This identifies that an mpesa transaction is being carried out.

is_mpesa_lipa

True
expected 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' => 'tester@flutter.co',
    '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.

<?php
$postdata = array(
     'PBFPubKey' => 'FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X',
     'client' => $post_enc,
     'alg' => '3DES-24');
    
    $ch = curl_init();
    
    curl_setopt($ch, CURLOPT_URL, "https://ravesandboxapi.flutterwave.com/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 ?

  1. 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": "user@ymail.com",
    "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 chargecode returned in the response to be 00.

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://ravesandboxapi.flutterwave.com/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://ravesandboxapi.flutterwave.com/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://ravesandboxapi.flutterwave.com/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://ravesandboxapi.flutterwave.com/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": "user@ymail.com",
    "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
      }
    ]
  }
}
Suggest Edits

QR Code Payments

This shows you how to accept QR Code payments via API

 

QR code payments allow you accept payments using a QR code image, the steps to execute the payment are listed below.

How QR Payments work

  1. You display a QR base64 image to the customer. See a guide for displaying base64 URIs here.

  2. The customer would open up their mobile banking app, or any financial app that has a QR payment functionality, to scan the QR code for payment. Currently, the banks who support QR payments are Diamond Bank, Ecobank, Skye Bank, Zenith Bank, Access Bank, First Bank.

  3. The customer scans the QR code and completes payments.

  4. We notify you via Webhooks to get the status of the completed payment.

Pre-requisites for accepting card payments on rave.

  1. Sign-up for a test account here, and for a live account here .

  2. You can use Webhooks to get notified of transactions or use the response returned by the endpoint.

  3. QR payments is an asynchronous payment method, it is implemented in 2 steps, initiate payment, verify payment after a webhook is sent to you.

Available in Nigeria only

QR code payment on rave is for Nigeria only. Please ensure you only pass NGN as currency.

Also, QR payments can only be tested with live credentials, and you would need an app that can process QR payments to complete an end to end test.

Step 1: Collect customer details

You can initiate a QR payment by sending the following details to the charge endpoint.

{
  "PBFPubKey": "FLWPUBK-981ae1ed1ef801254329cb7b318a0ea5-X",
  "amount": 40,
  "txRef": "m3s4m0c1526722407366",
  "is_qr": "qr",
  "payment_type": "pwc_qr",
  "ip": "::1",
  "device_fingerprint": "ada1d43c29279d9f743956edfb98d801",
  "meta": [
    {
      "metaname": "flightid",
      "metavalue": "93849-MK5000"
    }
  ],
  "email": "user@ravepay.co"
}

Parameter Description

Parameter
Required
Description

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.

amount

true

This is the amount to be charged, it is passed as - (“amount”:10).

Pass 0 as amount if you would like the customer to enter an amount for payment in their respective mobile banking or QR app.

currency

false
defaults to NGN

This is the specified currency to charge the card in.

QR payments can only be done in NGN.

country

false
defaults to NG

This is the pair country for the transaction with respect to the currency. See a list of Multicurrency support here Multicurrency Payments ]

txRef

true

This is the unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction

is_qr

true
expected value: qr

This is a flag identifying that it's a QR payment being initiated.

payment_type

true
expected value: pwc_qr

This recognises the payment type as QR payment

IP

true

IP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction.

device_fingerprint

false

This is the fingerpringt for the device being used. It can be generated using a library on whatever platform is being used.

meta

false

Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format.

email

true

This is the email address of the customer.

phonenumber

false

This is the phone number 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.

submerchant_business_name

false

This allows you pass a dynamic merchant name that would be displayed on the QR app.

Step 2: Encrypt QR payment details

To see how to encrypt using any of our encryption function copy the sample request below and visit the Rave Encryption section.

Step 3: Initiate payment

After encryption, the next step is to initiate your payment using the encrypted string by sending a request to the /charge endpoint. See how to do that below.

Sandbox Endpoint: https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge

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

Sample Request:

Copy the curl request and make a request in your terminal to see how it works!

curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge \
  --header 'content-type: application/json' \
  --data '{"PBFPubKey":"FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X","client":"VodhvFFsni0CBeieHPq9HTuG5lbNPgmD5rbEw6Uxb0TD9eD9B3VM5uZ1B5lC3thQMbPypNBCAYwvbi+o9E4lKa4gZF+XaDB+zzsNMC/jhHXTQKZt727+8tLzsHDr3IU5O8Uj0/XKWgf525xIjV8yG9zhE0Y+RPeTHWWgnGJsoBuhc1D8/tNo/en31kO3CfZgU9Ku9ltuQBgJd5mqxHVpFeuwXhsohZ0BGMQfcEpKaW0qZysVB7lnLoB6pJeiGiOiNUiiD41IeBj5t2ILIFKCj7mbD9FShJfLpsTK2rLj+k8cj5F1J9K0Dcve4nRizNKUJKdVCbpTjwzmuHzYQzLsvhl2c0KaSXlq1eRgCbFm/oICbLRRwqH5/ZktfJOfVqTAngEtbZ/eIGYcbdDSe2RmXPQTTsKNzIiSrMby3awYap5XeiylHdnHLamAZZ+ZPcRe8yhnWJUgJG0ppk4gdafQa6mAZZ+ZPcRembqjp8mZVAl4e7uBVLTksQ==","alg":"3DES-24"}'
{
   "PBFPubKey":"FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",    "client":"VodhvFFsni0CBeieHPq9HTuG5lbNPgmD5rbEw6Uxb0TD9eD9B3VM5uZ1B5lC3thQMbPypNBCAYwvbi+o9E4lKa4gZF+XaDB+zzsNMC/jhHXTQKZt727+8tLzsHDr3IU5O8Uj0/XKWgf525xIjV8yG9zhE0Y+RPeTHWWgnGJsoBuhc1D8/tNo/en31kO3CfZgU9Ku9ltuQBgJd5mqxHVpFeuwXhsohZ0BGMQfcEpKaW0qZysVB7lnLoB6pJeiGiOiNUiiD41IeBj5t2ILIFKCj7mbD9FShJfLpsTK2rLj+k8cj5F1J9K0Dcve4nRizNKUJKdVCbpTjwzmuHzYQzLsvhl2c0KaSXlq1eRgCbFm/oICbLRRwqH5/ZktfJOfVqTAngEtbZ/eIGYcbdDSe2RmXPQTTsKNzIiSrMby3awYap5XeiylHdnHLamAZZ+ZPcRe8yhnWJUgJG0ppk4gdafQa6mAZZ+ZPcRembqjp8mZVAl4e7uBVLTksQ==",
   "alg":"3DES-24"
}
  • client: This is the encrypted request parameters.

  • PBFPubKey: This is your merchant public key.

  • alg: must always be passed as 3DES-24

Some of the important responses you need to check are broken down below:

  • data.chargeResponseCode: This is the response code of the transaction, it typically tells you when a transaction is successful with a response code 00 or when the transaction requires validation 02.

  • data.chargeResponseMessage: This is the response message and it can be shown to the customer to show what needs to be done next.

  • data.qr_image: This returns the base64 encoded URI to load to the customer i.e. the QR code. See a guide on loading base64 URIs here

Sample response after Initiate payment call.

{
    "status": "success",
    "message": "V-COMP",
    "data": {
        "id": 150744,
        "txRef": "m3s5660c1526780007366",
        "orderRef": "RV16D4AA38C8A7E0",
        "flwRef": "FLW5169a66def65454e955",
        "redirectUrl": "http://127.0.0",
        "device_fingerprint": "ada1d43c29279d9f743956edfb98d801",
        "settlement_token": null,
        "cycle": "one-time",
        "amount": 40,
        "charged_amount": 40,
        "appfee": 0,
        "merchantfee": 0,
        "merchantbearsfee": 1,
        "chargeResponseCode": "02",
        "raveRef": null,
        "chargeResponseMessage": "QR GENERATED. PENDING VALIDATION",
        "authModelUsed": "MVISA-QR",
        "currency": "NGN",
        "IP": "::ffff:10.142.195.245",
        "narration": "Synergy Group",
        "status": "success-pending-validation",
        "modalauditid": "N/A",
        "vbvrespmessage": "N/A",
        "authurl": "NO-URL",
        "vbvrespcode": "N/A",
        "acctvalrespmsg": null,
        "acctvalrespcode": null,
        "paymentType": "mvisa-qr",
        "paymentPlan": null,
        "paymentPage": null,
        "paymentId": "118",
        "fraud_status": "ok",
        "charge_type": "normal",
        "is_live": 0,
        "createdAt": "2018-05-19T11:53:23.000Z",
        "updatedAt": "2018-05-19T11:53:26.000Z",
        "deletedAt": null,
        "customerId": 28276,
        "AccountId": 134,
        "customer": {
            "id": 28276,
            "phone": null,
            "fullName": "Dele Moruf Quadri",
            "customertoken": null,
            "email": "user@ravepay.co",
            "createdAt": "2018-05-19T11:53:22.000Z",
            "updatedAt": "2018-05-19T11:53:22.000Z",
            "deletedAt": null,
            "AccountId": 134
        },
        "validateInstructions": "No Message",
        "qr_image": "data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAABWQAAAVkCAYAAABTjRaxAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAP+lSURBVHhe7NhBii1btizZ1/9OZxaCiJI4mCPKcf1z6QAp62TZ9svl/N//NzMzMzMzMzMzMzP/xP5BdmZmZmZmZmZmZuYf2T/IzszMzMzMzMzMzPwj+wfZmZmZmZmZmZmZmX9k................base64uri.............."
    }
}

Step 3: Receive webhook response

Rave allows you configure Webhooks so you can get notified when actions like payment have been completed, it is a seamless way to handle offline transactions. Webhooks are returned in application/x-www-form-urlencoded, but you can configure to receive your responses in JSON .

Visit the Webhooks section to see more details on implementing and receiving webhooks.

{
  "id": 130438,
  "txRef": "m3s5660c1526780007366",
  "flwRef": "FLW5169a66def65454e955",
  "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": "NGN",
  "customer": {
    "id": 23858,
    "phone": "254791498442",
    "fullName": "Dele Moruf Quadri",
    "customertoken": null,
    "email": "user@ymail.com",
    "createdAt": "2018-04-15T16:32:05.000Z",
    "updatedAt": "2018-04-15T16:32:05.000Z",
    "deletedAt": null,
    "AccountId": 1884
  },
  "entity": {
    "id": "NO-ENTITY"
  }
}

Step 4: Verify the payment

After charging a customer via QR successfully, you need to verify that the payment was successful with Rave before giving value to your customer.

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 chargecode returned in the response to be 00.

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":"<Your live reference>","SECKEY":"<Your live secret key>"}'

When you successfully verify a completed payment see sample response below.

{
    "status": "success",
    "message": "Tx Fetched",
    "data": {
        "txid": 873293,
        "txref": "m3s5660c1526780007366",
        "flwref": "FLW5169a66def65454e955",
        "devicefingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
        "cycle": "one-time",
        "amount": 10,
        "currency": "NGN",
        "chargedamount": 10.13,
        "appfee": 0.125,
        "merchantfee": 0,
        "merchantbearsfee": 0,
        "chargecode": "00",
        "chargemessage": "Approved",
        "authmodel": "MVISA-QR",
        "ip": "::ffff:10.101.207.111",
        "narration": "Raver",
        "status": "successful",
        "vbvcode": "N/A",
        "vbvmessage": "N/A",
        "authurl": "NO-URL",
        "acctcode": null,
        "acctmessage": null,
        "paymenttype": "mvisa-qr",
        "paymentid": "9584",
        "fraudstatus": "ok",
        "chargetype": "normal",
        "createdday": 1,
        "createddayname": "MONDAY",
        "createdweek": 14,
        "createdmonth": 3,
        "createdmonthname": "APRIL",
        "createdquarter": 2,
        "createdyear": 2018,
        "createdyearisleap": false,
        "createddayispublicholiday": 0,
        "createdhour": 11,
        "createdminute": 38,
        "createdpmam": "am",
        "created": "2018-04-02T11:38:31.000Z",
        "customerid": 557049,
        "custphone": "08166009393",
        "custnetworkprovider": "MTN",
        "custname": "Dele Moruf Quadri",
        "custemail": "salesmode@ravepay.co",
        "custemailprovider": "COMPANY EMAIL",
        "custcreated": "2018-04-02T10:45:31.000Z",
        "accountid": 48,
        "acctbusinessname": "Raver",
        "acctcontactperson": "Desola Adesina",
        "acctcountry": "NG",
        "acctbearsfeeattransactiontime": 0,
        "acctparent": 1,
        "acctvpcmerchant": "N/A",
        "acctalias": "raver",
        "acctisliveapproved": 0,
        "orderref": "RVDAED218620CE66",
        "paymentplan": null,
        "paymentpage": null,
        "raveref": null,
        "amountsettledforthistransaction": 10.005
    }
}
Suggest Edits

US ACH Payments

This shows you how to accept US ACH charges from your customers.

 

Rave allows you to charge US accounts using our ACH account charge flow. Your customers would be taken to a page where they can select their US bank account login into their Ibanking and complete the transaction.

Pre-requisites for accepting account payments on rave.

  1. Sign-up for a test account here, and for a live account here .

  2. You can use webhooks to get notified on transactions using the webhook guide or use the response returned by the endpoint.

  3. Pass a redirect URL so we can redirect your customer back to a location you want us to redirect them to and append the response as query parameters.

Step 1: Collect the customer's details.

You can use a custom form to collect the customer's details like name, email and phone number including extra information needed as meta, see a sample of the details to collect.

{
  "PBFPubKey": "FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",
  "currency": "USD",
  "payment_type": "account",
  "country": "US",
  "amount": "20",
  "email": "user@example.com",
  "phonenumber": "0000000000",
  "firstname": "Temi",
  "lastname": "Tester",
  "IP": "355426087298442",
  "txRef": "rave-checkout-" + Date.now(),
  "is_us_bank_charge": "true",
  "redirect_url": "https://rave-webhook.herokuapp.com/receivepayment",
  "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}

Parameter Description

Parameters
Required
Description

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.

currency

True
Expected: USD

This is the specified currency to charge the account in.

payment_type

True
(Expected value: account)

This specifies that the payment method being used is for account payments.

country

True
(Expected value: US)

This is the pair country for the transaction with respect to the currency.

amount

True

This is the amount to be charged from the account it is passed as - (“amount”:10).

email

True

This is the email address of the customer.

phonenumber

False

This is the phone number of the customer.

firstname

False

This is the first name of the account holder or the customer.

lastname

False

This is the last name of the account holder or the customer.

IP

True

IP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction.

txRef

True

This is the unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction.

redirect_url

True

This is a url you provide, we redirect to it after the customer completes payment and append the response to it as query parameters.

device_fingerprint

False

This is the fingerpringt for the device being used. It can be generated using a library on whatever platform is being used.

Step 2: Encrypt your request

To see how to encrypt using any of our encryption function copy the sample request below and visit the Rave Encryption section.

Step 3: Initiate your payment.

After encryption the next step is to initiate your payment using the encrypted string by sending a request to the /charge endpoint. See how to do that below.

Sandbox Endpoint: https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge

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

Sample Request:

curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge \
  --data '{"PBFPubKey":"FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X","client":"VodhvFFsni0CBeieHPq9HTuG5lbNPgmD5rbEw6Uxb0TD9eD9B3VM5uZ1B5lC3thQMbPypNBCAYykQt74g4x4l0DDFmuEwyIsOCSsOU6MqqIJd5mqxHVpFRu+OaSOzrm9nMXbTwwxnJ/Ay8R4CSOcp/eceUXRommw5FaPdU8/jLzbuCeIYBtcqR738mQfK3f/ieeXzZY8MYgf+nOBmJZEXZF9bKCExsjKn84jeK055gp56ZpCk0hFCa9f3NMxUQw5MzO3zY7qArj2XvgMQONcum50KqHoOeylPeUNE3gre3BKV3ZVNpEmdd4wTLF57CWR7EjGog/Hu3RcEnm5CAGrvfJSJ9UlNcuAIJj9yH3+SHK4XRZSXxYegdc/0C0z9BmopDjMMXbKBT8gSw3Ux0Lx0ajHKk4BX7COUQbJwspTqd+B+A+qRkH6QhUFz08H7tOQ+4qq/mP3SVOhEZn9Hs0mc8nw2FiYfbyM965oGp/dR843Ezo4lOYBzixZmsHtu7bKMT8qFEGbQ1rmk8cJgjU5trdNsR7RRWrkQIZ4zxEPXEdBXpW/1DeaROS8l3qLdlb+eHu7gVS05LE=","alg":"3DES-24"}'
{
  "PBFPubKey": "FLWPUBK-7adb6177bd71dd43c2efa3f1229e3b7f-X",
  "client": "VodhvFFsni0CBeieHPq9HTuG5lbNPgmD5rbEw6Uxb0TD9eD9B3VM5uZ1B5lC3thQMbPypNBCAYykQt74g4x4l0DDFmuEwyIsOCSsOU6MqqIJd5mqxHVpFRu+OaSOzrm9nMXbTwwxnJ/Ay8R4CSOcp/eceUXRommw5FaPdU8/jLzbuCeIYBtcqR738mQfK3f/ieeXzZY8MYgf+nOBmJZEXZF9bKCExsjKn84jeK055gp56ZpCk0hFCa9f3NMxUQw5MzO3zY7qArj2XvgMQONcum50KqHoOeylPeUNE3gre3BKV3ZVNpEmdd4wTLF57CWR7EjGog/Hu3RcEnm5CAGrvfJSJ9UlNcuAIJj9yH3+SHK4XRZSXxYegdc/0C0z9BmopDjMMXbKBT8gSw3Ux0Lx0ajHKk4BX7COUQbJwspTqd+B+A+qRkH6QhUFz08H7tOQ+4qq/mP3SVOhEZn9Hs0mc8nw2FiYfbyM965oGp/dR843Ezo4lOYBzixZmsHtu7bKMT8qFEGbQ1rmk8cJgjU5trdNsR7RRWrkQIZ4zxEPXEdBXpW/1DeaROS8l3qLdlb+eHu7gVS05LE=",
  "alg": "3DES-24"
}
  • client: This is the encrypted request parameters.

  • PBFPubKey: This is your merchant public key.

  • alg: must always be passed as 3DES-24

When you initiate the payment you would get a response that looks like responses below:

{
  "status": "success",
  "message": "V-COMP",
  "data": {
    "id": 174024,
    "txRef": "rave-checkout-1529594387128",
    "orderRef": null,
    "flwRef": "FLWT000985573",
    "redirectUrl": "https://rave-webhook.herokuapp.com/receivepayment",
    "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
    "settlement_token": null,
    "cycle": "one-time",
    "amount": 20,
    "charged_amount": 20,
    "appfee": 0,
    "merchantfee": 0,
    "merchantbearsfee": 1,
    "chargeResponseCode": "02",
    "raveRef": null,
    "chargeResponseMessage": "Pending Validation",
    "authModelUsed": "AUTH",
    "currency": "USD",
    "IP": "::ffff:10.65.204.22",
    "narration": "Synergy Group",
    "status": "success-pending-validation",
    "modalauditid": "86b720efbd4cabe890def64945fe757a",
    "vbvrespmessage": "N/A",
    "authurl": "https://flutterwavestaging.com:9443/flwusprocessor/redirect?hid=FLW3f9f99f0e5534d438c15297bc608f21d",
    "vbvrespcode": "N/A",
    "acctvalrespmsg": null,
    "acctvalrespcode": null,
    "paymentType": "account-ach-us",
    "paymentPlan": null,
    "paymentPage": null,
    "paymentId": "N/A",
    "fraud_status": "ok",
    "charge_type": "normal",
    "is_live": 0,
    "createdAt": "2018-06-21T15:23:12.000Z",
    "updatedAt": "2018-06-21T15:23:16.000Z",
    "deletedAt": null,
    "customerId": 22892,
    "AccountId": 134,
    "customer": {
      "id": 22892,
      "phone": "0000000000",
      "fullName": "Temi Tester",
      "customertoken": null,
      "email": "user@example.com",
      "createdAt": "2018-04-09T01:07:13.000Z",
      "updatedAt": "2018-04-09T01:07:13.000Z",
      "deletedAt": null,
      "AccountId": 134
    },
    "validateInstructions": {
      "valparams": [],
      "instruction": ""
    }
  }
}

Some of the important responses you need to check are broken down below:

  • data.status: The status object inside the data object is the right status to check for, possible values are successful, failed, pending, success-pending-validation and pending-validation.

  • data.chargeResponseCode: This is the response code of the transaction, it typically tells you when a transaction is successful with a response code 00 or when the transaction requires validation 02.

  • data.authurl: This object contains the link to redirect the customer too so they can complete their payment.

  • data.paymentType: This shows you the payment instrument used i.e. if the customer used a card, account or USSD to complete the payment.

When performing a US ACH charge, the validation happens on the page where you redirect the customer to, once the transaction is completed, we redirect the customer to the redirect URL you sent in the initial charge request and append a response, we also send a response to your hook URL so you can process the transaction.

Page customer is redirected to, to complete payment.

Implement Webhooks

It is advised all merchants use webhooks to get automatic updates on their transactions. To set up webhooks please visit the Webhooks section.

Step 5: Verify the payment.

After charging an account 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 chargecode returned in the response to be 00.

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://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify \
  --header 'content-type: application/json' \
  --data '{"txref":"MC-09182829","SECKEY":"FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"}'
<?php 

$result = array();

$postdata =  array( 
  'txref' => 'MC-09182829',
  'SECKEY' => 'FLWSECK-bb971402072265fb156e90a3578fe5e6-X'
  );

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"https://ravesandboxapi.flutterwave.com/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://ravesandboxapi.flutterwave.com/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 = "OH-AAED44", SECKEY = "FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"};
            var client = new HttpClient();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var responseMessage = client.PostAsJsonAsync("https://ravesandboxapi.flutterwave.com/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": 144534,
    "txref": "MC-09182829",
    "flwref": "FLWT000985573",
    "devicefingerprint": "N/A",
    "cycle": "one-time",
    "amount": 100,
    "currency": "NGN",
    "chargedamount": 100,
    "appfee": 0,
    "merchantfee": 0,
    "merchantbearsfee": 1,
    "chargecode": "02",
    "chargemessage": "Pending OTP validation",
    "authmodel": "AUTH",
    "ip": "::ffff:10.102.148.117",
    "narration": "Synergy Group",
    "status": "success-pending-validation",
    "vbvcode": "N/A",
    "vbvmessage": "N/A",
    "authurl": "NO-URL",
    "acctcode": null,
    "acctmessage": null,
    "paymenttype": "account",
    "paymentid": "2",
    "fraudstatus": "ok",
    "chargetype": "normal",
    "createdday": 4,
    "createddayname": "THURSDAY",
    "createdweek": 19,
    "createdmonth": 4,
    "createdmonthname": "MAY",
    "createdquarter": 2,
    "createdyear": 2018,
    "createdyearisleap": false,
    "createddayispublicholiday": 0,
    "createdhour": 9,
    "createdminute": 1,
    "createdpmam": "am",
    "created": "2018-05-10T09:01:52.000Z",
    "customerid": 24728,
    "custphone": "09090838390",
    "custnetworkprovider": "ETISALAT",
    "custname": "yemi alade",
    "custemail": "user@example.com",
    "custemailprovider": "COMPANY EMAIL",
    "custcreated": "2018-04-21T11:37:43.000Z",
    "accountid": 134,
    "acctbusinessname": "Synergy Group",
    "acctcontactperson": "Desola Ade",
    "acctcountry": "NG",
    "acctbearsfeeattransactiontime": 1,
    "acctparent": 1,
    "acctvpcmerchant": "N/A",
    "acctalias": "temi",
    "acctisliveapproved": 0,
    "orderref": "URF_1525942912124_3844735",
    "paymentplan": null,
    "paymentpage": null,
    "raveref": "RV31525942911654CA881BAE82",
    "account": {
      "id": 2,
      "account_number": "0690000031",
      "account_bank": "044",
      "first_name": "NO-NAME",
      "last_name": "NO-LNAME",
      "account_is_blacklisted": 0,
      "createdAt": "2016-12-31T04:09:24.000Z",
      "updatedAt": "2018-06-04T09:20:10.000Z",
      "deletedAt": null,
      "account_token": {
        "token": "flw-t0e1bb79f967612fc1-k3n-mock"
      }
    },
    "meta": []
  }
}
Suggest Edits

Uganda mobile money

This page describes how to collect payments via Uganda mobile money.

 

Rave currently allows merchants to use two (2) payment methods in Uganda (card and mobilemoney).

We would show you how to accept payments using the mobile money method.

Pre-requisites for accepting mobile money payments in Uganda.

  1. Sign-up for a test account here, and for a live account here .

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

  3. When trying to accept payment on a website you can use our inline js method and pass currency as UGX and country as NG, once you do this, the options for the card and mobile money would come up.

  4. 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.

  5. 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-4e581ebf8372cd691203b27227e2e3b8-X",
  "currency": "UGX",
  "payment_type": "mobilemoneyuganda",
  "country": "NG",
  "amount": "50",
  "email": "user@example.com",
  "phonenumber": "054709929220",
  "network": "UGX",
  "firstname": "temi",
  "lastname": "desola",
  "IP": "355426087298442",
  "txRef": "MC-" + Date.now(),
  "orderRef": "MC_" + Date.now(),
  "is_mobile_money_ug": 1,
  "redirect_url": "https://rave-webhook.herokuapp.com/receivepayment",
  "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c"
}

Parameter Definition

Parameter
Required
Description

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.

currency

True
(expected value: UGX)

This is the specified currency to charge in.

country

True

(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-type

True

(expected value: mobilemoneyuganda)

This specifies that the payment method being used is for mobile money payments

amount

True

This is the amount to be charged it is passed as - (“amount”:10).

Minimum amount for GHS is 1 GHS

network

True

(possible values: UGX)

This is the customer's mobile money network provider.

email

True

This is the email address of the customer.

phonenumber

True

This is the phone number linked to the customer's mobile money account.

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

True

IP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction.

txRef

True

This is a unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction.

orderRef

True

Unique ref for the mobilemoney transaction to be provided by the merchant.

is_mobile_money_ug

True

(expected value: 1)

This identifies that a mobile money transaction is being carried out.

device_fingerprint

False

This 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 payviamobilemoneygh(){ // set up a function to test card payment.
    
    error_reporting(E_ALL);
    ini_set('display_errors',1);
    
    $data = array('PBFPubKey' => 'FLWPUBK-e634d14d9ded04eaf05d5b63a0a06d2f-X',
    'currency' => 'UGX',
    'country' => 'NG',
    'payment_type' => 'mobilemoneyuganda',
    'amount' => '30',
    'phonenumber' => '054709929300',
    'firstname' => 'Edward',
    'lastname' => 'Kisane',
    'network' => 'UGX',
    'email' => 'tester@flutter.co',
    'IP' => '103.238.105.185',
    'txRef' => 'MXX-ASC-4578',
    'orderRef' => 'MXX-ASC-90929',
    'is_mobile_money_ug' => 1,
    '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);
}

payviamobilemoneygh();

Step 2: Call the charge endpoint with your encrypted data

Sandbox Endpoint: https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge

Live endpoint: 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);
}

payviamobilemoneygh();

What happens after I call the charge endpoint?

When you call the charge endpoint you would need to display a prompt to the user instructing them that a USSD prompt would be sent to their phone to approve the transaction.

See customer payment flow samples below.

Customer enters phone number.

Customer enters phone number.

You call the charge endpoint and show them instructions on receiving USSD push on their phone.

You call the charge endpoint and show them instructions on receiving USSD push on their phone.

User is asked to input pin to approve the transaction.

User is asked to input pin to approve the transaction.

The customer's transaction is approved.

The customer's transaction is approved.

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

Handling Mobile money transactions

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

However, we advise merchants implement manual re-query into the system for edge cases and to deal with any failure point that might occur.

An example is when a customer says they have been debited for a transaction but the transaction returned an error or timeout from the processor. With a manual re-query, you can get the updated status at any time and give value to the customer.

{
  "id": 126090,
  "txRef": "rave-checkout-1523183226335",
  "flwRef": "flwm3s4m0c1523183355860",
  "orderRef": null,
  "paymentPlan": null,
  "createdAt": "2018-04-08T10:29:15.000Z",
  "amount": 2000,
  "charged_amount": 2000,
  "status": "successful",
  "IP": "197.149.95.62",
  "currency": "UGX",
  "customer": {
    "id": 22823,
    "phone": "0578922930",
    "fullName": "Anonymous Customer",
    "customertoken": null,
    "email": "user@example.com",
    "createdAt": "2018-04-08T10:28:01.000Z",
    "updatedAt": "2018-04-08T10:28:01.000Z",
    "deletedAt": null,
    "AccountId": 134
  },
  "entity": {
    "id": "NO-ENTITY"
  }
}
{
  "status": "success",
  "message": "V-COMP",
  "data": {
    "id": 690055,
    "txRef": "MC-1520528216374",
    "orderRef": null,
    "flwRef": "FLWMM1522085245161",
    "redirectUrl": "http://127.0.0",
    "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
    "settlement_token": null,
    "cycle": "one-time",
    "amount": 3,
    "charged_amount": 3,
    "appfee": 0.037500000000000006,
    "merchantfee": 0,
    "merchantbearsfee": 1,
    "chargeResponseCode": "02",
    "raveRef": null,
    "chargeResponseMessage": "pending charge processing",
    "authModelUsed": "MOBILEMONEY",
    "currency": "UGX",
    "IP": "::ffff:10.5.186.67",
    "narration": "Raver",
    "status": "success-pending-validation",
    "vbvrespmessage": "N/A",
    "authurl": "NO-URL",
    "vbvrespcode": "N/A",
    "acctvalrespmsg": null,
    "acctvalrespcode": null,
    "paymentType": "mobilemoneygh",
    "paymentPlan": null,
    "paymentPage": null,
    "paymentId": "N/A",
    "fraud_status": "ok",
    "charge_type": "normal",
    "is_live": 0,
    "createdAt": "2018-03-08T16:57:23.000Z",
    "updatedAt": "2018-03-08T16:57:26.000Z",
    "deletedAt": null,
    "customerId": 437599,
    "AccountId": 48,
    "customer": {
      "id": 437599,
      "phone": "5475309092",
      "fullName": "temi desola",
      "customertoken": null,
      "email": "user@example.com",
      "createdAt": "2018-03-08T16:57:23.000Z",
      "updatedAt": "2018-03-08T16:57:23.000Z",
      "deletedAt": null,
      "AccountId": 48
    },
    "validateInstructions": "pending charge processing"
  }
}

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 chargecode returned in the response to be 00.

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://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify \
  --header 'content-type: application/json' \
  --data '{"txref":"rave-checkout-1523183226335","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://ravesandboxapi.flutterwave.com/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://ravesandboxapi.flutterwave.com/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://ravesandboxapi.flutterwave.com/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": 126088,
      "txref": "rave-checkout-1523183226335",
      "flwref": "flwm3s4m0c1523183281767",
      "devicefingerprint": "9f04df238ec44e48babdd6f02bf3dfec",
      "cycle": "one-time",
      "amount": 2000,
      "currency": "UGX",
      "chargedamount": 2000,
      "appfee": 0,
      "merchantfee": 0,
      "merchantbearsfee": 1,
      "chargecode": "00",
      "chargemessage": "Pending Payment Validation",
      "authmodel": "MOBILEMONEY",
      "ip": "197.149.95.62",
      "narration": "Synergy Group",
      "status": "successful",
      "vbvcode": "N/A",
      "vbvmessage": "N/A",
      "authurl": "NO-URL",
      "acctcode": "00",
      "acctmessage": "Approved",
      "paymenttype": "mobilemoneygh",
      "paymentid": "N/A",
      "fraudstatus": "ok",
      "chargetype": "normal",
      "createdday": 0,
      "createddayname": "SUNDAY",
      "createdweek": 14,
      "createdmonth": 3,
      "createdmonthname": "APRIL",
      "createdquarter": 2,
      "createdyear": 2018,
      "createdyearisleap": false,
      "createddayispublicholiday": 0,
      "createdhour": 10,
      "createdminute": 28,
      "createdpmam": "am",
      "created": "2018-04-08T10:28:01.000Z",
      "customerid": 22823,
      "custphone": "0578922930",
      "custnetworkprovider": "UNKNOWN PROVIDER",
      "custname": "Anonymous Customer",
      "custemail": "user@example.com",
      "custemailprovider": "COMPANY EMAIL",
      "custcreated": "2018-04-08T10:28:01.000Z",
      "accountid": 134,
      "acctbusinessname": "Synergy Group",
      "acctcontactperson": "Desola Ade",
      "acctcountry": "NG",
      "acctbearsfeeattransactiontime": 1,
      "acctparent": 1,
      "acctvpcmerchant": "N/A",
      "acctalias": "temi",
      "acctisliveapproved": 0,
      "orderref": null,
      "paymentplan": null,
      "paymentpage": null,
      "raveref": null,
      "amountsettledforthistransaction": 2000
    }
}
Suggest Edits

Save a card

This shows you how to save a card using rave.

 

This shows you how to save a customers card after an initial charge using Rave's APIs.

Saving a card successfully

To save a card successfully, you need to perform an initial charge on the card. To see how to perform an initial charge visit the Card Payments section.

After charging a card successfully on rave, In your verify payment response you would find an object:

  • "embedtoken": "flw-t0-f6f915f53a094671d98560272572993e-m03k"

This is the token you would use for card tokenization.

Sample requery response:

{
    "status": "success",
    "message": "Tx Fetched",
    "data": {
        "txid": 123976,
        "txref": "MC-1522866789642",
        "flwref": "FLW-MOCK-20bdec1097b925b9aa8224e06adb73d7",
        "devicefingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
        "cycle": "one-time",
        "amount": 10,
        "currency": "NGN",
        "chargedamount": 10,
        "appfee": 0,
        "merchantfee": 0,
        "merchantbearsfee": 1,
        "chargecode": "00",
        "chargemessage": "Approved. Successful",
        "authmodel": "VBVSECURECODE",
        "ip": "::ffff:127.0.0.1",
        "narration": "FLW-PBF CARD Transaction ",
        "status": "successful",
        "vbvcode": "00",
        "vbvmessage": "Approved. Successful",
        "authurl": "http://flw-pms-dev.eu-west-1.elasticbeanstalk.com/mockvbvpage?ref=FLW-MOCK-20bdec1097b925b9aa8224e06adb73d7&code=00&message=Approved. Successful&receiptno=RN1522866810978",
        "acctcode": null,
        "acctmessage": null,
        "paymenttype": "card",
        "paymentid": "1450",
        "fraudstatus": "ok",
        "chargetype": "normal",
        "createdday": 3,
        "createddayname": "WEDNESDAY",
        "createdweek": 14,
        "createdmonth": 3,
        "createdmonthname": "APRIL",
        "createdquarter": 2,
        "createdyear": 2018,
        "createdyearisleap": false,
        "createddayispublicholiday": 0,
        "createdhour": 18,
        "createdminute": 33,
        "createdpmam": "pm",
        "created": "2018-04-04T18:33:30.000Z",
        "customerid": 22015,
        "custphone": "0902620185",
        "custnetworkprovider": "AIRTEL",
        "custname": "temi desola",
        "custemail": "user@gmail.com",
        "custemailprovider": "GMAIL",
        "custcreated": "2018-04-02T14:49:59.000Z",
        "accountid": 134,
        "acctbusinessname": "Synergy Group",
        "acctcontactperson": "Desola Ade",
        "acctcountry": "NG",
        "acctbearsfeeattransactiontime": 1,
        "acctparent": 1,
        "acctvpcmerchant": "N/A",
        "acctalias": "temi",
        "acctisliveapproved": 0,
        "orderref": "URF_1522866810933_7293235",
        "paymentplan": 16,
        "paymentpage": null,
        "raveref": "RV3152286680999275AD9704CC",
        "amountsettledforthistransaction": 10,
        "card": {
            "expirymonth": "12",
            "expiryyear": "20",
            "cardBIN": "475176",
            "last4digits": "9647",
            "brand": "VISA ACCESS BANK PLC CREDITPREMIER",
            "card_tokens": [
                {
                    "embedtoken": "flw-t1nf-16dead8e2217a10dccc8278b18dd5b71-m03k",
                    "shortcode": "61c4a",
                    "expiry": "9999999999999"
                }
            ],
            "life_time_token": "flw-t1nf-16dead8e2217a10dccc8278b18dd5b71-m03k"
        },
        "meta": [
            {
                "id": 25700,
                "metaname": "flightID",
                "metavalue": "123949494DC",
                "createdAt": "2018-04-04T18:33:32.000Z",
                "updatedAt": "2018-04-04T18:33:32.000Z",
                "deletedAt": null,
                "getpaidTransactionId": 123976
            }
        ]
    }
}

Tokenising Verve cards

Currently, verve cards cannot be tokenised, if you call the tokenise endpoint with a verve token, OTP would still be requested for the transaction. We advise merchants stop the cards from being tokenised on their platform.

Step 1: Charge a Saved card using a token

Once the charge and validation leg is complete for the first charge on the card, you can make use of the token for subsequent charges.

NB: You need to pass the same email used in the initial charge leg to the tokenised endpoint.

Sandbox Endpoint: https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/tokenized/charge

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

  • Call the endpoint above using the sample payload.
curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/tokenized/charge \
  --header 'content-type: application/json' \
  --data '{"SECKEY":"FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X","token":"flw-t1nf-404dff6823ff91ce154f04dd40085b9e-m03k","currency":"NGN","country":"NG","amount":"100","email":"user@example.com","firstname":"Yemi","lastname":"Oyeleke","IP":"190.233.222.1","narration":"Internet Renewal","txRef":"MC_1522966555872","meta":""}'
{
   "currency":"NGN",
   "SECKEY":"FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X",
   "token":"flw-t0876849e016386b2d-k3n-mock",
   "country":"NG",
   "amount":1000,
   "email":"desola.ade1@gmail.com",
   "firstname":"temi",
   "lastname":"Oyekole",
   "IP":"190.233.222.1",
   "txRef":"MC-7666-YU"
}

Sample response:

{
  "status": "success",
  "message": "Charge success",
  "data": {
    "id": 124983,
    "txRef": "MC_1522966555872",
    "orderRef": null,
    "flwRef": "FLW-M03K-3705232bce4536328b24d03579365e9f",
    "redirectUrl": "http://127.0.0",
    "device_fingerprint": "N/A",
    "settlement_token": null,
    "cycle": "one-time",
    "amount": 100,
    "charged_amount": 100,
    "appfee": 0,
    "merchantfee": 0,
    "merchantbearsfee": 1,
    "chargeResponseCode": "00",
    "raveRef": null,
    "chargeResponseMessage": "Approved",
    "authModelUsed": "noauth",
    "currency": "NGN",
    "IP": "190.233.222.1",
    "narration": "Internet Renewal",
    "status": "successful",
    "vbvrespmessage": "Approved",
    "authurl": "N/A",
    "vbvrespcode": "00",
    "acctvalrespmsg": null,
    "acctvalrespcode": null,
    "paymentType": "card",
    "paymentPlan": null,
    "paymentPage": null,
    "paymentId": "1057",
    "fraud_status": "ok",
    "charge_type": "normal",
    "is_live": 0,
    "createdAt": "2018-04-05T22:30:43.000Z",
    "updatedAt": "2018-04-05T22:30:43.000Z",
    "deletedAt": null,
    "customerId": 22536,
    "AccountId": 134,
    "customer": {
      "id": 22536,
      "phone": "09026420185",
      "fullName": "temi desola",
      "customertoken": null,
      "email": "user@example.com",
      "createdAt": "2018-04-05T07:38:39.000Z",
      "updatedAt": "2018-04-05T07:38:39.000Z",
      "deletedAt": null,
      "AccountId": 134
    },
    "chargeToken": {
      "user_token": "d843b",
      "embed_token": "flw-t0-953c59a47e4a98ad1f4dee7096c81d02-m03k"
    }
  }
}

Great you are done charging a card without the user needing to enter the card details/verify again. Now you need to verify the transaction before giving value to the customer.

Step 2: Verify the payment.

After charging a saved card 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 chargecode returned in the response to be 00.

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://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify \
  --header 'content-type: application/json' \
  --data '{"txref":"MC_1522966555872","SECKEY":"FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"}'
<?php 

$result = array();

$postdata =  array( 
  'txref' => 'MC_1522966555872',
  'SECKEY' => 'FLWSECK-bb971402072265fb156e90a3578fe5e6-X',
  );

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"https://ravesandboxapi.flutterwave.com/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://ravesandboxapi.flutterwave.com/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 = "MC_1522966555872", SECKEY = "FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"};
            var client = new HttpClient();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var responseMessage = client.PostAsJsonAsync("https://ravesandboxapi.flutterwave.com/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": 121257,
        "txref": "MC-1522438968515",
        "flwref": "FLW-MOCK-5e52517f0b314c73c56992dc620d8998",
        "devicefingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
        "cycle": "one-time",
        "amount": 10,
        "currency": "NGN",
        "chargedamount": 10,
        "appfee": 0,
        "merchantfee": 0,
        "merchantbearsfee": 1,
        "chargecode": "00",
        "chargemessage": "Charge successful. Please enter the OTP sent to your mobile number 080****** and email te**@rave**.com",
        "authmodel": "VBVSECURECODE",
        "ip": "::ffff:127.0.0.1",
        "narration": "FLW-PBF CARD Transaction ",
        "status": "successful",
        "vbvcode": "00",
        "vbvmessage": "successful",
        "authurl": "http://flw-pms-dev.eu-west-1.elasticbeanstalk.com/mockvbvpage?ref=FLW-MOCK-5e52517f0b314c73c56992dc620d8998&code=00&message=Approved. Successful&receiptno=RN1522438999815",
        "acctcode": null,
        "acctmessage": null,
        "paymenttype": "card",
        "paymentid": "1057",
        "fraudstatus": "ok",
        "chargetype": "normal",
        "createdday": 5,
        "createddayname": "FRIDAY",
        "createdweek": 13,
        "createdmonth": 2,
        "createdmonthname": "MARCH",
        "createdquarter": 1,
        "createdyear": 2018,
        "createdyearisleap": false,
        "createddayispublicholiday": 0,
        "createdhour": 19,
        "createdminute": 43,
        "createdpmam": "pm",
        "created": "2018-03-30T19:43:19.000Z",
        "customerid": 21887,
        "custphone": "0902620185",
        "custnetworkprovider": "AIRTEL",
        "custname": "temi desola",
        "custemail": "desola.ade1@gmail.com",
        "custemailprovider": "GMAIL",
        "custcreated": "2018-03-30T19:43:19.000Z",
        "accountid": 134,
        "acctbusinessname": "Synergy Group",
        "acctcontactperson": "Desola Ade",
        "acctcountry": "NG",
        "acctbearsfeeattransactiontime": 1,
        "acctparent": 1,
        "acctvpcmerchant": "N/A",
        "acctalias": "temi",
        "acctisliveapproved": 0,
        "orderref": "URF_1522438999774_1285835",
        "paymentplan": null,
        "paymentpage": null,
        "raveref": "RV31522438998679C0566DED05",
        "amountsettledforthistransaction": 10,
        "card": {
            "expirymonth": "12",
            "expiryyear": "20",
            "cardBIN": "543889",
            "last4digits": "0229",
            "brand": "MASTERCARD MASHREQ BANK CREDITSTANDARD",
            "card_tokens": [
                {
                    "embedtoken": "flw-t1nf-4877921998c0d784bbaf3949d23647a5-m03k",
                    "shortcode": "6a50e",
                    "expiry": "9999999999999"
                }
            ],
            "life_time_token": "flw-t1nf-4877921998c0d784bbaf3949d23647a5-m03k"
        }
    }
}
Suggest Edits

Charge with Token

This endpoint allows you charge cards using a token .

 
posthttps://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/tokenized/charge
curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/tokenized/charge
var request = require("request");

var options = { method: 'POST',
  url: 'https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/tokenized/charge' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/tokenized/charge")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/tokenized/charge");

xhr.send(data);
import requests

url = "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/tokenized/charge"

response = requests.request("POST", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

{
  "status":"success",
  "message":"V-COMP",
  "data":
  {
    "id":2667,
    "txRef":"GON-0.06208890843489279",
    "flwRef":"FLW-MOCK-a5dba0959266978397231b2b4f73070d",
    "redirectUrl":"http://127.0.0",
    "device_fingerprint":"69e6b7f0b72037aa8428b70fbe03986c",
    "cycle":"one-time",
    "amount":10.3766666288888,
    "charged_amount":10.4,
    "appfee":0.025941666572222,
    "merchantfee":0,
    "merchantbearsfee":0,
    "chargeResponseCode":"02",
    "chargeResponseMessage":"Success-Pending-otp-validation",
    "authModelUsed":"VBVSECURECODE",
    "currency":"NGN",
    "IP":"41.190.3.8",
    "narration":"FLW-PBF CARD Transaction ",
    "status":"success-pending-validation",
    "vbvrespmessage":"Approved. Successful",
    "authurl":"http://flw-pms-dev.eu-west-1.elasticbeanstalk.com/mockvbvpage?ref=FLW-MOCK-a5dba0959266978397231b2b4f73070d&code=00&message=ApprovedSuccessful",
    "vbvrespcode":"00",
    "acctvalrespmsg":null,
    "acctvalrespcode":null,
    "paymentType":"card",
    "paymentId":"2",
    "fraud_status":"ok",
    "charge_type":"normal",
    "is_live":0,
    "createdAt":"2017-03-06T07:06:15.000Z",
    "updatedAt":"2017-03-06T07:06:17.000Z",
    "deletedAt":null,
    "customerId":159,
    "AccountId":134,
    "customer":
    {"id":159,
      "phone":null,
      "fullName":"Anonymous customer",
      "email":"gondy4life@gmail.com",
      "createdAt":"2017-02-23T04:51:15.000Z",
      "updatedAt":"2017-02-23T04:51:15.000Z",
      "deletedAt":null,
      "AccountId":134},
      "chargeToken":
      {"user_token":"f0209",
        "embed_token":"flw-t0-9f3aa69a806f6440fbb78cc9e8b2f135-k3n"
        }
        }
      }

Body Params

SECKEY
string
required

This is a unique key generated for each button created on Rave’s dashboard. It starts with a prefix FLWSECK and ends with suffix X.

token
string
required

This is the embed_token property returned from the call to charge a card e.g.“chargeToken”:{“user_token”:“f0209”,“embed_token”:“flw-t0-9f3aa69a806f6440fbb78cc9e8b2f135-k3n”}

currency
string
required

This is the specified currency to charge the card in.

country
string
amount
string
required

This is the amount to be charged

email
string
required

This is the email address of the customer

firstname
string

This is the first name of the card holder or the customer.

lastname
string

This is the last name of the card holder or the customer.

IP
string
required

IP - Internet Protocol. This represents the IP address of where the transaction is being carried out.

narration
string

This is a custom description added by the merchant.

txRef
string
required

This is the unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction

meta
string

passed as "meta": [{"metaname": "flightID", "metavalue": "123949494DC"}] . These are extra set of parameters that can be passed to your pay button.

device_fingerprint
string

This is the fingerpringt for the device being used. It can be generated using a library on whatever platform is being used.

Headers

Content-Type
string
 
Suggest Edits

Verify transaction

How to verify transactions using your own transaction reference.

 
posthttps://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify
{
	"txref": "MC-09182829",
	"SECKEY": "FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"
}
A binary file was returned

You couldn't be authenticated

{
  "status": "success",
  "message": "Tx Fetched",
  "data": {
    "txid": 157524,
    "txref": "Rave-Pages655350753556",
    "flwref": "FLWACHMOCK-1527583529027",
    "devicefingerprint": "532b4e9fa7695279392f4780b9868b9b",
    "cycle": "one-time",
    "amount": 700,
    "currency": "NGN",
    "chargedamount": 700,
    "appfee": 0,
    "merchantfee": 0,
    "merchantbearsfee": 1,
    "chargecode": "00",
    "chargemessage": "Approved. Successful.",
    "authmodel": "AUTH",
    "ip": "41.190.30.27",
    "narration": "Synergy Group",
    "status": "successful",
    "vbvcode": "N/A",
    "vbvmessage": "N/A",
    "authurl": "NO-URL",
    "acctcode": null,
    "acctmessage": null,
    "paymenttype": "account",
    "paymentid": "478",
    "fraudstatus": "ok",
    "chargetype": "normal",
    "createdday": 2,
    "createddayname": "TUESDAY",
    "createdweek": 22,
    "createdmonth": 4,
    "createdmonthname": "MAY",
    "createdquarter": 2,
    "createdyear": 2018,
    "createdyearisleap": false,
    "createddayispublicholiday": 0,
    "createdhour": 8,
    "createdminute": 45,
    "createdpmam": "am",
    "created": "2018-05-29T08:45:26.000Z",
    "customerid": 29378,
    "custphone": "N/A",
    "custnetworkprovider": "UNKNOWN PROVIDER",
    "custname": "Temi Adesina",
    "custemail": "temiloluwa_adesina@yahoo.com",
    "custemailprovider": "YAHOO",
    "custcreated": "2018-05-29T08:45:26.000Z",
    "accountid": 134,
    "acctbusinessname": "Synergy Group",
    "acctcontactperson": "Desola Ade",
    "acctcountry": "NG",
    "acctbearsfeeattransactiontime": 1,
    "acctparent": 1,
    "acctvpcmerchant": "N/A",
    "acctalias": "temi",
    "acctisliveapproved": 0,
    "orderref": "URF_1527583526904_2107135",
    "paymentplan": null,
    "paymentpage": null,
    "raveref": "RV315275835263042C559EA650",
    "amountsettledforthistransaction": 700,
    "account": {
      "id": 478,
      "account_number": "0690000037",
      "account_bank": "044",
      "first_name": "Dele Moruf",
      "last_name": "Quadri",
      "account_is_blacklisted": 0,
      "createdAt": "2018-04-05T13:30:04.000Z",
      "updatedAt": "2018-06-01T06:03:41.000Z",
      "deletedAt": null,
      "account_token": {
        "token": "flw-t0cd8f7ac849807c50-k3n-mock"
      }
    },
    "meta": [
      {
        "id": 30106,
        "metaname": "Book ID",
        "metavalue": "hsjsjsj",
        "createdAt": "2018-05-29T08:45:26.000Z",
        "updatedAt": "2018-05-29T08:45:26.000Z",
        "deletedAt": null,
        "getpaidTransactionId": 157524
      }
    ]
  }
}
{
  "status": "error",
  "message": "Transaction not found",
  "data": {
    "code": "NO_TX",
    "message": "Transaction not found"
  }
}
{
  "status": "success",
  "message": "Tx Fetched",
  "data": {
    "txid": 157906,
    "txref": "Rave-Pages247523551187",
    "flwref": "FLW-MOCK-2c72cec359fd4ea1ee6f188b364726ad",
    "devicefingerprint": "532b4e9fa7695279392f4780b9868b9b",
    "cycle": "one-time",
    "amount": 700,
    "currency": "NGN",
    "chargedamount": 700,
    "appfee": 0,
    "merchantfee": 0,
    "merchantbearsfee": 1,
    "chargecode": "00",
    "chargemessage": "Please enter the OTP sent to your mobile number 080****** and email te**@rave**.com",
    "authmodel": "AVS_VBVSECURECODE",
    "ip": "197.149.95.62",
    "narration": "CARD Transaction ",
    "status": "successful",
    "vbvcode": "00",
    "vbvmessage": "Approved. Successful",
    "authurl": "https://ravesandboxapi.flutterwave.com/mockvbvpage?ref=FLW-MOCK-2c72cec359fd4ea1ee6f188b364726ad&code=00&message=Approved. Successful&receiptno=RN1527618494727",
    "acctcode": "RN1527618494727",
    "acctmessage": null,
    "paymenttype": "card",
    "paymentid": "915",
    "fraudstatus": "ok",
    "chargetype": "normal",
    "createdday": 2,
    "createddayname": "TUESDAY",
    "createdweek": 22,
    "createdmonth": 4,
    "createdmonthname": "MAY",
    "createdquarter": 2,
    "createdyear": 2018,
    "createdyearisleap": false,
    "createddayispublicholiday": 0,
    "createdhour": 18,
    "createdminute": 28,
    "createdpmam": "pm",
    "created": "2018-05-29T18:28:14.000Z",
    "customerid": 29457,
    "custphone": null,
    "custnetworkprovider": "N/A",
    "custname": "Temi Adesina",
    "custemail": "temiloluwa_adesina@yahoo.com",
    "custemailprovider": "YAHOO",
    "custcreated": "2018-05-29T18:28:13.000Z",
    "accountid": 134,
    "acctbusinessname": "Synergy Group",
    "acctcontactperson": "Desola Ade",
    "acctcountry": "NG",
    "acctbearsfeeattransactiontime": 1,
    "acctparent": 1,
    "acctvpcmerchant": "N/A",
    "acctalias": "temi",
    "acctisliveapproved": 0,
    "orderref": "URF_1527618494622_833935",
    "paymentplan": null,
    "paymentpage": 681,
    "raveref": "RV315276184933313A412EE9C1",
    "amountsettledforthistransaction": 700,
    "card": {
      "expirymonth": "01",
      "expiryyear": "19",
      "cardBIN": "455605",
      "last4digits": "2643",
      "brand": " CREDIT",
      "card_tokens": [
        {
          "embedtoken": "flw-t1nf-ad7bc612ce74edf8ef2d348fa83ee2c9-m03k",
          "shortcode": "89b11",
          "expiry": "9999999999999"
        }
      ],
      "type": "VISA",
      "life_time_token": "flw-t1nf-ad7bc612ce74edf8ef2d348fa83ee2c9-m03k"
    },
    "meta": [
      {
        "id": 30117,
        "metaname": "Book ID",
        "metavalue": "jsjsjsjs",
        "createdAt": "2018-05-29T18:28:16.000Z",
        "updatedAt": "2018-05-29T18:28:16.000Z",
        "deletedAt": null,
        "getpaidTransactionId": 157906
      },
      {
        "id": 30118,
        "metaname": "actual_charge_amount",
        "metavalue": "700.00",
        "createdAt": "2018-05-29T18:28:16.000Z",
        "updatedAt": "2018-05-29T18:28:16.000Z",
        "deletedAt": null,
        "getpaidTransactionId": 157906
      },
      {
        "id": 30119,
        "metaname": "converted_amount",
        "metavalue": "2.02",
        "createdAt": "2018-05-29T18:28:16.000Z",
        "updatedAt": "2018-05-29T18:28:16.000Z",
        "deletedAt": null,
        "getpaidTransactionId": 157906
      }
    ]
  }
}

Body Params

txref
string
required

This is the merchant's unique transaction reference.

SECKEY
string
required

This is a unique key generated for each button created on Rave’s dashboard. It starts with a prefix FLWSECK and ends with suffix X.

Headers

Content-Type
string
required
 

After integrating Rave checkout button and a user has successfully paid, 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 chargecode returned in the response to be 00.

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

Completing a successful verification test

Rave uses two environments one for test and one for live. On test environment the keys and script url are different from the keys and script url on live. To get your test keys sign up on https://ravesandbox.flutterwave.com and retrieve your test API Keys .

<?php 

$result = array();

$postdata =  array( 
  'txref' => 'OH-AAED44',
  'SECKEY' => 'FLWSECK-bb971402072265fb156e90a3578fe5e6-X'
  );

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"https://ravesandboxapi.flutterwave.com/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://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify"; 
    
    /**
     * 
     * Method to 
     * 
     * @param flwRef - <b>flwRef - is the flutterwave reference returned for the transaction</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 include_payment_entity) 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 = "OH-AAED44", SECKEY = "FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"};
            var client = new HttpClient();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var responseMessage = client.PostAsJsonAsync("https://ravesandboxapi.flutterwave.com/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");
               
            }
curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify \
  --header 'content-type: application/json' \
  --data '{"txref":"MC-1520443531487","SECKEY":"FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"}'
Suggest Edits

Refund

This page shows you how to initiate a refund on rave.

 

Rave allows you initiate refunds for Successful transactions, there are two ways this can be acheived using the rave dashboard by:

  • Navigating to Transaction History > Click on the refund Action button.

We also allow you refund via an API, so you can send a request to refund a transaction or extend the ability to refund outside the rave dashboard

Refunding Transactions

On rave, only successful transactions can be refunded.

Suggest Edits

/refund/:post

 
posthttps://ravesandboxapi.flutterwave.com/gpx/merchant/transactions/refund
curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/gpx/merchant/transactions/refund \
  --header 'content-type: application/json'
var request = require("request");

var options = { method: 'POST',
  url: 'https://ravesandboxapi.flutterwave.com/gpx/merchant/transactions/refund',
  headers: { 'content-type': 'application/json' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://ravesandboxapi.flutterwave.com/gpx/merchant/transactions/refund")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["content-type"] = 'application/json'

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://ravesandboxapi.flutterwave.com/gpx/merchant/transactions/refund");
xhr.setRequestHeader("content-type", "application/json");

xhr.send(data);
import requests

url = "https://ravesandboxapi.flutterwave.com/gpx/merchant/transactions/refund"

headers = {'content-type': 'application/json'}

response = requests.request("POST", url, headers=headers)

print(response.text)
A binary file was returned

You couldn't be authenticated

{
  "data": {
    "AmountRefunded": 15,
    "walletId": 976,
    "createdAt": "2017-12-18T11:19:15.000Z",
    "AccountId": 832,
    "id": 76,
    "FlwRef": "FLW-MOCK-f129ce9ac1fe993091795ce08c43fb9b",
    "TransactionId": 57898,
    "status": "completed",
    "updatedAt": "2017-12-18T11:19:15.000Z"
  },
  "message": "Refunded",
  "status": "success"
}
{
  "status": "error",
  "message": "No transaction found",
  "data": {
    "code": "NO_TX",
    "message": "No transaction found"
  }
}

Body Params

ref
string
required

This is the flwRef returned in the charge response.

seckey
string
required

This is a unique key generated for each button created on Rave’s dashboard. It starts with a prefix FLWSECK and ends with suffix X.

amount
string

This is an optional parameter and should be sent if you would like to refund a partial amount.

Headers

Content-Type
string
required
 
Suggest Edits

BVN Validation

This shows how to validate your customer's BVN

 

BVN Validation is only available for Nigerian customers. It allows you verify BVN supplied by a customer and can also be used for customer KYC methods such as; validating date of birth supplied by the customer, validating the mobile number, first name & last name etc.

BVN API calls cost N50 per call. To use this service, you would need to fund your rave balance, by navigating to transfers on the dashboard and using the top up balance option. To top up use the access bank account payment option and use this test account 0690000031.

Pre-requisites for using the BVN validation service.

  1. Sign-up for a test account here, and for a live account here .

  2. Retrieve your secret key to make authenticated calls to the BVN API.

Step 1: Collect BVN from customer

In this step, you only need to collect BVN from the customer, and pass to us using the sample request below:

Sandbox Endpoint: https://ravesandboxapi.flutterwave.com/v2/kyc/bvn

Live Enpoint: https://api.ravepay.co/v2/kyc/bvn

Sample Request:

curl --request GET \
  --url https://ravesandboxapi.flutterwave.com/v2/kyc/bvn/12345678901?seckey=FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X \
  --header 'content-type: application/json'
  

What happened in the request above?

We passed the bvn of the customer as a path to the endpoint and added our secret key as a query parameter e.g. https://ravesandboxapi.flutterwave.com/v2/kyc/bvn/:bvnnumber?seckey=FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X

Sample Response:

{
    "status": "success",
    "message": "BVN-DETAILS",
    "data": {
        "bvn": "12345678901",
        "first_name": "Wendy",
        "middle_name": "Chucky",
        "last_name": "Rhoades",
        "date_of_birth": "01-01-1905",
        "phone_number": "08012345678",
        "registration_date": "01-01-1921",
        "enrollment_bank": "044",
        "enrollment_branch": "Idejo"
    }
}
 
posthttps://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/fee
curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/fee \
  --header 'content-type: application/json'
var request = require("request");

var options = { method: 'POST',
  url: 'https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/fee',
  headers: { 'content-type': 'application/json' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/fee")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["content-type"] = 'application/json'

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/fee");
xhr.setRequestHeader("content-type", "application/json");

xhr.send(data);
import requests

url = "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/fee"

headers = {'content-type': 'application/json'}

response = requests.request("POST", url, headers=headers)

print(response.text)
A binary file was returned

You couldn't be authenticated

{
  "status": "success",
  "message": "Charged fee",
  "data": {
    "charge_amount": "1052.50",
    "fee": 52.5,
    "merchantfee": "0",
    "ravefee": "52.5"
  }
}

Body Params

amount
string
required

This is the amount of the product or service to charged from the customer

PBFPubKey
string
required

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’.

currency
string
required

This is the specified currency to charge the card in.

ptype
string

This is an optional parameter to be used when the payment type is account payment. A value of 2 is to be passed to the endpoint.

card6
string

This can be used only when the user has entered first 6digits of their card number, it also helps determine international fees on the transaction if the card being used is an international card

Headers

Content-Type
string
required
 

Understanding Get Fees response

The successful response for get fees endpoint is broken down this way:

  • data.charge_amount: This the total amount to be charged, total amount = amount + fee

  • data.fee: This is a cumulative of the merchantfee (if applicable) + ravefee

  • data.merchantfee: This is the merchant fee on the transaction, it is applicable when using a subdomain. Subdomains allow you white-label rave, and offer it as a customised service to your merchant, we allow you set a markup fee on it and earn transaction fees. In this scenario the merchant-fee would be the subdomain markup fee if applicable.

  • data.ravefee: This is the fee charged per transaction by rave.

Suggest Edits

List of Direct Bank Charge

This page describes a list of banks that can be charged on rave.

 
gethttps://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/flwpbf-banks.js?json=1
curl --request GET \
  --url 'https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/flwpbf-banks.js?json=1' \
  --header 'content-type: application/json'
var request = require("request");

var options = { method: 'GET',
  url: 'https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/flwpbf-banks.js',
  qs: { json: '1' },
  headers: { 'content-type': 'application/json' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/flwpbf-banks.js?json=1")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["content-type"] = 'application/json'

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("GET", "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/flwpbf-banks.js?json=1");
xhr.setRequestHeader("content-type", "application/json");

xhr.send(data);
import requests

url = "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/flwpbf-banks.js"

headers = {'content-type': 'application/json'}

response = requests.request("GET", url, headers=headers)

print(response.text)
A binary file was returned

You couldn't be authenticated

[
  {
    "bankname": "ACCESS BANK NIGERIA",
    "bankcode": "044",
    "internetbanking": false
  },
  {
    "bankname": "ECOBANK NIGERIA PLC",
    "bankcode": "050",
    "internetbanking": false
  },
  {
    "bankname": "STERLING BANK PLC",
    "bankcode": "232",
    "internetbanking": false
  },
  {
    "bankname": "ZENITH BANK PLC",
    "bankcode": "057",
    "internetbanking": false
  },
  {
    "bankname": "FIRST CITY MONUMENT BANK PLC",
    "bankcode": "214",
    "internetbanking": false
  },
  {
    "bankname": "SKYE BANK PLC",
    "bankcode": "076",
    "internetbanking": false
  },
  {
    "bankname": "FSDH Merchant Bank Limited",
    "bankcode": "601",
    "internetbanking": false
  }
]

Headers

Content-Type
string
required
 

This endpoint provides a list of banks that can be charged on rave. It returns a key/value pair internetbanking in the response, if set to false it means the account can be charged using the direct account method, if set to true it means the account would be charged using the internet banking flow

Suggest Edits

Exchange Rates

 

Rave allows your convert currencies real time via api's to charge your customers in alternate currencies. See table below show possible exchange rates combination via the API

origin currency
destination currency

GHS

USD

USD

KES

USD

GHS

USD

NGN

GBP

EUR

GBP

NGN

EUR

NGN

Suggest Edits

/rates/forex:/input

This page allows you do conversion of multi currencies on rave

 
posthttps://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/forex
curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/forex \
  --header 'content-type: application/json'
var request = require("request");

var options = { method: 'POST',
  url: 'https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/forex',
  headers: { 'content-type': 'application/json' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/forex")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["content-type"] = 'application/json'

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/forex");
xhr.setRequestHeader("content-type", "application/json");

xhr.send(data);
import requests

url = "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/forex"

headers = {'content-type': 'application/json'}

response = requests.request("POST", url, headers=headers)

print(response.text)
A binary file was returned

You couldn't be authenticated

{
    "status": "success",
    "message": "Rate Fetched",
    "data": {
        "rate": 385,
        "origincurrency": "USD",
        "destinationcurrency": "NGN",
        "lastupdated": "2017-05-29 13:03:35",
        "converted_amount": 7707700,
        "original_amount": "20020"
    }
}
{
    "status": "success",
    "message": "Rate Fetched",
    "data": {
        "rate": 385,
        "origincurrency": "USD",
        "destinationcurrency": "NGN",
        "lastupdated": "2017-05-29 13:03:35"
    }
}

Body Params

SECKEY
string

This is a unique key generated for each button created on Rave’s dashboard. It starts with a prefix FLWSECK and ends with suffix X.

origin_currency
string
required

This is the currency to convert from

destination_currency
string
required

This is the currency to convert to

amount
string

This is the amount being converted, it is an optional field.

Headers

Content-Type
string
required
 
Suggest Edits

Test cards

Test cards for Rave

 

Using Test cards

To use test cards on rave you would need to sign up on the test environment here and:

  1. create test keys and change your public key in your inlineJS or html embed.

  2. change your script url to test script url: <script type="text/javascript" src="https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/flwpbf-inline.js"></script>

Test MasterCard 3DSecure authentication

5438898014560229
cvv 789
Expiry: 09/19
Pin 3310
otp 12345

Test Mastercard PIN authentication

5399 8383 8383 8381
cvv 470
Expiry: 10/22
Pin 3310
otp 12345

Test Noauth Visa Card

4751763236699647
Expiry: 09/21

Test Noauth VisaCard

4242 4242 4242 4242
cvv: 812
Expiry: 01/19

Test Verve Card

5061460410120223210
Expiry Month 12
Expiry Year 21
cvv: 780
Pin: 3310
otp 12345

Test VisaCard (Local)

4187427415564246
cvv: 828
Expiry: 09/19
Pin 3310
otp 12345

Test VisaCard (International)

4556052704172643
cvv: 899
Expiry: 01/19

Test American Express Card (International)

344173993556638
cvv: 828
Expiry: 01/22

Test card Declined

5143010522339965
cvv 276
Expiry: 08/19
Pin 3310

Test Card Fraudulent

5590131743294314
cvv 887
Expiry: 11/20
Pin 3310
otp 12345

Test Card Insufficient Funds

5258585922666506
cvv 883
Expiry: 09/19
Pin 3310
otp 12345

Pre-authorization Test Card

5377283645077450
cvv 789
Expiry: 09/19
Pin 3310

Suggest Edits

Test Bank Accounts

Rave Test Bank accounts

 

Here are the test account details on rave.

Access Bank

Account number: 0690000031
otp: 12345

Providus Bank

Account number: 5900102340, 5900002567
otp: 12345

Suggest Edits

List Transactions

This helps you list all transactions on an account in rave.

 
posthttps://ravesandboxapi.flutterwave.com/v2/gpx/transactions/query
{
  "seckey": "Merchant secret key",
  "from": "2018-01-01",
  "to": "2018-03-30",
  "currency": "NGN",
  "status": "successful"
}
A binary file was returned

You couldn't be authenticated

{
  "status": "success",
  "message": "QUERIED-TRANSACTIONS",
  "data": {
    "page_info": {
      "total": 1,
      "current_page": 1,
      "total_pages": 1
    },
    "transactions": [
      {
        "id": "121257",
        "transaction_reference": "MC-1522438968515",
        "processor_reference": "FLW-MOCK-5e52517f0b314c73c56992dc620d8998",
        "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
        "transaction_cycle": "one-time",
        "amount": 10,
        "currency": "NGN",
        "amount_charged": 10,
        "rave_fee": 0,
        "merchant_fee": 0,
        "merchant_bore_fee": 1,
        "processor_response_code": "00",
        "processor_response_message": "Charge successful. Please enter the OTP sent to your mobile number 080****** and email te**@rave**.com",
        "auth_model": "VBVSECURECODE",
        "customer_ip": "::ffff:127.0.0.1",
        "narration": "Card Transaction",
        "status": "successful",
        "processor_verification_url": "http://flw-pms-dev.eu-west-1.elasticbeanstalk.com/mockvbvpage?ref=FLW-MOCK-5e52517f0b314c73c56992dc620d8998&code=00&message=Approved. Successful&receiptno=RN1522438999815",
        "processor_vbv_response_code": "00",
        "processor_vbv_response_message": "successful",
        "processor_acct_response_code": null,
        "processor_acct_response_message": null,
        "payment_entity": "card",
        "payment_entity_id": "1057",
        "fraud_status": "ok",
        "date_created": "2018-03-30T19:43:19.000Z",
        "unique_reference": "URF_1522438999774_1285835",
        "amount_due_merchant": 10,
        "customer": {
          "id": "21887",
          "customer_email": "desola.ade1@gmail.com",
          "customer_phonenumber": "0902620185",
          "customer_fullname": "temi desola",
          "date_created": "2018-03-30T19:43:19.000Z"
        },
        "card": {
          "id": "1057",
          "masked_pan": "543889*********0229",
          "expiry_year": "20",
          "expiry_month": "12",
          "bin": "543889",
          "type": "MASTERCARD",
          "country": "EGYPT EG",
          "issuer_info": "MASTERCARD MASHREQ BANK CREDITSTANDARD",
          "date_created": "Thu Feb 15 2018 11:13:45 GMT+0000 (UTC)"
        },
        "merchant": {
          "id": "134",
          "business_name": "Synergy Group",
          "country": "NG",
          "contact_person": "Desola Ade"
        }
      }
    ]
  }
}
{
    "status": "error",
    "message": "SEC KEY IS INVALID",
    "data": null
}
{
    "status": "error",
    "message": "SEC KEY IS REQUIRED",
    "data": null
}

Body Params

seckey
string
required

This is the merchant secret key

from
string

This is the specified date to start the list from e.g. 2018-01-01

to
string

The is the specified end period for the search e.g. 2018-03-20

page
string

This is the page number to retrieve e.g. setting 1 retrieves the first page.

customer_email
string

This is the email of the customer who carried out a transaction. Use for more specific listing.

status
string

This is the transaction status, can be set to successful, failed etc.

customer_fullname
string

This is the combination of the customer first name and last name passed to rave during transaction.

transaction_reference
string

This is the merchant reference ties to a transaction. Use for more specific searches

currency
string

This is the currency the transaction list should come in e.g. NGN, USD, EUR, GHS, KES, GBP and ZAR

Headers

Content-Type
string
required
 
Suggest Edits

List of Banks for Transfer

This gets a list of banks you can transfer to in NGN, GHS, KES

 
gethttps://ravesandboxapi.flutterwave.com/banks
curl --request GET \
  --url https://ravesandboxapi.flutterwave.com/banks \
  --header 'content-type: application/json'
var request = require("request");

var options = { method: 'GET',
  url: 'https://ravesandboxapi.flutterwave.com/banks',
  headers: { 'content-type': 'application/json' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://ravesandboxapi.flutterwave.com/banks")

http = Net::HTTP.new(url.host, url.port)
http