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"
}
}
Updated less than a minute ago