---
title: "Play Integrity API no Android com Kotlin: proteja seu app em 2026"
url: "https://kotlin.dev.br/blog/play-integrity-api-android-kotlin-2026/"
markdown_url: "https://kotlin.dev.br/blog/play-integrity-api-android-kotlin-2026.MD"
description: "Implemente Play Integrity API no Android com Kotlin, gere tokens, valide no backend, interprete veredictos e reduza fraude, abuso e apps modificados hoje."
date: "2026-08-21"
author: "Karina Melo"
---

# Play Integrity API no Android com Kotlin: proteja seu app em 2026

Implemente Play Integrity API no Android com Kotlin, gere tokens, valide no backend, interprete veredictos e reduza fraude, abuso e apps modificados hoje.


**Resposta rápida:** use a **Play Integrity API** para o backend avaliar se uma requisição sensível provavelmente veio do app reconhecido pelo Google Play, instalado em um ambiente confiável e associado a uma conta ou licença compatível. No Android, prepare o provedor de token, gere um `requestHash` a partir da operação e solicite um token perto da ação protegida. Envie esse token ao seu servidor, decodifique-o pela API oficial do Google e aplique uma decisão baseada no risco. Nunca valide apenas no celular, nunca coloque credenciais de serviço no APK e não bloqueie todo usuário ao primeiro sinal: combine os veredictos com autenticação, rate limiting, histórico e uma resposta proporcional.

Apps Android que distribuem cupons, conteúdo pago, pontos, ingressos, benefícios, partidas competitivas ou operações financeiras atraem automação e adulteração. Um atacante pode modificar o APK, executar o app em um ambiente comprometido, repetir uma chamada capturada ou criar um cliente próprio para conversar diretamente com sua API.

A Play Integrity API ajuda a responder uma pergunta específica: **qual é o nível de confiança desta interação com o app?** Ela não é um antivírus e não transforma o cliente em ambiente seguro. Seu valor aparece quando o token é verificado no backend e participa de uma política em camadas.

Este guia explica a arquitetura recomendada, o fluxo da Standard API, o vínculo da requisição com `requestHash`, a validação no servidor, os principais veredictos, estratégias de resposta e cuidados de privacidade e operação. Para proteger também o binário distribuído, combine a solução com [R8 e ProGuard no Android](/blog/r8-proguard-android-kotlin-regras-shrinking-2026/) e com o guia de [segurança de dados locais em apps Kotlin](/blog/seguranca-dados-locais-android-kotlin-2026/).

## O que a Play Integrity API verifica?

A API produz um token assinado e criptografado com sinais calculados pelo ecossistema Google Play. Depois de decodificado no servidor, o resultado pode incluir grupos de veredictos sobre:

- **integridade do app:** se o pacote e o certificado reconhecidos correspondem à versão esperada;
- **integridade do dispositivo:** se o ambiente atende a níveis de confiança informados pelo Google;
- **licenciamento ou conta:** se a instalação e a conta têm relação esperada com a distribuição pelo Google Play, quando aplicável;
- **atividade recente do dispositivo:** sinal adicional para reconhecer volume anormal de solicitações;
- **risco de acesso por outros apps:** quando disponível e habilitado, ajuda a detectar situações como captura, sobreposição ou controle por aplicativos de risco;
- **Play Protect:** sinal opcional relacionado ao estado de proteção e a apps potencialmente nocivos.

A presença exata dos campos depende da configuração do projeto, da disponibilidade do recurso e da modalidade usada. Por isso, seu backend deve tolerar campos opcionais e tratar um veredicto ausente de maneira diferente de um veredicto explicitamente negativo.

Também é importante separar **sinal** de **decisão**. A API informa evidências. Quem decide liberar, pedir nova autenticação, limitar ou bloquear é o seu serviço.

## Standard API ou Classic API: qual escolher?

Para a maioria das integrações novas e frequentes, a **Standard API** é a escolha normal. Ela prepara um provedor de token com antecedência e depois solicita tokens sob demanda, reduzindo a latência percebida em ações do usuário.

