Android Native

Overview

The EdfaPay SoftPos SDK is a comprehensive Android SDK that enables payment processing functionality in your Android application. It provides a complete solution for accepting card payments through NFC (Near Field Communication) technology, transforming any Android device into a point-of-sale terminal.

Key Features

  • Card Payment Processing: Accept payments via NFC-enabled Android devices
  • Purchase Transactions: Process purchase transactions with full card verification
  • Transaction Reversal: Reverse the last transaction when needed
  • Reconciliation: Perform reconciliation and retrieve reconciliation history
  • Transaction Management: Get transaction details and manage transaction history
  • Customizable UI: Theme customization with colors, fonts, and branding
  • Multiple Authentication Methods: Support for email/password or auth token authentication

Requirements

  • Minimum SDK Version: 28 (Android 9.0)
  • Target SDK Version: 36 (recommended)
  • Kotlin: Required
  • Java Version: 11 or higher
  • NFC Support: Device must support NFC for card reading
  • Location Permission: Required for payment processing

Use Cases

  • Retail point-of-sale systems
  • Mobile payment acceptance
  • In-store payment processing
  • Transaction management and reconciliation
  • Multi-terminal payment solutions

Installation

[!IMPORTANT]

Configure Repository

It is important to add the gradlePluginPortal and maven repository with authorization repositories to your project. It allows the gradle to download edfapay plugin from gradlePluginPortal and the native dependency from the edfapay-mobile repository.

If your project build was configured to prefer settings repositories, Place the below maven block to project ./settings.gradle or ./settings.gradle.kts

pluginManagement {
    repositories {
        ...
        mavenCentral()
        gradlePluginPortal()
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("https://build.edfapay.com/nexus/content/repositories/edfapay-mobile/")
            credentials {
                username = "USERNAME"  // USERNAME provided by edfapay
                password = "PASSWORD"  // PASSWORD provided by edfapay
            }
        }
    }
}
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

dependencyResolutionManagement {
    repositoriesMode = RepositoriesMode.PREFER_SETTINGS
    repositories {
        google()
        mavenCentral()
        maven {
            url "https://build.edfapay.com/nexus/content/repositories/edfapay-mobile/"
            credentials {
                username "USERNAME"  // USERNAME provided by edfapay
                password "PASSWORD"  // PASSWORD provided by edfapay
            }
        }
    }
}

If your project build was configured to prefer traditional build.gradle repositories, Place the below maven block to project ./build.gradle or ./build.gradle.kts

allprojects {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        maven {
            url "https://build.edfapay.com/nexus/content/repositories/edfapay-mobile/"
            credentials {
                username "USERNAME"  // USERNAME provided by edfapay
                password "PASSWORD"  // PASSWORD provided by edfapay
            }
        }
    }
}
allprojects {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        maven {
            url = uri("https://build.edfapay.com/nexus/content/repositories/edfapay-mobile/")
            credentials {
                username = "USERNAME"  // USERNAME provided by edfapay
                password = "PASSWORD"  // PASSWORD provided by edfapay
            }
        }
    }
}


Adding dependency

It is important to add dependency to your project module build.gradle or build.gradle.kts

Requirements:

  • Minimum SDK Version: 28 (Android 9.0)
  • Target SDK Version: 36 (recommended)
  • Kotlin: Required
  • Java Version: 11 or higher
android {
    ...
    ...
    ...
		//This is optional block, if you are getting compile time crash, then add whatever you need from below exclude packages. 
    packaging {
        resources {
            excludes += setOf(
                "META-INF/DEPENDENCIES",
                "META-INF/LICENSE",
                "META-INF/LICENSE.txt",
                "META-INF/NOTICE",
                "META-INF/NOTICE.txt"
            )
        }
    }
}

