BirrJS
Concepts

Payment Providers

Integrate payment gateways with the PaymentProvider interface.

BirrJS uses a provider interface to abstract payment gateway logic. Each provider implements the same three methods.

Provider Interface

interface PaymentProvider {
  initializeTransaction(request: TransactionRequest): Promise<TransactionResponse>;
  verifyTransaction(txRef: string): Promise<VerificationResponse>;
  handleWebhook(payload: unknown, rawBody: string | Buffer, headers: Record<string, string>): Promise<WebhookEvent>;
}

TransactionRequest → TransactionResponse

type TransactionRequest = {
  amount: number;       // Minor units (e.g. 2990 = 29.90 ETB)
  currency: string;
  email: string;
  firstName?: string;
  lastName?: string;
  phoneNumber?: string;
  txRef: string;        // Unique transaction reference
  callbackUrl: string;
  returnUrl?: string;
  metadata?: Record<string, string>;
  customization?: {
    title?: string;
    description?: string;
  };
};

type TransactionResponse = {
  success: boolean;
  checkoutUrl?: string;
  paymentInstructions?: PaymentInstructions;
  txRef?: string;
  error?: string;
};

type PaymentChannel = {
  type: "telebirr" | "cbe" | "awash";
  label: string;
  value: string;
  accountHolder?: string;
};

type PaymentInstructions = {
  amount: number;
  channels: PaymentChannel[];
};

Available providers

BirrJS ships with the following provider packages. Each has its own dedicated page with full setup and usage details.

Chapa — redirect-based checkout

import { chapa } from "@birrjs/chapa";

Redirect users to a Chapa checkout page for payment. Supports webhooks, callback URLs, and automatic subscription activation. See the Chapa provider page for configuration, events, and known gaps.

Vodit — receipt-based manual payment

import { vodit } from "@birrjs/vodit";

Display bank account instructions for users to send money directly, then verify via receipt URL. Supports Telebirr, CBE, Zemen, BoA, and Awash. See the Vodit provider page for setup, per-bank parsing details, and recipient verification.

Switching Providers

Your app code never references the provider directly — all calls go through the PaymentProvider interface. Swapping from Chapa to Vodit means changing one import:

// Before
import { chapa } from "@birrjs/chapa";
// After
import { vodit } from "@birrjs/vodit";

createBirr({
  provider: vodit({
    apiKey: process.env.VODIT_API_KEY!,
    channels: [
      { type: "telebirr", value: "0912345678", name: "Abebe Kebede" },
      { type: "cbe", value: "1000200030004000", name: "Abebe Kebede" },
    ],
  }),
  // Everything else stays the same
  database: process.env.DATABASE_URL,
  plans: [free, pro],
});

Your subscribe(), check(), report(), and webhook handler all work identically regardless of which provider is configured.

Adding a New Provider

Create a new package (e.g. @birrjs/arifpay) with:

  1. A provider factory function that returns PaymentProvider
  2. Types for provider-specific request/response shapes
  3. A Zod schema for webhook payload validation

Chapa and Vodit are available now. The provider system is designed to make adding new ones straightforward — planned providers include ArifPay, Santim Pay and more.