A **Classic API** usa outro modelo de solicitação e pode aparecer em integrações existentes ou cenários específicos. Não escolha Classic apenas porque um tutorial antigo mostra `nonce`. Para um projeto novo, confirme a recomendação atual na documentação oficial e comece pela Standard API, a menos que seu caso exija explicitamente o fluxo clássico.

| Critério | Standard API | Classic API |
|---|---|---|
| Uso típico | ações recorrentes e integração moderna | compatibilidade e casos específicos |
| Preparação | provedor preparado antes da ação | solicitação mais autocontida |
| Vínculo da operação | `requestHash` | `nonce` |
| Estratégia | baixa latência depois do warm-up | avaliar conforme requisitos e quotas |

Não misture exemplos dos dois fluxos. Um `nonce` de Classic não é substituto automático para o `requestHash` de Standard.

## Arquitetura segura de ponta a ponta

O desenho recomendado tem quatro participantes:

1. **app Android:** solicita o token de integridade;
2. **seu backend:** recebe a operação e o token;
3. **serviço da Play Integrity:** decodifica o token para o backend autenticado;
4. **motor de decisão:** combina veredictos, usuário, valor, histórico e risco.

O fluxo para resgatar um cupom, por exemplo, pode ser:

1. o usuário autentica normalmente;
2. o app monta os dados essenciais do resgate;
3. o app calcula um hash determinístico desses dados;
4. a Standard API emite um token ligado ao hash;
5. o app envia operação e token ao backend;
6. o backend recalcula o mesmo hash;
7. o backend decodifica o token pelo Google;
8. o backend compara pacote, certificado, horário, hash e veredictos;
9. uma transação atômica registra o uso do cupom e impede repetição.

A Play Integrity API não substitui a última etapa. Se dois requests válidos conseguem consumir o mesmo benefício, existe uma falha de concorrência no servidor, não uma falha de atestado.

## Configuração inicial no Google Play Console

Antes do código, faça a configuração administrativa:

1. publique ou associe o app no Google Play Console;
2. vincule o projeto do Google Cloud usado pela integração;
3. habilite a Play Integrity API no projeto correto;
4. configure as respostas e os sinais necessários no Play Console;
5. crie a identidade de serviço usada **somente pelo backend**;
6. conceda apenas as permissões necessárias;
7. registre ambientes e contas de teste para validar respostas antes de bloquear produção.

Guarde o **número do projeto do Google Cloud**, pois ele é usado na preparação do provedor Standard. Não confunda esse número com o ID textual do projeto.

As credenciais do backend devem ficar em um secret manager ou identidade de workload. Nunca adicione um JSON de service account em `res/raw`, `assets`, BuildConfig ou repositório Git. Tudo que entra no APK pode ser extraído.

## Dependência no Gradle Kotlin DSL

Use a versão estável atual da biblioteca no catálogo de versões do projeto:

```toml
# gradle/libs.versions.toml
[versions]
play-integrity = "<versao-estavel>"

[libraries]
play-integrity = {
    module = "com.google.android.play:integrity",
    version.ref = "play-integrity"
}
```

```kotlin
// app/build.gradle.kts
dependencies {
    implementation(libs.play.integrity)
}
```

Confira a versão na documentação e no repositório oficial antes de atualizar. Centralizar dependências em um [Version Catalog do Gradle](/blog/gradle-version-catalog-kotlin-2026/) facilita revisão e rollback.

## Preparando o provedor Standard

Prepare o provedor antes do momento crítico, mas não necessariamente no primeiro milissegundo do processo. Uma boa opção é iniciar quando o usuário entra na área autenticada ou se aproxima de uma funcionalidade protegida.

```kotlin
class IntegrityTokenProvider(
    context: Context,
    private val cloudProjectNumber: Long,
) {
    private val manager = IntegrityManagerFactory
        .createStandard(context.applicationContext)

    @Volatile
    private var provider: StandardIntegrityManager.StandardIntegrityTokenProvider? = null

    suspend fun prepare() {
        provider = manager.prepareIntegrityToken(
            PrepareIntegrityTokenRequest.builder()
                .setCloudProjectNumber(cloudProjectNumber)
                .build(),
        ).await()
    }

    suspend fun tokenFor(requestHash: String): String {
        val current = provider ?: error("Provedor de integridade não preparado")

        return current.request(
            StandardIntegrityTokenRequest.builder()
                .setRequestHash(requestHash)
                .build(),
        ).await().token()
    }
}
```

