Verify Payment

Verify the payment with quidpay.

After integrating Quidpay checkout button and a user has successfully paid, you need to verify that the payment was successful with Quidpay before giving value to your customer on your website.

Although the Quidpay 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 are sample code snippets showing how to implement server-side validation in different programming languages

πŸ‘

Completing a successful verification test

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

❗️

Invalid Secret Key

This happens when you are using keys from the live environment with verify URL from the test environment. The snippet below currently uses test verify URL from the test environment.

To avoid the invalid secret key error, you can see how to set up a test and live environment here.

Test verify URL:  https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify 
Live verify URL: https://api.ravepay.co/flwv3-pug/getpaidx/api/v2/verify 
curl --request POST \
  --url https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/xrequery \
  --header 'content-type: application/json' \
  --data '{"txref":"MC-1520443531487","SECKEY":"FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X", "include_payment_entity": 1}'
const unirest = require("unirest"); //unirest is an http request library so any other preferred library can be used.

var payload =
 {
  "SECKEY": "FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X",
  "txref": "MC-1520443531487"
};

var server_url = "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify"; 
//please make sure to change this to production url when you go live

//make a post request to the server
unirest.post(server_url)
      .headers({'Content-Type': 'application/json'})
      .send(payload)
      .end(function(response) {
      		//check status is success.
          if (response.body.data.status === "successful" && response.body.data.chargecode == 00) {
              //check if the amount is same as amount you wanted to charge just to be very sure
              if (response.body.data.amount === "2000") {
                  console.log("Payment successful");
                  //then give value for the payment
              }
          }
      });
<?php

use Unirest\Request\Body;

$data = array('txref' => 'MC-1520443531487',
  'SECKEY' => 'FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X' //secret key from pay button generated on quidpay dashboard
);



  // make request to endpoint using unirest.
  $headers = array('Content-Type' => 'application/json');
  $body = Unirest\Request\Body::json($data);
  $url = "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify"; //please make sure to change this to production url when you go live

// Make `POST` request and handle response with unirest
  $response = Unirest\Request::post($url, $headers, $body);
  
  //check the status is success
  if ($response->body->data->status === "success" && $response->body->data->chargecode === "00") {
      //confirm that the amount is the amount you wanted to charge
      if ($response->body->data->amount === 100) {
          echo("Give value");
      }
  }

?>
import unirest #unirest is a http library. You can use any http library you prefer
import json

data = {
"txref": "MC-1520443531487", #this is the reference from the payment button response after customer paid.
"SECKEY": "FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X" #this is the secret key of the pay button generated
}


#function called after your request gets a response from quidpay's server
def callback_function(response):
    #confirm that the response for the transaction is successful
    if response.body['data']['status'] == 'success':
      #confirm that the amount for that transaction is the amount you wanted to charge
      	if response.body['data']['chargecode'] == '00'
        
      	  if response.body['data']['amount'] == 2000:
        	    print("Payment successful then give value");
      

#this is the url of the staging server. Please make sure to change to that of production server when you are ready to go live.
url = "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify"

#make the http post request to our server with the parameters
thread = unirest.post(url, headers={"Content-Type":"application/json"}, params=data, callback=callback_function)
require 'net/http'
require 'uri'
require 'json'


uri = URI.parse("https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify") // Please make sure to change to that of production server when you are ready to go live.
header = {'Content-Type': 'application/json'}
payload = {
  'SECKEY' => 'FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X', //merchant secret key
	'txref'=> 'MC-1520443531487' //payment reference
}


http = Net::HTTP.new(uri.host)
request = Net::HTTP::Post.new(uri.request_uri, header)
request.body = payload.to_json

response = http.request(request);
body = JSON.parse(response.body);
if body['data'] && body['data']['status'] == 'successful'&& body['data']['chargecode'] == '00'
	#check amount and do other stuff with response
	# body['data']['amount'] == amount_to_give_value_for
end
package main

import (
   "bytes"
   "io/ioutil"
   "encoding/json"
   "net/http"
   "strings"
)

func main() {
   url := "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify" //Please make sure to change to that of production server when you are ready to go live.

   data := make(map[string]string)
   data["txref"] = "MC-1520443531487"
   // merchant secret key 
   data["SECKEY"] = "FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X"

   //convert data map to json byte[]
   jsonData, _ := json.Marshal(data)

   // make request to endpoint
   client := &http.Client{}
   request, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
   request.Header.Set("Content-Type", "application/json")

   // Make `POST` request and handle response
   response, err := client.Do(request)

   if err == nil && response != nil {
      responseJSON, _ := ioutil.ReadAll(response.Body)
      var responseData map[string]interface{}
      err = json.Unmarshal(responseJSON, &responseData)
      
      if err == nil {
         if strings.Compare(responseData["data"]["status"].(string), "successful") == 0 {
            amount, _ := responseData["data"].(map[string]interface{})["amount"].(float64)
            if amount == 2000 {
               print("Give value")
            }
         }
      }
   }
}
//Endpoint to verify transaction, Please make sure to change to that of production server when you are ready to go live.
    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 quidpay
        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; //please make sure to change this to production url when you go live
            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");
               
            }

How to pass your reference from the client to verify a transaction

