Skip to content

Official PHP Client Library for WeBirr Payment Gateway APIs

License

Notifications You must be signed in to change notification settings

webirr/webirr-api-php-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Official PHP Client Library for WeBirr Payment Gateway APIs

This Client Library provides convenient access to WeBirr Payment Gateway APIs from PHP Applications.

Install

$ composer require webirr/webirr

Usage

The library needs to be configured with a merchant Id & API key. You can get it by contacting webirr.com

You can use this library for production or test environments. you will need to set isTestEnv=true for test, and false for production apps when creating objects of class WeBirrClient

Example

Creating a new Bill / Updating an existing Bill on WeBirr Servers

<?php

require 'vendor/autoload.php';

use WeBirr\Bill;
use WeBirr\WeBirrClient;

// Create & Update Bill
function main()
{
    $apiKey = getenv('wb_apikey_1') !== false ? getenv('wb_apikey_1') : "";
    $merchantId = getenv('wb_merchid_1') !== false ? getenv('wb_merchid_1') : "";

    //$apiKey = 'YOUR_API_KEY';
    //$merchantId = 'YOUR_MERCHANT_ID';

    $api = new WeBirrClient($merchantId, $apiKey, true);

    $bill = new Bill();

    $bill->amount = '270.90';
    $bill->customerCode = 'cc01';  // it can be email address or phone number if you dont have customer code
    $bill->customerName =  'Elias Haileselassie';
    $bill->time = '2021-07-22 22:14'; // your bill time, always in this format
    $bill->description = 'hotel booking';
    $bill->billReference = 'php/2021/128'; // your unique reference number
    $bill->merchantID = $merchantId;

    echo "\nCreating Bill...";

    $res = $api->createBill($bill);

    if (!$res->error) {
        // success
        $paymentCode = $res->res;  // returns paymentcode such as 429 723 975
        echo "\nPayment Code = $paymentCode"; // we may want to save payment code in local db.

    } else {
        // fail
        echo "\nerror: $res->error";
        echo "\nerrorCode: $res->errorCode"; // can be used to handle specific busines error such as ERROR_INVLAID_INPUT_DUP_REF
    }

    //var_dump($res);

    // Update existing bill if it is not paid
    $bill->amount = "278.00";
    $bill->customerName = 'Elias php';
    //$bill->billReference = "WE CAN NOT CHANGE THIS";

    echo "\nUpdating Bill...";

    $res = $api->updateBill($bill);

    if (!$res->error) {
        // success
        echo "\nbill is updated succesfully"; //res.res will be 'OK'  no need to check here!
    } else {
        // fail
        echo "\nerror: $res->error";
        echo "\nerrorCode: $res->errorCode"; // can be used to handle specific busines error such as ERROR_INVLAID_INPUT
    }
}

main();

Getting Payment status of an existing Bill from WeBirr Servers

<?php

require 'vendor/autoload.php';

use WeBirr\WeBirrClient;
use WeBirr\PaymentDetail;

// Get Payment Status of a Bill
function main()
{

    $apiKey = getenv('wb_apikey_1') !== false ? getenv('wb_apikey_1') : "";
    $merchantId = getenv('wb_merchid_1') !== false ? getenv('wb_merchid_1') : "";

    //$apiKey = 'YOUR_API_KEY';
    //$merchantId = 'YOUR_MERCHANT_ID';

    $api = new WeBirrClient($merchantId, $apiKey, true);

    $paymentCode = '149 233 514';   // PAYMENT_CODE_YOU_SAVED_AFTER_CREATING_A_NEW_BILL

    echo "\nGetting Payment Status...";

    $res = $api->getPaymentStatus($paymentCode);

    if (!$res->error) {
        // success
        if ($res->res->status == 2) {  // 0. Pending  ( $res->res->data will be null !), 1. Payment in Progress (unconfirmed payment) 2. Paid
          $payment = new PaymentDetail($res->res->data);
          echo "\nbill is paid";
          echo "\nbill payment detail";
          echo "\nBank: $payment->bankID";
          echo "\nBank Reference Number: $payment->paymentReference";
          echo "\nAmount Paid: $payment->amount";
        } else
          echo "\nbill is pending payment";
      } else {
        // fail
        echo "\nerror: $res->error";
        echo "\nerrorCode: $res->errorCode"; // can be used to handle specific busines error such as ERROR_INVLAID_INPUT
      }

    //var_dump($res);
}

main();

Sample object returned from getPaymentStatus()

{
  error: null,
  res: {
    status: 2,
        data: {
            status: 2,
            id: 111219507,
            bankID: "cbe_mobile",
            paymentReference: "TX70e78862148f4c249606",
            paymentDate: "2025-02-26 22:17:19",
            confirmed: true,
            confirmedTime: "2025-02-26 22:17:19",
            amount: "278",
            wbcCode: "149 233 514",
            updateTimeStamp: "2025022622171981338"
        }
    },
    errorCode: null
}

Deleting an existing Bill from WeBirr Servers (if it is not paid)

<?php

require 'vendor/autoload.php';

