npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

webirr

v2.2.0

Published

Official JavaScript Client Library for WeBirr Payment Gateway APIs

Readme

Official JavaScript Client Library for WeBirr Payment Gateway APIs

This Client Library provides convenient access to WeBirr Payment Gateway APIs from JavaScript/Node/ReactNative Apps.

Requires Javascript engine in browser or Node

Install

run the following command to install webirr client library

With npm

$ npm install webirr

With yarn

$ yarn add webirr

With bower

$ bower install 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

Examples assume the WeBirr TestEnv and read credentials from environment variables:

export WEBIRR_TEST_ENV_MERCHANT_ID="YOUR_TEST_MERCHANT_ID"
export WEBIRR_TEST_ENV_API_KEY="YOUR_TEST_API_KEY"

Create the client with merchant ID, API key, and environment once. The client automatically sets bill.merchantID before sending bill create/update requests, so application code and examples should not set merchantID on the bill object.

const webirr = require('webirr');

const merchantId = process.env.WEBIRR_TEST_ENV_MERCHANT_ID || 'YOUR_MERCHANT_ID';
const apiKey = process.env.WEBIRR_TEST_ENV_API_KEY || 'YOUR_API_KEY';

var api = new webirr.WeBirrClient(merchantId, apiKey, true);

In 2.x, the client constructor requires the merchant ID argument. The client sends merchant_id on every request and sets bill.merchantID from the client value before create/update calls.

TypeScript

The package includes TypeScript declarations. You can import the client and public model types from the existing webirr package:

import { ApiResponse, Bill, PaymentStatus, SupportedBank, WeBirrClient } from 'webirr';

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

const bill: Bill = {
  amount: '270.90',
  customerCode: 'cc01',
  customerName: 'Test Customer',
  customerPhone: '0911000000',
  time: '2026-06-18 10:00',
  description: 'TypeScript example bill',
  billReference: `typescript/example/${Date.now()}`,
  extras: {}
};

const created: ApiResponse<string> = await api.createBill(bill);
const paymentCode = created.res;

if (paymentCode) {
  const status: ApiResponse<PaymentStatus> =
    await api.getPaymentStatus(paymentCode);
  console.log(status.res?.status);
}

const banks: ApiResponse<SupportedBank[]> = await api.getSupportedBanks();
for (const bank of banks.res || []) {
  console.log(`${bank.bankID} - ${bank.name}`);
}

Example

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


const webirr = require('webirr');

async function main() 
{
  const apiKey = process.env.WEBIRR_TEST_ENV_API_KEY || 'YOUR_API_KEY';
  const merchantId = process.env.WEBIRR_TEST_ENV_MERCHANT_ID || 'YOUR_MERCHANT_ID';

  var api = new webirr.WeBirrClient(merchantId, apiKey, true);

  let bill = {
    amount: '270.90',
    customerCode: 'cc01',  // it can be email address or phone number if you dont have customer code
    customerName: 'Elias Haileselassie',
    customerPhone: '0911000000',
    time: '2021-07-22 22:14',   // your bill time, always in this format
    description: 'hotel booking',
    billReference: `javascript/example/${Date.now()}`, // your unique reference number
    extras: {}
  };

  console.log('Creating Bill...');
  var res = await api.createBill(bill);
  
  if (!res.error) {
    // success
    let paymentCode = res.res;  // returns paymentcode such as 429 723 975
    console.log( `Payment Code = ${paymentCode}`); // we may want to save payment code in local db.

  } else {
    // fail
    console.log(`error: ${res.error}`);
    console.log(
        `errorCode: ${res.errorCode}`); // can be used to handle specific busines error such as ERROR_INVLAID_INPUT_DUP_REF
  }

  // update existing bill if it is not paid
  bill.amount = "278.00";
  bill.customerName = 'Elias javascript';
  //bill.billReference = "WE CAN NOT CHANGE THIS";

  console.log('Updating Bill...');
  res = await api.updateBill(bill);

  if (!res.error) {
    // success
    console.log('bill is updated succesfully'); //res.res will be 'OK'  no need to check here!
  } else {
    // fail
    console.log(`error: ${res.error}`);
    console.log(`errorCode: ${res.errorCode}`); // can be used to handle specific busines error such as ERROR_INVLAID_INPUT
  }

}

main();

Getting a Bill and Listing Bills


const webirr = require('webirr');

