> 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-pt/area-do-desenvolvedor/codelab-implementacao-sdk-ttp/implementando-o-uso-do-sdk.md).

# Implementando o uso do SDK

{% stepper %}
{% step %}
Agora que a dependência foi configurada, é necessário realizar a configuração inicial. Essas configurações são necessárias para iniciar o SDK de pagamento. Vamos definir essas configurações na classe Application.\
Crie uma classe que estenda Application e defina as configurações para pagamento.

Na classe de inicialização do seu projeto (Application), deve “estender” TapOnPhoneApplication

Chame o método isApplicationInitAllowed() para verificar se a aplicação pode ser inicializada.

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

O método estático initializeTerminal() da classe TapOnPhoneInitializer deve ser executado. Ele realiza a inicialização do terminal, um passo essencial para utilizar o SDK.

`TapOnPhoneInitializer.initializeTerminal(this)`&#x20;

Para definir as configurações iniciais, execute o método `setTerminalConfig(config: TerminalConfigEntity)`. Para isso, crie um objeto TerminalConfigEntity. Os valores informados abaixo são para o cenário de sandbox (sandbox).

```
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.
)
```

Ao final desse processo, a estrutura do arquivo deverá ser semelhante à estrutura abaixo.\
Importante: Os valores mostrados referem-se ao ambiente de sandbox.

<figure><img src="/files/7rk7grt78Dy4NwLtDTIe" alt=""><figcaption></figcaption></figure>

{% endstep %}

{% step %}
O projeto já está configurado. Em seguida, vamos modificar a MainActivity. Abaixo, apresentamos um exemplo de layout para ilustrar o uso do nosso SDK. Sinta-se à vontade para adaptá-lo de acordo com o seu layout. Nossa atividade segue este modelo.

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

{% endstep %}

{% step %}
Todas as operações de manipulação do SDK são efectuadas através do ViewModel, que recebe a TapOnPhoneApplication como parâmetro durante a sua criação.\
No inicio da Activity, temos as seguintes declarações:

```
private val progressDialog by lazy { ProgressDialog(this) } //<- Este componente de interface de usuário do Android é usado para mostrar uma caixa de diálogo de progresso.
private val binding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } //<- Define a variável binding como do tipo ActivityMainBinding. //<- Esta é uma classe gerada automaticamente pelo recurso View Binding do Android.
private val mppProviderViewModel by lazy { MPPPProviderViewModel(application as Application) } //<- ViewModel para gerenciar os dados e a lógica da interface de usuário.
```

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

{% endstep %}

{% step %}
Ficando nossa MainActivity da seguinte maneira.

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

**OBS**: Utilizamos o método **askPermission**, para solicitar permissão para coletar a localização do usuário ,informação que será utilizada para uma melhor analise de erro posteriormente

```
 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 %}
Iniciar um pagamento

Executar a função startPaymentFlow(), fornecida pelo MPPProviderViewModel. Para isso, é necessário passar a classe TransactionInfoEntity como parâmetro, que recebe os seguintes valores no seu construtor.

`installments:`Um número inteiro que contém o número de parcelas

`paymentType:`  Uma das opções  `PaymentType` , que pode ser `PaymentType.DEBIT`  ou `PaymentType.CREDIT`

`amount:`  O montante total da transação, expresso `Long` .

| Valor    | 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/cg3yoSgUEMBYMKHLGbuX" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Parada manual do processo de pagamento
{% endhint %}

Caso ocorra um encerramento no fluxo de pagamento, seja por ação do usuário ou por um fluxo interno do próprio aplicativo, é necessário utilizar o método disponibilizado para encerrar a sessão (transação). O método **clearCurrentTerminalSession()** do objeto SessionHelper deverá ser chamado. Essa ação é fundamental, pois, caso contrário, a sessão anterior permaneceria ativa. Consequentemente, ao tentar avançar novamente para a tela de transação, ocorreria um erro na criação de uma nova sessão, uma vez que já existiria uma em andamento.

Este método poderá ser chamado em qualquer ponto do seu aplicativo. Nos exemplos abaixo, demonstramos como implementá-lo em Kotlin e React Native.

Para utilizá-lo em Kotlin, basta chamar o seguinte método:

```
SessionHelper.clearCurrentTerminalSession()
```

No aplicativo React Native, utilizaremos o botão "Voltar" (Back) para acionar o método de encerramento de sessão disponibilizado pelo SDK. A implementação detalhada abaixo é específica para o ambiente **React Native**.

```
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)
        }
    }
}

```

Agora definiremos como utilizar o nosso novo método, localizado no arquivo DeviceInfoModule, por meio do "wrapper" JavaScript que já foi empregado anteriormente para outras funções. Sinta-se à vontade para criar um novo arquivo dedicado a esta funcionalidade ou integrar ao arquivo existente em nosso aplicativo de amostra.

```
import { NativeModules } from 'react-native';

const { DeviceInfoModule } = NativeModules;

