Pular para o conteúdo principal
Versão: Guardian v0.1.0

Android

Integração nativa Android para o Surt Guardian SDK, distribuída via Maven (GitHub Packages). O SDK coleta sinais do dispositivo, executa verificações de integridade do dispositivo e retorna a decisão de risco do backend.

O SDK nunca guarda uma chave de API da Surt. Seu backend guarda a chave sp_live_* (somente no lado do servidor) e a troca por um JWT de curta duração que o app passa para verify().

Version

Este guia cobre o SDK v0.3.0, que também corrige a detecção de VPN.

Requisitos​

  • Android SDK 24+ (Android 7.0+)
  • Kotlin 1.9+
  • Gradle 8+
  • Java 17

1. Autenticação​

Adicione seu token de acesso ao gradle.properties do seu projeto (ou ~/.gradle/gradle.properties para todos os projetos):

gradle.properties
SURT_GITHUB_TOKEN=<YOUR_TOKEN>

Substitua <YOUR_TOKEN> pelo token fornecido pela Surt.

aviso

Não faça commit do gradle.properties com tokens no controle de versão. Adicione-o ao .gitignore, ou use o ~/.gradle/gradle.properties em vez disso.

2. Adicionar o Repositório​

No settings.gradle (ou settings.gradle.kts) do seu projeto, adicione o repositório Maven da Surt:

settings.gradle
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/surtTech/surt-guardian-sdk")
credentials {
username = "surt-customer"
password = settings.ext.find("SURT_GITHUB_TOKEN")
?: System.getenv("SURT_GITHUB_TOKEN") ?: ""
}
}
}
}
settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/surtTech/surt-guardian-sdk")
credentials {
username = "surt-customer"
password = providers.gradleProperty("SURT_GITHUB_TOKEN").orNull
?: System.getenv("SURT_GITHUB_TOKEN")
}
}
}
}

3. Adicionar a Dependência​

No build.gradle (ou build.gradle.kts) do seu app:

build.gradle
dependencies {
implementation 'com.surt.guardian:securitysdk:0.3.0'
}
build.gradle.kts
dependencies {
implementation("com.surt.guardian:securitysdk:0.3.0")
}

Core Library Desugaring​

Se o seu app tem como alvo minSdk < 26, garanta que o core library desugaring esteja habilitado no build.gradle do seu app:

build.gradle
android {
compileOptions {
coreLibraryDesugaringEnabled true
}
}

dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
}

4. Permissões do AndroidManifest​

Se você habilitar a coleta de localização, adicione estas permissões ao seu AndroidManifest.xml:

AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

Se você habilitar a coleta de informações do cartão SIM, adicione esta permissão:

AndroidManifest.xml
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

O SDK solicita automaticamente a permissão de localização durante a verificação quando collectLocation está habilitado. A caixa de diálogo de permissão padrão do Android aparece automaticamente - nenhum código adicional é necessário.

5. Inicializar o SDK​

Chame uma vez na inicialização do app (tipicamente na sua classe Application). Nenhuma chave de API é necessária no SDK - sua chave de API do lado do servidor nunca chega ao dispositivo.

MyApplication.kt
import android.app.Application
import com.surt.guardian.GuardianSDK
import com.surt.guardian.core.Environment
import com.surt.guardian.core.FailurePolicy
import com.surt.guardian.core.GuardianOptions
import com.surt.guardian.utils.Logger

class MyApplication : Application() {
override fun onCreate() {
super.onCreate()

GuardianSDK.initialize(
context = this,
options = GuardianOptions(
environment = Environment.Production,
failurePolicy = FailurePolicy.Fail,
logLevel = Logger.Level.WARN,
collectLocation = true // Habilita a coleta de GPS (opcional)
)
)
}
}

Ambientes​

AmbienteURL Base
Environment.Productionhttps://api.surt.com
Environment.Sandboxhttps://sandbox-api.surt.com

Opções de Coleta de Dados​

OpçãoPadrãoDescrição
collectLocationfalseCoordenadas GPS - requer permissões de localização no manifest
collectWifiInfofalseDetalhes de rede WiFi
collectSimCardInfofalseInformações de SIM/operadora - requer a permissão READ_PHONE_STATE
collectCameraInfofalseContagem/informações da câmera

Quando collectLocation está habilitado, o SDK solicita automaticamente ao usuário a permissão de localização na primeira vez que verify() é chamado. Se o usuário negar, o SDK continua sem dados de GPS - o backend pontua a transação com menos confiança.

6. Referência da Activity (para solicitações automáticas de permissão)​

Para que o SDK exiba a caixa de diálogo de permissão, ele precisa de uma referência à Activity atual. Chame setActivity() no ciclo de vida da sua Activity:

MainActivity.kt
override fun onResume() {
super.onResume()
GuardianSDK.getInstance().setActivity(this)
}

override fun onPause() {
super.onPause()
GuardianSDK.getInstance().setActivity(null)
}
observação

