SDK Calls & Integration Guide

Access to the Nexus repository must be configured in the build.gradle file so that the Tap to Phone SDK is correctly imported into the project. The Nexus repository contains the staging and production versions of the SDK. Follow the steps below to complete the configuration:

1

Locate the build.gradle file:

Access the build.gradle file at the project level (project root).

2

Add the Nexus repository to the project's build.gradle:

It is recommended to add the Nexus repository in the root build.gradle file. This ensures that the repository is globally available for all modules, avoiding unnecessary repetitions.

3

Configure the Nexus repository with credentials:

In the repositories block, add the Nexus configuration with the provided credentials.

Example: Adding Nexus Repository


buildscript {
    repositories {
        maven {
            url = uri("https://nexus.first-tech.net/repository/taponphone-dock/")
            credentials {
                username = "XXX"
                password = "YYY"
            }
        }
    }
}

allprojects {
    repositories {
        maven {
            url = uri("https://nexus.first-tech.net/repository/taponphone-dock/")
            credentials {
                username = "XXX"
                password =  "YYY"
            }
        }
    }
}

Consider that the username and password will be provided during the onboarding process by the repository provider, which may be First Tech or the company that purchased the product under the white-label model.

Configure the SDK in the build.gradle file of the app module

  • Open the build.gradle file at the app module level.

  • Add the SDK dependency in the dependencies block.

Example:

debugImplementation(libs.taponphone.sdk.v2.hml)
releaseImplementation(libs.taponphone.sdk.v2.release)

As a best programming practice, the path of these variables is configured to be retrieved from the libs.versions.toml file.

At this point, you can execute the 'Sync Project with Gradle Files' command to synchronize the project.

If an error occurs during 'Sync Project with Gradle Files':

If an error occurs during 'Sync Project with Gradle Files':

Remove the following snippet from the settings.gradle.kts file:

dependencyResolutionManagement {
   repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
   repositories {
       google()
       mavenCentral()
   }
}

And run the command again.

[CTRL + Shift + o]

The expected output in the console should be: BUILD SUCCESSFUL.

Implementing the use of the SDK

In your project's initialization class (Application), you must "extend" Application

In this class, it is necessary to replace the terminalConfig variable, which is instantiated from the TerminalConfigEntity entity, considering the parameters provided during onboarding with First Tech, which are:

class Application : Application() {

    override fun onCreate() {
        super.onCreate()

        if (!TapOnPhoneInitializer.isApplicationInitAllowed(this)) return

        TapOnPhoneInitializer.initializeTerminal(this)

        TapOnPhoneInitializer.setTerminalConfig(
            TerminalConfigEntity(
                companyDocument = BuildConfig.COMPANY_DOCUMENT,
                companyName = BuildConfig.COMPANY_NAME,
                merchantId = UUID.fromString(BuildConfig.MERCHANT_ID),
                terminalNumber = BuildConfig.TERMINAL_NUMBER,
                clientId = BuildConfig.CLIENT_ID,
                clientSecret = BuildConfig.CLIENT_SECRET,
                sdkScope = BuildConfig.SDK_SCOPE,
                sdkClientId = BuildConfig.SKD_CLIENT_ID,
                sdkClientSecret = BuildConfig.SDK_CLIENT_SECRET,
                appVersion = "1.0.0",
                packageName = applicationContext.packageName,
                sdkOrganization = "Organization 123",
                versionCode = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                    packageManager.getPackageInfo(packageName, 0).longVersionCode
                } else {
                    packageManager.getPackageInfo(packageName, 0).versionCode.toLong()
                }).toString()
            )
        )
    }
}
  • companyDocument → Your company's CNPJ document without special characters.

  • companyName → Your company's trade name.

  • merchantId → UUID provided by First Tech during the onboarding process. (Note: This field is the Java UUID object.)

  • terminalNumber → Terminal number provided by First Tech during the onboarding process.

  • clientId → Client ID provided by First Tech during the onboarding process.

  • clientSecret → Client key provided by First Tech during the onboarding process.

  • sdkScope -> Client Secret for auth, given by FirstTech team, but, submitted by the consummer of this SDK

  • sdkClientId -> Client ID for SDK, given by FirstTech team, but, submitted by the consummer of this SDK

  • sdkClientSecret -> Client Secret for SDK, given by FirstTech team, but, submitted by the consummer of this SDK

  • appVersion -> Version number of the client application.

  • packageName -> Name of the package where the client application is running

  • sdkOrganization -> This figure is provided by First Tech.

  • versionCode -> Value of the application's versionCode.

