How to Integrate Telebirr API in Web & Mobile Apps: Step-by-Step Guide
Integrating Ethio Telecom’s Telebirr API enables your web or mobile app to accept digital wallet payments directly. Whether you are building an e-commerce platform, a mobile service app, or a SaaS product, this guide covers the complete step-by-step technical architecture, encryption setup, backend order generation, and client-side integration.
GitHub
Integration Architecture Overview
[ Mobile/Web Client ] ---> 1. Request Order ---> [ Your Backend Server ]
[ Mobile/Web Client ] <--- 2. Send Checkout URL/ReceiveCode <--- [ Your Backend Server ]
|
3. Sign & Encrypt Payload
|
v
[ Mobile App / H5 ] <--- 4. Redirect / In-App Pay <--- [ Telebirr Gateway ]
|
5. User Approves Payment
|
v
[ Telebirr Gateway ] ---> 6. Webhook (Notify URL) ---> [ Your Backend Server ]
Security best practice requires that all request signatures, RSA encryption, and App Secrets stay strictly on your backend server. Never expose your private keys or appKey inside client-side Flutter, React Native, or web code.
Prerequisites & Credentials
Sign up on the Ethio Telecom Developer Portal to obtain your credentials:
appId/appKey: Unique application identifiers. GitHubshortCode: Merchant business short code. Pub.dev- Telebirr Public Key: Provided by Ethio Telecom for encrypting payloads.
- Merchant Private Key: Generated RSA private key used to sign transaction requests.
Step 1: Backend Setup (Node.js / Express Order Generation)
Your server must construct the order payload, sort the fields alphabetically, sign it, and encrypt the request using Telebirr’s RSA parameters.
GitHub
Install Dependencies
Bash
npm install express axios crypto
Node.js Integration Code (server.js)
JavaScript
const express = require('express');
const crypto = require('crypto');
const axios = require('axios');
const app = express();
app.use(express.json());
// Telebirr Configuration Credentials
const TELEBIRR_CONFIG = {
appId: process.env.TELEBIRR_APP_ID,
appKey: process.env.TELEBIRR_APP_KEY,
shortCode: process.env.TELEBIRR_SHORT_CODE,
telebirrPublicKey: process.env.TELEBIRR_PUBLIC_KEY, // RSA 2048 Public Key
notifyUrl: 'https://yourdomain.com/api/telebirr/notify',
returnUrl: 'https://yourdomain.com/payment-success',
baseUrl: 'https://api.telebirr.et:8443/api/pay' // UAT/Production URL
};
// Step A: Format & Sort Payload Parameters
function buildStringA(params) {
const sortedKeys = Object.keys(params).sort();
return sortedKeys.map(key => `${key}=${params[key]}`).join('&');
}
// Step B: RSA Encryption (245-byte block chunking for RSA-2048)
function encryptPayload(jsonPayload, publicKeyString) {
const buffer = Buffer.from(JSON.stringify(jsonPayload));
const maxChunkSize = 245;
const chunks = [];
for (let i = 0; i < buffer.length; i += maxChunkSize) {
const chunk = buffer.slice(i, i + maxChunkSize);
const encryptedChunk = crypto.publicEncrypt(
{
key: publicKeyString,
padding: crypto.constants.RSA_PKCS1_PADDING,
},
chunk
);
chunks.push(encryptedChunk);
}
return Buffer.concat(chunks).toString('base64');
}
// Step C: Generate SHA-256 Signature
function generateSignature(stringA) {
return crypto.createHash('sha256').update(stringA).digest('hex').toUpperCase();
}
// Order Creation Route (Web & Mobile)
app.post('/api/telebirr/create-order', async (req, res) => {
try {
const { amount, itemTitle } = req.body;
const outTradeNo = `ORDER_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
const timestamp = Date.now().toString();
const nonce = crypto.randomBytes(16).toString('hex');
// 1. Construct raw parameters object
const rawParams = {
appId: TELEBIRR_CONFIG.appId,
appKey: TELEBIRR_CONFIG.appKey,
nonce: nonce,
notifyUrl: TELEBIRR_CONFIG.notifyUrl,
outTradeNo: outTradeNo,
receiveName: 'Your Business Merchant',
returnUrl: TELEBIRR_CONFIG.returnUrl,
shortCode: TELEBIRR_CONFIG.shortCode,
subject: itemTitle,
timeoutExpress: '30', // In minutes
timestamp: timestamp,
totalAmount: parseFloat(amount).toFixed(2),
};
// 2. Generate stringA and signature (sign)
const stringA = buildStringA(rawParams);
const sign = generateSignature(stringA);
// 3. Encrypt payload (ussd field)
const ussd = encryptPayload(rawParams, TELEBIRR_CONFIG.telebirrPublicKey);
// 4. Submit order request to Telebirr API
const response = await axios.post(`${TELEBIRR_CONFIG.baseUrl}/applyFabricToken`, {
appid: TELEBIRR_CONFIG.appId,
sign: sign,
ussd: ussd,
});
// Extract checkout URL or receiveCode
if (response.data && response.data.code === 0) {
return res.status(200).json({
success: true,
toPayUrl: response.data.data.toPayUrl, // For H5 Web Integration
receiveCode: response.data.data.receiveCode // For Native App SDK
});
} else {
return res.status(400).json({ success: false, message: response.data.msg });
}
} catch (error) {
return res.status(500).json({ success: false, error: error.message });
}
});
app.listen(3000, () => console.log('Backend listening on port 3000'));
Step 2: Client-Side Integration
Option A: Web App (H5 Redirect Checkout)
For web applications, once your frontend receives toPayUrl from your server, simply redirect the browser to open Telebirr’s secure payment page.
JavaScript
// Web Client Script
async function initiateWebPayment(orderAmount, productName) {
const response = await fetch('/api/telebirr/create-order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount: orderAmount, itemTitle: productName })
});
const data = await response.json();
if (data.success && data.toPayUrl) {
// Redirect customer to Telebirr landing page
window.location.href = data.toPayUrl;
} else {
alert("Payment initialization failed!");
}
}
Option B: Mobile App (Flutter SDK Integration)
For native mobile applications, use the receiveCode generated by your server to open the native Telebirr payment sheet inside Flutter.
Dart
// Flutter Implementation (pub.dev package: telebirr_inapp_purchase_plus)
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:telebirr_inapp_purchase_plus/telebirr_inapp_purchase_plus.dart';
class PaymentScreen extends StatefulWidget {
@override
_PaymentScreenState createState() => _PaymentScreenState();
}
class _PaymentScreenState extends State<PaymentScreen> {
@override
void initState() {
super.initState();
// Initialize Telebirr SDK once on app launch
Telebirr.initialize(
appId: 'YOUR_MERCHANT_APP_ID',
shortCode: 'YOUR_SHORT_CODE',
returnScheme: 'yourappscheme',
environment: TelebirrEnvironment.production,
);
}
Future<void> processPayment() async {
// 1. Fetch receiveCode from your server
final response = await http.post(
Uri.parse('https://yourdomain.com/api/telebirr/create-order'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'amount': '150.00', 'itemTitle': 'Order Payment'}),
);
final data = jsonDecode(response.body);
final String receiveCode = data['receiveCode'];
// 2. Invoke Native Telebirr App / SDK
final result = await Telebirr.pay(receiveCode: receiveCode);
if (result.isSuccess) {
print("Payment successful!");
} else {
print("Payment canceled or failed: ${result.message}");
}
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: processPayment,
child: Text("Pay with Telebirr"),
);
}
}
Step 3: Webhook Verification & Order Fulfillment
Never rely solely on frontend redirects or app callbacks to mark orders as paid. Telebirr sends an asynchronous POST notification (notifyUrl) directly to your backend upon successful payment.
GitHub
JavaScript
// Server-side Webhook Endpoint
app.post('/api/telebirr/notify', (req, res) => {
const { outTradeNo, totalAmount, tradeStatus, transactionNo } = req.body;
// 1. Confirm transaction status
if (tradeStatus === 'COMPLETED' || tradeStatus === 'PAY_SUCCESS') {
// 2. Validate against your stored order in database
const order = await findOrderInDatabase(outTradeNo);
if (order && order.amount === parseFloat(totalAmount) && order.status === 'PENDING') {
// 3. Mark order as PAID idempotently
await updateOrderStatus(outTradeNo, 'SUCCESS', transactionNo);
// 4. Return success JSON back to Telebirr platform
return res.status(200).json({ code: 0, msg: 'success' });
}
}
return res.status(400).json({ code: -1, msg: 'verification_failed' });
});
Telebirr Integration Checklist
- Server-Side Security: Keep private RSA keys,
appKey, and signing algorithms on your server. - Idempotency: Ensure your
notifyUrlendpoint processes duplicate payment events safely without double-fulfilling orders. - Environment Separation: Use Telebirr’s UAT testing endpoints during development before requesting production credential approval.
- Error Handling: Validate
totalAmounton server notifications to prevent amount-spoofing attacks.