Revamped Flutter Edfapay SoftPOS SDK
Helps developers to easly integrate EdfaPay SoftPos to flutter mobile application with few simple steps.
Wiki & Documentation
EdfaPay SoftPos SDK
Important
Install edfapay-softpos-sdk
- Add the dependency in to your project by running terminal command
pub addat project directory.
flutter pub add edfapay_softpos_sdk
- or add the dependency in to project
pubspec.yaml
dependencies:
edfapay_softpos_sdk: 1.0.0+1 # or specify 'any' to always look for the latest version
Important
Set Repository Access Credentials
- Add below credentials properties to your project
./android/gradle.propertiesor~/.gradle/gradle.properties
PARTNER_REPO_USERNAME=edfapay-sdk-consumer
PARTNER_REPO_PASSWORD=Edfapay@123
Important
FlutterFragmentActivity
- Change the android MainActivity super class from
FlutterActivitytoFlutterFragmentActivity - Path: At project directory:
./android/app/src/main/kotlin/app_package_tree/MainActivity.kt
Example
package com.edfapay.sample_app // your application package
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity: FlutterFragmentActivity()
Usage
1: Imports
- All files under package
package:edfapay_softpos_sdkyou need to import to your integration class.
import 'package:edfapay_softpos_sdk/edfapay_softpos_sdk.dart';
import 'package:edfapay_softpos_sdk/models/edfapay_credentials.dart';
import 'package:edfapay_softpos_sdk/enums/flow_type.dart';
import 'package:edfapay_softpos_sdk/enums/purchase_secondary_action.dart';
import 'package:edfapay_softpos_sdk/enums/presentation.dart';
import 'package:edfapay_softpos_sdk/models/txn_params.dart';
import 'package:edfapay_softpos_sdk/enums/env.dart';
import 'package:edfapay_softpos_sdk/helpers.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
2: Setting Theme (Optional)
2.1: Setting Colors and Logos
final logo = "base64 of image";
// final logo = await assetsBase64('path to image asset');
EdfaPayPlugin.theme()
.setPrimaryColor("#06E59F")
.setSecondaryColor("#000000")
.setPoweredByImage(logo)
.setHeaderImage(logo)
Tip
There is an helper method in SDK to convert image asset to base64
final logo = await assetsBase64('path to image asset');
2.2: Setting Presentation
- Presentation.FULLSCREEN
- Presentation.DIALOG_CENTER
- Presentation.DIALOG_TOP_FILL
- Presentation.DIALOG_BOTTOM_FILL
- Presentation.DIALOG_TOP_START
- Presentation.DIALOG_TOP_END
- Presentation.DIALOG_TOP_CENTER
- Presentation.DIALOG_BOTTOM_START
- Presentation.DIALOG_BOTTOM_END
- Presentation.DIALOG_BOTTOM_CENTER
Note: Properties of each presentation can be change from default to change User Experience
// Usage:
EdfaPayPlugin.theme()
.setPresentation(
Presentation.DIALOG_CENTER
.sizePercent(0.85) // aligned to screen smallest axis 0.20 to 1.0
.dismissOnBackPress(true)
.dismissOnTouchOutside(true)
.animateExit(true)
.animateEntry(true)
.dimBackground(true)
.dimAmount(1.0) // 0.0 to 1.0
.marginAll(0) // open number
.cornerRadius(20) // open number
.marginHorizontal(10) // open number
.marginVertical(10) // open number
.setPurchaseSecondaryAction(PurchaseSecondaryAction.none)
);
2.3: Secondary/Post Purchase Transaction Action
- PurchaseSecondaryAction.reverse
- PurchaseSecondaryAction.refund
- PurchaseSecondaryAction.none
// Usage:
EdfaPayPlugin.theme()
.setPresentation(
Presentation.DIALOG_CENTER
.setPurchaseSecondaryAction(PurchaseSecondaryAction.none)
);
2.4: Enable or disable PIN PAD shuffle at PIN Transaction
// Usage:
EdfaPayPlugin.theme()
.setPresentation(
Presentation.DIALOG_CENTER
.setShufflePinPad(true); // default false
);
3: Initialization
3.1: Create Credentials
-
I will prompt for the credentials whether user Email/Password or Token
EdfaPayCredentials credentials = EdfaPayCredentials.withEmailPassword( environment: Env.DEVELOPMENT, email: null, password: null ); -
I will also prompt for the credentials whether user Email/Password or Token
EdfaPayCredentials credentials = EdfaPayCredentials.withInput( environment: Env.PRODUCTION, ); -
I will prompt for the credentials with prefilled user Email/Password
EdfaPayCredentials credentials = EdfaPayCredentials.withEmailPassword( environment: Env.DEVELOPMENT, email: "user@email.com", password: "Password@123" ); -
I will prompt for the credentials with prefilled user Email
EdfaPayCredentials credentials = EdfaPayCredentials.withEmail( environment: Env.DEVELOPMENT, email: "user@email.com", ); -
I will silently prepare and initialize the SDK with prefilled terminal Token
- This should be Terminal Token to behave as silent SDK initialization
EdfaPayCredentials credentials = EdfaPayCredentials.withToken( environment: Env.DEVELOPMENT, token: "****Terminal Token Should be Generated at Edfapay SoftPOS Portal****", );
3.2: Initialize with Created Credentials
- This function will begin initialization and configure your phone for transactions.
- Note: Make sure to catch the exception and observe the reason of failed initialization.
EdfaPayPlugin.initiate( credentials: credentials, onError: (e){ print('** Error Initializing SDK **'); print('>>> Error'); print(' >>> ${jsonEncode(e)}'); }, onTerminalBindingTask: (bindingTask){ print('>>> Terminal Binding Required'); List<Terminal> terminals = bindingTask.terminals; // Binding Option 1: Show SDK's built-in terminal selection UI bindingTask.bind(); // Binding Option 2: Bind programmatically or InApp UI list terminals.firstOrNull?.bind(); // Refresh terminal list, then show custom UI bindingTask.refresh((error, data){ if(error != null){ print('** Error Refreshing Terminal List **'); print('>>> Error'); print(' >>> ${jsonEncode(error)}'); }else if (data != null){ print('** Success Refreshing Terminal List **'); print('>>> Success'); print(' >>> ${jsonEncode(data)}'); } }); }, onSuccess: (sessionId){ print('** Successful Initialized SDK **'); print('>>> Success'); print(' >>> Session ID: $sessionId'); setState(() { _edfaPluginInitiated = true; }); } );
3.3: Enable start transaction from remote source Optional
- Make sure to open RemoteChannel after successful SDK initialization.
// Usage: Make sure to open RemoteChannel after initialize successful EdfaPayPlugin.RemoteChannel.LocalNetwork(8080, 3.0).open(); - Request from remote source:
# This command passing json to local IP and port of device running our SDK, via Netcat nc echo '{"id":"pay.1","data":{"fun":"purchase","flowType":"DETAIL","amount":"12.0"}}' | nc 192.168.100.70 8080 - Remote Source will receive response in json.
// This json may receive multiple time depends on FlowType: // IMIDIATE:1time | STATUS:2time | DETAIL:3time) // Developer have to observe the {{json}}.data.flowComplete {"id":"pay.1","status":"Status.Approved","data":{"status":true,"code":"0000","transaction":{"acquirer_bank":"RAJB","mada_merchant_id":"800150400566","mada_terminal_id":"5116203000000000","amount":"12.00","apk_version_no":"1.0.0","application_cryptogram":"97FA72F45E2921A9","application_id":"A0000002281010","auth_code":"963680","bid":"RAJB","card_acceptor_business_code":"7399","card_expiration_date":"2902","credit_number":"5069 68** **** 0286","credit_number_length":4,"cvm":"3F0000","cardholder_name":"202F","created_date":"2026-02-18T17:18:40.883889000","cryptogram_information_data":"80","currency":"682","cvmr":{"cvmr_code":"3","cvmr_message_arabic":"لا يتطلب التحقق","cvmr_message_english":"NO VERIFICATION REQUIRED"},"downloadParameter":0,"finish_date":"2026-02-18T17:18:40.900968079","forceReconciliation":0,"formatted_created_date":"18/02/2026","formatted_created_time":"17:18:40","formatted_finish_date":"18/02/2026","formatted_finish_time":"17:18:40","kernel_id":"02","location":"null/null","merchant_id":"cf6858e4-8396-46db-880f-2ca9cc0335ee","operation_type":"PURCHASE","qr_data":"MTI 1200 PCODE 000000 TXNAMT 000000001200 STAN 000212 L-DATE\u0026TIME 260218171840 BID RAJB SCHEME P1 TID 5116203000000000 MID 800150400566 POSCODE 71030170333C FCODE 200 MRCODE 1990 MCC 7399 RRN 181418400212 APPCODE 963680 ACTCODE 000 9F24 3233523033313038334848384149523255373550413158495039323449 9F25 0544","response_message_code":"000","response_message_text_english":"APPROVED","response_message_text_arabic":"مقبولة","rrn":"181418400212","scheme":"P1","stan":"000212","status":"APPROVED","tag9F24":"3233523033313038334848384149523255373550413158495039323449","tag9F25":"0544","tvr":"0480000000","transaction_number":"8d174e11-2a5a-48c6-84c1-d1cb8871ce7c","transaction_status_info":"0000","tsn":"102030","request":{"22":"710101711334","23":"000","47":"RAJBP1","cryptogram_data":"80","26":"7399","acquirer_bank":"RAJB","terminalCurrencyCode":"682","merchant_id":"800150400566","outlet_name":"","tsn":"102030","cvmr_code":"3","cardAcceptorNameLocation":"SULAIMAN ALSAID CENTER FOR HOUSE\\KING KHALIED ROAD\\\\","pos_sn":"ffffffff-8356-b142-ffff-ffff8356b142","cvm":"3F0000","terminal_id":"5116203000000000","53":"51FFFF10203000000002","32":"400022","cryptogram":"97FA72F45E2921A9","55":"BAF4A0F13C76E75FF6A5DA05BA8B96B24A3D823D3768DC2B19FD47ECE087780EA09945B21B4DFCC8661E69AB760B243815C5B5C05D15C0A259D548C8710BD8CFEAB4325E4C1010B2F7E771ED8347C36EF7B0CFF0A8C3ED2AF9961EC1D98B6932B8042C228EBBC28EB9665F3ACF77220D89B97F2CF20A60F678C82509304C169E85D6CE806D1858ACF7B131D454F3AA3AB6CC6478BCF2AAE9ECFA70DABF989DF9D7915BBFDD0B50463EB1FCBDB0F6A459A065A09694BA83D4D0E47158C110FBD3BA5FC1CCCDD24D7B224E61CAC8D968CC94DE1D06C5A65D3DA66A0D8F697650D49298B517D6751D4AE386DC8EFBE500F18BDFC361A05A240C6D099F96BFB0808D","12":"2026-02-18 17:18:41 GMT+03:00","transportData":"002004TMS2003004000000400200006001S","35":"1003A1C26FF6D281B2874A1406BDA85C7421EA76987AC91313C975E412C174C2","transaction_status_info":"0000","device_id":"ffffffff-8356-b142-ffff-ffff8356b142","kernel":"02","cardholder_name":"202F","version":"1.0.0","tvr":"0480000000","4":"12.00","outlet_id":"","business_unit_name":"Zohaib Kambrani 102030","61":"8d174e11-2a5a-48c6-84c1-d1cb8871ce7c","bid":"cf6858e4-8396-46db-880f-2ca9cc0335ee","62":{"11":"000000000","12":"170263041","02":"0","13":"000000000","03":"000000","04":"1","05":"1","16":"01020202","09":"0","location_in_utm_format":"59N244659E0464222","long":46.7061832,"lat":24.7830929,"10":"000000000"},"63":"us-en","41":"5116203000000000","order_id":"8d174e11-2a5a-48c6-84c1-d1cb8871ce7c","aid":"A0000002281010","application_label":"mada"}},"flowComplete":true}}
4: Purchase
- FlowType.immediate
- It will close the Card Scanning Interface immediately after receive response from server.
- FlowType.status
- It will close the Card Scanning Interface at the status animation check/cross.
- FlowType.detail
- It will take the UI flow up to transaction detail screen and will completed by closing screen/ui.
Note: Observe the 4th isFlowCompleted parameter of onPaymentProcessComplete callback.
final params = TxnParams(
amount: amountToPay,
orderId: "12340987"
);
EdfaPayPlugin.purchase(
txnParams: params,
flowType: FlowType.DETAIL,
onPaymentProcessComplete: (status, code, result, isFlowCompleted) {
if(status){
print(' >>> [ Success ]');
print(' >>> [ ${jsonEncode(result)} ]');
}else{
print(' >>> [ Failed ]');
print(' >>> [ ${jsonEncode(result)} ]');
}
},
onRequestTimerEnd: () {
print('>>> Server Timeout');
print(' >>> The request timeout while performing transaction at backend');
},
onCardScanTimerEnd: () {
print('>>> Scan Card Timeout');
print(' >>> The scan card timeout, no any card tap on device');
},
onCancelByUser: () {
print('>>> Canceled By User');
print(' >>> User have cancel the scanning/payment process on its own choice');
},
onError: (error) {
print('>>> Error');
print(' >>> "Scanning/Payment process through an error');
print(' >>> ${jsonEncode(error)}');
});
5: Invoice Payment & Refund (BNPL / Checkout)
Use this when the customer pays through a hosted checkout (BNPL providers like Tamara/Tabby, or a payment-gateway link) instead of tapping a card. invoicePayment returns a checkoutDeeplink you open in a browser/WebView so the customer can complete payment; invoiceRefund reverses a completed invoice by its transactionId.
Why a deeplink? These flows are hosted by the provider, so the SDK hands you a URL to redirect to rather than driving the card UI itself.
import 'package:edfapay_softpos_sdk/models/invoice_response.dart';
EdfaPayPlugin.invoicePayment(
request: InvoicePaymentRequest(
paymentMethod: PaymentMethod.tamara, // tamara | tabby | checkout
merchantId: 'your-merchant-id',
amount: 11100, // minor units (e.g. 111.00)
currency: 'SAR',
phoneNumber: '+966512345678',
email: 'customer@example.com',
orderReferenceId: 'ORD-111',
orderNumber: '10111',
locale: 'ar_SA',
paymentType: 'PAY_BY_INSTALMENTS',
invoice: Invoice(
total: 11100,
lineItems: [
InvoiceLineItem(sku: 'SKU-001', description: 'Product A', unitCost: 11100, quantity: 1, netTotal: 11100, total: 11100),
],
),
additionalData: AdditionalData(storeId: 'STORE-01'),
),
onSuccess: (InvoicePaymentResponse res) {
// Open res.checkoutDeeplink for the customer to finish paying
print('Deeplink: ${res.checkoutDeeplink}, txn: ${res.transactionId}');
},
onError: (e) => print('Error: ${e['message']}'),
);
// Refund a completed invoice by its transaction id
EdfaPayPlugin.invoiceRefund(
request: InvoiceRefundRequest(paymentMethod: PaymentMethod.tamara, amount: 11100, transactionId: 'txn-id'),
onSuccess: (res) => print('Refunded: ${res.transactionId}'),
onError: (e) => print('Error: ${e['message']}'),
);
6: Transaction Search & Gateway Status
searchTxnApi queries the backend for transactions (useful for reconciliation screens or lookups when you don't have a local record), and pgTxnStatusApi polls the payment-gateway status for a hosted checkout session (pair it with the sessionId/reference from an invoice payment to confirm the customer finished).
EdfaPayPlugin.searchTxnApi(
parameters: {'search': 'ORD-111'},
onSuccess: (result) => print('Matches: $result'),
onError: (e) => print('Error: ${e['message']}'),
);
EdfaPayPlugin.pgTxnStatusApi(
sessionId: 'pg-session-id',
onSuccess: (result) => print('Gateway status: $result'),
onError: (e) => print('Error: ${e['message']}'),
);
7: Online Receipts
Generate a shareable receipt URL for a transaction or a reconciliation, or open it directly. Use generateOnlineReceipt when you want the link (to render a QR, email it, etc.); use openOnlineReceipt to just launch it in the browser.
// Get the URL
EdfaPayPlugin.Extension.generateOnlineReceipt(
type: ReceiptType.TRANSACTION, // or ReceiptType.RECONCILIATION
id: 'transaction-or-reconciliation-id',
onSuccess: (url) => print('Receipt: $url'),
onError: (e) => print('Error: ${e['message']}'),
);
// Or open it straight away
EdfaPayPlugin.Extension.openOnlineReceipt(
type: ReceiptType.TRANSACTION,
id: 'transaction-id',
onError: (e) => print('Error: ${e['message']}'),
);
8: Device Readiness (Permissions)
Reconciliation can run in the background and notify the merchant. On modern Android that needs several allowances that the OS won't grant silently. Check first, and request only when missing, so you don't nag users unnecessarily. Each request… returns whether it was granted.
Why it matters: without notifications the merchant misses reconciliation results; without exact-alarms / battery-unrestricted / (on aggressive OEMs) autostart, a scheduled reconciliation may never fire while the app is in Doze.
// Notifications (Android 13+)
if (!await EdfaPayPlugin.Utils.haveNotificationPermission()) {
EdfaPayPlugin.Utils.requestNotificationPermission(
callback: (granted) => print('Notifications: $granted'),
);
}
// Exact alarms (Android 12+) — needed for on-time scheduled reconciliation
if (!await EdfaPayPlugin.Utils.canScheduleExactAlarms()) {
EdfaPayPlugin.Utils.requestExactAlarmPermission(callback: (granted) => print('Exact alarms: $granted'));
}
// Battery "Unrestricted" — lets background work reach the network in Doze
if (!await EdfaPayPlugin.Utils.isBatteryUnrestricted()) {
EdfaPayPlugin.Utils.requestBatteryUnrestricted(callback: (granted) => print('Battery: $granted'));
}
// Autostart allowlist — only some OEMs (no-op on stock Android)
if (await EdfaPayPlugin.Utils.deviceNeedsAutostart()) {
EdfaPayPlugin.Utils.requestAutostartPermission(callback: (handled) => print('Autostart: $handled'));
}
9: Reconciliation Notification Sounds
Customise (or mute) the sounds played for reconciliation notifications. Pass a base64-encoded sound file per state; pass nothing to mute. This is a theme setting, so apply it before/around initialization like the rest of the theme.
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;
Future<String> b64(String asset) async =>
base64Encode((await rootBundle.load(asset)).buffer.asUint8List());
EdfaPayPlugin.theme().setReconciliationNotificationSounds(
success: await b64('assets/sounds/success.wav'),
error: await b64('assets/sounds/error.wav'),
processing: await b64('assets/sounds/processing.wav'),
);
// Mute all reconciliation sounds
EdfaPayPlugin.theme().setReconciliationNotificationSounds();
10: API Key Credential & Unbind Terminal
Attach an API key to any credential with the chainable setApiKey, and detach the bound terminal from the device with Extension.unbindTerminal (e.g. before handing the device to another merchant).
final credentials = EdfaPayCredentials
.withToken(environment: Env.PRODUCTION, token: 'login-token')
.setApiKey('your-api-key');
EdfaPayPlugin.Extension.unbindTerminal(
onSuccess: (res) => print('Unbound: $res'),
onError: (e) => print('Error: ${e['message']}'),
);
Example
Instruction to Run Example:
- Make sure to install below plugins to your sample flutter app
- Dont forget to apply Important marked configuration/change at the top in to your project
- Copy the below content and paste it to main.dart (make sure to replace all)
// ignore_for_file: avoid_print
import 'dart:convert';
import 'package:edfapay_softpos_sdk/edfapay_softpos_sdk.dart';
import 'package:edfapay_softpos_sdk/enums/flow_type.dart';
import 'package:edfapay_softpos_sdk/enums/presentation.dart';
import 'package:edfapay_softpos_sdk/enums/purchase_secondary_action.dart';
import 'package:edfapay_softpos_sdk/models/edfapay_credentials.dart';
import 'package:edfapay_softpos_sdk/models/txn_params.dart';
import 'package:edfapay_softpos_sdk/models/invoice_request.dart';
import 'package:edfapay_softpos_sdk/models/invoice_response.dart';
import 'package:edfapay_softpos_sdk/enums/env.dart';
import 'package:edfapay_softpos_sdk/enums/receipt_type.dart';
import 'package:edfapay_softpos_sdk/helpers.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/* add the plugin for below: (hexcolor: any) https://pub.dev/packages/hexcolor */
import 'package:hexcolor/hexcolor.dart';
// add the plugin for below: (fluttertoast: any) https://pub.dev/packages/fluttertoast
import 'package:fluttertoast/fluttertoast.dart';
const TERMINAL_TOKEN = "D08BE4C0FE041A155F0028C0FCD042087771DA505D54087EFC3A0FC1183213D6";
const logoPath = "assets/images/edfa_logo.png";
const amountToPay = "01.01";
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var _edfaPluginInitiated = false;
String? _lastTxnId; // captured from the last purchase, used for the receipt
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Padding(
padding: const EdgeInsets.all(15),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 20),
Expanded(
flex: 2,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FractionallySizedBox(widthFactor: 0.3, child: Image.asset(logoPath)),
const SizedBox(height: 30),
const Text("SDK", style: TextStyle(fontSize: 65, fontWeight: FontWeight.w700), textAlign: TextAlign.center),
const SizedBox(height: 10),
const Text("v0.0.1", style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
],
),
),
const Expanded(
flex: 1,
child: Padding(
padding: EdgeInsets.all(10),
child: Text("You're on your way to enabling your Android App to allow your customers to pay in a very easy and simple way just click the payment button and tap your payment card on NFC enabled Android phone.",
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black45), textAlign: TextAlign.center),
),
),
if(!_edfaPluginInitiated)
ElevatedButton(onPressed: initiate, style: ButtonStyle(backgroundColor: MaterialStatePropertyAll(HexColor("06E59F"))), child: const Text("Initiate", style: TextStyle(color: Colors.black))),
if(_edfaPluginInitiated)
...[
ElevatedButton(onPressed: pay, style: ButtonStyle(backgroundColor: MaterialStatePropertyAll(HexColor("06E59F"))), child: const Text("Pay $amountToPay", style: TextStyle(color: Colors.black))),
ElevatedButton(onPressed: refund, style: ButtonStyle(backgroundColor: MaterialStatePropertyAll(HexColor("06E59F"))), child: const Text("Refund With RRN", style: TextStyle(color: Colors.black))),
ElevatedButton(onPressed: reconcile, style: ButtonStyle(backgroundColor: MaterialStatePropertyAll(HexColor("06E59F"))), child: const Text("Reconciliation", style: TextStyle(color: Colors.black))),
ElevatedButton(onPressed: txnHistory, style: ButtonStyle(backgroundColor: MaterialStatePropertyAll(HexColor("06E59F"))), child: const Text("Txn History $amountToPay", style: TextStyle(color: Colors.black))),
ElevatedButton(onPressed: checkout, style: ButtonStyle(backgroundColor: MaterialStatePropertyAll(HexColor("06E59F"))), child: const Text("Checkout / BNPL", style: TextStyle(color: Colors.black))),
ElevatedButton(onPressed: onlineReceipt, style: ButtonStyle(backgroundColor: MaterialStatePropertyAll(HexColor("06E59F"))), child: const Text("Open Last Receipt", style: TextStyle(color: Colors.black))),
ElevatedButton(onPressed: prepareDevice, style: ButtonStyle(backgroundColor: MaterialStatePropertyAll(HexColor("06E59F"))), child: const Text("Prepare Device", style: TextStyle(color: Colors.black))),
ElevatedButton(onPressed: unbindTerminal, style: ButtonStyle(backgroundColor: MaterialStatePropertyAll(HexColor("06E59F"))), child: const Text("Unbind Terminal", style: TextStyle(color: Colors.black))),
],
const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Text("Click on button above to test the card processing with 10.00 SAR", style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400), textAlign: TextAlign.center),
),
],
),
),
),
);
}
initiate() async {
EdfaPayPlugin.enableLogs(true);
/*
EdfaPayCredentials credentials = EdfaPayCredentials.withEmailPassword(
environment: Env.DEVELOPMENT,
email: null,
password: null
);
EdfaPayCredentials credentials = EdfaPayCredentials.withEmailPassword(
environment: Env.DEVELOPMENT,
email: "user@mail.com",
password: "Password@123"
);
EdfaPayCredentials credentials = EdfaPayCredentials.withInput(
environment: Env.PRODUCTION,
);
EdfaPayCredentials credentials = EdfaPayCredentials.withEmail(
environment: Env.DEVELOPMENT,
email: "user@mail.com",
);
EdfaPayCredentials credentials = EdfaPayCredentials.withToken(
environment: Env.DEVELOPMENT,
token: TERMINAL_TOKEN,
);
*/
EdfaPayCredentials credentials = EdfaPayCredentials.withToken(
environment: Env.DEVELOPMENT,
token: TERMINAL_TOKEN,
);
// Optionally attach an API key to any credential (chainable):
// credentials = credentials.setApiKey("your-api-key");
// setTheme();
EdfaPayPlugin.initiate(
credentials: credentials,
onError: (e){
toast("** Error Initializing SDK **");
print('** Error Initializing SDK **');
print('>>> Error');
print(' >>> ${jsonEncode(e)}');
},
onTerminalBindingTask: (bindingTask){
print('>>> Terminal Binding Required');
List<Terminal> terminals = bindingTask.terminals;
// Option 1: Show SDK's built-in terminal selection UI
bindingTask.bind();
// Option 2: Refresh terminal list, then show custom UI
// Option 3: Bind programmatically or InApp UI list
terminals.firstOrNull?.bind();
},
onSuccess: (sessionId){
toast('** Successful Initialized SDK **');
print('** Successful Initialized SDK **');
print('>>> Success');
print(' >>> Session ID: $sessionId');
setState(() {
_edfaPluginInitiated = true;
});
}
);
}
setTheme() async {
final logo = await assetsBase64(logoPath);
final presentation = Presentation.DIALOG_CENTER
.sizePercent(0.85)
.dismissOnTouchOutside(true)
.dimBackground(true)
.dimAmount(1.0)
.marginAll(0)
.animateEntry(true)
.animateExit(true)
.cornerRadius(20)
.dismissOnBackPress(true)
.marginHorizontal(10)
.marginVertical(10)
.setPurchaseSecondaryAction(PurchaseSecondaryAction.REVERSE)
.setShufflePinPad();
EdfaPayPlugin.theme()
.setPrimaryColor("#06E59F")
.setSecondaryColor("#000000")
.setHeaderImage(logo)
.setPoweredByImage(logo)
.setPresentation(presentation)
// Custom reconciliation sounds: pass base64-encoded audio per state, e.g.
// .setReconciliationNotificationSounds(
// success: await assetsBase64('assets/sounds/success.wav'),
// error: await assetsBase64('assets/sounds/error.wav'),
// processing: await assetsBase64('assets/sounds/processing.wav'))
// Called with no arguments it mutes every reconciliation sound:
.setReconciliationNotificationSounds();
}
pay() async {
if (!_edfaPluginInitiated) {
toast("Edfapay plugin not initialized.");
return;
}
final params = TxnParams(
amount: amountToPay,
orderId: "12340987"
);
EdfaPayPlugin.enableLogs(true);
EdfaPayPlugin.purchase(
txnParams: params,
flowType: FlowType.DETAIL,
onPaymentProcessComplete: (status, code, result, isFlowCompleted) {
toast("Transaction Completed | Check the logs");
if(status){
// Keep the transaction id so we can pull its online receipt later
_lastTxnId = result?['transaction_number'] as String? ?? result?['id'] as String?;
print(' >>> [ Success ]');
print(' >>> [ ${jsonEncode(result)} ]');
}else{
print(' >>> [ Failed ]');
print(' >>> [ ${jsonEncode(result)} ]');
}
},
onRequestTimerEnd: () {
toast("Server Request Timeout");
print('>>> Server Timeout');
print(' >>> The request timeout while performing transaction at backend');
},
onCardScanTimerEnd: () {
toast("Scan Card Timeout");
print('>>> Scan Card Timeout');
print(' >>> The scan card timeout, no any card tap on device');
},
onCancelByUser: () {
toast("Cancel By User");
print('>>> Canceled By User');
print(' >>> User have cancel the scanning/payment process on its own choice');
},
onError: (error) {
toast("Error occurred | Check the logs");
print('>>> Error');
print(' >>> "Scanning/Payment process through an error');
print(' >>> ${jsonEncode(error)}');
});
}
refund() async {
if (!_edfaPluginInitiated) {
toast("Edfapay plugin not initialized.");
return;
}
final params = TxnParams(
amount: amountToPay,
originalTransaction: Transaction.withRRN("1234567890", DateTime.now())
);
EdfaPayPlugin.enableLogs(true);
EdfaPayPlugin.refund(txnParams: params, onPaymentProcessComplete: (status, code, result, isFlowCompleted) {
toast("Card Payment Process Completed");
print('>>> Payment Process Complete');
}, onRequestTimerEnd: () {
toast("Server Request Timeout");
print('>>> Server Timeout');
print(' >>> The request timeout while performing transaction at backend');
}, onCardScanTimerEnd: () {
toast("Card Scan Timeout");
print('>>> Scan Card Timeout');
print(' >>> The scan card timeout, no any card tap on device');
}, onCancelByUser: () {
toast("Cancel By User");
print('>>> Canceled By User');
print(' >>> User have cancel the scanning/payment process on its own choice');
}, onError: (error) {
toast(error.toString());
print('>>> Exceptio¬n');
print(' >>> "Scanning/Payment process through an exception, Check the logs');
print(' >>> ${error.toString()}');
});
}
reconcile() async {
if (!_edfaPluginInitiated) {
toast("Edfapay plugin not initialized.");
return;
}
EdfaPayPlugin.reconcile(
onSuccess: (response){
toast("Success reconcile");
},
onError: (error){
print('>>> Exception');
print(' >>> "Scanning/Payment process through an exception, Check the logs');
print(' >>> ${error.toString()}');
}
);
}
txnHistory() async {
if (!_edfaPluginInitiated) {
toast("Edfapay plugin not initialized.");
return;
}
EdfaPayPlugin.txnHistory(
onSuccess: (response){
toast("Success txnHistory");
},
onError: (error){
print('>>> Exception');
print(' >>> "Scanning/Payment process through an exception, Check the logs');
print(' >>> ${error.toString()}');
}
);
}
// Hosted checkout (BNPL / payment-gateway link). Returns a deeplink you open
// in a browser so the customer can complete payment outside the card UI.
checkout() async {
if (!_edfaPluginInitiated) {
toast("Edfapay plugin not initialized.");
return;
}
EdfaPayPlugin.invoicePayment(
request: InvoicePaymentRequest(
paymentMethod: PaymentMethod.tamara, // tamara | tabby | checkout
merchantId: "your-merchant-id",
amount: 11100, // minor units (111.00)
currency: "SAR",
phoneNumber: "+966512345678",
email: "customer@example.com",
orderReferenceId: "ORD-111",
orderNumber: "10111",
invoice: Invoice(
total: 11100,
lineItems: [
InvoiceLineItem(sku: "SKU-001", description: "Product A", unitCost: 11100, quantity: 1, netTotal: 11100, total: 11100),
],
),
additionalData: AdditionalData(storeId: "STORE-01"),
),
onSuccess: (InvoicePaymentResponse res) {
toast("Checkout created | Open the deeplink");
print('>>> Deeplink: ${res.checkoutDeeplink}');
print('>>> Txn: ${res.transactionId}');
},
onError: (e) {
toast("Checkout error | Check the logs");
print(' >>> ${jsonEncode(e)}');
},
);
}
// Build (and open) the online receipt for the last successful purchase.
onlineReceipt() async {
if (_lastTxnId == null) {
toast("Make a purchase first");
return;
}
EdfaPayPlugin.Extension.openOnlineReceipt(
type: ReceiptType.TRANSACTION, // or ReceiptType.RECONCILIATION
id: _lastTxnId!,
onError: (e) => print(' >>> ${jsonEncode(e)}'),
);
}
// Ask for the permissions background reconciliation relies on. Check first so
// you only prompt when something is actually missing.
prepareDevice() async {
if (!await EdfaPayPlugin.Utils.haveNotificationPermission()) {
EdfaPayPlugin.Utils.requestNotificationPermission(callback: (g) => print('Notifications: $g'));
}
if (!await EdfaPayPlugin.Utils.canScheduleExactAlarms()) {
EdfaPayPlugin.Utils.requestExactAlarmPermission(callback: (g) => print('Exact alarms: $g'));
}
if (!await EdfaPayPlugin.Utils.isBatteryUnrestricted()) {
EdfaPayPlugin.Utils.requestBatteryUnrestricted(callback: (g) => print('Battery: $g'));
}
if (await EdfaPayPlugin.Utils.deviceNeedsAutostart()) {
EdfaPayPlugin.Utils.requestAutostartPermission(callback: (g) => print('Autostart: $g'));
}
toast("Requested device readiness permissions");
}
// Detach the bound terminal from this device (e.g. before reassigning it).
unbindTerminal() async {
EdfaPayPlugin.Extension.unbindTerminal(
onSuccess: (res) {
toast("Terminal unbound");
setState(() => _edfaPluginInitiated = false);
},
onError: (e) {
toast("Unbind error | Check the logs");
print(' >>> ${jsonEncode(e)}');
},
);
}
toast(String text) {
Fluttertoast.showToast(msg: text);
}
}
License
MIT
Libraries
- edfapay_bridge
- edfapay_plugin
- edfapay_softpos_sdk
- enums/bnpl_provider
- enums/env
- enums/flow_type
- enums/function_code
- enums/presentation
- enums/purchase_secondary_action
- enums/receipt_type
- enums/transaction_type
- helpers
- models/edfapay_credentials
- models/invoice_request
- models/invoice_response
- models/location
- models/pagination
- models/presentation_config
- models/sdk_theme
- models/terminal
- models/terminal_binding_task
- models/transaction
- models/txn_params
- models/user_info