Telebirr Payment Integration Guide
Base URL for Telebirr API
Base URL:
[Link]
Implementation Overview
1. Backend Implementation
Ensure that the backend API endpoints are set up to initialize Telebirr payments and validate
transactions.
2. Mobile App Implementation(Andriod)
2.1 Download the SDK
● Test SDK:
[Link]
=sharing
● Production SDK:
[Link]
sharing
2.2 Add SDK to the Project
● Place the downloaded SDK file ([Link]) inside the
libs folder at the app level in the Android project.
2.3 Add ProGuard Rules
● Create a file named [Link] under the app directory and add the following content:
# Keep all classes within the package for Huawei Ethiopia Pay SDK
-keep class [Link].** { *; }
2.4 Update Dependencies
● Add the SDK dependency to the [Link] file (app level):
implementation files('libs/[Link]')
2.5 Implement Native Code in [Link]
package your_app_package_id
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link];
import [Link];
class MainActivity : FlutterFragmentActivity() { // Changed to FlutterFragmentActivity
private val CHANNEL = "telebirr"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
[Link](flutterEngine)
// MethodChannel setup for communication with Flutter
MethodChannel([Link], CHANNEL).setMethodCallHandler { call, result ->
if ([Link] == "nativeFunction") {
val appid = [Link]<String>("appid")
val merchCode = [Link]<String>("merch_code")
val prepayId = [Link]<String>("prepay_id")
val timestamp = [Link]<String>("timestamp")
val amount = [Link]<String>("amount")
Log.d("merchCode", merchCode ?: "merchCode is null")
initiateTelebirrPayment(result, appid, merchCode, prepayId, timestamp, amount)
}
else {
[Link]()
}
}
}
private fun initiateTelebirrPayment(result: [Link], appid: String?, merchCode: String?, prepayId: String?,
timestamp: String?, amount: String?) {
val BUYGOODS = "Virtual Equb"
val shortCode = merchCode // Replace with your actual short code
val amount = amount // Replace with the amount
// val amount = "1" // Replace with the amount
val prepayId = prepayId // Replace with your prepay_id
val timestamp = timestamp // Replace with the actual timestamp
val receiveCode = "TELEBIRR$$BUYGOODS$$shortCode$$amount$$prepayId$$timestamp"
Log.d("Telebirr", receiveCode)
val payInfo = [Link]()
.setAppId(appid)
.setShortCode(shortCode)
.setReceiveCode(receiveCode)
// .setReturnApp("[Link]")
.build();
[Link]().pay(this, payInfo)
Log.d("Telebirr","...........")
// Set the callback to handle payment results
[Link]().setPayCallback { code, errMsg ->
Log.d("Telebirr from call back", "onPayCallback: code $code errMsg $errMsg")
[Link](this@MainActivity, "$code $errMsg", Toast.LENGTH_SHORT).show()} }
}
2.6 Invoke Native Code from Flutter
1. Define a MethodChannel:
static const platform = MethodChannel('telebirr');
2. Create a Function to Invoke the Native Method:
Future<String> invokeNativeMethod(Map<String, dynamic> responseMap) async {
try {
final String result =
await [Link]('nativeFunction', responseMap);
return result;
} on PlatformException catch (e) {
return "Failed to invoke native code: ${[Link]}";
}
}
3. Backend Integration in Flutter
Create a function to initialize the payment by hitting the backend:
Future<Result> paymentInitializes(String workOrderId, String amount) async {
return _handlePaymentInitialization(
workOrderId,
amount,
[Link](workOrderId),
);
}
Future<Result> _handlePaymentInitialization(
String workOrderId, String amount, Uri endpoint) async {
try {
final response = await httpClient
.post(
endpoint,
headers: [Link](),
body: jsonEncode({"title": "Telebirr Payment", "amount": amount}),
)
.timeout(_timeoutDuration);
if ([Link] == 200) {
final responseData = [Link]([Link]);
if (responseData['code'] == 500) {
throw Exception(responseData['message']);
}
final telebirrResponse =
[Link](responseData['data']);
final responseMap = {
...[Link](),
"amount": amount,
};
SharedPreferences prefs = await [Link]();
[Link]("merchOrderId", [Link]);
// [Link]("transactionNo", [Link]);
await invokeNativeMethod(responseMap);
return Result(responseData['code'].toString(), true,
responseData['message'] ?? "Success");
}
_handleErrorResponse(response);
throw Exception("Unknown error occurred");
} on SocketException catch (error) {
_handleSocketException(error);
rethrow;
}
}
4. Verify Payment Status
Future<bool> checkStatus(String transactionNo) async {
try {
final url = [Link](transactionNo);
final response = await httpClient
.get(
url,
headers: [Link](),
)
.timeout(_timeoutDuration);
if ([Link] == 200) {
TransactionResponse transactionResponse =
[Link]([Link]([Link]));
final isPaid = [Link]?.status == "paid";
if (!isPaid && _retryCount < _maxRetries) {
_retryCount++;
await [Link](const Duration(seconds: 2));
return checkStatus(transactionNo);
}
_retryCount = 0;
return isPaid;
}
_handleErrorResponse(response);
return false;
} catch (e) {
_retryCount = 0;
rethrow;
}
}
5. Handle App Lifecycle Changes
Use AppLifecycleState to check transaction status when the app resumes:
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
[Link](state);
if (state == [Link]) {
SharedPreferences prefs = await [Link]();
if ([Link]("merchOrderId") != null) {
[Link]<PaymentBloc>(context).add(PaymentCheckStatus(
transactionNo: [Link]("merchOrderId") ?? "",
));
}
}
}
6. Listen to bloc and show message
BlocConsumer<PaymentBloc, PaymentState>(
listener: (context, state) async {
if (state is PaymentPaidSuccess) {
[Link]("Payment Successfully Paid");
SharedPreferences prefs =
await [Link]();
[Link]("merchOrderId");
[Link](context);
}
3. Mobile App Implementation(Ios with Flutter)
Quick Steps:
1. Download and Add the SDK
○ Download the [Link] file from this link:
[Link]
○ Copy it into your project's ios folder as shown in the diagram.
2. Import the SDK
● Open the Runner-Bridging-Header.h file under the Runner folder.
● Add the following line to import the SDK:
#import "EthiopiaPaySDK/EthiopiaPayManager.h"
3. Modify [Link]
1. Open the [Link] file and update it as follows:
○ Import necessary modules:
import UIKit
import Flutter
○ Make the AppDelegate class conform to the EthiopiaPayManagerDelegate protocol:
@main
@objc class AppDelegate: FlutterAppDelegate, EthiopiaPayManagerDelegate {
2. Handle Flutter Native Method Calls
Add a FlutterMethodChannel in didFinishLaunchingWithOptions:
let controller = window?.rootViewController as! FlutterViewController
let telebirrChannel = FlutterMethodChannel(name: "telebirr", binaryMessenger: [Link])
[Link] { [weak self] (call: FlutterMethodCall, result: @escaping
FlutterResult) in
if [Link] == "nativeFunction" {
guard let arguments = [Link] as? [String: Any],
let appId = arguments["appid"] as? String,
let shortCode = arguments["merch_code"] as? String,
let prepayId = arguments["prepay_id"] as? String,
let amount = arguments["amount"] as? String else {
print("Received call from flutter: \([Link] )")
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing or invalid arguments",
details: nil))
return
print("Received arguments: appId=\(appId), shortCode=\(shortCode), prepayId=\(prepayId),
amount=\(amount)")
self?.initiatePayment(appId: appId, shortCode: shortCode, prepayId: prepayId, amount: amount)
result("Payment initiated")
} else {
result(FlutterMethodNotImplemented)
}
3. Open URL Handling
Add the following method to handle URL responses:
override func application(
_ app: UIApplication,
open url: URL,
options: [[Link] : Any] = [:]
) -> Bool {
[Link]().handleOpen(url)
return true
4. Initiate Payment Function
Add a function to process payments:
func initiatePayment(appId: String, shortCode: String, prepayId: String, amount: String) {
do {
print("initiatePayment: \(appId) \(shortCode) \(prepayId) \(amount)")
let timestamp = getCurrentTimestampString()
let receiveCode = "TELEBIRR$BUYGOOD$\(shortCode)$\(amount)$\(prepayId)$\(timestamp)"
let manager = [Link]()
[Link] = self
[Link](withAppId: appId, shortCode: shortCode, receiveCode: receiveCode, returnAppScheme: "PSTS")
print("Payment initiated")
} catch {
print("Error: \(error)")
5. Helper Function for Timestamp
Add a helper method to generate the current timestamp:
func getCurrentTimestampString() -> String {
let currentDate = Date()
let formatter = DateFormatter()
[Link] = "yyyyMMddHHmmss"
return [Link](from: currentDate)
6. Handle Payment Results
Implement the delegate method to handle the payment response:
// EthiopiaPayManagerDelegate method
func payResultCallback(withCode code: Int, msg: String) {
// Handle the payment result
The full code of [Link] is here
import UIKit
import Flutter
@main
@objc class AppDelegate: FlutterAppDelegate, EthiopiaPayManagerDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [[Link]: Any]?
) -> Bool {
[Link](with: self)
let controller = window?.rootViewController as! FlutterViewController
let telebirrChannel = FlutterMethodChannel(name: "telebirr", binaryMessenger: [Link])
[Link] { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in
if [Link] == "nativeFunction" {
guard let arguments = [Link] as? [String: Any],
let appId = arguments["appid"] as? String,
let shortCode = arguments["merch_code"] as? String,
let prepayId = arguments["prepay_id"] as? String,
let amount = arguments["amount"] as? String else {
print("Received call from flutter: \([Link] )")
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing or invalid arguments", details: nil))
return
}
print("Received arguments: appId=\(appId), shortCode=\(shortCode), prepayId=\(prepayId), amount=\(amount)")
self?.initiatePayment(appId: appId, shortCode: shortCode, prepayId: prepayId, amount: amount)
result("Payment initiated")
} else {
result(FlutterMethodNotImplemented)
}
}
return [Link](application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(
_ app: UIApplication,
open url: URL,
options: [[Link] : Any] = [:]
) -> Bool {
[Link]().handleOpen(url)
return true
}
func initiatePayment(appId: String, shortCode: String, prepayId: String, amount: String) {
do {
print("initiatePayment: \(appId) \(shortCode) \(prepayId) \(amount)")
let timestamp = getCurrentTimestampString()
let receiveCode = "TELEBIRR$BUYGOOD$\(shortCode)$\(amount)$\(prepayId)$\(timestamp)"
let manager = [Link]()
[Link] = self
[Link](withAppId: appId, shortCode: shortCode, receiveCode: receiveCode, returnAppScheme: "PSTS")
print("Payment initiated")
} catch {
print("Error: \(error)")
}
func getCurrentTimestampString() -> String {
let currentDate = Date()
let formatter = DateFormatter()
[Link] = "yyyyMMddHHmmss"
return [Link](from: currentDate)
}
// EthiopiaPayManagerDelegate method
func payResultCallback(withCode code: Int, msg: String) {
// Handle the payment result
}
}
Then configure Xcode
Configure URL Schemes in Xcode
1. Queried URL Schemes
● Go to Targets > Info tab.
● Add a Queried URL Schemes entry with values like telebirrcustomerApp.
2. Define URL Schemes
● Add the URL Types in the Info tab:
○ Set the URL scheme to your desired string (e.g., vims).
● Alternatively, update the [Link]:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>vims</string>
</array>
</dict>
</array>
3. Application Query Schemes
● Add supported schemes to LSApplicationQueriesSchemes in [Link]:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>telebirrcustomerApp</string>
</array>
Final Step
You can now call the native code from Flutter using the telebirr channel as implemented in the
[Link]. This approach integrates the EthiopiaPaySDK and allows the app to handle
payment flows seamlessly.