Pular para o conteúdo principal

Configuração do SDK

Guia de configuração e do fluxo de inicialização do SDK.

Ciclo de Uso​

O fluxo de preparação do terminal segue estas etapas:

  1. Criar a instância de PosPinpad via create().
  2. (Opcional) Configurar os endpoints via setEndpointProvider().
  3. Na primeira execução no terminal: buscar a personalização do parceiro via getTerminalConfig(slug) e registrar o terminal (register(...)).
  4. A cada execução do app: inicializar o terminal via terminalInitialization(request) (carrega tabelas EMV).
  5. A partir daí, executar pagamentos e demais operações.

Os métodos suspend devem ser chamados dentro de uma coroutine (por exemplo, lifecycleScope.launch). Os callbacks do fluxo de pagamento são entregues na main thread; os de impressão, de tela secundária e de erro HTTP chegam na thread de origem. Veja Callbacks.

Criar instância do SDK​

Implemente PosPinpadCallback e crie a instância passando o Context e o callback. Sempre use PosPinpad.create() — ele inicializa o HttpClient com mTLS antes de qualquer request.

import com.tupifintech.apossdk.sdk.api.PosPinpad
import com.tupifintech.apossdk.sdk.api.PosPinpadCallback

private lateinit var posPinpad: PosPinpad

posPinpad = PosPinpad.create(context, callback)

Mantenha uma única instância por processo. Em apps com várias telas, crie-a no Application e repasse os eventos para a tela ativa.

Certificado mTLS​

O certificado cliente do gateway vem dentro do SDK, conforme o artefato (apossdk-debug-staging = staging, apossdk-release-prod = produção). Para sobrescrevê-lo, passe um MtlsCertificate no terceiro parâmetro de create:

val posPinpad = PosPinpad.create(
context,
callback,
MtlsCertificate.fromAssets(context, "minha-cadeia.pem", "minha-chave.pem")
)
// ou MtlsCertificate.fromPem(cadeiaEmPem, chaveEmPem)
ParteFormato
CadeiaX.509 em PEM, um ou mais blocos BEGIN CERTIFICATE
ChavePKCS#8 em PEM (BEGIN PRIVATE KEY), sem senha. Chave PKCS#1 (BEGIN RSA PRIVATE KEY) precisa ser convertida

Ordem de resolução: o MtlsCertificate passado em create → o par embutido no SDK → erro (IllegalStateException).

O par embutido é lido dos assets yby-pos-v1-fullchain.pem e yby-pos-v1.key.pem. No merge de assets, o do app vence o da biblioteca: um app que entregue arquivos com esses nomes nos próprios assets substitui o certificado embutido sem passar MtlsCertificate.

Configurar endpoints (EndpointProvider)​

Cada artefato já aponta para os endpoints do seu ambiente. O SDK separa os endpoints administrativos (não-PCI) dos financeiros (PCI). Para usar endpoints informados pela Tupi para um ambiente específico, configure-os logo depois de create:

import com.tupifintech.apossdk.sdk.api.EndpointProvider
import com.tupifintech.apossdk.sdk.api.setEndpointProvider

posPinpad.setEndpointProvider(
EndpointProvider(
adminBaseUrl = BuildConfig.ADMIN_BASE_URL, // autenticação, registro, tabelas, config, consultas
financialBaseUrl = BuildConfig.FINANCIAL_BASE_URL // pagamentos, estornos, PIX
)
)

Tempo de Espera do Cartão​

Por padrão, o terminal espera o cartão por 30 s em cada tentativa, até 3 tentativas. No estorno e na pré-autorização, a captura tem ainda um teto total de 60 s. Para ajustar:

import com.tupifintech.apossdk.sdk.api.model.CardReadTimeouts
import com.tupifintech.apossdk.sdk.api.setCardReadTimeouts

posPinpad.setCardReadTimeouts(
CardReadTimeouts(perAttemptSeconds = 45, maxAttempts = 2, totalSeconds = 95)
)
CampoPadrãoFaixa
perAttemptSeconds301 a 300
maxAttempts31 a 10
totalSeconds60de perAttemptSeconds a 300. Vale só no estorno e na pré-autorização

Valores fora da faixa lançam IllegalArgumentException no construtor. A configuração vale a partir da próxima leitura e pode ser trocada entre transações. Ela não altera os tempos fixos abaixo.

EtapaTempo
Entrega das coordenadas do teclado de senha5 s
Digitação da senha60 s
CVV, últimos dígitos, seleção de produto e seleção de transação (estorno e pré-autorização)30 s
Seleção de aplicação EMVDefinido pelo pinpad

Usuários Locais​

O SDK guarda o cadastro de Supervisores e Operadores do terminal, usado para autorizar operações restritas (como o estorno) e identificar quem operou. O cadastro fica no armazenamento privado do seu app, criptografado; não é sincronizado com o backend.

val usuarios = posPinpad.localUsers

when (val r = usuarios.create("Ana", "52998224725", LocalUserProfile.SUPERVISOR, senha)) {
is LocalUserResult.Success -> mostrar(r.value) // LocalUserInfo, sem senha
is LocalUserResult.Failure -> tratar(r.error) // LocalUserError
}