All SDK manipulation operations are performed through the ViewModel, which receives TapOnPhoneApplication as a parameter during its creation.

private val mppProviderViewModel by lazy { MPPPProviderViewModel(application as TapOnPhoneApplication) }

Starting a payment

Execute the startPaymentFlow() function, provided by MPPProviderViewModel. To do this, you need to pass the TransactionInfoEntity class as a parameter, which receives the following values in its constructor:

  • installments: An integer containing the number of installments.

  • paymentType: One of the options from the PaymentType ENUM, which can be either PaymentType.DEBIT or PaymentType.CREDIT.

  • amount: The total transaction amount, expressed as a Long.

Value R$
Long

R$ 23,34

2334

R$ 15,00

1500

R$ 0,15

15

R$0,01

1

    private fun startPayment() {
        mppProviderViewModel.startPaymentFlow(
            TransactionInfoEntity(
                paymentType.DEBIT, //IT COULD BE paymentType.CREDIT
                100, //For example, this 100 means R$ 1.00, 
                2 //Number of installments, in this case, two installments
            )
        )
    }

After this step, simply react to the events published in onMessage and capture the information from the entities of these events.

Reacting to messages

MPPProviderViewModel provides the LiveData onMessage, which will receive an 'extended' object from the MPPViewData class.

It is important to observe onMessage within the correct lifecycle, ensuring that there are no memory leaks or unexpected reactions.

In this project, we use the concept of Sealed Classes for a more efficient abstraction. Below, we list all the objects that can be returned in onMessage and their respective meanings.

Class
Description

TerminalInitializingViewData

The terminal has started the initialization process.

TerminalInitializingErrorViewData

The terminal has completed the initialization process with an error.

TerminalInitializingSuccessViewData

The terminal has successfully completed the initialization process.

TerminalNotCreatedViewData

The terminal instance has not yet been created.

TerminalCreatingViewData

The terminal instance is being created.

TerminalCreatedSuccessViewData

The terminal instance has been successfully created.

TerminalCreatedErrorViewData

The terminal instance was not created.

TerminalSessionCreatingViewData

The payment session is being created.

TerminalSessionCreatedSuccessViewData

The payment session has been successfully created.

TerminalSessionCreatedErrorViewData

The payment session was not created.

TerminalSessionTimeoutViewData

The payment session has expired.

TerminalPaymentStartingViewData

The transaction process has started.

TerminalPaymentProcessingViewData

The transaction is being processed.

TerminalPaymentCancelledViewData

The transaction has been canceled.

TerminalPaymentSuccessViewData

The transaction was successfully completed.

TerminalPaymentErrorViewData

The transaction was completed with an error.

TerminalPaymentFinishedViewData

The entire process has been completed.

Here, we obtain the reference to the LiveData onMessage and start observing it:

private fun setupObserver() {
   mppProviderViewModel.onMessage.observe(this, ::onMessage)
}

And we react to events as follows:

Note: In this example, we do not list all MPPViewData classes, but we strongly recommend implementing all classes.

Note: Some MPPViewData classes contain objects that help understand the message that should be displayed at a specific step.

  private fun onMessage(viewData: MPPViewData?) {
        viewData?.let {
            when (viewData) {
                MPPViewData.TerminalInitializingViewData -> {
                    showLoadingDialog("Inicializando terminal")
                    binding.tvMessage.text = "Inicializando terminal"
                }

                MPPViewData.TerminalInitializingErrorViewData -> {
                    showFeedbackDialog("Erro ao inicializar terminal")
                    progressDialog.dismiss()
                }

                MPPViewData.TerminalInitializingSuccessViewData -> {
                    progressDialog.dismiss()
                    binding.tvMessage.text = "Inicialização concluída"
                }
                
                //Mapear todos os cenários de MPPViewData

            }
        }
    }

Next, we will explain in more detail the messages returned in the onMessage interface, which are extensions of the MPPViewData object.

Classes not detailed below do not have internal variables. In these cases, follow the guidelines described in Table 2 – OnMessage Return List.

1

TerminalCreatedErrorViewData | TerminalSessionCreatedErrorViewData

  • cause → Throwable object returned by the payment SDK, used for debugging only.

  • logMessage → Conversion of cause to a String.

2

TerminalPaymentProcessingViewData