async function main() 
{
  const apiKey = process.env.WEBIRR_TEST_ENV_API_KEY || 'YOUR_API_KEY';
  const merchantId = process.env.WEBIRR_TEST_ENV_MERCHANT_ID || 'YOUR_MERCHANT_ID';

  var api = new webirr.WeBirrClient(merchantId, apiKey, true);

  var billReference = 'BILL_REFERENCE_YOU_SAVED_AFTER_CREATING_A_NEW_BILL';
  var paymentCode = 'PAYMENT_CODE_YOU_SAVED_AFTER_CREATING_A_NEW_BILL';

  console.log('Getting Bill By Reference...');
  var res = await api.getBillByReference(billReference);

  if (!res.error) {
    // success
    console.log(`Payment Code = ${res.res.wbcCode}`);
    console.log(`Payment Status = ${res.res.paymentStatus}`);
    console.log(`Last Timestamp = ${res.res.updateTimeStamp}`);
  } else {
    // fail
    console.log(`error: ${res.error}`);
    console.log(`errorCode: ${res.errorCode}`);
  }

  console.log('Getting Bill By Payment Code...');
  res = await api.getBillByPaymentCode(paymentCode);

  if (!res.error) {
    // success
    console.log(`Bill Reference = ${res.res.billReference}`);
    console.log(`Payment Status = ${res.res.paymentStatus}`);
    console.log(`Last Timestamp = ${res.res.updateTimeStamp}`);
  } else {
    // fail
    console.log(`error: ${res.error}`);
    console.log(`errorCode: ${res.errorCode}`);
  }

  console.log('Listing Bills...');
  var paymentStatus = -1; // -1 all, 0 pending, 1 unconfirmed payment, 2 paid.
  var lastTimeStamp = '20251231'; // Date-only cursor; use "20251231235959" when you need time precision.
  var limit = 10;

  res = await api.getBills(paymentStatus, lastTimeStamp, limit);

  if (!res.error) {
    // success
    console.log(`Bills returned: ${(res.res || []).length}`);
    for (const bill of res.res || []) {
      console.log(`Bill Reference = ${bill.billReference}`);
      console.log(`Payment Code = ${bill.wbcCode}`);
      console.log(`Payment Status = ${bill.paymentStatus}`);
      console.log(`Last Timestamp = ${bill.updateTimeStamp}`);
    }
  } else {
    // fail
    console.log(`error: ${res.error}`);
    console.log(`errorCode: ${res.errorCode}`);
  }
}

main();

Timestamp cursors can be date-only (yyyyMMdd) or include time (yyyyMMddHHmmss). Use empty string only when you intentionally want all history from the beginning.

Getting Payment status of an existing Bill from WeBirr Servers


const webirr = require('webirr');

async function main() 
{
  const apiKey = process.env.WEBIRR_TEST_ENV_API_KEY || 'YOUR_API_KEY';
  const merchantId = process.env.WEBIRR_TEST_ENV_MERCHANT_ID || 'YOUR_MERCHANT_ID';
  
  var api = new webirr.WeBirrClient(merchantId, apiKey, true);

  var paymentCode = 'PAYMENT_CODE_YOU_SAVED_AFTER_CREATING_A_NEW_BILL';  // suchas as '141 263 782';
  
  console.log('Getting Payment Status...');
  var r = await api.getPaymentStatus(paymentCode);

  if (!r.error) {
    // success
    if (r.res.status == 2) {
      console.log('bill is paid');
      console.log('bill payment detail');
      console.log(`Bank: ${r.res.data.bankID}`);
      console.log(`Bank Reference Number: ${r.res.data.paymentReference}`);
      console.log(`Amount Paid: ${r.res.data.amount}`);
      console.log(`Payment Date: ${r.res.data.paymentDate}`);
    } else
      console.log('bill is pending payment');
  } else {
    // fail
    console.log(`error: ${r.error}`);
    console.log( `errorCode: ${r.errorCode}`); // can be used to handle specific busines error such as ERROR_INVLAID_INPUT
  }

}

main();

Sample object returned from getPaymentStatus()

{
  error: null,
  res: {
    status: 2,
    data: {
      id: 111112347,
      paymentReference: '8G3303GHJN',
      paymentDate: '2021-07-03 10:25:33',
      confirmed: true,
      confirmedTime: '2021-07-03 10:25:35',
      bankID: 'cbe_birr',
      amount: '4.60',
      wbcCode: '624 549 955',
      updateTimeStamp: '20210703102535000001'
    }
  },
  errorCode: null
}

Use paymentDate as the payment date/time field in payment-status, bulk-polling, and webhook examples.

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


const webirr = require('webirr');

async function main() 
{
  const apiKey = process.env.WEBIRR_TEST_ENV_API_KEY || 'YOUR_API_KEY';
  const merchantId = process.env.WEBIRR_TEST_ENV_MERCHANT_ID || 'YOUR_MERCHANT_ID';

  var api = new webirr.WeBirrClient(merchantId, apiKey, true);

  var paymentCode = 'PAYMENT_CODE_YOU_SAVED_AFTER_CREATING_A_NEW_BILL';  // suchas as '141 263 782';
  
  console.log('Deleting Bill...');
  var res = await api.deleteBill(paymentCode);

  if (!res.error) {
    // success
    console.log('bill is deleted succesfully'); //res.res will be 'OK'  no need to check here!
  } else {
    // fail
    console.log(`error: ${res.error}`);
    console.log(`errorCode: ${res.errorCode}`); // can be used to handle specific bussines error such as ERROR_INVLAID_INPUT
  }
  
}  