//REQUIRED DEPENDENCY
dependencies {
    implementation("com.edfapay:sa-edfapay-revamp:1.0.1")
}
android {
    ...
    ...
    ...
    //This is optional block, if you are getting compile time crash, then add whatever you need from below exclude packages.
    packaging {
        resources {
            exclude "META-INF/DEPENDENCIES"
            exclude "META-INF/LICENSE"
            exclude "META-INF/LICENSE.txt"
            exclude "META-INF/NOTICE"
            exclude "META-INF/NOTICE.txt"
        }
    }
}


//REQUIRED DEPENDENCY
dependencies {
    implementation "com.edfapay:sa-edfapay-revamp:1.0.1"
}


Configure AndroidManifest.xml

Add the following permissions to your AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

Important: Set allowBackup to false in your application tag:

<application
    android:allowBackup="false">
    ...
    ...
</application>

Usage

This section guides you through the essential steps to integrate and utilize the EdfaPay SoftPos SDK in your Android application.

Importing the SDK

import com.edfapay.paymentcard.EdfaPayPlugin
import com.edfapay.paymentcard.model.EdfaPayCredentials
import com.edfapay.paymentcard.model.TransactionType
import com.edfapay.paymentcard.model.TxnParams
import com.edfapay.paymentcard.model.enums.Env
import com.edfapay.paymentcard.model.enums.FlowType
import com.edfapay.paymentcard.model.responses.Transaction
import com.edfapay.paymentcard.EdfaPayPlugin;
import com.edfapay.paymentcard.model.EdfaPayCredentials;
import com.edfapay.paymentcard.model.TransactionType;
import com.edfapay.paymentcard.model.TxnParams;
import com.edfapay.paymentcard.model.enums.Env;
import com.edfapay.paymentcard.model.enums.FlowType;
import com.edfapay.paymentcard.model.responses.Transaction;

Authenticating with EdfaPay

// 1. Direct auto-fill email/password
val credentials = EdfaPayCredentials(
    environment = Env.REVAMP, // For production use Env.PRODUCTION
    email = "[email protected]",
    password = "yourPassword123"
)

// 2. Enable email/password (without providing credentials immediately)
val credentials = EdfaPayCredentials(
    environment = Env.REVAMP // For production use Env.PRODUCTION
)

// 3. Using Token (Auth Code) from the Dashboard / Backend
val credentials = EdfaPayCredentials(
    environment = Env.REVAMP, // For production use Env.PRODUCTION
    authCode = "AUTH-CODE-FROM-PORTAL"
)
// 1. Direct auto-fill email/password
EdfaPayCredentials credentials1 = new EdfaPayCredentials(
    Env.REVAMP, // For production use Env.PRODUCTION
    "[email protected]",
    "yourPassword123"
);

// 2. Enable email/password (without providing credentials immediately)
EdfaPayCredentials credentials2 = new EdfaPayCredentials(
    Env.REVAMP // For production use Env.PRODUCTION
);

// 3. Using Token (Auth Code) from the Dashboard / Backend
EdfaPayCredentials credentials3 = new EdfaPayCredentials(
    Env.REVAMP, // For production use Env.PRODUCTION
    "AUTH-CODE-FROM-BACKEND"
);

Initializing the Plugin

[!IMPORTANT]

Before initializing the SDK:

  1. Request location permission (mandatory)
// 1. Configure theme (OPTIONAL)
EdfaPayPlugin.theme(this)
    .setPrimaryColor("#000000")      // Primary color (hex format)
    .setSecondaryColor("#d3d3d3")    // Secondary color (hex format)
    .setFontScale(1.5f)              // Font scale factor

// 2. Request location permission (MANDATORY), you can use your own requestLocation flow. 

EdfaPayPlugin.Utils.requestLocationPermission(this) { granted ->
    if (granted) {
       Log.d("EdfaPay", "Location permission granted")
    } else {
        Log.d("EdfaPay", "Location permission not granted")
    }
}

// 3. Once location request granted you can call initiate plugin
val credentials = EdfaPayCredentials(
            environment = Env.PRODUCTION,
            email = "[email protected]",
            password = "yourPassword123"
        )
        
        EdfaPayPlugin.initiate( // this will initialize the SDK
            context = this,
            credentials = credentials,
            onSuccess = { plugin, sessionId ->
            // check for initilization status    
            },
            onError = {/* Check if there's any error */}
        )
// 1. Configure theme (REQUIRED - must be done before initialization)
EdfaPayPlugin.INSTANCE.getTheme()
    .setPrimaryColor("#000000")
    .setSecondaryColor("#d3d3d3")
    .setFontScale(1.5f);

// 2. Request location permission (MANDATORY)
EdfaPayPlugin.Utils.INSTANCE.requestLocationPermission(this, new Function1<Boolean, Unit>() {
    @Override
    public Unit invoke(Boolean granted) {
        if (granted) {
            // 3. Initialize SDK
            EdfaPayCredentials credentials = new EdfaPayCredentials(
                Env.REVAMP, // For production use Env.PRODUCTION
                "[email protected]",
                "yourPassword123"
            );
            
            EdfaPayPlugin.INSTANCE.initiate(
                MainActivity.this,
                credentials,
                new EdfaPayPlugin.OnSuccessCallback() {
                    @Override
                    public void onSuccess(EdfaPayPlugin plugin, String sessionId) {
                        Toast.makeText(
                            getApplicationContext(),
                            "SDK Initialized Successfully",
                            Toast.LENGTH_SHORT
                        ).show();
                        Log.d("EdfaPay", "Initialized with session: " + sessionId);
                    }
                },
                new EdfaPayPlugin.OnErrorCallBack() {
                    @Override
                    public void onError(Throwable err) {
                        err.printStackTrace();
                        Toast.makeText(
                            getApplicationContext(),
                            "Error Initializing: " + err.getMessage(),
                            Toast.LENGTH_SHORT
                        ).show();
                    }
                }
            );
        } else {
            Log.d("EdfaPay", "Location permission not granted");
        }
        return Unit.INSTANCE;
    }
});

// 4. Enable logs (optional)
EdfaPayPlugin.INSTANCE.setEnableLogs(true);

Enable logs (optional)


EdfaPayPlugin.enableLogs = true // If true, you will see SDK logs

Transaction Operations

Performing a Purchase Transaction

val params = TxnParams(
    amount = amount, // Transaction amount as string
    orderId = "INV-1234", // Unique order ID
    transactionType = TransactionType.PURCHASE
)

EdfaPayPlugin.purchase(
    requireActivity(),
    flowType = FlowType.DETAIL,
    txnParams = params,
    onRequestTimerEnd = {},
    onCardScanTimerEnd = {
       //card scan timeout
    },
    onPaymentProcessComplete = { status, code, txn, isComplete ->
        //utilize which param you need
    },
    onCancelByUser = {},
    onError = {}
)
TxnParams params = new TxnParams(
    amount,                 // String
    "INV-1234",            // Optional orderId
    TransactionType.PURCHASE
);