uiMessage → Enumeration of all possible scenarios during card information processing, including:

  • APPROVED

  • NOT_AUTHORISED

  • PLEASE_ENTER_PIN

  • PROCESSING_ERROR

  • PRESENT_CARD

  • CARD_READ_OK_PLEASE_REMOVE

  • APPROVED_PLEASE_SIGN

  • AUTHORISING_PLEASE_WAIT

  • TRY_ANOTHER_CARD

  • CLEAR_DISPLAY

  • SEE_PHONE

  • PRESENT_CARD_AGAIN

  • HOLD_STILL

  • UNKNOWN

A default message is provided for display on the screen, but developers have the freedom to customize it as long as they comply with ABECS regulations.

3

TerminalPaymentCancelledViewData

reason → Enumeration of error scenarios that occur during card information reading and capture, including:

  • TRANSACTION_WAS_TERMINATED

  • USER_CANCELLED_PIN_PAD

  • SESSION_DEACTIVATED

  • DEVICE_STATE_FAILURE

  • TIME_CHECK_ERROR

  • UNKNOWN

A default message is provided for display on the screen, but developers have the freedom to customize it as long as they comply with ABECS regulations.

4

TerminalPaymentSuccessViewData

resultTransactionCompleted object containing all payment details

Note: This does not guarantee that the transaction was successfully completed, as issues may have occurred with the acquirer. All fields will be explained in the following sections.

  1. cardHolder

When available, it returns the card brand used for the transaction:

  • MASTERCARD

  • VISA

  • AMERICAN_EXPRESS

  • DISCOVER

  • EFTPOS

  • UNKNOWN

  1. discretionaryTagData

Returns EMV Tags read by the SDK. Two methods are provided for better abstraction:

  • getDiscretionaryTagDataHashMap() → Returns a key-value array containing EMVTag and its value.

  • getDiscretionaryTagDataPrintable() → Same function as above, but returns a formatted String for display.

  1. metaData

A ByteArray of metadata returned by the backend. Two methods are available for better interpretation:

  • getMetaDataTranslated() → Returns a parsed Metadata object with additional information.

  • getMetaDataString() → Same as the above method but returns a JSON-formatted String.

  1. processingResult

This object, as a SealedClass, can have two abstractions:

  1. TransactionApproved

cardScheme → When available, returns the card brand used in the transaction:

  • MASTERCARD

  • VISA

  • AMERICAN_EXPRESS

  • DISCOVER

  • EFTPOS

  • UNKNOWN

  1. TransactionNotAuthorised

reason → Enumeration of authorization failure reasons, including:

  • ONLINE_DECLINED

  • OFFLINE_DECLINED

  • INVALID_AUTHORISATION_DATA

  • NO_CARD_APPLICATION

  • NO_CARD_APPLICATION_SELECTOR_MISMATCHED

  • PROCESSING_ERROR

  • CARD_ERROR

  • UNKNOWN

A default message is provided for display on the screen, but developers have the freedom to customize it as long as they comply with ABECS regulations.

  1. serviceTransactionId

A unique identifier for the transaction (UUID).

  1. uiMessage

Enumeration of all possible scenarios during card information processing:

  • ONLINE_DECLINED

  • OFFLINE_DECLINED

  • INVALID_AUTHORISATION_DATA

  • NO_CARD_APPLICATION

  • NO_CARD_APPLICATION_SELECTOR_MISMATCHED

  • PROCESSING_ERROR

  • CARD_ERROR

  • UNKNOWN

  1. subMessage

An enumeration of sub-messages that should also be displayed when returned, following ABECS standards.

5

TerminalPaymentErrorViewData

Returned when a payment error is detected.

  • errorCause → Enumeration of possible failures.

  • exception → Throwable object returned by the payment SDK, used for debugging only.

  • discretionaryTagData → Key-value return of EMV Tags read by the SDK.

A default message is provided for display on the screen, but developers have the freedom to customize it as long as they comply with ABECS regulations.

With these instructions, we understand that there are macro guidelines for setting up the environment, downloading dependencies, and best practices for product implementation. The development team should have a more specific and comprehensive document for application creation, which is not covered here. Some excerpts have been included only to illustrate to the operations support team the steps involved in the application's development.

Device Information

To obtain the deviceID, in order to facilitate log analysis. A method has been provided within the DeviceInformationUtils class. To use it, use the following syntax

DeviceInformationUtils.getDeviceIdentifier(applicationContext)