O exemplo usa a extensão `await()` de integração entre Tasks e coroutines. Adapte imports e tratamento de erros às versões adotadas pelo projeto. Mantenha um único responsável pelo provider e evite preparar uma nova instância a cada clique.

Se o processo for encerrado, o objeto em memória desaparece. Seu código deve conseguir preparar novamente. Não persista token pronto em DataStore ou banco local para reutilização futura.

## Como criar um `requestHash` corretamente

O `requestHash` liga o token a uma operação. Sem esse vínculo, um token obtido para uma ação de baixo risco pode ser reaproveitado em outro request.

Escolha apenas campos estáveis e relevantes. Para um resgate:

```kotlin
data class CouponRedemption(
    val couponId: String,
    val userId: String,
    val campaignId: String,
)

fun CouponRedemption.canonicalPayload(): String = buildString {
    append("couponId=").append(couponId)
    append("&userId=").append(userId)
    append("&campaignId=").append(campaignId)
}

fun sha256Base64Url(value: String): String {
    val digest = MessageDigest.getInstance("SHA-256")
        .digest(value.toByteArray(StandardCharsets.UTF_8))

    return Base64.encodeToString(
        digest,
        Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING,
    )
}
```

Uso:

```kotlin
val operation = CouponRedemption(
    couponId = couponId,
    userId = session.userId,
    campaignId = campaignId,
)

val requestHash = sha256Base64Url(operation.canonicalPayload())
val integrityToken = integrityTokenProvider.tokenFor(requestHash)

api.redeemCoupon(
    RedeemCouponRequest(
        couponId = couponId,
        campaignId = campaignId,
        integrityToken = integrityToken,
    ),
)
```

O backend precisa gerar **exatamente a mesma representação canônica**. JSON serializado livremente pode mudar ordem de campos, espaços ou formato numérico. Defina o contrato e crie testes com vetores conhecidos para Android e servidor.

Não inclua senha, token de sessão ou dado pessoal bruto no hash. Hash não é criptografia de conteúdo sensível e pode permitir correlação. Use identificadores internos mínimos e mantenha a comunicação sob HTTPS.

## Validação obrigatória no backend

O servidor recebe o token como dado não confiável. Em seguida, chama o endpoint oficial de decodificação da Play Integrity usando uma identidade autorizada. A URL, a biblioteca cliente e os nomes dos tipos podem evoluir; prefira o SDK oficial ou a especificação atual em vez de montar uma integração antiga por cópia.

Um serviço Kotlin pode separar decodificação de política:

```kotlin
data class IntegrityEvidence(
    val packageName: String?,
    val requestHash: String?,
    val timestampMillis: Long?,
    val appRecognized: Boolean,
    val deviceLabels: Set<String>,
    val licensed: Boolean?,
)

interface PlayIntegrityDecoder {
    suspend fun decode(packageName: String, token: String): IntegrityEvidence
}

class RedeemCouponUseCase(
    private val decoder: PlayIntegrityDecoder,
    private val policy: IntegrityPolicy,
    private val coupons: CouponRepository,
) {
    suspend fun execute(command: RedeemCouponCommand): RedeemResult {
        val evidence = decoder.decode(
            packageName = EXPECTED_PACKAGE,
            token = command.integrityToken,
        )

        val expectedHash = sha256Base64Url(command.canonicalPayload())
        val decision = policy.evaluate(evidence, expectedHash, command)

        if (!decision.allow) {
            return RedeemResult.Rejected(decision.publicReason)
        }

        return coupons.redeemAtomically(command)
    }
}
```

Depois de decodificar, valide no mínimo:

- package name esperado;
- reconhecimento e certificado/versão do app conforme sua política;
- `requestHash` igual ao recalculado;
- timestamp dentro de uma janela curta e coerente;
- nível de integridade do dispositivo exigido para a operação;
- licenciamento, quando relevante para o produto;
- autenticação e autorização do usuário;
- idempotência e regras de negócio no banco.