use WeBirr\WeBirrClient;

// Delete an existing Bill
function main()
{

    $apiKey = getenv('wb_apikey_1') !== false ? getenv('wb_apikey_1') : "";
    $merchantId = getenv('wb_merchid_1') !== false ? getenv('wb_merchid_1') : "";

    //$apiKey = 'YOUR_API_KEY';
    //$merchantId = 'YOUR_MERCHANT_ID';

    $api = new WeBirrClient($merchantId, $apiKey, true);

    $paymentCode =  '460 609 416'; // PAYMENT_CODE_YOU_SAVED_AFTER_CREATING_A_NEW_BILL

    echo "\nDeleting Bill...";

    $res = $api->deleteBill($paymentCode);

    if (!$res->error) {
        // success
        echo "\nbill is deleted succesfully"; //res.res will be 'OK'  no need to check here!
      } else {
        // fail
        echo "\nerror: $res->error";
        echo "\nerrorCode: $res->errorCode"; // can be used to handle specific bussines error such as ERROR_INVLAID_INPUT
      }
    //var_dump($res);
}

main();

Getting list of Payments and process them with Bulk Polling Consumer

<?php

require 'vendor/autoload.php';

use WeBirr\WeBirrClient;
use WeBirr\Payment;

// Get list of Payments and process them with bulk polling consumer
class PaymentProcessor
{
    private $apiKey;
    private $merchantId;
    private $api;
    private $lastTimeStamp;

    public function __construct()
    {
        $this->apiKey = getenv('wb_apikey_1') !== false ? getenv('wb_apikey_1') : "";
        $this->merchantId = getenv('wb_merchid_1') !== false ? getenv('wb_merchid_1') : "";
        $this->api = new WeBirrClient($this->merchantId, $this->apiKey, true);
        $this->lastTimeStamp = '20250224120000'; // Example timestamp, replace with your actual last timestamp retrieved from your database to current date stamp for first time call
    }

    public function Run()
    {
        while (true) {
            echo "\nRetrieving Payments...";
            $this->fetchAndProcessPayments();
            echo "\nSleeping for 5 seconds...";
            sleep(5); // Sleep for 5 seconds before the next polling
        }
    }

    private function fetchAndProcessPayments()
    {
        $limit = 100;  // Number of records to retrieve depending on your processing requirement & capacity
        $response = $this->api->getPayments($this->lastTimeStamp, $limit);

        if (!$response->error) {
            // success
            if (count($response->res) == 0) {
                echo "\nNo new payments found.";
            }
            foreach ($response->res as $obj) {
                $payment = new Payment($obj);
                $this->processPayment($payment);
                echo "\n-----------------------------";
            }

            if (count($response->res) > 0) {
                $this->lastTimeStamp = $response->res[count($response->res) - 1]->updateTimeStamp;
                echo "\nLast Timestamp: " . $this->lastTimeStamp; // save this to your database for next polling/call to getPayments()
            }

        } else {
            // fail
            echo "\nerror: " . $response->error;
            echo "\nerrorCode: " . $response->errorCode; // can be used to handle specific business error such as ERROR_INVALID_INPUT
        }
    }

    /**
      * Process Payment should be impleneted as idempotent operation for production use cases
      * This method and logic can be shared among all payment processing consumers: 1. bulk polling, 2. webhook, 3. single payment polling.
     */
    private function processPayment(Payment $payment)
    {
        echo "\nPayment Status: " . $payment->status;
        if ($payment->IsPaid()) {
            echo "\nPayment Status Text: Paid.";
        }
        if ($payment->IsReversed()) {
            echo "\nPayment Status Text: Reversed.";
        }
        echo "\nBank: " . $payment->bankID;
        echo "\nBank Reference Number: " . $payment->paymentReference;
        echo "\nAmount Paid: " . $payment->amount;
        echo "\nPayment Date: " . $payment->paymentDate;
        echo "\nReversal/Cancel Date: " . $payment->canceledTime;
        echo "\nUpdate Timestamp: " . $payment->updateTimeStamp;
    }
}

// Run the payment processor
$processor = new PaymentProcessor();
$processor->Run();

Webhooks - Payment processing using Webhook Callbacks

<?php

require 'vendor/autoload.php';

use WeBirr\Payment;

// Webhook handler for processing payment updates from WeBirr.
// This script should be hosted on a secure server with HTTPS enabled.

class Webhook
{
    /**
     * Handle incoming webhook POST requests.
     *  Validates request method (must be POST).
     *  Checks authentication using the authKey from the query string, otherwise system will not know if the request is coming from WeBirr(authorized) or not
     */
    public function handleRequest()
    {
        // Validate request method is POST
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            http_response_code(405);  // Method Not Allowed
            header('Content-Type: application/json');
            echo json_encode(["error" => "Method Not Allowed. POST required."]);
            return;
        }

        // Authenticate using authKey query string parameter, otherwise system will not know if the request is from WeBirr(authorized) or not
        if (!$this->isAuthenticated()) {
            http_response_code(403);  
            header('Content-Type: application/json');
            echo json_encode(["error" => "Unauthorized access. Invalid authKey."]);
            return;
        }