This example shows you how to get the txRef from the callback response on the client page and send it to a server page to verify and log the transaction before giving value.

<input type="submit" style="cursor:pointer;" value="Pay Now" id="submit" />


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/flwpbf-inline.js"></script>
<script type="text/javascript">
  document.addEventListener("DOMContentLoaded", function(event) {
    document.getElementById('submit').addEventListener('click', function () {

    var flw_ref = "", chargeResponse = "", trxref = "FDKHGK"+ Math.random(), API_publicKey = "FLWPUBK-4e89f89528693434b14647e83598d546-X";

    getpaidSetup(
      {
        PBFPubKey: API_publicKey,
      	customer_email: "[email protected]",
      	amount: 2000,
      	customer_phone: "234099940409",
      	currency: "NGN",
      	payment_method: "both",
      	txref: "quidpay-123456",
      	meta: [{metaname:"flightID", metavalue: "AP1234"}],
        onclose:function(response) {
        },
        callback:function(response) {
          txref = response.tx.txRef, chargeResponse = response.tx.chargeResponseCode;
          if (chargeResponse == "00" || chargeResponse == "0") {
            window.location = "https://your_URL/paymentverification.php?txref="+txref; //Add your success page here
          } else {
            window.location = "https://your_URL/paymentverification.php?txref="+txref;  //Add your failure page here
          }
        }
      }
    );
    });
  });
</script>
<?php
    if (isset($_GET['txref'])) {
        $ref = $_GET['txref'];
        $amount = ""; //Correct Amount from Server
        $currency = ""; //Correct Currency from Server

        $query = array(
            "SECKEY" => "Your Secret Key",
            "txref" => $ref
        );

        $data_string = json_encode($query);
                
        $ch = curl_init('https://api.ravepay.co/flwv3-pug/getpaidx/api/v2/verify ');                                                                      
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);                                              
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));

        $response = curl_exec($ch);

        $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $header = substr($response, 0, $header_size);
        $body = substr($response, $header_size);

        curl_close($ch);

        $resp = json_decode($response, true);

      	$paymentStatus = $resp['data']['status'];
        $chargeResponsecode = $resp['data']['chargecode'];
        $chargeAmount = $resp['data']['amount'];
        $chargeCurrency = $resp['data']['currency']

        if (($chargeResponsecode == "00" || $chargeResponsecode == "0") && ($chargeAmount == $amount)  && ($chargeCurrency == $currency)) {
          //Give Value and return to Success page
        } else {
            //Dont Give Value and return to Failure page
        }
    }

?>

Verify payment 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": "[email protected]",
        "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
            }
        ]
    }
}
{
    "status": "success",
    "message": "Tx Fetched",
    "data": {
        "txid": 124161,
        "txref": "MC-1522914089167",
        "flwref": "ACHG-1522914115739",
        "devicefingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
        "cycle": "one-time",
        "amount": 10,
        "currency": "NGN",
        "chargedamount": 10,
        "appfee": 0,
        "merchantfee": 0,
        "merchantbearsfee": 1,
        "chargecode": "00",
        "chargemessage": "Pending OTP validation",
        "authmodel": "AUTH",
        "ip": "::ffff:127.0.0.1",
        "narration": "Synergy Group",
        "status": "successful",
        "vbvcode": "N/A",
        "vbvmessage": "N/A",
        "authurl": "NO-URL",
        "acctcode": "00",
        "acctmessage": "Approved Or Completed Successfully",
        "paymenttype": "account",
        "paymentid": "2",
        "fraudstatus": "ok",
        "chargetype": "normal",
        "createdday": 4,
        "createddayname": "THURSDAY",
        "createdweek": 14,
        "createdmonth": 3,
        "createdmonthname": "APRIL",
        "createdquarter": 2,
        "createdyear": 2018,
        "createdyearisleap": false,
        "createddayispublicholiday": 0,
        "createdhour": 7,
        "createdminute": 41,
        "createdpmam": "am",
        "created": "2018-04-05T07:41:53.000Z",
        "customerid": 22536,
        "custphone": "09026420185",
        "custnetworkprovider": "AIRTEL",
        "custname": "temi desola",
        "custemail": "[email protected]",
        "custemailprovider": "COMPANY EMAIL",
        "custcreated": "2018-04-05T07:38:39.000Z",
        "accountid": 134,
        "acctbusinessname": "Synergy Group",
        "acctcontactperson": "Desola Ade",
        "acctcountry": "NG",
        "acctbearsfeeattransactiontime": 1,
        "acctparent": 1,
        "acctvpcmerchant": "N/A",
        "acctalias": "temi",
        "acctisliveapproved": 0,
        "orderref": "URF_1522914113761_6077035",
        "paymentplan": null,
        "paymentpage": null,
        "raveref": "RV31522914113478DA28603ABF",
        "amountsettledforthistransaction": 10,
        "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-04-05T07:42:28.000Z",
            "deletedAt": null,
            "account_token": {
                "token": "flw-t0e1bb79f967612fc1-k3n-mock"
            }
        },
        "meta": []
    }
}
{
  "status": "error",
  "message": "No transaction found",
  "data": {
    "code": "NO TX",
    "message": "No transaction found"
  }
}