Inicialização do Terminal
Com o merchantId em mãos, inicialize o terminal. A inicialização baixa a carga do gateway, grava as tabelas EMV de cada adquirente no pinpad e prepara o contexto de transação.
Chame a cada execução do app: o estado de inicializado vive só em memória e não é restaurado quando o processo reinicia. Sem inicialização, as operações financeiras respondem onAppError(SdkErrorCode.TERMINAL_NOT_INITIALIZED, ...).
import com.tupifintech.apossdk.sdk.api.model.TerminalInitializationRequest
val initResponse = posPinpad.terminalInitialization(
request = TerminalInitializationRequest(merchantId = merchantId)
)
if (initResponse.success) {
// Dados do estabelecimento (header, recibo, senha de acesso)
val merchant = initResponse.merchant
} else {
// Falha na ativação, no download da carga ou na preparação do contexto.
// Não libere vendas; ofereça nova tentativa.
}
O callback onTablesLoaded(ok) informa o resultado da gravação das tabelas no pinpad, uma vez por adquirente da carga. Ele também é chamado quando o SDK recarrega as tabelas sozinho (após transações ou quando o pinpad informa tabelas expiradas). Recomendação:
- Considere a inicialização concluída só se
initResponse.successfortruee nenhumonTablesLoaded(false)tiver chegado durante a chamada. - Quando
success == false, chega tambémonAppError(SdkErrorCode.TERMINAL_INITIALIZATION_FAILED, ...). - Falha de chave do adquirente padrão chega como
onAppError(SdkErrorCode.PP_ERRKEY, ...). Nova tentativa não resolve: acione o suporte.
Para verificar o estado depois:
val ready = posPinpad.isTerminalInitialized() // só indica que terminalInitialization foi chamado
val tables = posPinpad.getTablesInfo() // versões das tabelas EMV por adquirente; null se nunca carregou
isTerminalInitialized() é true mesmo quando a inicialização falhou. Para saber se deu certo, use o retorno de terminalInitialization.
Coordenadas do Teclado PIN
Importante: as coordenadas do teclado PIN não são configuradas antecipadamente. O SDK as solicita de forma reativa, na primeira captura de senha do processo, pelo callback
onStartGetPinCoordinates. O app deve medir as posições dos botões na tela e devolvê-las pela função recebida.
override fun onStartGetPinCoordinates(setPinCoordinates: (List<String>) -> Unit) {
// Exiba a tela de PIN, meça os botões e chame setPinCoordinates(coords)
showPinScreen(onCoordinatesReady = { coords -> setPinCoordinates(coords) })
}
Formato e ordem da lista
São 13 itens, cada um uma string "left,top,right,bottom" em pixels da janela (positionInWindow() no Compose ou getLocationInWindow() em Views). A ordem esperada é:
| Índice | Tecla |
|---|---|
| 0–8 | Números 1 a 9 |
| 9 | Número 0 |
| 10 | CANCELAR |
| 11 | OK / Confirma |
| 12 | Backspace |
- O SDK aguarda as coordenadas por 5 segundos. Sem resposta, o pinpad segue sem as teclas mapeadas e a digitação da senha falha.
- O portador tem 60 segundos para digitar a senha.
- As coordenadas ficam em cache durante o processo: o callback não é chamado de novo nas transações seguintes. Mantenha o teclado sempre na mesma posição.
- Se
MerchantInfo.accessibilityKeyboardfortrue, exiba o teclado de senha de acessibilidade (4 colunas, compatível com a película tátil da Positivo). - A máscara da senha digitada chega por
onPinEntry(pinMask, message, amount).
No Compose, as coordenadas podem ser obtidas com onGloballyPositioned + positionInWindow(). Veja o exemplo completo em Exemplos.
Informações do Dispositivo
val info = posPinpad.getDeviceInfo() // lança IllegalStateException se o pinpad ainda não conectou
Log.d("YBY", "Modelo: ${info.hardwareModel}")
Log.d("YBY", "Serial: ${info.serialNo}")
Log.d("YBY", "Firmware: ${info.firmwareVersion}")
val tid = posPinpad.getTerminalTid() // TID do adquirente, o mesmo impresso no comprovante
val sim = posPinpad.getSimInfo() // suspend: ICCID, operadora e transporte ativo; null se o serviço não responder
A conexão com o pinpad acontece em segundo plano após create. Logo depois de criar a instância, trate a IllegalStateException de getDeviceInfo() e tente de novo mais tarde.
Health Check
val health = posPinpad.checkInternetConnection()
if (!health.isConnected) {
showError(health.errorMessage ?: "Sem conexão")
}
HealthCheckResult traz também hasNetworkConnection, isApiReachable e latencyMs.
Boas Práticas
Tratamento de erros
Implemente os callbacks de erro (onAppError, onNetworkConnectionError, onUnauthorizedError, onSessionExpired, etc.). Os de erro HTTP não chegam na main thread. Veja Callbacks.
override fun onAppError(error: SdkErrorCode, message: String) {
Log.e("YBY", "Erro ${error.name} (${error.code}): $message")
showErrorDialog(message)
}
override fun onNetworkConnectionError() {
runOnUiThread { showErrorDialog("Sem conexão com a internet") }
}
Dados sensíveis
Nunca registre em log dados de pagamento (PAN, PIN, track) nem a accessPassword do merchant.