        // Read the raw JSON input
        $rawPayload = file_get_contents('php://input');
        if (empty($rawPayload)) {
            http_response_code(400);  // Bad Request
            header('Content-Type: application/json');
            echo json_encode(["error" => "Empty request body."]);
            return;
        }

        // Decode the JSON payload into an associative array
        $jsonBody = json_decode($rawPayload, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            http_response_code(400);
            header('Content-Type: application/json');
            echo json_encode(["error" => "Invalid JSON format."]);
            return;
        }

        try {
            $payment = new Payment($jsonBody['data']);
        } catch (Exception $e) {
            http_response_code(400);
            header('Content-Type: application/json');
            echo json_encode(["error" => "Invalid payment data: " . $e->getMessage()]);
            return;
        }

        // Process the payment asynchronously (or enqueue it for later processing)
        // here instead of immediate processing, this should be handled asynchronously or enqueued to a background worker/job.
        $this->processPayment($payment);

        // Return a JSON success response (can also be just empty body with 200 OK status) 
        http_response_code(200);
        header('Content-Type: application/json');
        echo json_encode(["success" => true, "message" => "Payment received and queued for processing"]);
    }

    /**
     * Authenticate the request using authKey from the query string.
     * 
     * The authKey is compared against the environment variable `WB_WEBHOOK_AUTH_KEY`.
     * 
     * @return bool True if authentication succeeds, false otherwise.
     */
    private function isAuthenticated(): bool
    {
        // Retrieve the authKey from the query string
        $providedAuthKey = $_GET['authKey'] ?? '';
        
        // TODO: Replace this with your own auth key (preferably from environment variable)
         $expectedAuthKey = "please-change-me-to-secure-key-5114831AFD5D4646901DCDAC58B92F8E";
        //$expectedAuthKey = getenv('wb_webhook_authkey') !== false ? getenv('wb_webhook_authkey') : ""; 
    
        // Compare the provided key with the expected key
        return !empty($expectedAuthKey) && hash_equals($expectedAuthKey, $providedAuthKey);
    }
    
     /**
      *  Process Payment should be impleneted as idempotent operation for production use cases
      *  Prefered approach is: Payment should be processed asynchronously or enqueued to a background worker.
      *  This method and logic can be shared among all payment processing consumers: 1. bulk polling, 2. webhook, 3. single payment polling.
      * @param Payment $payment The Payment object.
      */
     private function processPayment(Payment $payment)
     {
         echo "\nPayment Status: " . $payment->status;
         if ($payment->IsPaid()) {
             echo "\nPayment Status Text: Paid.";
         }
         if ($payment->IsReversed()) {
             echo "\nPayment Status Text: Reversed.";
         }
         echo "\nBank: " . $payment->bankID;
         echo "\nBank Reference Number: " . $payment->paymentReference;
         echo "\nAmount Paid: " . $payment->amount;
         echo "\nPayment Date: " . $payment->paymentDate;
         echo "\nReversal/Cancel Date: " . $payment->canceledTime;
         echo "\nUpdate Timestamp: " . $payment->updateTimeStamp;
     }
 
}

// Instantiate and handle the webhook request. once hosted, the url needs to be shared with WeBirr for configuration
$webhook = new Webhook();
$webhook->handleRequest();

Gettting basic Statistics about bills created and payments received for a date range

<?php

require 'vendor/autoload.php';

use WeBirr\WeBirrClient;
use WeBirr\Stat;

// Get basic statistics about bills created and payments received for a date range 
function main()
{
    $apiKey = getenv('wb_apikey_1') !== false ? getenv('wb_apikey_1') : "";
    $merchantId = getenv('wb_merchid_1') !== false ? getenv('wb_merchid_1') : "";

    //$apiKey = 'YOUR_API_KEY';
    //$merchantId = 'YOUR_MERCHANT_ID'; 

    $api = new WeBirrClient($merchantId, $apiKey, true);

    $dateFrom = '2025-01-01'; // YYYY-MM-DD
    $dateTo = '2030-01-31'; // YYYY-MM-DD
    
    echo "\nRetrieving Statistics...";
    echo "\nDate From: $dateFrom";
    echo "\nDate To: $dateTo";

    $response = $api->getStat($dateFrom, $dateTo);

    if (!$response->error){
        //success
        $stat = new Stat($response->res);

        echo "\nNumber of Bills Created: " . $stat->nBills;
        echo "\nNumber of Paid Bills: " . $stat->nBillsPaid;
        echo "\nNumber of Unpaid Bills: " . $stat->nBillsUnpaid;
        echo "\nAmount of Bills: " . $stat->amountBills;
        echo "\nAmount Paid: " . $stat->amountPaid;
        echo "\nAmount Unpaid: " . $stat->amountUnpaid;
    } else {
        // error
        echo "\nError: " . $response->error;    
    }
}

main();

About

Official PHP Client Library for WeBirr Payment Gateway APIs

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages