Collect (Servidor para Servidor)
Dois caminhos de verificação
O Guardian SDK oferece duas formas de verificar dispositivos. Escolha com base na sua arquitetura:
verify() | collect() | |
|---|---|---|
| Quem chama a Surt | O SDK (a partir do dispositivo) | Seu backend (servidor para servidor) |
| Chamadas de rede do SDK | Sim | Não |
| Seu backend envolvido | Não | Sim |
| Resposta | VerificationResult (permitido/negado) | CollectResult (payload criptografado) |
| Ideal para | Integração simples, decisões no frontend | Decisões no backend, lógica personalizada, requisitos de auditoria |
Fluxo de verify()
App → SDK.verify() → Surt backend → risk decision → App
O SDK chama a Surt diretamente e retorna allowed: true/false. Seu aplicativo age sobre a decisão imediatamente.
Fluxo de collect()
App → SDK.collect() → encrypted payload → App → Your backend → Surt /evaluate → Your backend → App
O SDK coleta dados do dispositivo e os criptografa localmente. Zero chamadas de rede para a Surt. Seu backend envia o payload para o endpoint de avaliação da Surt, recebe a avaliação de risco completa e decide o que retornar ao seu aplicativo.
Quando usar collect()
- Seu backend precisa dos dados de risco antes de responder ao cliente
- Você quer combinar o risco do dispositivo com sua própria lógica de negócio no lado do servidor
- Você precisa de controle total sobre o que o cliente vê
- Conformidade exige que todas as chamadas a terceiros se originem da sua infraestrutura
Uso
1. Coletar no dispositivo
import { useGuardian } from '@surtai/guardian-rn';
function PaymentScreen() {
const { collect, setCustomer } = useGuardian();
const handlePayment = async () => {
setCustomer('user_123', 'John Doe', 'john@example.com');
const { payload } = await collect('withdrawal', 'User Payment');
// Send payload to YOUR backend
const response = await fetch('https://your-api.com/verify-device', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
payload,
amount: 500,
currency: 'USD',
}),
});
const result = await response.json();
// Your backend already made the risk decision
};
}
2. Chamar a Surt a partir do seu backend
Seu backend recebe o payload criptografado do aplicativo e então chama o endpoint de avaliação da Surt:
POST https://api.surt.com/geolocation/transactions/evaluate
Content-Type: application/json
Authorization: Bearer YOUR_SURT_API_KEY
{
"customer_id": "user_123",
"transaction_type": "withdrawal",
"transaction_name": "User Payment",
"payload": {
"type": "encrypted",
"data": "<payload from SDK>"
},
"config": {
"response": {
"address": { "type": "include" }
}
}
}
Campos da requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
customer_id | string | Sim | Seu identificador de usuário |
transaction_type | string | Sim | login, sign_up, deposit ou withdrawal |
transaction_name | string | Não | Rótulo legível para humanos |
payload.type | string | Sim | Sempre "encrypted" |
payload.data | string | Sim | O payload criptografado de collect() |
config.response.address | object | Não | { "type": "include" } para obter o endereço completo, omitido por padrão |
Config
O objeto config controla quais dados opcionais são incluídos na resposta.
{
"config": {
"response": {
"address": { "type": "include" }
}
}
}
| Campo | Valores | Padrão | Descrição |
|---|---|---|---|
config.response.address.type | "include" ou "omit" | "omit" | Se o endereço completo de geocodificação reversa (rua, cidade, estado, país, código postal) deve ser incluído na resposta. Requer dados GPS no payload. |
Quando address é omitido (padrão), o campo address na resposta será null mesmo se dados de localização foram coletados. Defina como "include" se seu backend precisa do endereço físico para conformidade, revisão de fraude ou exibição.
Autenticação
Use sua chave de API da Surt no cabeçalho Authorization como um token Bearer. Esta é uma chamada servidor para servidor - a chave de API nunca toca o dispositivo.
3. Tratar a resposta
O endpoint de avaliação retorna os mesmos dados que verify(), mas com detalhes completos:
{
"status_code": 200,
"message": "Transaction evaluated successfully",
"data": {
"transaction": {
"transaction_id": "1775514112-f2e3034b891e...",
"created_at": "2026-04-06T22:21:52Z",
"customer_id": "user_123",
"transaction_type": "withdrawal",
"transaction_name": "User Payment",
"status": {
"type": "completed",
"risk_level": "low",
"result": {
"status": "accepted",
"review": false
},
"address": {
"street": "123 Main St",
"city": "San Francisco",
"state": "California",
"country": "United States",
"postal_code": "94103",
"formatted_address": "123 Main St, San Francisco, CA 94103, USA"
},
"signals": [ ... ],
"triggered_scenarios": [ ... ]
},
"device": {
"device_id": "fingerprint_abc",
"manufacturer": "Apple",
"model": "iPhone 15",
"os_version": "18.0"
},
"metadata": {
"device_locations": [ ... ],
"ip_locations": [ ... ],
"device_ids": [ ... ]
},
"network_threat": {
"status": "not_analyzed"
},
"country": "United States",
"ip_address": "203.0.113.50"
}
}
}
Seu backend pode usar status.risk_level, status.result.status, signals, triggered_scenarios e address para tomar sua própria decisão antes de responder ao cliente.
Substituição de localização
Assim como verify(), você pode substituir a coleta de localização por chamada:
// Coletar com localização
const { payload } = await collect('login', 'Login', { collectLocation: true });
// Coletar sem localização
const { payload } = await collect('login', 'Login', { collectLocation: false });
CollectResult
interface CollectResult {
/** Base64-encoded encrypted payload. Pass as payload.data in the evaluate request. */
payload: string;
}
O payload é criptografado e só pode ser descriptografado pelo backend da Surt. Ele contém a impressão digital do dispositivo, dados de atestação, sinais de segurança e opcionalmente localização.
Exemplo de backend
- Node.js
- Java
- Python
app.post('/verify-device', async (req, res) => {
const { payload, userId } = req.body;
const surtResponse = await fetch(
'https://api.surt.com/geolocation/transactions/evaluate',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.SURT_API_KEY}`,
},
body: JSON.stringify({
customer_id: userId,
transaction_type: 'login',
payload: { type: 'encrypted', data: payload },
config: { response: { address: { type: 'include' } } },
}),
}
);
const { data } = await surtResponse.json();
const risk = data.transaction.status.risk_level;
const accepted = data.transaction.status.result.status === 'accepted';
res.json({ allowed: accepted, risk });
});
@PostMapping("/verify-device")
public ResponseEntity<Map<String, Object>> verifyDevice(@RequestBody Map<String, Object> body) {
String payload = (String) body.get("payload");
String userId = (String) body.get("userId");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + System.getenv("SURT_API_KEY"));
Map<String, Object> request = Map.of(
"customer_id", userId,
"transaction_type", "login",
"payload", Map.of("type", "encrypted", "data", payload),
"config", Map.of("response", Map.of("address", Map.of("type", "include")))
);
ResponseEntity<Map> response = restTemplate.exchange(
"https://api.surt.com/geolocation/transactions/evaluate",
HttpMethod.POST,
new HttpEntity<>(request, headers),
Map.class
);
Map data = (Map) response.getBody().get("data");
Map transaction = (Map) data.get("transaction");
Map status = (Map) transaction.get("status");
String risk = (String) status.get("risk_level");
Map result = (Map) status.get("result");
boolean accepted = "accepted".equals(result.get("status"));
return ResponseEntity.ok(Map.of("allowed", accepted, "risk", risk));
}
@app.post("/verify-device")
async def verify_device(request: Request):
body = await request.json()
surt_response = requests.post(
"https://api.surt.com/geolocation/transactions/evaluate",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['SURT_API_KEY']}",
},
json={
"customer_id": body["userId"],
"transaction_type": "login",
"payload": {"type": "encrypted", "data": body["payload"]},
"config": {"response": {"address": {"type": "include"}}},
},
)
data = surt_response.json()["data"]
risk = data["transaction"]["status"]["risk_level"]
accepted = data["transaction"]["status"]["result"]["status"] == "accepted"
return {"allowed": accepted, "risk": risk}