Verify Payment

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

👍

Verify endpoint for Live and Test environment.

Quidpay has two base url's one for live environment and the other for test environment, you would need to use keys from live when using the live base url and vice versa.

Live verify URL: https://api.ravepay.co/flwv3-pug/getpaidx/api/v2/verify

Test verify URL: https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify

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"}'
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"; //url to staging server. please make sure to change when in production.

// 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 this to production url when you go live
header = {'Content-Type': 'application/json'}
payload = {
	'SECKEY' => 'FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X', //secret key from pay button generated
	'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 this to production url when you go live
   data := make(map[string]string)
   data["txref"] = "MC-1520443531487"
   //secret key from pay button generated on quidpay dashboard
   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
    private final String VERIFY_ENDPOINT = "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/v2/verify"; 
//please make sure to change this to production url when you go live
    
    /**
     * 
     * 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 verify payment on your server.

This examples 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://ravesandboxapi.flutterwave.com/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)) {
          // transaction was successful...
  			 // please check other things like whether you already gave value for this ref
          // if the email matches the customer who owns the product etc
          //Give Value and return to Success page
        } else {
            //Dont Give Value and return to Failure page
        }
    }
		else {
      die('No reference supplied');
    }

?>

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"
  }
}