2 - Realizar Pagamento
Realize pagamentos por aproximação (Tap to Pay) utilizando o SDK Android da Zoop.
Realize pagamentos por aproximação (Tap to Pay) utilizando o SDK Android da Zoop. Esta página cobre as 3 etapas necessárias para processar um pagamento:
① Inicialização do Kernel → ② Configuração do SDK → ③ Pagamento
kernelInitialize() setConfig() pay()Inicialização do Kernel
O kernelInitialize() é o primeiro método obrigatório a ser chamado antes de usar qualquer funcionalidade do SDK. Implemente-o na classe Application do seu app, dentro do onCreate().
Chame
kernelInitialize()logo apóssuper.onCreate(). Caso contrário, os processos internos do SDK podem não funcionar corretamente.
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Garante que o código abaixo só execute no processo da sua aplicação
if (!TapOnPhone.kernelInitialize(this))
return
// Restante da inicialização do seu app...
}
}Por que o if (!kernelInitialize()) return é necessário?
✅ Retornotrue
O processo pertence à sua aplicação (Host Application). O código deve continuar executando normalmente.
⛔ Retornofalse
O processo pertence ao SDK (Crypto Process). O código deve retornar imediatamente com return para evitar inicialização duplicada.
Durante a inicialização, o SDK cria um processo criptográfico (Crypto Process) em background, fazendo com que o onCreate() seja chamado mais de uma vez. O retorno booleano permite distinguir o processo:
✅ Retornotrue — Processo da sua aplicação. Continue normalmente.
⛔ Retornofalse — Processo do SDK (Crypto). Retorne imediatamente.
Application.onCreate()
│
├─ super.onCreate()
│
├─ TapOnPhone.kernelInitialize(this)
│ │
│ ├─ retorna true → Host Application → continua a inicialização do app
│ │
│ └─ retorna false → Crypto Process → return (encerra imediatamente)
│
└─ Restante da inicialização do app (só executa se true)Sem essa verificação, o onCreate() executaria duas vezes — uma para cada processo — causando erros e comportamento inconsistente.
Definir configurações do SDK
O método TapOnPhone.setConfig() permite configurar credenciais, timeout, volume do beep, tema e comportamento de telas de erro em uma única chamada.
Chame
TapOnPhone.setConfig()apósTapOnPhone.kernelInitialize()e antes da inicialização do terminal. Sem essa chamada, o SDK não funcionará corretamente.
Estrutura
data class ConfigParameters(
val context: Context,
val credentials: Credentials,
val sdkConfig: SdkConfig
)data class SdkConfig(
val theme: TapOnPhoneTheme? = null,
val timeout: TimeoutConfig? = null,
val beepVolume: BeepVolumeConfig? = null,
val showErrorScreen: Boolean? = null
)Configurações disponíveis
| Parâmetro | Descrição | Valores |
|---|---|---|
timeout | Tempos de timeout para cada etapa da transação | Vide Configurações de timeout |
beepVolume | Volume do beep do dispositivo durante a transação | Vide Volume do Beep |
theme | Cores e aparência da interface do SDK | Vide Configurações de tema |
showErrorScreen | Exibir telas de erro quando ocorrerem falhas | true (padrão) ou false |
Exemplo completo de configuração
// Credenciais
val credentials = Credentials(
clientId = "seu_client_id",
clientSecret = "seu_client_secret",
marketplace = "seu_marketplace",
seller = "seu_seller",
accessKey = "seu_access_key",
attestation = Attestation(
clientId = "seu_client_id_attestation",
clientSecret = "seu_client_secret_attestation"
)
)
// Timeout
val timeoutConfig = TimeoutConfig(
discoveryTimeout = 30_000, // Tempo para detectar o cartão (ms)
processingTimeout = 20_000, // Tempo de processamento do kernel (ms)
networkTimeout = 30_000, // Tempo de comunicação com o servidor (ms)
totalElapsedTimeout = 180_000 // Tempo total máximo da transação (ms)
)
// Tema
val theme = TapOnPhoneTheme(
backgroundColor = Color.parseColor("#FFFFFFFF"),
amountTextColor = Color.parseColor("#000000"),
paymentTypeTextColor = Color.parseColor("#FFFF0000"),
statusTextColor = Color.parseColor("#FF000000"),
statusBarColor = Color.parseColor("#FF7300")
)
// Aplicar configurações
TapOnPhone.setConfig(
ConfigParameters(
context = context,
credentials = credentials,
sdkConfig = SdkConfig(
timeout = timeoutConfig,
theme = theme,
beepVolume = BeepVolumeConfig(beepVolume = 0.5f),
showErrorScreen = true
)
)
)Nunca inclua credenciais diretamente no código-fonte. Armazene-as em variáveis de ambiente ou em um serviço de gerenciamento de segredos no backend e injete-as em tempo de execução.
Inicialização
Para utilizar o plugin, é necessário realizar a inicialização.
Maneiras de inicializar
Para que os pagamentos sejam realizados, é necessário que o dispositivo esteja ativado no nosso ambiente, o plugin realiza a ativação automaticamente na inicialização.
O tempo para ativação varia entre 2 e 3 segundos.
A inicialização também pode ser feita de forma automática ao iniciar um pagamento, caso o dispositivo não esteja inicializado. O processo será feito internamente pelo SDK e em seguida o pagamento será iniciado.
Métodos de inicialização disponíveis
A partir da 2.1.0, o método initialize() realiza a inicialização com callbacks. Na 2.3.1, foi adicionado o parâmetro opcional seller para suporte a multiseller (veja Multiseller).
initialize() - Método assíncrono com callbacks:
fun initialize(
seller: String? = null,
onSuccess: InitializeCallback,
onError: ErrorCallback,
onEvent: EventCallback,
)Tipos dos callbacks:
typealias InitializeCallback = (status: InitializationStatus) -> Unit
typealias ErrorCallback = (error: ErrorResponse) -> Unit
typealias EventCallback = (event: ApplicationEvent) -> UnitQuando usar:
- Método recomendado para inicialização
- Executa de forma assíncrona, não bloqueando a thread atual
- Permite acompanhar o progresso através dos callbacks
- Pode ser chamado de qualquer thread, incluindo a main thread
Exemplo de uso:
TapOnPhone.initialize(
onSuccess = { status ->
Log.d("Init", "Inicialização concluída com status: $status")
if (status == InitializationStatus.SUCCESS) {
// Continuar com o fluxo
}
},
onError = { error ->
// A partir da 2.3.1, o payload em error.error pode incluir kernelRedactLog: List<String> com mensagens de log redatadas da operação
Log.e("Init", "Erro na inicialização: ${error.type}")
when (error.type) {
ErrorResponse.ErrorType.Initialize -> {
// Erro durante a inicialização
}
ErrorResponse.ErrorType.Terminal -> {
// Erro do terminal
}
else -> {
// Outros tipos de erro
}
}
},
onEvent = { event ->
Log.d("Init", "Evento: ${event.name}")
// Acompanhar eventos em tempo real
}
)Importante: Na versão 2.1.0,
TapOnPhoneé um singleton (object), não é mais necessário instanciar a classe. Use diretamenteTapOnPhone.initialize(),TapOnPhone.pay(), etc.Ao prover o contexto no
setConfig(), é recomendado passar oapplicationContexte não o contexto da activity. ex.context.applicationContext
Configurando as credenciais
val credentials = Credentials(
clientId = {clientId},
clientSecret = {clientSecret},
marketplace = {marketplace},
seller = {seller},
accessKey = {accessKey},
// Opcional
attestation = Credentials.Attestation(
clientId = {clientId},
clientSecret = {clientSecret},
)
)
Dados de credenciais
clientId: id do cliente, recebido no processo de onboarding.
clientSecret: secret do cliente, recebido no processo de onboarding.
marketplace: marketplace recebido no processo de onboarding.
seller: seller recebido no processo de onboarding. A partir da 2.3.1, é opcional (String?): com null ou vazio no setConfig, ativa o modo multiseller e o seller efetivo deve ser passado em initialize(seller = ...). Veja Multiseller.
accessKey: accessKey recebido no processo de onboarding.
Credenciais de attestation (opcionais): Usado apenas se você quiser receber informações detalhadas sobre falhas de segurança do dispositivo.
- attestation.clientId: ClientId específico para a API de attestation, recebido no processo de onboarding.
- attestation.clientSecret: ClientSecret específico para a API de attestation, recebido no processo de onboarding
Para mais informações sobre attestation e como funciona a verificação de segurança, consulte a seção Attestation.
Importante: O campo
attestationé completamente opcional. Você só precisa preenchê-lo se quiser receber informações detalhadas sobre falhas de segurança do dispositivo durante o pagamento. Se não preencher, o SDK funcionará normalmente, mas não fornecerá detalhes específicos sobre erros de attestation.
Retorno com status da inicialização
O status da inicialização tem tipo InitializationStatus.
Se a inicialização for bem sucedida, o status será InitializationStatus.SUCCESS.
Se a inicialização falhar, o status será InitializationStatus.FAILED.
Se não foi chamado ainda, o status será InitializationStatus.NOT_INITIALIZED.
Se a inicialização estiver em andamento, o status será InitializationStatus.PROCESSING.
Se o retorno da
TapOnPhone.isApplicationInitAllowedforfalse, ignore a inicialização.
Exemplo completo de inicialização
// 1. Inicializar o kernel (na classe Application)
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
if (!TapOnPhone.kernelInitialize(this)) {
return
}
}
}
// 2. Configurar credenciais e SDK config
val credentials = Credentials(
clientId = "seu-client-id",
clientSecret = "seu-client-secret",
marketplace = "seu-marketplace",
seller = "seu-seller",
accessKey = "seu-access-key"
)
val sdkConfig = SdkConfig(
theme = ThemeConfig(...),
timeout = 30000,
beepVolume = BeepVolume.HIGH
)
TapOnPhone.setConfig(
ConfigParameters(
context = context.applicationContext,
credentials = credentials,
sdkConfig = sdkConfig
)
)
// 3. Inicializar (opcional - pode ser feito automaticamente no pagamento)
TapOnPhone.initialize(
onSuccess = { status ->
when (status) {
InitializationStatus.SUCCESS -> {
Log.d("Init", "SDK inicializado com sucesso")
// SDK pronto para uso
}
InitializationStatus.FAILED -> {
Log.e("Init", "Falha na inicialização")
}
InitializationStatus.PROCESSING -> {
Log.d("Init", "Inicialização em andamento")
}
InitializationStatus.NOT_INITIALIZED -> {
Log.w("Init", "SDK não inicializado")
}
}
},
onError = { error ->
Log.e("Init", "Erro: ${error.type}")
// Tratar erro conforme necessário
},
onEvent = { event ->
Log.d("Init", "Evento: ${event.name}")
// Acompanhar eventos em tempo real
}
)instanceId
A SDK também oferece a opção de obter o ID da instância do terminal. Para isso, você pode usar o método getInstanceId():
val instanceId = TapOnPhone.getInstanceId()O instanceId só estará disponível após a inicialização bem-sucedida do terminal.
Pagamento
Utilize o método TapOnPhone.pay() para realizar pagamentos por aproximação.
Se o dispositivo ainda não estiver inicializado, o SDK executará
initialize()eactivateSession()automaticamente antes do pagamento — o que pode adicionar alguns segundos de espera. Para evitar esse tempo, chame-os previamente.
Parâmetros do pagamento
O método pay() recebe um objeto PaymentRequest:
data class PaymentRequest(
val amount: Long,
val paymentType: PaymentType,
val installments: Int? = null,
val referenceId: String? = null,
val metadata: String? = null
)| Parâmetro | Tipo | Descrição | Exemplo |
|---|---|---|---|
amount | Long | Valor da transação em centavos. | 10000 (R$ 100,00) |
paymentType | PaymentType | Tipo do pagamento. | PaymentType.CREDIT |
installments | Int? | Quantidade de parcelas (opcional). | 2 |
referenceId | String? | Identificador próprio do parceiro (opcional, máx. 50 caracteres). | "237ab31-g99c-..." |
metadata | String? | Metadados em JSON fornecidos pelo parceiro (opcional, máx. 512 caracteres). | Vide Metadados |
Tipos de pagamento e metadados
PaymentType
enum class PaymentType {
CREDIT, // Crédito
DEBIT, // Débito
PIX // Pix
}Metadados em JsonObject
val metadata = buildJsonObject {
put("clientId", "1234")
put("name", "John Doe")
}Exemplo completo
Chamada do pay() com tratamento de sucesso, erro e eventos:
TapOnPhone.pay(
request = PaymentRequest(
amount = 10000, // R$ 100,00
paymentType = PaymentType.CREDIT,
installments = 2,
referenceId = UUID.randomUUID().toString(),
metadata = """
{
"clientId": "1234",
"name": "John Doe"
}
"""
),
onSuccess = { result ->
println("Pagamento Aprovado! Id: ${result.transactionId}")
println("Bandeira: ${result.cardBrand}")
println("Bin: ${result.binNumber}")
result.paymentDevice?.let {
println("Dispositivo: $it")
}
},
onError = { error ->
when (error.type) {
ErrorResponse.ErrorType.Payment -> {
val paymentError = error.error as? PaymentErrorResponse
paymentError?.let {
println("Pagamento negado - id: ${it.transactionId}")
println("Mensagem: ${it.message}")
println("Código: ${it.code}")
println("Descrição: ${it.description}")
// Informações de attestation (se disponíveis)
it.attestationStatus.forEach { attestation ->
Log.d("status", attestation.status)
Log.d("timestamp", attestation.timestampUtc)
attestation.outcomes.forEach { outcome ->
Log.d(
"outcome",
"moniker[${outcome.moniker}] code[${outcome.statusCode}] reason[${outcome.reason}]"
)
}
}
// Código de erro da Google Play Integrity API (se disponível)
it.integrityErrorCode?.let { integrityError ->
Log.e("Payment", "Integrity Error Code: ${integrityError.code}")
Log.e("Payment", "Integrity Error Description: ${integrityError.description}")
}
}
}
ErrorResponse.ErrorType.Session -> {
val sessionError = error.error as? SessionErrorResponse
sessionError?.let {
println("Erro de sessão: ${it.message}")
it.integrityErrorCode?.let { integrityError ->
Log.e("Payment", "Integrity Error Code: ${integrityError.code}")
}
}
}
else -> {
// Tratar outros tipos de erro
}
}
},
onEvent = { event ->
Log.d("Payment", "Evento: ${event.name}")
}
)Respostas
Sucesso — PaymentApprovedResponse
PaymentApprovedResponsedata class PaymentApprovedResponse(
val transactionId: String, // ID da transação na Zoop
val referenceId: String, // Seu ID para a transação, se fornecido no request
val cardBrand: CardBrand, // Bandeira do cartão
val binNumber: String, // Primeiros dígitos do cartão
val paymentDevice: String? // Tipo de dispositivo (CARD, WATCH), se disponível
)Erro — Tratamento por tipo
O callback onError recebe um ErrorResponse unificado. Verifique o type para acessar detalhes específicos:
onError = { error ->
when (error.type) {
ErrorResponse.ErrorType.Payment -> {
val paymentError = error.error as? PaymentErrorResponse
// Detalhes do erro de pagamento
}
ErrorResponse.ErrorType.Session -> {
val sessionError = error.error as? SessionErrorResponse
// Detalhes do erro de sessão
}
else -> {
// Outros tipos de erro
}
}
}Detalhes do erro — PaymentErrorResponse
PaymentErrorResponseO campo attestationStatus reflete o resultado da API de attestation quando aplicável; ele depende de existir instanceId do terminal. Ver Attestation e instanceId.
Contém informações detalhadas sobre a falha:
data class PaymentErrorResponse(
val transactionId: String?, // ID da transação na Zoop (null se falhou antes do envio)
val referenceId: String?, // Seu ID para a transação
val message: String?, // Mensagem de erro para o usuário
val code: Int?, // Código de erro
val source: String?, // Onde ocorreu o erro
val description: String?, // Descrição detalhada do erro
val errorSource: String?, // Origem: sdk, backend, kernel, acquirer
val operationType: String?, // Tipo de operação: pay, activation
val kernel: KernelError?, // Detalhes do erro do kernel
val attestationStatus: Array<AttestationStatus>, // Informações de attestation
val binNumber: String?, // Primeiros dígitos do cartão
val paymentDevice: String?, // Tipo de dispositivo (CARD, WATCH)
val integrityErrorCode: IntegrityErrorCode?, // Erro da Google Play Integrity API
val zoopDescription: String? // Descrição do erro retornada pela Zoop
)Objetos aninhados de PaymentErrorResponse
data class KernelError(
val code: Int, // Código do erro kernel
val name: String, // Nome do erro kernel
val description: String // Descrição do erro kernel
)data class AttestationStatus(
val outcomes: Array<Outcome>,
val status: String,
val timestampUtc: String
) {
data class Outcome(
val moniker: String,
val reason: String,
val statusCode: Int
)
}| Campo | Descrição |
|---|---|
transactionId | ID da transação na Zoop. Será null se a falha ocorreu antes do envio ao servidor. |
code | Código numérico do erro. Consulte a referência de erros para detalhes. |
errorSource | Indica a origem do erro: sdk, backend, kernel ou acquirer. |
kernel | Presente quando o erro é do kernel. Contém código, nome e descrição da falha. |
attestationStatus | Informações de attestation quando o erro está relacionado à segurança do dispositivo. |
integrityErrorCode | Código de erro da Google Play Integrity API, quando disponível. |
Updated 3 days ago