export function getDeviceInfo(): Promise<string> {
  return DeviceInfoModule.getDeviceInfo();
}

export function clearTerminal(): void {
  DeviceInfoModule.clearTerminalSession();
}
```

Em nosso arquivo index (ou index.tsx), que define a tela de processamento de pagamento da nossa aplicação, importaremos o método clearTerminal – definido anteriormente em nosso módulo nativo – para ser utilizado quando o botão "Voltar" (Back) for acionado.

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

Além disso realizaremos os seguintes imports&#x20;

**BackHandler**: Gerencia o botão "Voltar" físico do Android.\
**useFocusEffect**: Executa efeitos (como adicionar listeners do BackHandler) quando a tela ganha foco e os limpa quando perde o foco.\
**useCallback**: Memoiza a função passada para useFocusEffect, otimizando-a e garantindo que ela seja estável, prevenindo recriações e execuções desnecessárias do efeito de foco.

E em nosso componente que funciona como botão "Voltar", chamaremos o método clearTerminal(), implementado anteriormente. Este, por sua vez, utilizará o método de limpeza de sessão que foi implementado no SDK.

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

{% endstep %}

{% step %}
Reagindo às mensagens

O`MPPProviderViewModel` fornece o`LiveData onMessage`,que receberá um objeto “estendido” do `MPPViewData` classe.

{% hint style="info" %}
É importante observar onMessage dentro do ciclo de vida correto, garantindo que não há memory leaks ou reacções inesperadas.
{% endhint %}

Neste projeto, utilizamos o conceito de Sealed Classes para uma abstração mais eficiente. Abaixo, listamos todos os objetos que podem ser retornados em onMessage e seus respectivos significados.

Aqui, obtemos a referência ao `LiveData onMessage` e começamos a observá-lo:

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

{% hint style="info" %}
Nota: Neste exemplo, não listamos todas as classes MPPViewData, mas recomendamos a implementação de todas as classes.\
Em nosso repositorio , em nosso sample conterá a lista de todas as classes, que podera ter em nosso when
{% endhint %}

{% hint style="info" %}
Nota: Algumas classes MPPViewData contêm objectos que ajudam a compreender a mensagem que deve ser apresentada num passo específico.

{% 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

            }
        }
    }
```

Nesse exemplo TerminalPaymentSuccessViewData é quando a transação foi concluida com sucesso.

**OBS: Para acessar o messageJson, recupere o result que voce recebeu no viewData e extraia o messageJson da seguinte forma**

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

{% endstep %}

{% step %}
Imprimindo o comprovante na tela

Segue a implementação do método da função **createReceipt** que retorna uma String. String essa que esta montada para ser um comprovante,podendo ser customizado da forma que vocêprecisar.

<figure><img src="/files/Vg07ZMZb1ixMV8rfLkWB" 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"
}
```

Segue um exemplo do JSON com o campo messageJson, que utilizamos para montar nosso recibo

```
"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"
         }
      },
```

Descritivo de cada campo do messageJson<br>

* **acquirer** → Nome do adquirente ou facilitador de pagamentos (ex: "Via Loja").
* **merchant** → Nome do estabelecimento comercial (ex: "First Customer").
* **businessDocument** → CNPJ do estabelecimento comercial.
* **transactionId** → Identificador da transação (TID - Terminal ID).
* **card** → Informações do cartão utilizado na transação:
  * **number** → Número do cartão mascarado (últimos dígitos visíveis).
  * **brand** → Bandeira do cartão (ex: MASTERCARD, VISA).
  * **aid** → Application Identifier (identificador do tipo de cartão EMV).
* **transaction** → Dados da transação realizada:
  * **amount** → Valor total da transação (ex: 50).
  * **currency** → Moeda utilizada na transação (ex: BRL).
  * **datetime** → Data e hora da transação.
  * **stan** → CV ou STAN (System Trace Audit Number) — número identificador da transação.
  * **installments** → Informações sobre o parcelamento:
    * **value** → Valor de cada parcela.
    * **total** → Número total de parcelas.
  * **paymentMethod** → Forma de pagamento (ex: Crédito).
  * **messageAuthorizedBy** → Mensagem de autorização da transação (ex: "Transação autorizada pelo emissor").
* **terminalCode** → Código do terminal (ex: POS) que realizou a transação.

{% endstep %}

{% step %}

### Trava de valor minimo

A partir da versao 1.0.29, o sdk tem a função de impedir que uma transação seja iniciada, desde que o valor da transação seja maior que o limite minimo previamente cadastrado no back-end.\
Segue abaixo a implementação de como deve ser feito para quando essa condição for atingida, com isso temos a opção de enviar alguma mensagem como feedback para o usuário<br>

Seguindo o mesmo exemplo acima "6-Reagindo a mensagens", apartir do momento que for consumido a versão 1.0.29 o próprio Kotlin vai informar que uma novo viewData ("TerminalPaymentValueBelowMinimunLimitViewData") não foi colocado como condição dentro do when.\
Iremos inserir essa nova condição e dentro dela iremos colocar a mensagem que será visualizado pelo usuário. Dentro da viewData, temos o campo (minValue), que conterá o valorMinimo cadastrado<br>

Kotlin\
\
Segue o exemplo de implementação

```
    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