val autorizou = usuarios.verify(senhaDigitada, LocalUserProfile.SUPERVISOR) != null
FunçãoO que faz
list()Usuários cadastrados, em LocalUserResult<List<LocalUserInfo>>
hasSupervisor()Se há ao menos um Supervisor
create(name, cpf, profile, password)Cadastra
update(id, name, profile)Altera nome e/ou perfil (null mantém)
changePassword(id, newPassword)Troca a senha
delete(id)Remove
verify(password, requiredProfile)Confere uma senha; devolve o usuário ou null
clearAll()Apaga todo o cadastro

Regras garantidas pelo SDK:

RegraErro quando violada
Senha de exatamente 6 dígitos (CharArray, zerada ao fim da chamada)INVALID_PASSWORD
Nome não vazio e CPF com 11 dígitosINVALID_DATA
CPF únicoDUPLICATE_CPF
Sem Supervisor, o primeiro usuário tem de ser SupervisorSUPERVISOR_REQUIRED
No máximo 10 SupervisoresSUPERVISOR_LIMIT
O último Supervisor só sai se for o último usuárioLAST_SUPERVISOR
Id inexistenteNOT_FOUND
Cadastro existe mas não pôde ser lido nem gravadoSTORAGE_UNAVAILABLE

As funções fazem I/O de arquivo e derivação de chave: chame fora da main thread (por exemplo, withContext(Dispatchers.IO)). Pedir a senha do Supervisor antes de criar, editar ou remover é responsabilidade da tela do seu app.

Migrando um cadastro anterior​

Se o seu app já guardava usuários por conta própria, com a senha em SHA-256 hexadecimal sem salt, importe-os uma vez com importLegacy:

@OptIn(LegacyLocalUserImport::class)
usuarios.importLegacy(antigos.map { LegacyLocalUser(it.id, it.name, it.cpf, it.profile, it.hashHex) })
  • O hash antigo é aceito só na verificação e regravado no formato do SDK no primeiro acerto: ninguém precisa cadastrar senha de novo.
  • A importação não sobrescreve: se o cadastro do SDK já tem usuários, ela é ignorada. É seguro repetir; apague o cadastro antigo só depois de a importação dar certo.
  • A função exige @OptIn(LegacyLocalUserImport::class) porque é caminho de migração, não API para código novo.

Metadados da Transação​

createPayment, createPixPayment, startRefundFlow e startPreAuthFlow aceitam metadata: dados do seu app que seguem com a transação até o gateway (número do pedido, mesa, cliente).

posPinpad.createPayment(
type = TransactionType.AUTHORIZATION,
amount = 1500L,
paymentMethod = PaymentMethod.CREDIT,
installments = 1,
metadata = TransactionMetadata(mapOf("pedido" to "12345", "mesa" to "07"))
)

Limites (o construtor lança IllegalArgumentException fora deles): até 20 chaves; chave com até 40 caracteres em [A-Za-z0-9_.-]; valor com até 256 caracteres; 4 KB no total.

  • Os metadados vão em todas as etapas da transação: autorização, PIX, estorno, pré-autorização, captura, confirmação e desfazimento. Também ficam gravados na fila de pendências, para seguirem na confirmação ou no desfazimento enviados mais tarde.
  • Sem metadados, o campo não é enviado.
  • Hoje o gateway recebe os metadados, mas ainda não os persiste nem os devolve nas consultas de transação.

Não coloque dado de portador em metadata (número do cartão, trilha, CVV, nome impresso). O campo é gravado no terminal enquanto a transação estiver pendente e segue para o backend. O SDK recusa valores com aparência de número de cartão, mas essa checagem é uma proteção adicional, não uma autorização.

Adquirentes e Chaves​

O SDK é multi-adquirente: o terminal escolhe o adquirente de cada venda pela bandeira e pelo produto do cartão. O app não escolhe; o nome de quem processou volta em receipt.acquirerName.

val adquirentes = posPinpad.getConfiguredAcquirers() // ex.: ["pagseguro", "adiq"]; o primeiro é o padrão; vazio sem inicialização
val temChave = posPinpad.hasAcquirerKeys("pagseguro") // busca sem diferenciar maiúsculas

hasPagSeguroKeys() está obsoleto: use hasAcquirerKeys("pagseguro"). Falha de chave do adquirente padrão chega como onAppError(SdkErrorCode.PP_ERRKEY, ...); nova tentativa não resolve: acione o suporte.

Transações Pendentes​

Confirmações e desfazimentos que não puderam ser entregues ficam numa fila e são reenviados automaticamente.

val pendentes = posPinpad.getPendingTransactionsCount() // suspend; zero é o normal
posPinpad.resolvePendingTransactions() // reenvia já, por exemplo ao recuperar a conexão

A autorização sempre exige rede: a fila guarda apenas confirmações e desfazimentos, não vendas offline. Enquanto houver pendências, os totais vindos do backend podem não refletir tudo o que aconteceu no terminal. Use a contagem para avisar o lojista.

Boas Práticas​

1. Instância única​

class MyApplication : Application() {

lateinit var posPinpad: PosPinpad

override fun onCreate() {
super.onCreate()
posPinpad = PosPinpad.create(this, globalCallback)
}
}

2. Inicialização a cada execução​

lifecycleScope.launch {
val merchantId = armazenamento.merchantId ?: return@launch irParaAtivacao()
val init = posPinpad.terminalInitialization(TerminalInitializationRequest(merchantId))
if (!init.success) mostrarErroComNovaTentativa()
}

3. Logout​

lifecycleScope.launch {
posPinpad.logout() // descarta a sessão e os IDs de registro
}

Próximos Passos​