Quick Start

This section provides a step-by-step guide for creating your first purchase transaction.

Before you begin

The NearPay team is required to create a sandbox account for you using your email and phone number, as well as your Android package name to initiate this integration process.

Additionally, you must possess an Android physical device capable of supporting NFC to test the integration and run the app on it.

Start your first transaction

  1. Configuring the Secure Maven Repository / Dependencies

To get the private token , you need to contact Nearpay to get the token for the private repository.

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven(url = "https://developer.huawei.com/repo/")
        maven {
            url = uri("https://gitlab.com/api/v4/projects/37026421/packages/maven")
            credentials(HttpHeaderCredentials::class) {
                name = "Private-Token"
                value = "nearpayPosGitlabReadToken" //will be supported from Nearpay Product Team
            }
            authentication {
                create<HttpHeaderAuthentication>("header")
            }
        }
    }
}
  1. Include the following dependencies in your Module level build.gradle file:
implementation("io.nearpay:terminalsdk-release:0.0.143")
implementation("com.google.android.gms:play-services-location:20.0.0")
implementation("com.huawei.hms:location:6.4.0.300")
  1. change the minSdk version to 28 in your build.gradle file:

  2. Add the following line in your AndroidManifest.xml file :


<application
    android:allowBackup="true"
    tools:replace="android:allowBackup" // Add this line to avoid manifest merger issues


Make sure the following tools namespace is present in your <manifest> tag:// xmlns:tools="http://schemas.android.com/tools"

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"> // this tools namespace

...
  1. create a single instance of TerminalSDK, initialize it with the necessary configurations :
// initializing the terminalSDK may throw an exception, so wrap it in a try-catch block
try {
    val nearpay = TerminalSDK.Builder()
        .activity(this)  // Sets the activity context
        .environment(SdkEnvironment.SANDBOX)  // Choose SANDBOX or PRODUCTION or INTERNAL
        .googleCloudProjectNumber(12345678)  // Set Google Cloud project number
        .huaweiSafetyDetectApiKey("your_api_key")  // Set Huawei API key
        .uiDockPosition(UiDockPosition.BOTTOM_CENTER) // Optional: set the location of the Tap to Pay modal 
        .country(Country.SA)  // Set country SA, TR, USA, KEN , 
        .build()
    } catch (e: Throwable) {
        Timber.e("Error initializing TerminalSDK: $e")
    }
  1. Add required permissions :
  • Manifest.permission.ACCESS_FINE_LOCATION
  • Manifest.permission.ACCESS_NETWORK_STATE
  • Manifest.permission.INTERNET
  • Manifest.permission.READ_PHONE_STATE
  • Manifest.permission.NFC
  1. Send OTP :
val mobileLogin = MobileLogin("+966500000000")
nearpay.sendOTP(mobileLogin, object : SendOTPMobileListener {
    override fun onSendOTPMobileSuccess(otpResponse: OtpResponse) {
        Log.d("Login", " onSendOTPMobileSuccess ($otpResponse)")
    }
    override fun onSendOTPMobileFailure(otpMobileFailure: OTPMobileFailure) {
        Log.d("Login", " onSendOTPMobileFailure ($otpMobileFailure)")
    }
})
  1. Verify OTP :

