A complete, production-ready Telebirr C2B (Customer to Business) web payment integration solution for PHP applications. This library provides seamless integration with Ethio Telecom's Telebirr payment gateway, supporting one-time payments, order queries, refunds, and webhook notifications with enterprise-grade security and comprehensive logging.
- Features
- System Requirements
- Data Flow & Architecture
- Installation
- Configuration
- Usage Examples
- API Endpoints
- Database Schema
- Integration with Moodle
- Security
- Logging & Monitoring
- Testing
- Troubleshooting
- Production Checklist
- Contributing
- License
- Support
- β Complete C2B Payment Flow - Initiate, query, and refund payments
- β Fabric Token Management - Automatic token caching and refresh
- β RSA Signing & Verification - Secure SHA256withRSA signatures
- β Dark/Light Mode UI - Modern, responsive interface with theme toggle
- β Database Logging - Automatic logging of all API calls to database
- β File Logging - Comprehensive logging with Monolog
- β Comprehensive Logs Viewer - Filter, search, and export API logs
- β Moodle Plugin Ready - Can be easily integrated as a Moodle payment plugin
- β H5 Mobile Optimized - Responsive design for all devices
- β Production Ready - Enterprise-grade error handling and security
- β Developer Friendly - Clean, well-documented code with examples
- β PSR-4 Autoloading - Follows PHP-FIG standards
- PHP 7.4 or higher (8.0, 8.1, 8.2 supported)
- MySQL 5.7 or higher / MariaDB 10.2+
- Composer 2.0+
- Web Server (Apache/Nginx)
- OpenSSL extension
- cURL extension
- JSON extension
- PDO PHP extension
- SSL certificate for production
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β β β β β β
β Your App ββββββΆβ Telebirr PHP ββββββΆβ Telebirr β
β (Moodle/ β β Integration β β API Gateway β
β Custom) βββββββ Library βββββββ β
β β β β β β
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β β β
β β β
βΌ βΌ βΌ
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β Database β β Log Files β β Customer β
β (MySQL) β β (Monolog) β β (Browser) β
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β Customer β β Your App β β Telebirr β β Database β
β (Browser) β β β β Gateway β β β
ββββββββ¬ββββββββ ββββββββ¬ββββββββ ββββββββ¬ββββββββ ββββββββ¬ββββββββ
β β β β
β 1. Click Pay β β β
ββββββββββββββββββββ>β β β
β β β β
β β 2. Request Token β β
β βββββββββββββββββββββ>β β
β β β β
β β 3. Return Token β β
β β<βββββββββββββββββββββ β
β β β β
β β 4. Create Order β β
β βββββββββββββββββββββ>β β
β β β β
β β 5. Return PrepayId β β
β β<βββββββββββββββββββββ β
β β β β
β β 6. Store Transactionβ β
β ββββββββββββββββββββββββββββββββββββββββββ>β
β β β β
β 7. Redirect to β β β
β<ββββββββββββββββββββ β β
β β β β
β 8. Customer β β β
β authenticates β β β
ββββββββββββββββββββββββββββββββββββββββββ>β β
β β β β
β 9. Payment β β β
β processed β β β
β<ββββββββββββββββββββββββββββββββββββββββββ β
β β β β
β 10. Async β β β
β Notification β β β
β β<βββββββββββββββββββββ β
β β β β
β β 11. Update β β
β β Transaction β β
β ββββββββββββββββββββββββββββββββββββββββββ>β
β β β β
β 12. Redirect to β β β
β Success/Failure β β β
β<ββββββββββββββββββββ β β
β β β β
sequenceDiagram
participant Customer
participant App as Your Application
participant Telebirr as Telebirr API
participant DB as Database
Customer->>App: 1. Initiate Payment
App->>Telebirr: 2. Apply Fabric Token
Telebirr-->>App: 3. Return Token
App->>Telebirr: 4. Create Order (preOrder)
Telebirr-->>App: 5. Return prepay_id
App->>DB: 6. Store Transaction (PENDING)
App-->>Customer: 7. Redirect to Checkout URL
Customer->>Telebirr: 8. Authenticate & Pay
Telebirr-->>Customer: 9. Payment Result
Telebirr->>App: 10. Async Notification (POST)
App->>Telebirr: 11. Verify Signature
App->>DB: 12. Update Transaction Status
App-->>Telebirr: 13. Return Success Response
Customer->>App: 14. Return to Success/Failure Page
App->>DB: 15. Get Transaction Details
App-->>Customer: 16. Display Result
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TelebirrPayment (Facade) β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββ β
β βApplyFabric- β β CreateOrder β β QueryOrder β βRefund- β β
β βToken β β β β β βOrder β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββ β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββ β
β βNotifyHandlerβ β Signer β βSignature- β βApiLoggerβ β
β β β β β βVerifier β β β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β External Systems β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β Telebirr API β β Database β β Log Files β β
β β (HTTPS) β β (MySQL) β β (Monolog) β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
git clone https://github.com/MIKEINTOSHSYSTEMS/telebirr-php-payment-integration.git
cd telebirr-php-payment-integrationcomposer installcp .env.example .env
# Edit .env with your credentials# Import the database schema
mysql -u username -p database_name < config/db.sqlPlace your private and public keys in config/keys/:
private_key.pem- Your private key (keep secure, 644 permissions)public_key.pem- Your public key (share with Telebirr)
chmod 755 logs/
chmod 644 config/keys/*.pem
chmod 644 .env| Variable | Description | Example | Required |
|---|---|---|---|
BASE_URL |
API base URL (test/production) | https://developerportal.ethiotelebirr.et:38443/apiaccess/payment/gateway |
Yes |
WEB_BASE_URL |
Web checkout base URL | https://developerportal.ethiotelebirr.et:38443/payment/web/paygate? |
Yes |
FABRIC_APP_ID |
Your fabric app ID | ************************** |
Yes |
APP_SECRET |
Your app secret | ************************** |
Yes |
MERCHANT_APP_ID |
Your merchant app ID | ************************** |
Yes |
MERCHANT_CODE |
Your merchant short code | ********* |
Yes |
DEBUG_MODE |
Enable/disable debug mode | true or false |
Yes |
NOTIFY_URL |
Webhook notification URL | https://your-domain.com/checkout.php |
Yes |
REDIRECT_URL |
Success redirect URL | https://your-domain.com/payment-success.php |
Yes |
FAILURE_URL |
Failure redirect URL | https://your-domain.com/payment-failed.php |
Yes |
DB_HOST |
Database host | localhost |
No |
DB_NAME |
Database name | telebirr_payments |
No |
DB_USER |
Database user | root |
No |
DB_PASS |
Database password | ******** |
No |
LOG_LEVEL |
Logging level | DEBUG, INFO, ERROR |
Yes |
LOG_RETENTION_DAYS |
Days to keep logs | 30 |
No |
return [
'payment' => [
'currency' => 'ETB',
'timeout_express' => '120m',
'business_type' => 'BuyGoods',
'trade_type' => 'Checkout',
'version' => '1.0',
'sign_type' => 'SHA256WithRSA',
'payee_identifier_type' => '04',
'payee_type' => '5000',
],
// ... other config
];<?php
require_once 'vendor/autoload.php';
use Telebirr\TelebirrPayment;
// Load configuration
$config = require 'config/config.php';
// Initialize payment gateway
$telebirr = new TelebirrPayment($config);
// Initialize payment
$result = $telebirr->initializePayment(
'Premium Course Access', // Product title
1500.00, // Amount in ETB
[
'customer_name' => 'Abebech Kebede',
'customer_phone' => '0912345678',
'customer_email' => 'abebech.k@example.com',
'description' => 'Premium Course - Web Development'
]
);
if ($result['success']) {
// Store order ID for later reference
$_SESSION['last_order_id'] = $result['merch_order_id'];
// Redirect to Telebirr checkout
header('Location: ' . $result['checkout_url']);
exit;
} else {
// Handle error
error_log("Payment failed: " . $result['error']);
echo "Error: " . $result['error'];
}<?php
$orderId = $_GET['order_id'] ?? '17714632549580';
$result = $telebirr->queryPayment($orderId);
if ($result['success']) {
$data = $result['data'];
echo "Order ID: " . $data['merch_order_id'] . "\n";
echo "Status: " . $data['order_status'] . "\n";
echo "Amount: " . $data['total_amount'] . " ETB\n";
echo "Transaction Time: " . $data['trans_time'] . "\n";
} else {
echo "Query failed: " . $result['error'];
}<?php
$result = $telebirr->refundPayment(
'17714632549580', // Original order ID
500.00, // Refund amount
'Customer requested refund due to service issue' // Reason
);
if ($result['success']) {
echo "β
Refund initiated successfully!\n";
echo "Refund Request No: " . $result['refund_request_no'] . "\n";
print_r($result['data']);
} else {
echo "β Refund failed: " . $result['error'];
}<?php
// In your notify_url endpoint (checkout.php)
$result = $telebirr->handleNotification($_POST);
// Set HTTP response code
http_response_code($result['http_code']);
// Return response
header('Content-Type: application/json');
echo json_encode(['status' => $result['success'] ? 'success' : 'error']);<?php
$transaction = $telebirr->getTransaction('17714632549580');
if ($transaction) {
echo "Order: " . $transaction['merch_order_id'] . "\n";
echo "Status: " . $transaction['status'] . "\n";
echo "Amount: " . $transaction['amount'] . " ETB\n";
echo "Date: " . $transaction['created_at'] . "\n";
}<?php
$transactions = $telebirr->getTransactions(1, 10); // Page 1, 10 per page
if ($transactions['success']) {
foreach ($transactions['data'] as $tx) {
echo "{$tx['merch_order_id']} - {$tx['amount']} ETB - {$tx['status']}\n";
}
echo "Page {$transactions['pagination']['current_page']} of {$transactions['pagination']['total_pages']}";
}<?php
$logs = $telebirr->getApiLogs(1, 50, [
'method' => 'POST',
'status_code' => 200,
'date_from' => '2026-02-01'
]);
if ($logs['success']) {
foreach ($logs['data'] as $log) {
echo "[{$log['created_at']}] {$log['method']} {$log['endpoint']} - {$log['status_code']}\n";
}
}| Endpoint | Method | Description | Called By |
|---|---|---|---|
/payment/v1/token |
POST |
Get fabric token | ApplyFabricToken |
/payment/v1/merchant/preOrder |
POST |
Create payment order | CreateOrder |
/payment/v1/merchant/queryOrder |
POST |
Query order status | QueryOrder |
/payment/v1/merchant/refund |
POST |
Process refund | RefundOrder |
/payment/web/paygate? |
GET |
Checkout page | Customer browser |
| Endpoint | Purpose | Handled By |
|---|---|---|
/public/demo.php |
Payment form | TelebirrPayment::initializePayment() |
/public/checkout.php |
Webhook receiver | NotifyHandler::handleNotification() |
/public/payment-success.php |
Success page | TelebirrPayment::getTransaction() |
/public/payment-failed.php |
Failure page | TelebirrPayment::getTransaction() |
/public/query-order.php |
Order query | TelebirrPayment::queryPayment() |
/public/refund-order.php |
Refund form | TelebirrPayment::refundPayment() |
/public/logs.php |
Log viewer | TelebirrPayment::getApiLogs() |
/public/export-logs.php |
Log export | CSV generation |
CREATE TABLE transactions (
id INT AUTO_INCREMENT PRIMARY KEY,
merch_order_id VARCHAR(64) NOT NULL UNIQUE,
payment_order_id VARCHAR(64),
trans_id VARCHAR(64),
appid VARCHAR(32),
merch_code VARCHAR(16),
title VARCHAR(512),
amount DECIMAL(20,2),
currency VARCHAR(3) DEFAULT 'ETB',
status VARCHAR(20) DEFAULT 'PENDING',
trade_status VARCHAR(20),
prepay_id VARCHAR(128),
notify_data TEXT,
customer_phone VARCHAR(20),
payment_method VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
completed_at TIMESTAMP NULL,
INDEX idx_merch_order (merch_order_id),
INDEX idx_payment_order (payment_order_id),
INDEX idx_status (status),
INDEX idx_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE refunds (
id INT AUTO_INCREMENT PRIMARY KEY,
refund_request_no VARCHAR(64) NOT NULL UNIQUE,
transaction_id INT,
merch_order_id VARCHAR(64),
payment_order_id VARCHAR(64),
trans_order_id VARCHAR(64),
refund_order_id VARCHAR(64),
amount DECIMAL(20,2),
currency VARCHAR(3) DEFAULT 'ETB',
reason TEXT,
status VARCHAR(20) DEFAULT 'PENDING',
refund_data TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP NULL,
FOREIGN KEY (transaction_id) REFERENCES transactions(id),
INDEX idx_refund_request (refund_request_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE api_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
endpoint VARCHAR(255),
method VARCHAR(10),
request_data TEXT,
response_data TEXT,
status_code INT,
ip_address VARCHAR(45),
user_agent TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_endpoint (endpoint),
INDEX idx_status_code (status_code),
INDEX idx_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;- Create Plugin Structure
cd /path/to/moodle
mkdir -p payment/gateway/telebirr
cd payment/gateway/telebirr- Copy Core Classes
cp -r /path/to/telebirr-php/src/* payment/gateway/telebirr/classes/- Create Moodle Plugin Files
version.php
<?php
$plugin->component = 'paymentgateway_telebirr';
$plugin->version = 2026022000;
$plugin->requires = 2020061500;
$plugin->maturity = MATURITY_STABLE;
$plugin->release = '1.0.0';lib.php
<?php
function paymentgateway_telebirr_payment_method_list() {
return ['telebirr'];
}
function paymentgateway_telebirr_payment_method_config($method) {
return [
'base_url' => get_config('paymentgateway_telebirr', 'base_url'),
'fabric_app_id' => get_config('paymentgateway_telebirr', 'fabric_app_id'),
// ... other config
];
}gateway.php
<?php
namespace paymentgateway_telebirr;
use Telebirr\TelebirrPayment;
class gateway extends \core_payment\gateway {
public function execute_payment($paymentid, $userid, $amount, $currency) {
global $CFG;
require_once $CFG->dirroot . '/payment/gateway/telebirr/classes/TelebirrPayment.php';
$config = $this->get_config();
$telebirr = new TelebirrPayment($config);
$result = $telebirr->initializePayment(
'Moodle Course Enrollment',
$amount,
['userid' => $userid]
);
if ($result['success']) {
redirect($result['checkout_url']);
} else {
throw new \moodle_exception('paymentfailed', 'paymentgateway_telebirr');
}
}
}settings.php
<?php
if ($hassiteconfig) {
$settings = new admin_settingpage('paymentgateway_telebirr', 'Telebirr Settings');
$settings->add(new admin_setting_configtext(
'paymentgateway_telebirr/base_url',
'API Base URL',
'Telebirr API endpoint',
'https://developerportal.ethiotelebirr.et:38443/apiaccess/payment/gateway',
PARAM_URL
));
$settings->add(new admin_setting_configtext(
'paymentgateway_telebirr/fabric_app_id',
'Fabric App ID',
'Your fabric application ID',
'',
PARAM_TEXT
));
// ... other settings
}-
RSA-SHA256 Signatures
- All requests signed with private key
- All notifications verified with public key
- Prevents tampering and forgery
-
Environment Isolation
- Separate test/production credentials
- Environment variables for sensitive data
- No hardcoded credentials
-
Database Security
- PDO prepared statements prevent SQL injection
- Password hashing for stored data
- Encrypted sensitive information
-
XSS Protection
- HTML escaping for all output
- Content Security Policy headers
- Input validation and sanitization
-
CSRF Protection
- Session-based tokens for forms
- Origin validation for webhooks
- SameSite cookie attributes
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-XSS-Protection "1; mode=block"
Header set Referrer-Policy "strict-origin-when-cross-origin"
Header set Content-Security-Policy "default-src 'self'"
</IfModule>| Level | Description | Example |
|---|---|---|
DEBUG |
Detailed debugging info | Request/response payloads |
INFO |
Normal operations | Payment initialized, token obtained |
WARNING |
Non-critical issues | Retry attempts, slow responses |
ERROR |
Critical errors | API failures, signature mismatches |
logs/payment.log- Main application loglogs/error.log- Error-specific log- Database
api_logstable - Structured API call logs
Access the comprehensive logs viewer at /public/logs.php with features:
- Filter by endpoint, method, status code
- Date range filtering
- Search in request/response data
- JSON syntax highlighting
- CSV export
- Pagination
- Statistics dashboard
# Manual cleanup
php public/maintenance/clean-logs.php
# Automated via cron (daily at midnight)
0 0 * * * /usr/bin/php /path/to/project/public/maintenance/clean-logs.phpFABRIC_APP_ID=**************************
APP_SECRET=**************************
MERCHANT_APP_ID=**************************
MERCHANT_CODE=********
BASE_URL=https://developerportal.ethiotelebirr.et:38443/apiaccess/payment/gateway
WEB_BASE_URL=https://developerportal.ethiotelebirr.et:38443/payment/web/paygate?
-
Successful Payment
- Amount: Any amount > 0.01
- Complete payment on Telebirr
- Should redirect to success page
-
Failed Payment
- Amount: 0.01
- Cancel on Telebirr or enter wrong PIN
- Should redirect to failure page
-
Timeout
- Initiate payment but don't complete
- Wait 120 minutes
- Status should show as expired
-
Refund
- Complete a payment first
- Process refund for partial/full amount
- Check refund status
# Run tests
vendor/bin/phpunit tests/
# Test specific class
vendor/bin/phpunit tests/ApplyFabricTokenTest.php| Issue | Possible Cause | Solution |
|---|---|---|
"Invalid signature" |
Wrong key format | Check private key format (PKCS#8) |
"Token expired" |
Session/cache issue | Clear session, check time sync |
"Order not found" |
Wrong order ID format | Use numeric only (no hyphens) |
"cURL error 60" |
SSL certificate issue | Update CA certificates |
"Database not connected" |
Wrong credentials | Check .env database settings |
"API call not logged" |
Missing duration column |
Run database update script |
Enable debug mode in .env:
DEBUG_MODE=true
LOG_LEVEL=DEBUGAccess diagnostic tools:
/public/test-autoload.php- Check autoloader/public/test-token.php- Test token generation/public/diagnostic.php- Full system diagnostic
- Update
.envwith production credentials - Set
DEBUG_MODE=false - Enable SSL verification (
verify_ssl=truein config) - Configure proper web server (Apache/Nginx)
- Set up database backups (daily)
- Configure monitoring and alerts
- Test complete payment flow
- Verify webhook endpoints are publicly accessible
- Set up error reporting (Sentry, etc.)
- Configure proper file permissions
- Enable HTTPS with valid SSL certificate
- Set up log rotation (logrotate)
- Configure firewall rules
- Set up rate limiting
- Test refund process
- Verify email notifications
Contributions are welcome! Please read our Contributing Guidelines first.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
git clone https://github.com/MIKEINTOSHSYSTEMS/telebirr-php-payment-integration.git
cd telebirr-php-payment-integration
composer install
cp .env.example .env
# Edit .env with test credentialsThis project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2026 MIKEINTOSH SYSTEMS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files...
Michael Kifle Teferra
- Website: https://mikeintoshs.com
- Email: mikeintoshsys@gmail.com
- GitHub: @MIKEINTOSHSYSTEMS
- LinkedIn: Michael Kifle
- Ethio Telecom - For providing the Telebirr API
- PHP Community - For excellent libraries and tools
- Moodle Community - For the plugin architecture
- All Contributors - Who helped test and improve this library
See CHANGELOG.md for details.
For support and questions:
- π§ Email: mikeintoshsys@gmail.com
- π GitHub Issues: https://github.com/MIKEINTOSHSYSTEMS/telebirr-php-payment-integration/issues
- π Documentation: https://github.com/MIKEINTOSHSYSTEMS/telebirr-php-payment-integration/wiki
- π¬ Telegram: https://t.me/mikeintosh
- π Phone: +251913391985 / +251938048182
Made with β€οΈ in Ethiopia πͺπΉ by MIKEINTOSH SYSTEMS
Empowering Ethiopian businesses with seamless digital payments