A String value will be returned, this value can be used in some visualization on the screen, to make it easier to identify the device in any possible error analysis.

To obtain information about the device. A method has also been provided within the DeviceInformationUtils class.

Note: this method must be used within a coroutine, as it is a suspend method

To obtain this information, use the following syntax

  binding.btnDeviceInfo.setOnClickListener {
            lifecycleScope.launch {
                binding.tvLog.text = DeviceInformationUtils.getDeviceInfo(applicationContext)
            }
        }

A JSON (below) will be returned as a String, this value can be used in some visualization on the screen or some pop-up, to facilitate the identification of any configuration made on the device that is preventing its use for some transaction.

  • JSON return example

{
  "device" : {
    "apiVersionAndroid" : 33,               //Android OS API level of the device running the app
    "batteryLevel" : 92,                    //Battery percentage 
    "brand" : "samsung",                    //Device brand 
    "deviceId" : "2c9496cabbdde392",        //Id of the application installed on the device
    "googlePlayServiceVersion" : 251633029, //Version of Google Play Service installed on the device
    "isActivatedBatteryMode" : false,       //Check if the battery saving mode is active
    "isActivatedDeveloperMode" : true,      //Check if the developer mode is active
    "isEnabledAutomaticTime" : true,        //Check if the time is set automatically
    "isEnabledNfc" : false,                 //Check if the Nfc function is active
    "isRooted" : false,                     //Check if the device is in root mode
    "manufacturer" : "samsung",             //Manufacturer of the device
    "memoryCapacity" : "3. 45",             //Device memory capacity
    "memoryInUse" : "1. 51",                //Total memory in use
    "mobileSignal" : {
    "level" : 1,                            //Mobile network signal level
    "operatorName" : "CLARO BR",            //Mobile network operator
    "typeOfSignal" : "4G"                   //Type of signal captured
    },
    "model" : "SM-A326B",                   //Device model
    "soVersionAndroid" : "13",              //Trade name of A version
    "wifiSignal" : 4                        //Level of WI-FI signal received
  },
  "securityScan" : {
    "appsInstalledOnDevice" : [ "com.sec.android.gallery3d", "com.android.chrome", "com.android.settings", "com.android.vending", "com.google.android.apps.maps", "com.google.android.apps.messaging", "com.google.android.apps.tachyon", "com.google.android.gm", "com.google.android.youtube", "com.samsung.android.app.contacts", "com.samsung.android.arzone", "com.samsung.android.calendar", "com.samsung.android.dialer", "com.samsung.android.messaging", "com.sec.android.app.camera", "com.google.android.apps.docs", "com.google.android.apps.photos", "com.google.android.apps.youtube.music", "com.google.android.videos", "com.microsoft.office.outlook", "com.samsung.android.oneconnect", "com.sec.android.app.sbrowser", "com.sec.android.app.shealth", "com.android.stk", "com.claroColombia.contenedor", "com.gameloft.android.gdc", "com.google.android.googlequicksearchbox", "com.samsung.android.app.spage", "com.samsung.android.game.gamehome", "com.sec.android.app.clockpackage", "com.sec.android.app.fm", "com.sec.android.app.myfiles", "com.sec.android.app.samsungapps", "com.sec.android.usermanual", "br.com.claropay.claropay", "com.claro.claromusica.br", "com.clarodrive.android", "com.dla.android", "com.edifier.edifierconnect", "com.example.testemicrophone", "com.facebook.katana", "com.facebook.orca", "com.firsttech.taponphone.app", "com.firsttech.taponphone.app.dev", "com.firsttech.taponphone.dev.app", "com.firsttech.taponphone.kt.app.dev", "com.google.android.apps.playconsole", "com.handmark.expressweather", "com.instagram.android", "com.microsoft.office.officehubrow", "com.nvt.cs", "com.rsupport.rs.activity.rsupport.aas2", "com.samsung.android.app.notes", "com.samsung.android.app.watchmanager", "com.samsung.android.spay", "com.samsung.android.voc", "com.samsung.sree", "com.sec.android.app.popupcalculator", "com.sec.android.app.voicenote", "com.sec.android.easyMover" ], //Apps installed on the device
    "appsMalwareList" : [ ],                //Apps found in the list of apps not recommended to run in parallel with TTP
    "appsOutsidePlaystoreList" : [ ],       //Not implemented
    "appsRunningList" : [ ]                 //Not implemented
  }
}

Last updated