Skip to content

Setup SDK on CodeIgniter Project.

Abraham Olaobaju edited this page May 31, 2023 · 5 revisions

Configuration

Create a .env file as the package does not acknowledge the env file that ships with CodeIgniter 4. Ensure the following keys/value pairs are present in the .env file.

SECRET_KEY=FLWSECK_TEST-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-X
PUBLIC_KEY=FLWPUBK_TEST-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-X
ENCRYPTION_KEY=FLWSECK_TESTXXXXXXXXXX
ENV=staging

Installation via Composer.

Install the Software Development Kit via the command.

composer require flutterwavedev/flutterwave-v3

Initialization

Create a Controller to initiate a Payment in your App\Controllers directory. If you do not wish to store your keys in a .env file. you can review the wiki on how to setup custom configurations here.

<?php

# file path: app/Controllers/PaymentController.php

namespace App\Controllers;

use App\ThirdParty\Flutterwave\Config; # use if you do not wish to use the .env file.
use App\ThirdParty\Flutterwave\EventHandler;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Flutterwave\Flutterwave;
use Flutterwave\Util\Currency;
use Psr\Log\LoggerInterface;

class PaymentController extends BaseController
{
    public function initController(
        RequestInterface $request,
        ResponseInterface $response,
        LoggerInterface $logger
    ) {
        parent::initController($request, $response, $logger);

        if (! $this->request->isSecure()) {
            $this->forceHTTPS();
        }

        Flutterwave::bootstrap(); # To read the environment variables in .env and load other flutterwave services.
    }

    # Your business logic.
}

Create a route to receive requests, initiate payment and render payment modal.

Ensure all the data fields are passed to the payload builder before initiating the payment.

<?php

# file path: app/Controllers/PaymentController.php

namespace App\Controllers;

use App\ThirdParty\Flutterwave\Config; # use if you do not wish to use the .env file.
use App\ThirdParty\Flutterwave\EventHandler;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Flutterwave\Flutterwave;
use Flutterwave\Util\Currency;
use Psr\Log\LoggerInterface;

class PaymentController extends BaseController
{
    public function initController( ... ) {
        ...
    }

    public function index()
    {
        $session = \Config\Services::session();
        # prefix can be company initials.
        $transactionPrefix = "FLW_";
        $overrideRef = false;
        $redirect_url = base_url('callback');
        $data = [
            'amount' => 2000,
            'payment_options' => 'card,ussd,mobilemoneyghana',
            'currency' => Currency::NGN,
            'logo' => '',
            'title' => 'Payment Sample',
            'country' => 'NG',
            'email' => '[email protected]',
            'firstname' => 'john',
            'lastname' => 'doe',
            'description' => 'Payment for some rando',
            'phonenumber' => '+2348000000000',
            'pay_button_text' => 'Pay Now',
            'redirect_url' => $redirect_url
        ];

        # TODO: validate data input.

        $payment = new Flutterwave( $transactionPrefix, $overrideRef);

        $payment
        ->eventHandler(new EventHandler())
        ->setAmount($data['amount'])
        ->setPaymentOptions($data['payment_options']) // value can be a card, account or both
        ->setDescription($data['description'])
        ->setLogo($data['logo'])
        ->setTitle($data['title'])
        ->setCountry($data['country'])
        ->setCurrency($data['currency'])
        ->setEmail($data['email'])
        ->setFirstname($data['firstname'])
        ->setLastname($data['lastname'])
        ->setPhoneNumber($data['phonenumber'])
        ->setPayButtonText($data['pay_button_text'])
        ->setRedirectUrl( $data['redirect_url'])
        // ->setMetaData(array('metaname' => 'SomeDataName', 'metavalue' => 'SomeValue')) // can be called multiple times. Uncomment this to add meta datas
        // ->setMetaData(array('metaname' => 'SomeOtherDataName', 'metavalue' => 'SomeOtherValue')) // can be called multiple times. Uncomment this to add meta datas
        ->initialize();

        $session->set([ 'p' => $payment ]);
    }
}

Setup Callback function to verify the payment.

<?php

# file path: app/Controllers/PaymentController.php

namespace App\Controllers;

use App\ThirdParty\Flutterwave\Config; # use if you do not wish to use the .env file.
use App\ThirdParty\Flutterwave\EventHandler;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Flutterwave\Flutterwave;
use Flutterwave\Util\Currency;
use Psr\Log\LoggerInterface;

class PaymentController extends BaseController
{
    public function initController( ... ) {
       ...
    }

    public function index()
    {
        ...
    }

