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
| Evento | Descrição | Açã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 sucesso | Prosseguir |
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()
| Evento | Descriçã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
| Evento | Descrição |
|---|---|
onPrintCompleted() | Impressão concluída (somente quando notifyComplete=true) |
onNoHasPaper() | Sem papel |
onNotifyPrinterError(code, msg) | Erro na impressora |
Tela Secundária (OCTA 400)
| Evento | Descriçã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
| Evento | Descriçã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
- Sempre atualize a UI na main thread.
- Responda aos callbacks de input/seleção (
onInputReceived,onOptionSelected,confirmRefund,confirmPreAuth) — caso contrário o fluxo trava até o timeout. - Trate todos os erros para melhor experiência do operador.
- Não bloqueie dentro dos callbacks.
- Nunca registre em log dados sensíveis (PAN, PIN, track).