> For the complete documentation index, see [llms.txt](https://ftcoders.first-tech.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ftcoders.first-tech.com/first-tech-ttp-sdk/developer-zone/sdk-implementation-ttp/implementing-the-use-of-the-sdk.md).

# Implementing the use of the SDK

{% stepper %}
{% step %}
Now that the dependency has been set up, you need to carry out the initial configuration. These settings are necessary to start the payment SDK. Let's define these settings in the Application class.

Create a class that extends Application and define the payment settings.

In your project's initialization class (Application), you should “extend” TapOnPhoneApplication

Call the isApplicationInitAllowed() method to check that the application can be initialized.

`if (!TapOnPhoneInitializer.isApplicationInitAllowed(this)) return`&#x20;

`TapOnPhoneInitializer.initializeTerminal(this)` <br>

The static initializeTerminal() method of the TapOnPhoneInitializer class must be executed. It initializes the terminal, an essential step for using the SDK.

<br>

To define the initial settings, run the `setTerminalConfig(config: TerminalConfigEntity)` method. To do this, create a TerminalConfigEntity object. The values entered below are for the sandbox scenario.

```
data class TerminalConfigEntity(
    val companyDocument: String?, // O número do CNPJ (Cadastro Nacional de Pessoa Jurídica) da empresa. - Contém o número do CNPJ da empresa.
    val companyName: String?,     // O nome da empresa.
    val merchantId: UUID?,        // O identificador único do comerciante, fornecido pela equipe FirstTech.
    val terminalNumber: String?,  // O número único atribuído ao terminal, fornecido pela equipe FirstTech.
    val clientId: String?,        // O ID do Cliente usado para autenticação, fornecido pela equipe FirstTech. 
    val clientSecret: String?,    // O Client Secret usado para autenticação, fornecido pela equipe FirstTech.
    val sdkScope: String?,        // O escopo necessário para autenticação do SDK, fornecido pela equipe FirstTech e enviado pelo consumidor deste SDK.
    val sdkClientId: String?,     // O ID do Cliente para autenticação do SDK, fornecido pela equipe FirstTech e enviado pelo consumidor deste SDK.
    val sdkClientSecret: String?, // O Client Secret para autenticação do SDK, fornecido pela equipe FirstTech e enviado pelo consumidor deste SDK.
    val packageName: String?,     // O nome do pacote da aplicação do cliente, enviado pelo consumidor deste SDK.O Nome do Pacote é usado para referência de onde a aplicação do cliente está sendo executada, enviado pelo consumidor deste SDK.
    val appVersion: String?,      // A versão atual da aplicação do cliente, enviado pelo consumidor deste SDK. - A Versão do Aplicativo é uma informação sobre a versão do aplicativo do cliente atual, enviado pelo consumidor deste SDK.
    val sdkOrganization: String?  // O nome da organização, enviado pelo consumidor deste SDK.
)
```

At the end of this process, the file structure should look like the one below.

Important: The values shown refer to the sandbox environment.

<figure><img src="/files/tacZWl7cWcjJP554EixH" alt=""><figcaption></figcaption></figure>

{% endstep %}

{% step %}
The project is already set up. Next, let's modify MainActivity. Below is an example layout to illustrate the use of our SDK. Feel free to adapt it to suit your layout. Our activity follows this model.

<figure><img src="/files/M89aO6REcBL5Cp6tNKbz" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
All of the SDK's manipulation operations are carried out via the ViewModel, which receives the TapOnPhoneApplication as a parameter when it is created.

At the start of the Activity, we have the following declarations:

```
private val progressDialog by lazy { ProgressDialog(this) } //<- This Android user interface component is used to show a progress dialog box.
private val binding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } //<- Define the binding variable as ActivityMainBinding. //<- This is a class automatically generated by Android's View Binding feature.
private val mppProviderViewModel by lazy { MPPPProviderViewModel(application as Application) } //<- ViewModel to manage the data and logic of the user interface.
```

```

  override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        setupView()
        setupObserver()
        askPermission()
        getVersionCode()
    }
```

<br>
{% endstep %}

{% step %}
Our MainActivity looks like this.

<figure><img src="/files/MMANdfA9UIeRHeyJCRAl" alt=""><figcaption></figcaption></figure>

**OBS**: We use the **askPermission** method to request permission to collect the user's location, information that will be used for better error analysis later.

```
private fun askPermission() {
        val permissionsToRequest = mutableListOf<String>()

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
            != PackageManager.PERMISSION_GRANTED
        ) {
            permissionsToRequest.add(Manifest.permission.ACCESS_FINE_LOCATION)
        }


        if (permissionsToRequest.isNotEmpty()) {
            Log.d("Signal", "Solicitando permissões.")

            ActivityCompat.requestPermissions(
                this,
                permissionsToRequest.toTypedArray(),
                ALL_PERMISSIONS_REQUEST_CODE
            )
        } else {
            Log.d("Signal", "Todas as permissões já concedidas.")
        }
    }
```

{% endstep %}

{% step %}
Start a payment

<br>

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 parcels

`paymentType:`  One of the options  `PaymentType` , which can be `PaymentType.DEBIT`  or `PaymentType.CREDIT`

`amount:`  The total amount of the transaction, expressed `Long` .

| Value    | Long |
| -------- | ---- |
| R$ 23,34 | 2334 |
| R$ 15,00 | 1500 |
| R$ 0,15  | 15   |
| R$0,01   | 1    |

```
 private fun startPayment() {
   mppProviderViewModel.startPaymentFlow( // <- Este é o método que inicia o fluxo de pagamento
        TransactionInfoEntity(
            operationType, //<- Define o tipo da operação
            ((binding.etValor.text.toString().toDigits() ?: 0.0) * 100).toLong(), // <- Valor da transação, multiplicado por 100 (para representar centavos)
            binding.etInstallments.text.toString().toInt() //<- Número de parcelas  //<- Número de parcelas (quando o pagamento é a crédito)
            )
        )
    }
    
```

<figure><img src="/files/56C4zR1mDxzVrL36uwVN" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Manual stoppage of the payment process
{% endhint %}

If the payment flow is terminated, either by user action or by an internal flow of the application itself, you must use the method provided to terminate the session (transaction). The **clearCurrentTerminalSession()** method of the SessionHelper object must be called. This action is essential because otherwise the previous session would remain active. Consequently, when trying to move on to the transaction screen again, there would be an error in creating a new session, since there would already be one in progress.

This method can be called at any point in your application. In the examples below, we demonstrate how to implement it in Kotlin and React Native.

To use it in Kotlin, just call the following method:

```
SessionHelper.clearCurrentTerminalSession()
```

In the React Native application, we will use the “Back” button to trigger the session termination method provided by the SDK. The implementation detailed below is specific to the **React Native** environment.

```
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.Promise
import com.firsttech.taponphone.sdk.v2.utils.DeviceInformationUtils
import com.firsttech.taponphone.sdk.v2.utils.SessionHelper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import android.util.Log
​
class DeviceInfoModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
    override fun getName(): String = "DeviceInfoModule"
​
    @ReactMethod
    fun getDeviceId(promise: Promise) {
        try {
            val id = DeviceInformationUtils.getDeviceIdentifier(reactApplicationContext)
            promise.resolve(id)
        } catch (e: Exception) {
            promise.reject("ERROR", e.message)
        }
    }
​
    @ReactMethod
    fun getDeviceInfo(promise: Promise) {
        CoroutineScope(Dispatchers.Default).launch {
            try {
                val info = DeviceInformationUtils.getDeviceInfo(reactApplicationContext)
                promise.resolve(info)
            } catch (e: Exception) {
                promise.reject("DEVICE_INFO_ERROR", e.message)
            }
        }
    }
    //
​
​
    fun addListener(eventName: String?) {
        // Requerido pelo NativeEventEmitter
    }
​
    fun removeListeners(count: Int) {
        // Requerido pelo NativeEventEmitter
    }
​
    @ReactMethod
    fun clearTerminalSession() {
        try {
            SessionHelper.clearCurrentTerminalSession()
        } catch (e: Exception) {
            Log.e("TerminalModule", "Erro ao limpar sessão do terminal", e)
        }
    }
}
```

Now we'll define how to use our new method, located in the DeviceInfoModule file, via the JavaScript wrapper that we've used before for other functions. Feel free to create a new file dedicated to this functionality or integrate it into the existing file in our sample application.

```

import { NativeModules } from 'react-native';
​
const { DeviceInfoModule } = NativeModules;
​
export function getDeviceInfo(): Promise<string> {
  return DeviceInfoModule.getDeviceInfo();
}
​
export function clearTerminal(): void {
  DeviceInfoModule.clearTerminalSession();
}
```

In our index file (or index.tsx), which defines our application's payment processing screen, we will import the clearTerminal method - previously defined in our native module - to be used when the “Back” button is pressed.

```
import { clearTerminal } from '../../native-modules/DeviceInfoModule
```

We'll also make the following imports

**BackHandler**: Manages Android's physical "Back" button.\
**useFocusEffect**: Executes effects (such as adding listeners from the BackHandler) when the screen gains focus and clears them when it loses focus.\
**useCallback**: Memoizes the function passed to useFocusEffect, optimizing it and ensuring that it is stable, preventing unnecessary recreations and executions of the focus effect.

And in our component that works as a "Back" button, we'll call the clearTerminal() method, implemented earlier. This, in turn, will use the session clearing method implemented in the SDK.

```
 <BackButtonWrapper
            onPress={() => {
              clearTerminal(); 
              goBack();        
            }}
        >
        <BackButtonArrowLeftVector source={arrowLeftVector} />
      </BackButtonWrapper>
```

{% endstep %}

{% step %}
Reacting to messages

The`MPPProviderViewModel` provides o`LiveData onMessage`, which will receive an “extended” object from the `MPPViewData` class.

{% hint style="info" %}
It is important to observe onMessage within the correct lifecycle, ensuring that there are no memory leaks or unexpected reactions.
{% endhint %}

In this project, we used 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.

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

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

{% hint style="info" %}
Note: In this example, we haven't listed all the MPPViewData classes, but we recommend implementing all the classes.

In our repository, our sample will contain a list of all the classes, which we can have in our when
{% endhint %}

{% hint style="info" %}
Note: Some MPPViewData classes contain objects that help you understand the message that should be displayed in a specific step.
{% endhint %}

```

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"
                }
                
                is MPPViewData.TerminalPaymentSuccessViewData -> {
                    enableEditFields(true)
                    binding.tvMessage.text =
                        if (viewData.result.isProcessingResultSuccess()) "Pagamento realizado" else "Pagamento recusado"
                    val metaData = viewData.result.getMetaDataTranslated()
​
                    binding.tvLog.text = "" +
                            "Resultado: ${viewData.result.uiMessage.messageId.sampleUiMessage}" +
                            createReceipt(metaData)
                }
                
                //Mapear todos os cenários de MPPViewData
​
            }
        }
    }

```

In this example TerminalPaymentSuccessViewData is when the transaction was successfully completed.

**OBS: To access the messageJson, retrieve the result you received in viewData and extract the messageJson as follows**

```
val metaData = viewData.result.getMetaDataTranslated()
val messageJson = metaData?.receipt?.merchant?.messageJson
```

{% endstep %}

{% step %}
Printing the receipt on the screen

Here is the implementation of the createReceipt function method, which returns a String. This string is set up to be a receipt and can be customized in any way you need.

<figure><img src="/files/4gJoHzK2l1AbmQOycME3" alt=""><figcaption></figcaption></figure>

```

package com.firsttech.taponphone.app.utils
​
import java.text.NumberFormat
import java.util.Locale
import com.firsttech.taponphone.sdk.v2.models.TransactionCompleted
​
fun createReceipt(metaDataTranslated: TransactionCompleted.Metadata?):String {
    val messageJson = metaDataTranslated?.receipt?.merchant?.messageJson
    val parcelas = messageJson?.transaction?.installments?.total ?: 0
    val valor = messageJson?.transaction?.amount
​
    return "\n${messageJson?.acquirer}" +
            "\n" +
            "\nCNPJ: ${messageJson?.businessDocument}" +
            "\nTID: ${messageJson?.transactionId}" +
            "\n" +
            "\n${messageJson?.card?.number}" +
            "\n${messageJson?.card?.brand}" +
            "\nAID: ${messageJson?.card?.aid}" +
            "\n${messageJson?.transaction?.datetime}" +
            "\nCV: ${messageJson?.transaction?.stan}" +
            "\nValor: ${messageJson?.transaction?.currency} ${valor?.let { it1 -> setCurrencyFormat(it1) }}" +
            "\nForma de Pagamento:  ${messageJson?.transaction?.paymentMethod}" +
            (if(messageJson?.transaction?.paymentMethod.equals("Crédito")){
                "\n${if(parcelas==1)"À vista" else "Valor de cada parcela: ${messageJson?.transaction?.currency} ${setCurrencyFormat(messageJson?.transaction?.installments?.value.toString())}" }" +
                        "\nQuantidade de parcela:  ${messageJson?.transaction?.installments?.total}"
            }else{
                "\n${setTextInstallments(parcelas)}"
            }) +
            "\nTerm: ${messageJson?.terminalCode}" +
            "\n" +
            "\nDADOS ORIGINAIS DA VENDA" +
            "\nValor: ${messageJson?.transaction?.currency} ${messageJson?.transaction?.amount?.let { it1 ->
                setCurrencyFormat(
                    it1
                )
            }}" +
            "\nTerm: ${messageJson?.terminalCode}" +
            "\n${messageJson?.transaction?.messageAuthorizedBy}"
}
​
private fun setCurrencyFormat(amount:String): String {
    val formatCurrency = NumberFormat.getCurrencyInstance(Locale("pt", "BR"))
    val amountToDouble = formatCurrency.format(amount.toDouble())
​
    val amountConverted = amountToDouble.replace("R$", "").trim()
    return amountConverted
}
private fun setTextInstallments(installements:Int): String {
    return if (installements==1) "À vista" else "Parcelado em $installements vezes"
}
```

Here's an example of the JSON with the messageJson field that we used to create our receipt

```

"receipt":{
      "merchant":{
         "message":"Via Loja\n\nCNPJ: 00.595.154/0001-73\nTID: 010000865033\n\n**** **** **** 2029\nMASTERCARD\nAID: A0000000041010\n17/06/25 11:32\nCV: 241393\nValor: R$ 50,00\nForma de pagamento: Crédito\nValor de cada parcela: 5\nQuantidade de parcelas: 10\nTerm: 00000026\n\nDADOS ORIGINAIS DA VENDA\nValor: R$ 50,00\nTerm: 00000026\nTransação autorizada pelo emissor\n",
         "messageJson":{
            "acquirer":"Via Loja",
            "merchant":"First Customer",
            "businessDocument":"00.595.154/0001-73",
            "transactionId":"010000865033",
            "card":{
               "number":"**** **** **** 2029",
               "brand":"MASTERCARD",
               "aid":"A0000000041010"
            },
            "transaction":{
               "amount":50,
               "currency":"BRL",
               "datetime":"17/06/25 11:32:25",
               "stan":"241393",
               "installments":{
                  "value":5,
                  "total":10
               },
               "paymentMethod":"Crédito",
               "messageAuthorizedBy":"Transação autorizada pelo emissor"
            },
            "terminalCode":"00000026"
         }
      },
```

Description of each messageJson field

* **acquirer** → Name of the acquirer or payment facilitator (e.g., "Via Loja")
* **merchant** → Name of the merchant or store (e.g., "First Customer")
* **businessDocument** → Tax ID of the merchant (CNPJ in Brazil)
* **transactionId** → Transaction identifier (TID - Terminal ID)
* **card** → Information about the card used in the transaction:
  * **number** → Masked card number (last visible digits)
  * **brand** → Card brand (e.g., MASTERCARD, VISA)
  * **aid** → Application Identifier (EMV card type identifier)
* **transaction** → Details of the transaction:
  * **amount** → Total transaction amount (e.g., 50)
  * **currency** → Currency used in the transaction (e.g., BRL)
  * **datetime** → Date and time of the transaction
  * **stan** → CV or STAN (System Trace Audit Number) — unique transaction identifier
  * **installments** → Installment details:
    * **value** → Value of each installment
    * **total** → Total number of installments
  * **paymentMethod** → Payment method (e.g., Credit)
  * **messageAuthorizedBy** → Authorization message of the transaction (e.g., "Transaction authorized by issuer")
* **terminalCode** → Code of the terminal (e.g., POS device) that processed the transaction
  {% endstep %}

{% step %}
Minimum value lock

Starting with version 1.0.29, the SDK has the function of preventing a transaction from being initiated if the transaction value exceeds the minimum limit previously registered in the back-end.

Below is the implementation of how this should be done when this condition is met, giving us the option to send a message as feedback to the user

Following the same example above, “6-Responding to messages,” from the moment version 1.0.29 is consumed, Kotlin itself will inform that a new viewData (“TerminalPaymentValueBelowMinimumLimitViewData”) has not been placed as a condition within the when.

We will insert this new condition and within it we will place the message that will be displayed to the user. Within viewData, we have the field (minValue), which will contain the registered minimum value

Kotlin

Here is an example of implementation

```
     is MPPViewData.TerminalPaymentValueBelowMinimunLimitViewData -> {
                    progressDialog.dismiss()
                    showFeedbackDialog("O valor do pagamento está abaixo do limite mínimo R$${viewData.minValue}")
                    binding.tvMessage.text = "O valor do pagamento está abaixo do limite mínimo."
                }
            }
```

React

Follow the implementation example

```
    is MPPViewData.TerminalPaymentValueBelowMinimunLimitViewData ->{
                      sendErrorMessage("O valor do pagamento está abaixo do limite mínimo R$${viewData.minValue}")
                }

```

{% endstep %}

{% step %}
Responding to invalid transactions

Starting with version 1.0.30, the SDK prevents a transaction from being initiated if certain parameters are incorrect (amount and installments).

When this occurs, specific **ViewData** (**PaymentInformationInvalidViewData**) will be sent.

The app needs to listen for these messages and, when this ViewData appears, **notify the user with a message.**

Kotlin

Follow the implementation example

```
  is  MPPViewData.PaymentInformationInvalidViewData -> {
                    showFeedbackDialog("Pagamento com parametros incorretos.")
                    binding.tvMessage.text = "Revise as condições de pagamento."
                }
```

React Native

Follow the implementation example

```
           is MPPViewData.PaymentInformationInvalidViewData -> {
                    sendErrorMessage("Condicoes de pagamento incorretos.")
                }
```

{% endstep %}
{% endstepper %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ftcoders.first-tech.com/first-tech-ttp-sdk/developer-zone/sdk-implementation-ttp/implementing-the-use-of-the-sdk.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
