Unofficial community documentation

Telebirr integration,
without the pain.

H5 C2B, InApp SDK, and B2B integration guides — explained clearly. Plus PHP and JavaScript/TypeScript libraries that handle the token juggling, request signing, and boilerplate so you don't have to.

Read the docsView libraries
$composer require melaku/telebirr
Side by side

Same result. Wildly different experience.

The official docs give you ~70 lines of raw Node.js. Either library does it in one call.

The hard wayrequestCreateOrder.js
// The "old way" — raw Node.js boilerplate
const applyFabricToken = require('./applyFabricTokenService')
const tools = require('./utils/tools')
const config = require('./config/config')
var request = require('request')

exports.createOrder = async (req, res) => {
  let applyFabricTokenResult = await applyFabricToken()
  let fabricToken = applyFabricTokenResult.token

  return new Promise((resolve) => {
    let reqObject = {
      timestamp: tools.createTimeStamp(),
      nonce_str: tools.createNonceStr(),
      method: 'payment.preorder',
      version: '1.0',
      biz_content: {
        notify_url: config.notifyUrl,
        appid: config.merchantAppId,
        merch_code: config.merchantCode,
        merch_order_id: new Date().getTime() + '',
        trade_type: 'Checkout',
        title: req.body.title,
        total_amount: req.body.amount,
        trans_currency: 'ETB',
        timeout_express: '120m',
        business_type: 'BuyGoods',
        payee_identifier: config.merchantCode,
        payee_identifier_type: '04',
        payee_type: '5000',
        redirect_url: config.redirectUrl,
      },
    }
    reqObject.sign = tools.signRequestObject(reqObject)
    reqObject.sign_type = 'SHA256WithRSA'

    var options = {
      method: 'POST',
      url: config.baseUrl + '/payment/v1/merchant/preOrder',
      headers: {
        'Content-Type': 'application/json',
        'X-APP-Key': config.fabricAppId,
        Authorization: fabricToken,
      },
      rejectUnauthorized: false,
      body: JSON.stringify(reqObject),
    }
    request(options, (error, response) => {
      let result = JSON.parse(response.body)
      let prepayId = result.biz_content.prepay_id
      let map = {
        appid: config.merchantAppId,
        merch_code: config.merchantCode,
        nonce_str: tools.createNonceStr(),
        prepay_id: prepayId,
        timestamp: tools.createTimeStamp(),
      }
      let sign = tools.signRequestObject(map)
      let rawRequest = [
        'appid=' + map.appid,
        'merch_code=' + map.merch_code,
        'nonce_str=' + map.nonce_str,
        'prepay_id=' + map.prepay_id,
        'timestamp=' + map.timestamp,
        'sign=' + sign,
        'sign_type=SHA256WithRSA',
      ].join('&')
      res.send(config.webBaseUrl + rawRequest
        + '&version=1.0&trade_type=Checkout')
    })
  })
}
With the librarycheckout.php
<?php
// The new way — telebirr-php library
use Melaku\Telebirr\Config;
use Melaku\Telebirr\Telebirr;

$config = Config::forProduction([
    'fabricAppId'   => env('FABRIC_APP_ID'),
    'appSecret'     => env('APP_SECRET'),
    'merchantAppId' => env('MERCHANT_APP_ID'),
    'merchantCode'  => env('MERCHANT_CODE'),
    'privateKey'    => env('PRIVATE_KEY_PEM'),
    'notifyUrl'     => 'https://your-site.com/pay/notify',
    'redirectUrl'   => 'https://your-site.com/pay/return',
]);

$checkout = (new Telebirr($config))
    ->createCheckoutUrl('Order #1337', '350.00');

// done. token, signing, URL assembly — all handled.
header('Location: ' . $checkout->getCheckoutUrl());
Old way
72 lines
Node.js + 3 custom utils
New way
20 lines
Plain PHP, no utils needed
Manual signing
you write it
sort, join, sign, encode
Token management
automatic
applied transparently
Integration types

Pick your integration path

WEB
H5 C2B

Web Checkout

Redirect browser customers to the Telebirr payment page. Covers token, order, URL assembly, return handling, and signed notifications.

Read guide
MOBILE
InApp SDK

iOS & Android

Embed Telebirr payments inside your native mobile app. Server-side order creation, receiveCode, SDK invocation, and callback handling.

Read guide
ALL
B2B + Others

Overview

B2B, C2B, Mini App, and Subscription payment types. Architecture overview, flow diagrams, and environment setup.

Read guide
Libraries

What we handle
so you don't have to

The Telebirr API requires you to manually fetch tokens, sort and sign request parameters with SHA256WithRSA, assemble URL query strings, and verify RSA-PSS signatures on notifications. Both libraries wrap every step — pick whichever matches your stack.

PHP · melaku/telebirr
JS/TS · @melakudemeke/telebirr-js
Token management
Auto-fetched on every request
Request signing
SHA256WithRSA handled internally
URL assembly
No rawRequest string building
Notification verification
One call to verify() + parse()
Test / production switch
Config::forTest() vs forProduction()
PSR-3 logging
Drop in Monolog, secrets auto-redacted
What the official docs don't explain well

The parts that trip everyone up

01

Request signing

Sort all biz_content fields by ASCII order, flatten into key=value pairs, join with &, sign with SHA256WithRSA PSS fill. Any field missing or out of order → "verify sign failed".

02

Token expiry

Fabric tokens expire in 60 minutes. Re-fetch before every request or cache with expirationDate. Samples hard-code a fresh fetch each time — that burns rate limits fast.

03

Notification verification

The notify callback arrives as a flat POST body. Extract sign, remove it, re-sort the rest, verify with Telebirr's public key using RSA-PSS. Skipping this is a security hole.

04

Environment URLs

Test and production use different base URLs and redirect URLs. The docs list both but it's easy to cross the streams — wrong env → silent failures with cryptic error codes.

Get started

Ready to integrate?

Pick an integration guide or install a library — PHP or JS/TS — and be processing Telebirr payments in an afternoon.

Browse the docs →H5 C2B guide →
$ composer require melaku/telebirr