EdfaPayPlugin.purchase(
    requireActivity(),
    FlowType.DETAIL,
    params,
    new EdfaPayPlugin.TimeOutCallBack() {
        @Override
        public void onTimeout() {
            Toast.makeText(requireContext(), "Server Request Timeout", Toast.LENGTH_SHORT).show();
        }
    },
    new EdfaPayPlugin.TimeOutCallBack() {
        @Override
        public void onTimeout() {
            Toast.makeText(requireContext(), "Card Scan Timeout", Toast.LENGTH_SHORT).show();
        }
    },
    new EdfaPayPlugin.ProcessCompleteCallback() {
        @Override
        public void onComplete(boolean status, String code, Transaction transaction, boolean isProcessComplete) {
            if (transaction != null) {
                // Save transaction
                // TransactionStorage.addTransaction(transaction);

                String rrn = transaction.getRrn() != null ? transaction.getRrn() : "Unknown";
                String txnAmount = transaction.getAmount() != null ? transaction.getAmount() : "0";

                Toast.makeText(
                    requireContext(),
                    "Transaction saved. RRN: " + rrn + ", Amount: " + txnAmount,
                    Toast.LENGTH_SHORT
                ).show();
            }

            Toast.makeText(
                requireContext(),
                status ? "Success" : "Failure",
                Toast.LENGTH_SHORT
            ).show();
        }
    },
    new EdfaPayPlugin.CancelByUserCallBack() {
        @Override
        public void onCancel() {
            Toast.makeText(requireContext(), "Cancelled by User", Toast.LENGTH_SHORT).show();
        }
    },
    new EdfaPayPlugin.OnErrorCallBack() {
        @Override
        public void onError(Throwable e) {
            Toast.makeText(requireContext(), "Error: " + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }
);

Reversing Last Transaction

EdfaPayPlugin.reverseLastTransaction(
    this,
    onSuccess = {},
    onError = {}
)
EdfaPayPlugin.reverseLastTransaction(
    this,
    new EdfaPayPlugin.OnSuccessCallback() {
        @Override
        public void onSuccess(EdfaPayPlugin plugin, String sessionId) {
            Toast.makeText(
                MainActivity.this,
                "Transaction reversed successfully",
                Toast.LENGTH_SHORT
            ).show();
        }
    },
    new EdfaPayPlugin.OnErrorCallBack() {
        @Override
        public void onError(Throwable error) {
            Toast.makeText(
                MainActivity.this,
                "Transaction reverse failed: " + error.getMessage(),
                Toast.LENGTH_SHORT
            ).show();
        }
    }
);

[!NOTE] The reverse feature must be enabled from the backend. The reverse button will not appear in the transaction status dialog if the feature is not enabled.

Getting Transaction Details

EdfaPayPlugin.txnDetail(
    txnId = "TRANSACTION_DETAILS", // Transaction ID
    onSuccess = { 
             // Handle transaction details
    },
    onError = {}
)
EdfaPayPlugin.txnDetail(
    "61adb013-4905-49d8-a4e6-e2fd28a53ec7", // Transaction ID
    new EdfaPayPlugin.TransactionDetailCallback() {
        @Override
        public void onSuccess(Transaction transaction) {
            Toast.makeText(
                getApplicationContext(),
                "Transaction details fetched successfully",
                Toast.LENGTH_SHORT
            ).show();
            Log.d("EdfaPay", "Transaction: " + transaction);
        }

        @Override
        public void onError(Throwable error) {
            Toast.makeText(
                getApplicationContext(),
                "Transaction details fetch failed: " + error.getMessage(),
                Toast.LENGTH_SHORT
            ).show();
        }
    }
);

Reconciling Transactions

EdfaPayPlugin.reconcile(
    onSuccess = {},
    onError = {}
)
EdfaPayPlugin.INSTANCE.reconcile(
    new Function1<com.edfapay.paymentcard.model.responses.revamp.Reconcile, Unit>() { // onSuccess
        @Override
        public Unit invoke(com.edfapay.paymentcard.model.responses.revamp.Reconcile model) {
            Log.d(TAG, "onSuccessReconcile: " + model);
            return Unit.INSTANCE;
        }
    },
    new Function1<Throwable, Unit>() { // onError
        @Override
        public Unit invoke(Throwable exception) {
            Log.d(TAG, "onErrorReconcile: " + exception);
            return Unit.INSTANCE;
        }
    }
);

[!NOTE] Reconciliation may not work id not enabled from admin.

Reconcile History

EdfaPayPlugin.reconciliationHistory(
    onSuccess = { historyList: List<com.edfapay.paymentcard.model.responses.revamp.ReconciliationHistory> ->
       // You can fetch all reconcliation history here
    },
    onError = {}
)
EdfaPayPlugin.reconciliationHistory(
    new EdfaPayPlugin.ReconciliationHistoryCallback() {
        @Override
        public void onSuccess(List<ReconciliationHistory> historyList) {
            Log.d("EdfaPay", "Reconciliation History:");
            for (ReconciliationHistory history : historyList) {
                Log.d("EdfaPay", "ID: " + history.getId() +
                        ", Date: " + history.getDate() +
                        ", Amount: " + history.getAmount());
            }
            Toast.makeText(
                requireContext(),
                "Fetched " + historyList.size() + " reconciliation records",
                Toast.LENGTH_SHORT
            ).show();
        }

        @Override
        public void onError(Throwable error) {
            Log.e("EdfaPay", "Failed to fetch reconciliation history: " + error.getMessage(), error);
            Toast.makeText(
                requireContext(),
                "Failed: " + error.getMessage(),
                Toast.LENGTH_SHORT
            ).show();
        }
    }
);

Reconcile Detail

EdfaPayPlugin.reconciliationDetail(
    id = "RECONCLIATION_ID",
    onSuccess = {},
    onError = {}
)
EdfaPayPlugin.reconciliationDetail(
    reconciliationId,
    new EdfaPayPlugin.ReconciliationDetailCallback() {
        @Override
        public void onSuccess(ReconciliationDetail detail) {
            Log.d("EdfaPay", "Reconciliation Detail for " + reconciliationId + ":");
            Log.d("EdfaPay", "Total Amount: " + detail.getTotalAmount());
            Log.d("EdfaPay", "Transactions: " + detail.getTransactions());
            
            Toast.makeText(
                requireContext(),
                "Reconciliation detail fetched",
                Toast.LENGTH_SHORT
            ).show();
        }

        @Override
        public void onError(Throwable error) {
            Log.e("EdfaPay", "Failed to get reconciliation detail: " + error.getMessage(), error);
            Toast.makeText(
                requireContext(),
                "Error: " + error.getMessage(),
                Toast.LENGTH_SHORT
            ).show();
        }
    }
);

[!TIP]

Enabling and Disabling Logs

The developer can enable or disable logging at the SDK

EdfaPayPlugin.enableLogs = true
EdfaPayPlugin.INSTANCE.setEnableLogs(true);

Setting Animation Speed

The developer can set the speed for animations for status and card scheme. It will control the process speed.

EdfaPayPlugin.animationSpeedX = 2.0f
EdfaPayPlugin.INSTANCE.setAnimationSpeedX(2.0f);

Customizing the Theme

EdfaPayPlugin.theme(this)
    .setPrimaryColor("#000000")
    .setSecondaryColor("#d3d3d3")
    .setFontScale(1.5f)
    .setButtonBackgroundColor("#06E59F")
    .setButtonTextColor("#000000")
    .setHeaderImage(this, R.drawable.logo)
    .setPoweredByImage(this, R.drawable.logo)
EdfaPayPlugin.INSTANCE.getTheme()
    .setPrimaryColor("#000000")
    .setSecondaryColor("#d3d3d3")
    .setFontScale(1.5f)
    .setButtonBackgroundColor("#06E59F")
    .setButtonTextColor("#000000")
    .setHeaderImage(this, R.drawable.logo)
    .setPoweredByImage(this, R.drawable.logo);

[!IMPORTANT] Theme configuration is required before SDK initialization. The app may crash if theme is not configured.

Important Notes

  1. Theme Configuration: Theme can be configured before SDK initialization.

  2. Location Permission: Location permission is mandatory and must be requested before initialization using EdfaPayPlugin.Utils.requestLocationPermission().

  3. Manifest Configuration: Set android:allowBackup="false" in your application tag to prevent backup issues.

  4. Reverse Transaction: Reverse feature must be enabled from the backend. The reverse button will not appear if the feature is disabled.

  5. Fragment Lifecycle: Be aware of fragment lifecycle when handling callbacks to avoid crashes related to detached fragments.

License

MIT