Segue o exemplo de implementação

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

{% endstep %}

{% step %}

### Reagindo a Transações inválidas

A partir da versão **1.0.30**, o SDK impede que uma transação seja iniciada se alguns parâmetros estiverem incorretos (valor e parcelas).\
Quando isso ocorrer, será enviado um **ViewData** específico (**PaymentInformationInvalidViewData**).\
O app precisa ficar escutando essas mensagens e, quando esse **ViewData** aparecer, deve **notificar o usuário com uma mensagem**.

Kotlin

Segue o exemplo de implementação

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

React Native

Segue o exemplo de implementação

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

{% endstep %}

{% step %}

### Cancelamento de Transações

A partir da versão 1.2.1, o SDK contém o recurso de cancelamento de transações.

A implementação desse recurso está descrita abaixo:&#x20;

1\. Primeiramente, é necessário chamar o método `getTodayPaymentList` do `ViewModel`.

```
binding.btList.setOnClickListener {
    mppProviderViewModel.getTodayPaymentList()
}
```

2\. Em seguida, será iniciado o processo de forma similar a uma transação de pagamento: o terminal será inicializado, a sessão será criada e o cartão será solicitado por aproximação.&#x20;

3\. Um novo `ViewData` foi criado para este fluxo: `TerminalRefundViewData`. Este `ViewData` contém uma variável chamada `isCompletedFlow: Boolean`, que indica se a etapa atual é a de listagem ou a etapa final (onde será exibido o recibo de cancelamento).&#x20;

4\. Após chamar o método `getTodayPaymentList`, quando o `ViewData` `TerminalRefundViewData` for notificado, deve-se verificar se a variável `isCompletedFlow: Boolean` está como `false`. Neste cenário, esta será a etapa de montar a lista com as transações realizadas com esse cartão no mesmo dia. 5. Será retornada uma lista do tipo `<RefundTransaction>`.

&#x20;Observação: Utilize o *import* `com.firsttech.taponphone.sdk.v2.models.RefundTransaction`.

```
data class RefundTransaction(
    val date:String,
    val amount:String,
    val brand:String,
    val lastFour:String,
    val originExternalReference:String
)
```

```
is MPPViewData.TerminalRefundViewData -> {
    if (viewData.isCompletedFlow) {
       //FLUXO DE CANCELAMENTO
    } else {
        //FLUXO DE LISTAGEM DE TRANSAÇÕES
        binding.tvMessage.text = "Lista de transações."
       
        val list = viewData.list
        loadTransactions(list)
    }
}
```

Após a montagem da lista(recyclerView), ao escolher o item da transação para ser cancelada, deve-se chamar o método `getRefund` do `ViewModel`, passando o campo `originExternalReference`, que é o ID da transação.

```
transactionAdapter = TransactionAdapter(emptyList) { transacaoEscolhida ->
    mppProviderViewModel.getRefund(transacaoEscolhida.originExternalReference)
}
```

O processo será reiniciado de forma similar a uma transação: o terminal será inicializado, a sessão será criada e o cartão será solicitado por aproximação. Após isso, em caso de sucesso, o `ViewData` `TerminalRefundViewData` será notificado novamente.

Nesta etapa, a variável `isCompletedFlow` será `true`, e neste fluxo deve-se exibir o recibo de cancelamento. Um exemplo de montagem do recibo se encontra mais abaixo.

```
is MPPViewData.TerminalRefundViewData -> {
    if (viewData.isCompletedFlow) {
        val metaData = viewData.result?.getMetaDataTranslated()
        binding.tvLog.text =
            "" + "Resultado: ${viewData.result?.getMetaDataTranslated()?.status}" + createReceipt(
                metaData
            )
        binding.recycler.isGone = true
    } else {
        //FLUXO DE MOSTRAR A LISTA DE TRANSACOES
    }
}
```

Exemplo de montagem de recibo.<br>

```
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
                    )
                }
            }" +
            if(messageJson?.transaction?.installments==null){
            ""
            } else {
                "\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"
}
```

Em caso de erro durante esse fluxo é necessário implementar o `ViewData`  `OperationErrorViewData` . A mensagem de erro estará na variavel message(titulo) e errorReason(motivo do erro).

```
is MPPViewData.OperationErrorViewData -> {
    enableEditFields(true)
    binding.tvMessage.text = "Status: ${viewData.message}"
    binding.recycler.isVisible = false
    binding.tvLog.text = viewData.errorReason
}
```

{% 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-pt/area-do-desenvolvedor/codelab-implementacao-sdk-ttp/implementando-o-uso-do-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.