After sending the OTP, verify it to authenticate the user.`

val loginData = LoginData(
    mobile = "+966500000000",
    code = "123456"
)
nearpay.verify(loginData, object : VerifyMobileListener {
    override fun onVerifyMobileSuccess(user: User) {
        Log.d("Login", " onVerifyMobileSuccess ${user.name}")
    }
    override fun onVerifyMobileFailure(verifyMobileFailure: VerifyMobileFailure) {
        Log.d("Login", " onVerifyMobileFailure ($failure)")
    }
})

  1. List Terminals Retrieves a paginated list of terminals associated with the user.
lateinit var firstTerminal: Terminal
userInstance.listTerminals(
    page = 1,
    pageSize = 10,
    filter = null, // You can pass the terminal ID to get a specific terminal
    object : GetTerminalsListener {
        override fun onGetTerminalsSuccess(terminalsConnection: List<TerminalConnection>) {
            // Handle success
            terminalsConnection.firstOrNull()?.let { terminal ->
                // Access terminal data
                val terminalName = terminal.terminalConnectionData.name
                // Update UI or store reference
                firstTerminal = terminal
            }
        }

        override fun onGetTerminalsFailure(getTerminalsFailure: GetTerminalsFailure) {
            // Handle failure
            Log.d("Terminals", "Terminals list failure: $getTerminalsFailure")
        }
    },
    )

  1. Connect Terminal

Establishes a connection with a terminal.

firstTerminal.connect(
    activity = this,
    listener = object : ConnectTerminalListener {
        override fun onConnectTerminalSuccess(terminal: Terminal) {
            // Terminal connected successfully
            // Store terminal instance for future operations
            terminalInstance = terminal
        }

        override fun onConnectTerminalFailure(connectTerminalFailure: ConnectTerminalFailure) {
            // Handle failure
            Log.d("Terminal", "Terminal connection failed: $connectTerminalFailure")
        }
    }
)
  1. Purchase transaction
    var amount = 100
    var intentUuid = UUID.randomUUID().toString() // the intent UUID should be unique for each transaction and managed by the developer to communicate with the SDK
    var customerReferenceNumber = "" //[optional] any number you want to add as a refrence

    terminal.purchase(
        amount = amount,
        scheme = null, // eg.PaymentScheme.VISA, specifying this as null will allow all schemes to be accepted
        intentUUID = transactionUUID,
        customerReferenceNumber = customerReferenceNumber,
        readCardListener = object : ReadCardListener { 
            override fun onReaderDismissed() {
                // Reader dismissed by user
                Log.d("ReadCard", "Reader dismissed by user")
            }   
                    
            override fun onReadCardSuccess() {
                // Card read successfully
                Log.d("ReadCard", "Card read successfully")
            }

            // Called when the card reading process fails - issues with the specific card or its interaction
            // Examples: card removed too quickly, unreadable card, wrong card orientation
            override fun onReadCardFailure(readCardFailure: ReadCardFailure) {
                // Handle card read failure
                Log.d("ReadCard", "Card read failure: $readCardFailure")
            }

            override fun onReaderDisplayed() {
                Log.d("ReaderCard", "Reader Displayed")
            }

            override fun onReaderClosed() {
                Log.d("ReaderCard", "Reading Closed")
            }

            override fun onReaderWaiting() {
                // Reader waiting for card
                Log.d("ReadCard", "Reader waiting for card")
            }

            override fun onReaderReading() {
                // Reading card in progress
                Log.d("ReadCard", "Reading card in progress")
            }

            override fun onReaderRetry() {
                // Reader retry needed
                Log.d("ReadCard", "Reader retry needed")
            }

            override fun onPinEntering() {
                // PIN entry in progress
                Log.d("ReadCard", "PIN entry in progress")
            }

            override fun onReaderFinished() {
                // Card read completed
                Log.d("ReadCard", "Card read completed")
            }
            override fun onReadingStarted() {
                // Card read started
                Log.d("ReadCard", "Card read started")
            }

            // Called when the card reader device itself encounters an error
            // Examples: hardware malfunction, connection issues, device not ready
            override fun onReaderError(error: String?) {
                // Handle reader error
                Log.d("ReadCard", "Reader error: $error")
            }
        },
        sendTransactionListener = object : SendTransactionListener {
            override fun onSendTransactionCompleted(purchaseResponse: PurchaseResponse) {
                // Handle completed transaction

                //PurchaseResponse will return all transaction with same intent id "transactionUUID"
                
                //purchaseResponse.status
                //will return the status of last transaction of the same intent id 
                //If the value is "declined", that means the transaction is not completed successfully 
                //If it is "approved", that means the transaction is completed successfully 

                //Also you can get the boolean status from last receipt using 
                //purchaseResponse.getLastReceipt().getMadaReceipt().isApproved
                //And the value will be true or false


                Log.d("Transaction", "Transaction completed: $purchaseResponse")
                // To get the approved receipt based on the country you can got it like :
                // purchaseResponse.getLastReceipt().getMadaReceipt() for Saudi Arabia
                // purchaseResponse.getLastReceipt().getEPXReceipt() for USA
                // purchaseResponse.getLastReceipt().getBKMReceipt() for Turkey
            }

            override fun onSendTransactionFailure(failure: SendTransactionFailure) {
                // Handle transaction failure
                Log.d("Transaction", "Transaction failure: $failure")
            }
        }
    )

Your setup is complete, allowing you to test the payment feature.