H5 C2B IntegrationOverview

H5 C2B Web Checkout

Redirect your web customers to the Telebirr-hosted payment page. They enter their phone and PIN, get a confirmation, and your server receives a signed notification.

Using PHP or JS/TS? The melaku/telebirr (PHP) and @melakudemeke/telebirr-js (JS/TS) libraries do all four steps below in one call. Skip to quickstart →

How it works

Your server                      Telebirr                 Customer
────────────────────────────────────────────────────────────────────
① POST /payment/v1/token      →  Get bearer token
② POST /merchant/preOrder     →  Get prepay_id
③ Build signed checkout URL
④ Redirect customer           →  ──────────────────────→  Pays
⑤                             ←  POST to notifyUrl (async)
⑥ Customer redirected back    ←  ──────────────────────←  Done

Four API calls, a URL redirect, and an async notification. That’s the whole flow.


Quickstart

composer require melaku/telebirr
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',
]);
 
$result = (new Telebirr($config))->createCheckoutUrl('Order #1337', '350.00');
 
// ⚠️ Persist this BEFORE redirecting. It's how you match the notification.
saveOrder($result->getMerchOrderId(), $result->getPrepayId());
 
header('Location: ' . $result->getCheckoutUrl());

The library handles ① ② ③ in that one call. You just do the redirect.

Paste your keys exactly as Ethio Telecom issues them. Since PHP v2.2.0 and JS/TS v3.1.0, the libraries accept bare base64 keys (no -----BEGIN…----- armor needed) and normalize them to PEM automatically. TLS against the test gateway also works out of the box now — the libraries handle its incomplete certificate chain, so you never need verifySsl: false.

Verify the payment

When the customer comes back (or the notification arrives), confirm the real status server-to-server with one call — never trust the browser redirect:

$status = $client->getOrderStatus($merchOrderId); // token handled internally
 
if ($status->paid && $status->amount === $expectedAmount) {
    // fulfill idempotently — see the settlement pattern in the library README
}

getOrderStatus() (PHP v2.2.0+, JS v3.1.0+) returns paid / failed / cancelled, the amount, currency, and Telebirr’s paymentOrderId — typed, with the raw response attached.


Step-by-step (all languages)


Environment URLs

Base URLWeb Checkout URL
Testdeveloperportal.ethiotelebirr.et:38443/apiaccess/payment/gatewaydeveloperportal.ethiotelebirr.et:38443/payment/web/paygate?
Productionsuperapp.ethiomobilemoney.et:38443/apiaccess/payment/gatewaysuperapp.ethiomobilemoney.et:38443/payment/web/paygate?
⚠️

Mixing test and production URLs is a very common source of failures. The library’s Config::forTest() and Config::forProduction() set both URLs together so you can’t accidentally cross them.