Integrating Flutterwave’s secure payment gateway into your website using PHP
Published: 2022-10-23 01:43:31


![Flutterwave banner](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jfotlucsuy9zhhmqe8sm.png)

Flutterwave is one of the leading payment gateways in Nigeria and Africa. If you have a business that requires secure online payment from customers, then I suggest you try them out. You can collect payments in USD, GBP, EUR, NGN and a host of other African currencies. They have so many payment solutions with which you can complete your payment easily and secured. In this guide, I’ll be showing you how to integrate rave into your website. So let’s dive in.

**A. Create a Flutterwave account**
If you have not created an account yet, go to https://app.flutterwave.com/register to create an account. If you already have, login with your account details. Navigate to the settings page and click on the API keys tabs to get your public and secret key, we are going to need it.

![Flutterwave register page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bqyoe24r7jeb8kuf8jck.jpg)


![Flutterwave settings page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qbuijyerzb2jku4otf3a.jpg)


**B. Collect payment information and charge the customer**

We have to collect the user’s email, the amount that will be paid and the currency if the payment will not be in naira(NGN). You can provide an input tag for the user to fill in the amount and their email or you can use a hidden input tag if the amount to be paid is static or you already have the user’s info. There are four (4) methods of doing this as seen in Flutterwave documentation;

(i) Flutterwave inline (ii) Flutterwave standard (iii) HTML chekout (iv) Direct charge

Let's look at integrating by Flutterwave standard method using the PHP language. You can refer to the documentation on the other methods: https://developer.flutterwave.com/docs/collecting-payments/overview

**Flutterwave standard**

Below is a sample form page I created with HTML and CSS (bootstrap) for demonstration. Here the user is to fund his wallet on a telecom (VTU) website.

```
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Fund Wallet</title>
    <link rel="stylesheet" href="bootstrap.min.css" />
</head>
<body>
    <div class="container-xxl flex-grow-1 container-p-y">
     <div class="row">
       <div class="col-xl">
         <div class="card mb-4">
           <div class="card-header d-flex justify-content-between align-items-center">
             <h5 class="mb-0">Fund account with Flutterwave</h5>
            </div>
            <div class="card-body">
              <form method="post" action="process_payment.php" id="form">                  
                <div class="mb-3">
                  <label class="form-label" for="phone-number">Amount*</label>
                   <input type="tel" name="amount" class="form-control" required>
                </div>

                <div class="mb-3">
                  <input type="email" name="email" class="form-control"  required>
                </div>
                <div class="mb-3">
                  <input type="tel" name="phone" class="form-control" required>
                </div>

                <button type="submit" class="btn btn-primary action" name="pay"> Pay</button>
              </form>
            </div>
          </div>
        </div>

      </div>          
   </div>
  <script src="jquery.min.js"></script>
  <script src="bootstrap.min.js"></script>
</body>
</html>

```

Here the user information is submitted in a form to a backend script. I named my own script process_payment.php. This script will call the https://api.ravepay.co/flwv3-pug/getpaidx/api/v2/hosted/pay endpoint.

```
<?php

if (isset($_POST['transfer'])) {
  extract($_POST);
  
   $curl = curl_init();

    $customer_email = $email; 
    $amount_pay = $amount;
    $currency = "NGN";
    $txref = "rave" . uniqid(); // ensure you generate unique references per transaction.
    // get your public key from the dashboard.
    $PBFPubKey = "FLWPUBK-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-X"; 
    $redirect_url = "https://www.fltwvapp.herokuapp.com/redirect.php?email=$email&amount=$amount_pay"; // Set your own redirect URL

     curl_setopt_array($curl, array(
      CURLOPT_URL => "https://api.ravepay.co/flwv3-pug/getpaidx/api/v2/hosted/pay",
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_CUSTOMREQUEST => "POST",
      CURLOPT_POSTFIELDS => json_encode([
        'amount'=>$amount_pay,
        'customer_email'=>$customer_email,
        'currency'=>$currency,
        'txref'=>$txref,
        'PBFPubKey'=>$PBFPubKey,
        'redirect_url'=>$redirect_url,
      ]),
      CURLOPT_HTTPHEADER => [
        "content-type: application/json",
        "cache-control: no-cache"
      ],
    ));

    $response = curl_exec($curl);
    $err = curl_error($curl);

    if($err){
      // there was an error contacting the rave API
      die('Curl returned error: ' . $err);
    }

    $transaction = json_decode($response);

    if(!$transaction->data && !$transaction->data->link){
      // there was an error from the API
      print_r('API returned error: ' . $transaction->message);
    }

    // redirect to page so User can pay

    header('Location: ' . $transaction->data->link); 
}
?>

```

