7 - Motivo do Attestation
Entenda o processo de attestation do SDK Tap to Pay — verificação de segurança, configuração de credenciais e tratamento de erros.
O attestation é uma verificação de segurança que garante que o SDK está sendo executado em um dispositivo seguro e confiável.
Como funciona?
O attestation ocorre automaticamente antes de cada transação. Se a verificação falhar, o pagamento é interrompido.
pay() → Attestation → ✅ Aprovado → Transação inicia
→ ❌ Reprovado → Pagamento interrompido (onError)Aviso
O erro de attestation pode ocorrer já no momento da criação do terminal. Nesse fluxo, não temos informações suficientes para identificar a causa exata do problema.
É possível reconhecer que se trata de um erro de attestation por meio do código retornado. No entanto, isso apenas indica a categoria do erro — não o motivo específico. Ou seja, saber que é um erro de attestation é diferente de saber o que causou esse erro, e neste ponto não temos visibilidade sobre essa causa.
Além disso, em alguns cenários, pode não haver detalhes adicionais disponíveis para consulta. Portanto, o integrador não deve assumir que todo erro de attestation virá acompanhado de informações completas para diagnóstico.
O que é verificado
| Verificação | Descrição |
|---|---|
| 🔒 Integridade do dispositivo | Dispositivo em modo seguro (sem root, sem debug). |
| 📦 Validação do aplicativo | Aplicativo instalado através da Google Play Store. |
| 🛡️ Verificação de segurança | Ambiente de execução confiável. |
| 🏷️ Validação de versão | Version code do aplicativo liberado e aprovado. |
Validações manuais do dispositivo
Antes de iniciar transações, verifique se o dispositivo atende aos requisitos do Play Integrity. Use o checklist abaixo para identificar e corrigir problemas preventivamente.
Checklist de verificação
Execute estas verificações no dispositivo para garantir que ele passará no attestation:
| # | Verificação | Como validar | Resultado esperado |
|---|---|---|---|
| 1 | Android 6+ | Settings > About phone > Android version | Versão 6.0 (API 23) ou superior. |
| 2 | Google Play Store instalada e atualizada | Settings > Apps > Google Play Store > App details | Play Store presente e na versão mais recente. |
| 3 | Google Play Services atualizado | Settings > Apps > Google Play services | Google Play Services presente e atualizado. |
| 4 | Conta Google configurada | Settings > Accounts | Pelo menos uma conta Google ativa no dispositivo. |
| 5 | Bootloader bloqueado | Settings > About phone > Status (varia por fabricante) | Bootloader no estado locked. Dispositivos com bootloader desbloqueado falham na verificação de integridade. |
| 6 | Dispositivo sem root | Verificar com apps como Root Checker | Dispositivo não rooteado. Root compromete a integridade do dispositivo. |
| 7 | ROM oficial do fabricante | Settings > About phone > Build number | ROM padrão do fabricante (não custom ROM como LineageOS, Pixel Experience, etc.). |
| 8 | Modo de desenvolvedor desativado | Settings > Developer options | Opções de desenvolvedor desativadas ou, no mínimo, USB debugging desligado. |
| 9 | Conexão com a internet | Testar acesso à internet | Conexão ativa. O Play Integrity precisa se comunicar com os servidores do Google para emitir o veredito. |
| 10 | App instalado pela Play Store | Settings > Apps > [Seu App] > App details in store | O app deve ter sido instalado pela Google Play Store, não por sideloading (APK manual). |
| 11 | Google Play Protect ativado | Play Store > Menu > Play Protect | Play Protect ativado e sem ameaças detectadas. |
Dispositivos que não passam nas verificações 5, 6 ou 7 não conseguirão realizar transações com o SDK Tap to Pay, pois falham na verificação de integridade do dispositivo (
DeviceIntegrity_NotMetouDeviceIntegrity_NotStrong).
Níveis de integridade do Play Integrity
O Google Play Integrity classifica dispositivos em três níveis. O SDK Tap to Pay exige no mínimo o nível MEETS_DEVICE_INTEGRITY:
| Nível | Label | Requisitos |
|---|---|---|
| 🟢 Básico | MEETS_BASIC_INTEGRITY | O dispositivo passa em verificações básicas de integridade do sistema. Pode incluir dispositivos com bootloader desbloqueado ou ROMs não certificadas. Não é suficiente para o SDK. |
| 🟡 Dispositivo | MEETS_DEVICE_INTEGRITY | O dispositivo é certificado pelo Google, possui Google Play Services e atende aos requisitos de compatibilidade do Android. Nível mínimo exigido pelo SDK. |
| 🔵 Forte | MEETS_STRONG_INTEGRITY | O dispositivo possui garantias de integridade baseadas em hardware (hardware-backed key attestation) e secure boot. Requer atualizações de segurança recentes (último ano) em Android 13+. Nível recomendado para transações financeiras. |
Validação programática
Adicione uma verificação antes de chamar pay() para orientar o usuário caso o dispositivo não atenda aos requisitos básicos:
import android.os.Build
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.common.ConnectionResult
fun checkDeviceReadiness(context: Context): List<String> {
val issues = mutableListOf<String>()
// Verificar versão do Android (mínimo API 23)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
issues.add("Android 6.0 (API 23) ou superior é necessário. Versão atual: ${Build.VERSION.RELEASE}")
}
// Verificar Google Play Services
val googleApiAvailability = GoogleApiAvailability.getInstance()
val resultCode = googleApiAvailability.isGooglePlayServicesAvailable(context)
if (resultCode != ConnectionResult.SUCCESS) {
if (googleApiAvailability.isUserResolvableError(resultCode)) {
issues.add("Google Play Services precisa ser atualizado.")
} else {
issues.add("Google Play Services não está disponível neste dispositivo.")
}
}
// Verificar conectividade
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as android.net.ConnectivityManager
val activeNetwork = connectivityManager.activeNetwork
if (activeNetwork == null) {
issues.add("Sem conexão com a internet. O attestation requer acesso à rede.")
}
return issues
}
// Uso antes de chamar pay()
val issues = checkDeviceReadiness(context)
if (issues.isNotEmpty()) {
issues.forEach { issue ->
Log.w("PreAttestation", "⚠️ $issue")
}
// Exibir mensagem ao usuário com os problemas encontrados
} else {
Log.d("PreAttestation", "✅ Dispositivo pronto para attestation")
// Prosseguir com TapOnPhone.pay(...)
}Essa verificação programática cobre apenas os pré-requisitos básicos. Verificações mais profundas — como bootloader, root e integridade da ROM — são realizadas diretamente pela Google Play Integrity API durante o attestation automático do SDK.
Configuração das credenciais de Attestation
Para receber informações detalhadas sobre falhas de segurança, configure as credenciais de attestation no parâmetro attestation da classe Credentials durante o setConfig:
val credentials = Credentials(
clientId = "seu_client_id",
clientSecret = "seu_client_secret",
marketplace = "seu_marketplace",
seller = "seu_seller",
accessKey = "seu_access_key",
// Credenciais de attestation (opcionais)
attestation = Attestation(
clientId = "seu_client_id_attestation",
clientSecret = "seu_client_secret_attestation"
)
)
TapOnPhone.setConfig(
ConfigParameters(
context = context,
credentials = credentials,
sdkConfig = sdkConfig
)
)O campo
attestationé completamente opcional. Sem ele, o SDK funciona normalmente, mas não fornece detalhes específicos sobre erros de attestation. Preencha apenas se quiser receber informações detalhadas sobre falhas de segurança do dispositivo.
As credenciais de attestation são diferentes das credenciais principais do SDK. Elas são recebidas durante o onboarding e são específicas para a Cliente Role. Consulte a seção Onboarding para mais detalhes sobre os tipos de credenciais.
API de Status de Attestation
A API de Status de Attestation por Instance ID fornece informações sobre as verificações realizadas contra um identificador de instância específico, permitindo identificar e gerenciar rapidamente problemas de attestation.
Os dados de attestation ficam disponíveis por apenas 1 hora após a consulta.
Quando o Attestation falha
Quando o attestation falha, o SDK executa automaticamente os seguintes passos:
① Identifica o erro → ② Consulta a API → ③ Retorna detalhes no onError
DEVICE_STATE_FAILURE de attestation attestationStatus + integrityErrorCodeTratamento no código
O callback onError recebe os detalhes da falha de attestation:
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}")
},
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
it.integrityErrorCode?.let { integrityError ->
Log.e("Payment", "Code: ${integrityError.code}")
Log.e("Payment", "Description: ${integrityError.description}")
}
}
}
else -> {
// Tratar outros tipos de erro
}
}
},
onEvent = { event ->
Log.d("Payment", "Evento: ${event.name}")
}
)Tabela de erros de Attestation
Códigos retornados no campo attestationStatus.outcomes:
| Código | Reason | Descrição |
|---|---|---|
| 100 | ERROR_UnableToDecode | Não foi possível decodificar a resposta. |
| 101 | RequestDetail_NameNotMatch | O nome na requisição não confere. |
| 102 | RequestDetail_NonceNotMatch | O nonce na requisição não confere. |
| 103 | RequestDetail_InvalidTimeStamp | Timestamp da requisição inválido. |
| 104 | AppIntegrity_Unevaluated | A integridade do app não foi avaliada. |
| 105 | AppIntegrity_UnrecognizedVersion | Versão do app não reconhecida pela Play Store. |
| 106 | AppIntegrity_PackageNameNotMatch | O packageName não confere com o esperado. |
| 107 | AppIntegrity_CertNotMatch | O certificado de assinatura não confere. |
| 108 | AppIntegrity_VersionCodeNotMatch | O versionCode não confere com o liberado. |
| 109 | AppIntegrity_UnsuccessfulResult | A verificação de integridade do app falhou. |
| 110 | DeviceIntegrity_NotMet | O dispositivo não atende aos requisitos de integridade. |
| 111 | DeviceIntegrity_NotStrong | O nível de integridade do dispositivo não é suficiente. |
| 112 | AccountDetails_Unlicensed | A conta Google não possui licença para o app. |
| 113 | AccountDetails_Unevaluated | Os detalhes da conta não foram avaliados. |
| 114 | AccountDetails_UnsuccessfulResult | A verificação da conta falhou. |
Integrity Error Codes
A Google Play Integrity API retorna códigos de erro quando não é possível verificar a integridade do dispositivo ou da aplicação. O SDK captura esses erros e os disponibiliza através do campo integrityErrorCode nos objetos SessionErrorResponse e PaymentErrorResponse.
Códigos de erro
| Código | Erro | Descrição |
|---|---|---|
| -1 | API_NOT_AVAILABLE | A Integrity API não está disponível. A Play Store pode estar desatualizada ou não instalada. |
| -2 | PLAY_STORE_NOT_FOUND | O app da Play Store não foi encontrado no dispositivo. |
| -3 | NETWORK_ERROR | Erro de conexão com a internet. Verifique a conexão do dispositivo. |
| -4 | PLAY_STORE_ACCOUNT_NOT_FOUND | Nenhuma conta Google encontrada no dispositivo. |
| -5 | APP_UID_MISMATCH | O UID da aplicação chamadora não corresponde ao UID do pacote. |
| -6 | TOO_MANY_REQUESTS | Muitas requisições feitas em curto período. Tente novamente mais tarde. |
| -7 | CANNOT_BIND_TO_SERVICE | Não foi possível conectar ao serviço da Play Store. Pode haver outra versão do serviço em execução. |
| -8 | GOOGLE_SERVER_UNAVAILABLE | Servidores do Google indisponíveis temporariamente. |
| -9 | PLAY_STORE_VERSION_OUTDATED | A versão da Play Store está desatualizada. Atualize a Play Store. |
| -10 | APP_NOT_INSTALLED | A aplicação não está instalada (pode ocorrer em emuladores ou ambientes de teste mal configurados). |
| -12 | CLIENT_TRANSIENT_ERROR | Erro transiente no cliente. Tente novamente com backoff exponencial. |
| -100 | INTERNAL_ERROR | Erro interno desconhecido. |
Como tratar
Trate esses erros exibindo mensagens apropriadas ao usuário. Os cenários mais comuns:
| Código | Cenário | Orientação ao usuário |
|---|---|---|
| -3 | Sem conexão | Solicite que o usuário verifique a internet. |
| -4 | Sem conta Google | Solicite que o usuário faça login com uma conta Google no dispositivo. |
| -9 | Play Store desatualizada | Solicite que o usuário atualize a Play Store. |
| -6 | Muitas requisições | Aguarde alguns minutos e tente novamente. |
| -8 | Servidores indisponíveis | Tente novamente mais tarde. |
Para erros internos ou desconhecidos (como
-100), oriente o usuário a tentar novamente mais tarde. Se o problema persistir, colete os logs do SDK e entre em contato com o suporte Zoop.
Onde o campo attestationStatus aparece
attestationStatus apareceO campo attestationStatus existe apenas nos objetos públicos abaixo:
SessionErrorResponse— usado emErrorResponse.Sessionquando a falha ocorre no contexto de sessão (OperationType.session).PaymentErrorResponse— usado emErrorResponse.Paymentquando a falha ocorre no pagamento (OperationType.pay).
ErrorResponse | Tipo de erros | Campo attestationStatus |
|---|---|---|
Initialize | TapOnPhoneError | Não |
Terminal | TapOnPhoneError | Não |
Session | SessionErrorResponse | Sim |
Payment | PaymentErrorResponse | Sim |
Outras variantes de ErrorResponse (ex.: Pix, SMS, Default) usam outros tipos em erros. O que esta página chama de status remoto de attestation restringe-se às linhas Sim da tabela. Nas linhas Não, TapOnPhoneError em Initialize e Terminal mantém mensagem, códigos, kernel e demais metadados; apenas não há enriquecimento com o array retornado pela API {instanceId}/attestation-status.
Falha antes de existir instância no backend
Se a criação do terminal falhar antes de haver instanceId (instância ainda não criada ou ID indisponível), o SDK não consegue montar a chamada de status de attestation. O erro exposto nesses casos segue como TapOnPhoneError (Initialize ou Terminal), sem attestationStatus. Isso se alinha à regra de que o instanceId só fica disponível após inicialização bem-sucedida do terminal ver Inicialização .
SessionErrorResponse
SessionErrorResponsedata class SessionErrorResponse(
override val message: String? = "session error",
val code: Int?,
val attestationStatus: Array<AttestationStatus>?,
val kernel: KernelError?,
val integrityErrorCode: IntegrityErrorCode?,
) : ErrorBase(message) {
data class KernelError(
val code: Int,
val name: String,
val description: String,
)
}PaymentErrorResponse
PaymentErrorResponseTrecho ilustrativo — a classe pública inclui também identificação da transação, fonte do erro, integridade e outros campos; o campo relevante para attestation remota é attestationStatus.
data class PaymentErrorResponse(
val transactionId: String?,
val referenceId: String?,
override val message: String? = "payment error",
val code: Int?,
val source: String?,
val description: String?,
val errorSource: String?,
val operationType: String?,
val kernel: KernelError?,
val attestationStatus: Array<AttestationStatus>,
) : ErrorBase(message) {
data class KernelError(
val code: Int,
val name: String,
val description: String,
)
}Array vazio
O SDK pode preencher attestationStatus com um array vazio quando o enriquecimento não se aplica ou quando a consulta à API de attestation não é concluída com sucesso internamente. O integrador deve tratar a presença do campo como informativa, não como garantia de que uma análise remota foi obtida.
Updated about 6 hours ago