Não confie em um booleano enviado pelo app como `isDeviceTrusted`. A única evidência útil para a decisão é a resposta decodificada no servidor.

## Como interpretar os veredictos sem bloquear usuários legítimos

Uma política binária global costuma causar problemas. Nem toda tela exige a mesma confiança.

| Ação | Resposta razoável |
|---|---|
| Ler conteúdo público | normalmente não solicitar token |
| Fazer login | observar risco, aplicar rate limiting e desafio adicional |
| Resgatar cupom único | exigir app reconhecido, hash válido e política de device definida |
| Alterar dado crítico | pedir reautenticação e avaliar integridade |
| Sincronizar preferência | permitir com limites, mesmo sob sinal inconclusivo |

Crie três resultados internos, por exemplo:

- **permitir:** sinais suficientes e regras atendidas;
- **desafiar ou limitar:** resposta ausente, transitória ou risco moderado;
- **negar:** token inválido, hash divergente, app adulterado ou replay confirmado.

Isso oferece uma saída para aparelhos com problemas temporários, indisponibilidade de rede, Google Play desatualizado ou configuração ainda em rollout. A mensagem ao usuário também deve ser útil: “Não foi possível confirmar a segurança deste dispositivo. Atualize o Google Play e tente novamente” é melhor que “erro -17”.

## Tratamento de erros no app

Falhas podem ocorrer ao preparar o provider ou solicitar o token. Modele-as como parte do fluxo:

```kotlin
sealed interface IntegrityTokenResult {
    data class Success(val token: String) : IntegrityTokenResult
    data class Retryable(val cause: Throwable) : IntegrityTokenResult
    data class Unavailable(val cause: Throwable) : IntegrityTokenResult
}

suspend fun requestIntegrityToken(hash: String): IntegrityTokenResult =
    try {
        IntegrityTokenResult.Success(
            integrityTokenProvider.tokenFor(hash),
        )
    } catch (error: IntegrityServiceException) {
        // Classifique pelos códigos documentados na versão usada.
        IntegrityTokenResult.Retryable(error)
    } catch (error: Exception) {
        IntegrityTokenResult.Unavailable(error)
    }
```

Não faça retry infinito. Use backoff, limite de tentativas e cancelamento ligado ao ciclo de vida. Se a tela fecha, a coroutine não precisa continuar. O guia de [coroutines e structured concurrency](/blog/coroutines-avancadas-structured-concurrency-kotlin/) ajuda a evitar jobs órfãos.

Registre métricas por etapa — preparação, emissão, chamada ao backend, decodificação e decisão — sem gravar o token inteiro. Tokens e payloads de segurança não devem aparecer em logs, analytics ou relatórios de crash.

## Replay, idempotência e vínculo com a sessão

`requestHash` dificulta usar o token em outra operação, mas sua API ainda precisa de controles de replay:

- chave de idempotência por comando;
- janela curta para aceitar o timestamp;
- estado transacional no servidor;
- limite de tentativas por usuário, IP, dispositivo lógico e recurso;
- vínculo entre usuário autenticado e identificador incluído no payload canônico;
- registro de decisões para investigação, com retenção mínima.

Para pagamentos, inventário, pontos e cupons, a garantia final deve estar no banco. Use uma constraint única ou atualização condicional dentro de transação. Segurança do cliente não conserta uma operação não atômica.

## Testes antes de ativar bloqueio

Implemente em etapas:

1. **modo observação:** decodifique e registre apenas categorias agregadas;
2. **dashboard:** meça taxa de sucesso, ausência e divergência por versão do app;
3. **testes controlados:** valide instalação pela Play Store, trilhas internas e cenários previstos;
4. **política moderada:** aplique desafio ou limite nas ações de maior risco;
5. **enforcement:** bloqueie apenas combinações de sinais bem compreendidas;
6. **rollback:** mantenha feature flag no backend para relaxar a política sem publicar outro APK.

