Pular para o conteúdo principal

Callbacks (PosPinpadCallback)

Referência da interface PosPinpadCallback (com.tupifintech.apossdk.sdk.api) — eventos e notificações do SDK.

Visão Geral

O integrador implementa PosPinpadCallback para receber os eventos do SDK. Alguns callbacks têm implementação default ({}) e são opcionais; os demais devem ser implementados.

Atualize a UI sempre na main thread (runOnUiThread / viewModelScope). Os callbacks de input e seleção entregam funções que o app deve invocar com a resposta do usuário (ou que serão liberadas por timeout).

Interface (resumo)

enum class NotificationType { INFO, ERROR, WAITING_CARD, PROCESSING }

interface PosPinpadCallback {
// Cartão
fun onStartGetCard()
fun onCardRemovalRequested() {}
fun onCardRemoved() {}

// PIN
fun onStartGetPinCoordinates(setPinCoordinates: (List<String>) -> Unit)
fun onPinEntry(pinMask: String, message: String, amount: Long)
fun onPinError(remainingAttempts: Int, message: String)

// Transação
fun onTransactionCompleted(result: PaymentResult)

// Inputs do usuário
fun onRequestUserInput(
inputCode: InputCode, minLength: Int, maxLength: Int,
onTimeout: (() -> Unit) -> Unit, onInputReceived: (String?) -> Unit
)
fun onRequestMenuSelection(
title: String, options: List<String>,
onTimeout: (() -> Unit) -> Unit, onOptionSelected: (selectedIndex: Int?) -> Unit
)

// PIX
fun onPixQrCodeGenerated(qrCode: String, transactionId: String, amount: Long, expiresInSeconds: Int? = null)

// Estorno
fun onRefundTransactionsAvailable(
transactions: List<TransactionSummary>, cardLastDigits: String, cardBrand: String, cardBin: String,
confirmRefund: (transactionIndex: Int?, refundAmount: Long?) -> Unit
)
fun onRefundCompleted(result: PaymentResult)
fun onRefundError(errorCode: String, message: String)

// Pré-autorização
fun onPreAuthTransactionsAvailable(
transactions: List<TransactionSummary>, cardLastDigits: String, cardBrand: String, cardBin: String,
confirmPreAuth: (selectedIndex: Int?, newAmount: Long?) -> Unit
)

// Impressão
fun onPrintCompleted()
fun onNoHasPaper()
fun onNotifyPrinterError(code: Int, msg: String)

// Tela secundária (OCTA 400)
fun onSecondaryDisplayReady() {}
fun onSecondaryDisplayError(errorCode: String, message: String) {}

// Notificações gerais
fun onUiNotify(text: String, type: NotificationType)
fun onTablesLoaded(ok: Boolean)

// Erros
fun onAppError(error: SdkErrorCode, message: String)
fun onNetworkConnectionError()
fun onClientError() // 400
fun onUnauthorizedError() // 401
fun onSessionExpired()
fun onNotFoundError() // 404
fun onServerError() // 5xx
fun onGenericError()
}

Eventos de Cartão

EventoDescriçãoAção recomendada
onStartGetCard()SDK aguardando cartão"Aproxime, insira ou passe o cartão"
onCardRemovalRequested()SDK solicita remoção do cartão"Retire o cartão"
onCardRemoved()Cartão removido com sucessoProsseguir
override fun onStartGetCard() {
runOnUiThread { showMessage("Aproxime, insira ou passe o cartão") }
}

override fun onCardRemovalRequested() {
runOnUiThread { showMessage("Retire o cartão") }
}

Eventos de PIN

onStartGetPinCoordinates()

fun onStartGetPinCoordinates(setPinCoordinates: (List<String>) -> Unit)

O SDK solicita as coordenadas do teclado PIN. O app exibe a tela de senha, mede a posição de cada botão e devolve a lista pela função setPinCoordinates. Formato e ordem em Configuração.

override fun onStartGetPinCoordinates(setPinCoordinates: (List<String>) -> Unit) {
runOnUiThread { showPinScreen(onCoordinatesReady = { setPinCoordinates(it) }) }
}

onPinEntry() / onPinError()

EventoDescrição
onPinEntry(pinMask, message, amount)Tecla pressionada — atualizar máscara ("****")
onPinError(remainingAttempts, message)PIN offline incorreto; remainingAttempts antes do bloqueio
override fun onPinEntry(pinMask: String, message: String, amount: Long) {
runOnUiThread { updatePinMask(pinMask) }
}

override fun onPinError(remainingAttempts: Int, message: String) {
runOnUiThread { showWarning("$message — tentativas restantes: $remainingAttempts") }
}

Resultado da Transação

fun onTransactionCompleted(result: PaymentResult)
override fun onTransactionCompleted(result: PaymentResult) {
runOnUiThread {
when (result.status) {
PaymentResult.PaymentStatus.APPROVED -> {
result.receipt?.let { showReceipt(it) }
if (result.requiresCardRemoval) showMessage("Retire o cartão")
}
PaymentResult.PaymentStatus.DECLINED -> showError(result.displayMessage ?: "Pagamento negado")
PaymentResult.PaymentStatus.ERROR -> showError(result.displayMessage ?: "Erro no processamento")
}
}
}