main();

Getting list of Payments and process them with Bulk Polling Consumer


const webirr = require('webirr');

class BulkPaymentPollingConsumer {
  constructor()
  {
    const apiKey = process.env.WEBIRR_TEST_ENV_API_KEY || 'YOUR_API_KEY';
    const merchantId = process.env.WEBIRR_TEST_ENV_MERCHANT_ID || 'YOUR_MERCHANT_ID';

    this.api = new webirr.WeBirrClient(merchantId, apiKey, true);
    this.lastTimeStamp = '20251231'; // use a saved cursor; time precision can be like "20251231235959"
  }

  async Run()
  {
    await this.fetchAndProcessPayments();
  }

  async fetchAndProcessPayments()
  {
    const limit = 100;

    console.log('Getting Payments...');
    var response = await this.api.getPayments(this.lastTimeStamp, limit);

    if (!response.error) {
      // success
      for (const payment of response.res || []) {
        this.processPayment(payment);
        if (payment.updateTimeStamp && payment.updateTimeStamp > this.lastTimeStamp) {
          this.lastTimeStamp = payment.updateTimeStamp;
          console.log(`Next cursor candidate: ${this.lastTimeStamp}`);
        }
      }
      // Save lastTimeStamp to your database only after the batch is processed successfully.
    } else {
      // fail
      console.log(`error: ${response.error}`);
      console.log(`errorCode: ${response.errorCode}`);
    }
  }

  processPayment(payment)
  {
    if (payment.status == 2) {
      console.log('bill is paid');
    } else if (payment.status == 3) {
      console.log('bill payment is reversed');
    }

    console.log(`Bank: ${payment.bankID}`);
    console.log(`Bank Reference Number: ${payment.paymentReference}`);
    console.log(`Amount Paid: ${payment.amount}`);
    console.log(`Payment Date: ${payment.paymentDate}`);
    console.log(`Canceled Time: ${payment.canceledTime}`);
    console.log(`Update Timestamp: ${payment.updateTimeStamp}`);
  }
}

new BulkPaymentPollingConsumer().Run();

Bulk polling should persist updateTimeStamp only after processing the batch successfully. Polling processors should be idempotent because duplicate/redundant reads are possible.

Webhooks - Payment processing using Webhook Callbacks

WeBirr can call your HTTPS webhook endpoint when payment updates happen. Validate that the request method is POST, authenticate the request with an authKey from the query string, parse the JSON wrapper payload, and process payload.data idempotently.


const http = require('http');

const expectedAuthKey = process.env.WEBIRR_WEBHOOK_AUTH_KEY || 'YOUR_WEBHOOK_AUTH_KEY';

function processPayment(payment)
{
  if (payment.status == 2) {
    console.log('bill is paid');
  } else if (payment.status == 3) {
    console.log('bill payment is reversed');
  }

  console.log(`Bank: ${payment.bankID}`);
  console.log(`Bank Reference Number: ${payment.paymentReference}`);
  console.log(`Amount Paid: ${payment.amount}`);
  console.log(`Payment Date: ${payment.paymentDate}`);
  console.log(`Canceled Time: ${payment.canceledTime}`);
  console.log(`Update Timestamp: ${payment.updateTimeStamp}`);

  // In production, save or enqueue this work in your application/database.
}

function jsonResponse(res, statusCode, body)
{
  res.writeHead(statusCode, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify(body));
}

http.createServer((req, res) => {
  const url = new URL(req.url, 'http://localhost');

  if (req.method !== 'POST') {
    jsonResponse(res, 405, { error: 'method not allowed' });
    return;
  }

  if (url.searchParams.get('authKey') !== expectedAuthKey) {
    jsonResponse(res, 401, { error: 'unauthorized' });
    return;
  }

  let body = '';
  req.on('data', (chunk) => body += chunk);
  req.on('end', () => {
    if (!body) {
      jsonResponse(res, 400, { error: 'empty request body' });
      return;
    }

    try {
      const payload = JSON.parse(body);
      if (!payload || !payload.data) {
        jsonResponse(res, 400, { error: 'invalid payment data' });
        return;
      }
      processPayment(payload.data);
      jsonResponse(res, 200, { error: null });
    } catch (error) {
      jsonResponse(res, 400, { error: 'invalid json' });
    }
  });
}).listen(3000);

Getting Supported Banks


const webirr = require('webirr');