Teste também rotação, processo recriado, troca de conta, rede instável e múltiplos cliques. Um teste feliz em aparelho de desenvolvimento não representa a variedade da base Android.

Feature flags ajudam a separar deploy de ativação, mas a decisão precisa continuar no servidor. Veja como organizar esse controle no guia de [feature flags com Kotlin no Android e backend](/blog/feature-flags-kotlin-android-backend-2026/).

## Erros comuns na implementação

### Validar o token no próprio app

O cliente pode ser modificado para pular a validação. A decisão real pertence ao backend.

### Reutilizar um token para várias operações

Solicite o token perto da ação e vincule-o ao conteúdo com `requestHash`. Não crie um “token da sessão” genérico.

### Colocar service account no APK

Credencial embutida é credencial vazada. Apenas o servidor deve autenticar a chamada de decodificação.

### Bloquear pelo modelo do aparelho ou por root detectado localmente

Heurísticas locais podem complementar telemetria, mas são contornáveis e geram falsos positivos. Use os veredictos oficiais e uma política proporcional.

### Logar token ou resposta completa

Além do risco de segurança e privacidade, logs podem parar em serviços de terceiros. Armazene apenas o necessário para auditoria, de preferência categorias agregadas e IDs de decisão.

### Tratar Play Integrity como única defesa

Continue usando autenticação, autorização, TLS, rate limiting, detecção de automação, validação de entrada e controles transacionais. Para observar abuso e falhas sem expor dados, integre uma estratégia de [Crashlytics e diagnóstico de ANR](/blog/firebase-crashlytics-anr-android-kotlin-2026/) com regras explícitas de sanitização.

## Checklist de produção

Antes de ativar enforcement, confirme:

- [ ] projeto Cloud e app do Play Console estão vinculados corretamente;
- [ ] backend usa identidade mínima e credenciais fora do APK;
- [ ] Standard API foi escolhida conscientemente;
- [ ] provider é preparado e recriado quando necessário;
- [ ] cada ação sensível recebe um `requestHash` determinístico;
- [ ] servidor recalcula e compara o hash;
- [ ] package, app, timestamp e device verdict são avaliados;
- [ ] tokens nunca aparecem em logs ou analytics;
- [ ] retries têm backoff e limite;
- [ ] banco impede replay e consumo duplicado;
- [ ] política distingue permitir, desafiar e negar;
- [ ] métricas foram observadas antes do bloqueio;
- [ ] feature flag permite rollback rápido;
- [ ] mensagens orientam o usuário sem revelar regras antifraude.

## Conclusão

A Play Integrity API é mais útil quando deixa de ser tratada como um selo mágico e passa a ser uma fonte de sinais para o backend. O app Kotlin prepara o provider, cria um hash da operação e solicita o token. O servidor decodifica a resposta pelo Google, verifica se o token corresponde ao request e combina os veredictos com identidade, histórico, limites e regras transacionais.

Comece por uma ação realmente valiosa, rode em modo observação e conheça a distribuição dos resultados antes de negar acesso. Depois, aplique respostas graduais. Essa abordagem reduz fraude sem transformar diferenças legítimas do ecossistema Android em bloqueios indiscriminados.

Para completar a camada de distribuição, revise [assinatura, shrinking e obfuscação com R8/ProGuard](/blog/r8-proguard-android-kotlin-regras-shrinking-2026/). Para operações protegidas que chamam serviços externos, aplique também [timeouts, retries e circuit breaker no Ktor Client](/blog/ktor-client-resiliente-timeout-retry-circuit-breaker-2026/) — disponibilidade e segurança precisam funcionar juntas.

## Referências oficiais

- [Visão geral da Play Integrity API](https://developer.android.com/google/play/integrity)
- [Standard API: fluxo e solicitações](https://developer.android.com/google/play/integrity/standard)
- [Veredictos da Play Integrity API](https://developer.android.com/google/play/integrity/verdicts)
- [Tratamento de erros da Play Integrity API](https://developer.android.com/google/play/integrity/error-codes)
- [Práticas recomendadas para Play Integrity](https://developer.android.com/google/play/integrity/setup)