Veja PaymentResult.

Inputs do Usuário

onRequestUserInput()

O SDK pede um dado adicional (InputCode.CVV ou InputCode.LAST_4_DIGITS). O app coleta e responde por onInputReceived; onTimeout registra um callback de expiração.

override fun onRequestUserInput(
inputCode: InputCode, minLength: Int, maxLength: Int,
onTimeout: (() -> Unit) -> Unit, onInputReceived: (String?) -> Unit
) {
onTimeout { runOnUiThread { dismissInputDialog() } }
val title = when (inputCode) {
InputCode.CVV -> "Digite o CVV"
InputCode.LAST_4_DIGITS -> "Últimos 4 dígitos"
}
runOnUiThread {
showInputDialog(title, minLength, maxLength) { value -> onInputReceived(value) }
}
}

onRequestMenuSelection()

override fun onRequestMenuSelection(
title: String, options: List<String>,
onTimeout: (() -> Unit) -> Unit, onOptionSelected: (selectedIndex: Int?) -> Unit
) {
onTimeout { runOnUiThread { dismissMenu() } }
runOnUiThread { showMenu(title, options) { index -> onOptionSelected(index) } }
}

PIX

override fun onPixQrCodeGenerated(qrCode: String, transactionId: String, amount: Long, expiresInSeconds: Int?) {
runOnUiThread { showPixQrCode(qrCode, expiresInSeconds) }
}

Estorno

override fun onRefundTransactionsAvailable(
transactions: List<TransactionSummary>, cardLastDigits: String, cardBrand: String, cardBin: String,
confirmRefund: (transactionIndex: Int?, refundAmount: Long?) -> Unit
) {
if (transactions.size == 1) { confirmRefund(0, null); return }
runOnUiThread {
showSelection("Selecione a transação (**** $cardLastDigits)", transactions) { index ->
confirmRefund(index, null) // refundAmount null = valor total
}
}
}

override fun onRefundCompleted(result: PaymentResult) {
runOnUiThread { showSuccess("Estorno concluído: ${result.receipt?.transactionId}") }
}

override fun onRefundError(errorCode: String, message: String) {
runOnUiThread { showError("Erro no estorno [$errorCode]: $message") }
}

Pré-Autorização

override fun onPreAuthTransactionsAvailable(
transactions: List<TransactionSummary>, cardLastDigits: String, cardBrand: String, cardBin: String,
confirmPreAuth: (selectedIndex: Int?, newAmount: Long?) -> Unit
) {
if (transactions.size == 1) { confirmPreAuth(0, null); return }
runOnUiThread {
showSelection("Pré-autorizações (**** $cardLastDigits)", transactions) { index ->
confirmPreAuth(index, null)
}
}
}

Impressão

EventoDescrição
onPrintCompleted()Impressão concluída (somente quando notifyComplete=true)
onNoHasPaper()Sem papel
onNotifyPrinterError(code, msg)Erro na impressora

Tela Secundária (OCTA 400)

EventoDescrição
onSecondaryDisplayReady()Tela secundária pronta
onSecondaryDisplayError(code, msg)Erro (NOT_AVAILABLE, INIT_FAILED, SHOW_ERROR, ...)

Notificações Gerais

override fun onUiNotify(text: String, type: NotificationType) {
runOnUiThread {
when (type) {
NotificationType.PROCESSING -> showLoading(text)
NotificationType.WAITING_CARD -> showMessage(text)
NotificationType.ERROR -> showError(text)
NotificationType.INFO -> showMessage(text)
}
}
}

override fun onTablesLoaded(ok: Boolean) {
runOnUiThread { if (!ok) showError("Erro ao carregar tabelas EMV") }
}

Eventos de Erro

EventoDescrição
onAppError(error: SdkErrorCode, message)Erro interno do SDK (ver SdkErrorCode)
onNetworkConnectionError()Sem conexão
onClientError()HTTP 400
onUnauthorizedError()HTTP 401 (token inválido)
onSessionExpired()Sessão expirada
onNotFoundError()HTTP 404
onServerError()HTTP 5xx
onGenericError()Outros erros não-2xx
override fun onAppError(error: SdkErrorCode, message: String) {
runOnUiThread {
Log.e("YBY", "${error.name} (${error.code}): $message")
showError(message)
}
}

override fun onUnauthorizedError() {
runOnUiThread { navigateToLogin() }
}

override fun onSessionExpired() {
runOnUiThread { navigateToLogin() }
}

Boas Práticas

  1. Sempre atualize a UI na main thread.
  2. Responda aos callbacks de input/seleção (onInputReceived, onOptionSelected, confirmRefund, confirmPreAuth) — caso contrário o fluxo trava até o timeout.
  3. Trate todos os erros para melhor experiência do operador.
  4. Não bloqueie dentro dos callbacks.
  5. Nunca registre em log dados sensíveis (PAN, PIN, track).

Próximos Passos

  • Modelos — estruturas de dados retornadas
  • Exemplos — exemplos práticos completos