async function main()
{
  const apiKey = process.env.WEBIRR_TEST_ENV_API_KEY || 'YOUR_API_KEY';
  const merchantId = process.env.WEBIRR_TEST_ENV_MERCHANT_ID || 'YOUR_MERCHANT_ID';

  var api = new webirr.WeBirrClient(merchantId, apiKey, true);

  console.log('Getting supported banks...');
  var response = await api.getSupportedBanks();

  if (!response.error) {
    // success
    for (const bank of response.res || []) {
      console.log(`${bank.bankID} - ${bank.name}`);
    }
  } else {
    // fail
    console.log(`error: ${response.error}`);
    console.log(`errorCode: ${response.errorCode}`);
  }
}

main();

Checkout applications should use this merchant-specific list when showing payment instructions, for example {Bank Name} -> WeBirr -> Payment Code. Do not show a broad global bank list in checkout UI; show only banks returned for the configured merchant.

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


const webirr = require('webirr');

async function main() 
{
  const apiKey = process.env.WEBIRR_TEST_ENV_API_KEY || 'YOUR_API_KEY';
  const merchantId = process.env.WEBIRR_TEST_ENV_MERCHANT_ID || 'YOUR_MERCHANT_ID';

  var api = new webirr.WeBirrClient(merchantId, apiKey, true);

  const dateFrom = '2025-01-01';
  const dateTo = '2030-01-31';

  console.log('Getting Stat...');
  var response = await api.getStat(dateFrom, dateTo);

  if (!response.error) {
    // success
    console.log(`Bills Created: ${response.res.nBills}`);
    console.log(`Bills Paid: ${response.res.nBillsPaid}`);
    console.log(`Bills Unpaid: ${response.res.nBillsUnpaid}`);
    console.log(`Amount Bills: ${response.res.amountBills}`);
    console.log(`Amount Paid: ${response.res.amountPaid}`);
    console.log(`Amount Unpaid: ${response.res.amountUnpaid}`);
  } else {
    // fail
    console.log(`error: ${response.error}`);
    console.log(`errorCode: ${response.errorCode}`);
  }
}

main();

Standalone Examples

The examples directory has runnable examples equivalent to the README sections:

node examples/example1-create-update-bill.js
node examples/example2-payment-status-single-poll.js
node examples/example3-delete-bill.js
node examples/example4-payment-status-bulk-poll.js
node examples/example5-stat-report.js
node examples/example6-payment-status-webhook.js
node examples/example7-get-bill-and-list-bills.js
node examples/example9-supported-banks.js

The TypeScript example can be checked with the package typecheck command:

npm run test:types

Tests

Fast tests cover endpoint shape, merchant ID handling, request defaults, and invalid API-key behavior:

npm test

Live TestEnv smoke tests call the real TestEnv endpoints and require credentials:

export WEBIRR_TEST_ENV_MERCHANT_ID="YOUR_TEST_MERCHANT_ID"
export WEBIRR_TEST_ENV_API_KEY="YOUR_TEST_API_KEY"
npm run test:testenv

Reusing an Axios Client

For batch or mass bill workloads, you can pass a configured axios client so your application controls timeout, interceptors, and connection reuse:

const http = require('http');
const https = require('https');
const axios = require('axios');
const webirr = require('webirr');

const httpClient = axios.create({
  timeout: 30000,
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true })
});

const api = new webirr.WeBirrClient(merchantId, apiKey, true, httpClient);

Error handling & retries

WeBirr business errors come back on HTTP 2xx responses in ApiResponse.error and ApiResponse.errorCode. Platform failures such as network/DNS/TLS errors, timeouts, non-2xx HTTP responses, and empty or non-JSON 2xx bodies reject the promise instead of returning ApiResponse.

Retry only transient platform failures with exponential backoff and jitter: connection errors, timeout rejections such as err.code === 'ECONNABORTED' or err.code === 'ETIMEDOUT', and HTTP 5xx, 429, or 408 from err.response.status. Use TransientErrors.isTransient(error) to apply that rule. Do not retry other 4xx responses. Create and read operations are safe to retry. DeleteBill is also safe to retry, but a retry after it already succeeded returns an "invalid payment code" error; treat that as already-deleted.

const { TransientErrors } = require('webirr');

try {
    const response = await api.createBill(bill);

    if (response.error) {
        // WeBirr business error from a 2xx ApiResponse envelope.
        console.log(`WeBirr error ${response.errorCode}: ${response.error}`);
        return;
    }

    console.log(`Payment Code = ${response.res}`);
} catch (error) {
    if (TransientErrors.isTransient(error)) {
        // Transient platform error: connection/timeout failure,
        // HTTP 5xx, 429, or 408.
        // Safe to retry with backoff + jitter.
    } else {
        // Non-transient platform error: HTTP 4xx other than 408/429.
        // Do not retry automatically.
    }
}