Se você não chamar setActivity(), o SDK ainda funciona, mas não pode solicitar permissões automaticamente. Você precisaria solicitar as permissões por conta própria antes de chamar verify().

7. Gerar um JWT a Partir do Seu Backend​

Antes de cada chamada verify(), seu app deve obter um GeolocationJwt de curta duração do seu próprio backend. Seu backend chama POST /geolocation/preflight com sua chave de API do lado do servidor e o contexto da transação, e então retorna o JWT para o app.

Exemplo de chamada do backend (seu servidor, não o app):

Backend preflight request
POST /geolocation/preflight
Authorization: Bearer sp_live_xxx
Content-Type: application/json

{
"customer_id": "user_abc123",
"transaction_type": "login",
"transaction_name": "User Login",
"name": "John Doe",
"email": "john@example.com"
}

A resposta do preflight retorna o token que seu app precisa:

Preflight response
{
"data": {
"token": "<jwt>"
}
}

Seu app obtém o JWT a partir do endpoint do seu próprio backend:

Fetch JWT from your backend
suspend fun fetchVerifyJwt(): String {
// Chame o endpoint do SEU PRÓPRIO backend, não a Surt diretamente
val response = httpClient.post("https://your-api.com/geolocation-jwt")
return response.body<YourJwtResponse>().jwt
}
Always fetch a fresh JWT per call

Sempre obtenha um JWT novo imediatamente antes de chamar verify(). Cada JWT é de uso único; reutilizar um JWT é rejeitado pelo backend.

8. Verificar uma Transação​

Chame em momentos sensíveis à segurança (login, pagamento, etc.). Obtenha um JWT do seu backend imediatamente antes de cada chamada.

Coroutines (recommended)
val jwt = fetchVerifyJwt()

val result = GuardianSDK.getInstance().verifySuspend(jwt = jwt)

if (result.allowed) {
// Prosseguir com a transação
} else {
// Tratar transação negada (result.riskLevel tem os detalhes)
}

Ou com um callback:

Callback
val jwt = fetchVerifyJwt()

GuardianSDK.getInstance().verify(jwt = jwt) { result ->
result.onSuccess { verification ->
if (verification.allowed) { /* prosseguir */ }
}
result.onFailure { error ->
// Tratar SurtError
}
}

Substituição de localização por chamada:

Per-call location override
// Forçar GPS ligado para esta chamada, independentemente do padrão de inicialização
val result = GuardianSDK.getInstance().verifySuspend(
jwt = jwt,
collectLocation = true
)

9. Collect (Servidor para Servidor, Opcional)​

collect() retorna um payload criptografado para o seu backend encaminhar à Surt. Nenhuma chamada direta do SDK para a Surt acontece durante o collect.

Collect payload
// Sem JWT - nenhuma chamada de rede para a Surt, sem IP no payload
val result = GuardianSDK.getInstance().collectSuspend()

// Com JWT - resolve o IP público do dispositivo (GET /geolocation/client-ip),
// e o embute no payload (melhor esforço)
val jwtForCollect = fetchCollectJwt()
val result = GuardianSDK.getInstance().collectSuspend(jwt = jwtForCollect)

// Envie result.payload para o seu backend
JWT is optional for collect

O JWT para collect() é opcional. Passe-o apenas para embutir o IP público do dispositivo. Diferente de verify(), um JWT para collect() pode ser reutilizado ou omitido - collect() não exige um JWT por chamada e não faz nenhuma chamada de rede à Surt quando nenhum JWT é fornecido.

Resultado da Verificação​

VerificationResult
data class VerificationResult(
val allowed: Boolean, // Decisão do backend - true = prosseguir
val riskLevel: RiskLevel, // LOW / MEDIUM / HIGH / BLOCKED / UNKNOWN
val sessionId: String, // ID da transação para referência de suporte
val errors: List<String>?, // Mensagens de erro do backend, se houver
val timestamp: Long // Timestamp da resposta (ms)
)

Tipos de Transação​

Estes valores são definidos pelo seu backend no campo transaction_type do preflight. Eles não são passados para verify() no app.

TipoCaso de uso
TransactionType.LOGINLogin do usuário
TransactionType.SIGN_UPCriação de nova conta
TransactionType.DEPOSITAdição de fundos
TransactionType.WITHDRAWALSaque de fundos

Solução de Problemas​

ErroCausaSolução
"Could not resolve com.surt.guardian:securitysdk"Token não configurado ou repositório não adicionadoVerifique o gradle.properties e o settings.gradle
"401 Unauthorized" de maven.pkg.github.comToken expirado ou inválidoObtenha um novo token da Surt
.notInitializedverify() chamado antes de initialize()Chame initialize() em Application.onCreate()
.invalidJwtJWT ausente, malformado ou rejeitadoGaranta que seu backend gere um JWT novo a cada chamada
.attestationFailedFalha na verificação de integridade do dispositivo - geralmente um JWT reutilizadoNunca reutilize um JWT entre chamadas verify()