Assign the email and amount submitted by the POST request to variables. Make sure to validate those variables to make sure that the user has not done anything dubious. Generate a unique transaction reference. It is advisable to store this reference, amount, and any other information you may wish to collect about the product or service to be paid for in a database and then set the transaction status to pending. Paste in your public key and specify your redirect URL.

**Note:** _Your redirect URL must be hosted on a web host_ 

The curl is used to call the https://api.ravepay.co/flwv3-pug/getpaidx/api/v2/hosted/pay. The amount, email, phone, currency, unique reference, your public key and redirect URL is passed into it as well and then executed. Use json_decode to decode the response returned by the API and store in a transaction variable. Check the transaction object to see if it has the data property and if the link that will process the payment also exists(this link is from Flutterwave and not the one you provided earlier. don’t worry about it). We are now redirected to the link which is the checkout page for our card details.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3e9hsqdfbm0p8jeco61d.jpg)

When the transaction is completed, Flutterwave will now redirect to the URL provided earlier.

**C. Verify the payment status**

After the transaction has been completed, use the reference returned in the redirect URL to check for the status of the transaction. Below is the redirect.php file which does the verification for us.

```
<?php

if (isset($_GET['txref'])) {
  $ref = $_GET['txref'];
  $amount = $_GET['amount']; //Get the correct amount of your product
  $email = $_GET['email'];
  $currency = "NGN"; //Correct Currency from Server

  $query = array(
    "SECKEY" => "FLWSECK-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-X",
    "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)) {
    // 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
    //   var_dump($resp);
    header('location: success.html');
  } else {
    //Dont Give Value and return to Failure page
    // var_dump($resp);
    header('location: error.html');
  }
}
else {
  die('No reference supplied');
}

?>

```

Use $_GET to collect the reference and other values passed to the redirect URL. Encode your secret key gotten from your dashboard and your txRef as JSON and call the https://api.ravepay.co/flwv3-pug/getpaidx/api/v2/verify endpoint with it to verify the payment. Decode the response returned. Check the chargecode, amount and currency to make sure they synchronize. Use the txRef to update the status of the transaction, redirect the user to the success or error page or do anything you’d like to do after a successful or failed transaction. 

**D. Use webhook to ensure stability**

Webhooks are used for notifying our server that a payment event has just occurred. A web application implementing Webhooks will POST a message to a URL when certain things happen. Webhooks are optional. Without it, everything will work but it makes our application stable. 

Let’s say that a user completes his payment successfully but maybe due to poor network signal was unable to get to your designated URL, or maybe closes the browser or the payment tab mistakenly, it will be difficult to ascertain the status of his payment unless we visit the dashboard manually and crosscheck. But with webhooks, you can still know if the transaction succeeded or failed and then you can update your transaction table and give or deny the user access to value being paid for whenever he comes back.

Another case is when the user is subscribing to a service and will be billed periodically, whenever the user is billed, the status of the transaction is sent to your webhook URL. You can now keep offering the service to the user or stop if he wasn’t billed. Other examples are for events — like getting paid via mobile money or USSD where the transaction is completed outside your application.

Enable webhook from your dashboard


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/krhokbhhz67jog3hqopm.jpg)

Below is a sample webhook from the documentation page.

```
<?php

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

// retrieve the signature sent in the reques header's.
$signature = (isset($_SERVER['verif-hash']) ? $_SERVER['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...
    // Update your database and set the transaction status to successful
}
exit();

```

**Note:** Your redirect page and webhook page might be doing the same thing. In either your redirect page or webhook, you might first check to see if the payment status has been updated to avoid giving value twice.