    public function callback() {
        # if it is not a get request deny.
        if(!$this->request->is('get')) {
            return 'Unauthorized access';
        }

        $session = \Config\Services::session();

        if( $session->get( 'p' ) === null ) {
            return redirect()->to( site_url() );
        }

        $tx_ref = $this->request->getVar('tx_ref');
        $transaction_id = $this->request->getVar('transaction_id');
        $payment = $session->get('p');

        if( $this->request->getVar('cancelled') !== null && $this->request->getVar('cancelled') === 'cancelled' ) {
            $payment
            ->eventHandler( new EventHandler() )
            ->paymentCanceled($tx_ref);
        }

        $payment
        ->eventHandler( new EventHandler() )
        ->requeryTransaction( $transaction_id );
    }

    public function webhook() {
        # if it is not a post request deny.
        if(!$this->request->is('post')) {
            return 'Unauthorized access';
        }
    }
}

Setup a Payment Event Handler.

The handler would handler payment events. you are to add actions that would be triggered on each event.

<?php

# file path: app/ThirdParty/Flutterwave/EventHandler.php

namespace App\ThirdParty\Flutterwave;

use Flutterwave\EventHandlers\EventHandlerInterface;

class EventHandler implements EventHandlerInterface
{
    /**
     * This is called when the Rave class is initialized
     * */
    public function onInit($initializationData): void
    {
        // Save the transaction to your DB.
    }

    /**
     * This is called only when a transaction is successful
     * */
    public function onSuccessful($transactionData): void
    {
        // Get the transaction from your DB using the transaction reference (txref)
        // Check if you have previously given value for the transaction. If you have, redirect to your successpage else, continue
        // Comfirm that the transaction is successful
        // Confirm that the chargecode is 00 or 0
        // Confirm that the currency on your db transaction is equal to the returned currency
        // Confirm that the db transaction amount is equal to the returned amount
        // Update the db transaction record (includeing parameters that didn't exist before the transaction is completed. for audit purpose)
        // Give value for the transaction
        // Update the transaction to note that you have given value for the transaction
        // You can also redirect to your success page from here
        if ($transactionData->status === 'successful') {

            $session = \Config\Services::session();
            $currency = $session->get('p')->getCurrency();
            $amount   = $session->get('p')->getAmount();

            if ($transactionData->currency === $currency && floatval( $transactionData->amount )  === floatval( $amount ) ) {
                # TODO: replace this a custom action.
                echo "Successful! replace this with an action on Line:".__LINE__. " in ". __CLASS__;
                $session->destroy();
            }

            if ($transactionData->currency === $currency && floatval( $transactionData->amount ) < floatval( $amount ) ) {
                # TODO: replace this a custom action.
                echo "Partial Payment Made ! replace this with an action on Line:".__LINE__. " in ". __CLASS__;
                $session->destroy();
            }

            if ($transactionData->currency !== $currency && floatval( $transactionData->amount ) === floatval( $amount ) ) {
                # TODO: replace this a custom action.
                echo "Currency mismatch. please look into it ! replace this with an action on Line:".__LINE__. " in ". __CLASS__;
                $session->destroy();
            }

        } else {
            $this->onFailure($transactionData);
        }
    }

    /**
     * This is called only when a transaction failed
     * */
    public function onFailure($transactionData): void
    {
        $session = \Config\Services::session();
        // Get the transaction from your DB using the transaction reference (txref)
        // Update the db transaction record (includeing parameters that didn't exist before the transaction is completed. for audit purpose)
        // You can also redirect to your failure page from here.
        # TODO: replace this a custom action.
        echo "Payment failed ! replace this with an action on Line:".__LINE__. " in ". __CLASS__;
        $session->destroy();
    }

    /**
     * This is called when a transaction is requeryed from the payment gateway
     * */
    public function onRequery($transactionReference): void
    {
        # TODO: replace this a custom action.
    }

    /**
     * This is called a transaction requery returns with an error
     * */
    public function onRequeryError($requeryResponse): void
    {
        
    }

    /**
     * This is called when a transaction is canceled by the user
     * */
    public function onCancel($transactionReference): void
    {
        # TODO: replace this a custom action.
        echo "Payment was cancelled ! replace this with an action.";
        $session->destroy();
    }

    /**
     * This is called when a transaction doesn't return with a success or a failure response. This can be a timedout transaction on the Rave server or an abandoned transaction by the customer.
     * */
    public function onTimeout($transactionReference, $data): void
    {
        # trigger webhook notification from Flutterwave.
        $service = new Flutterwave\Service\Transaction();
        $service->resendFailedHooks( $data->id );
        header('Location: ' . base_url());
    }
}

Add your new routes

After the Home route add the /pay and callback routes with their respective controllers and methods.

# file path: app/Config/Routes.php

/*
 * --------------------------------------------------------------------
 * Route Definitions
 * --------------------------------------------------------------------
 */
// We get a performance increase by specifying the default
// route since we don't have to scan directories.
$routes->get('/', 'Home::index');
+ $routes->get('/pay', 'PaymentController::index');
+ $routes->get('/callback', 'PaymentController::callback');

Run the application and test.

Run the application with this command.

php spark serve

Go to https://{base_url}/pay and the modal should load automatically.

Use test cards here to test.