Skip to content

webirr/webirr-api-php-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 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