Collect (Servidor a servidor)
Dos rutas de verificación
El Guardian SDK ofrece dos formas de verificar dispositivos. Elija según su arquitectura:
verify() | collect() | |
|---|---|---|
| Quién llama a Surt | El SDK (desde el dispositivo) | Su backend (servidor a servidor) |
| Llamadas de red desde el SDK | Sí | No |
| Su backend involucrado | No | Sí |
| Respuesta | VerificationResult (permitido/denegado) | CollectResult (payload cifrado) |
| Ideal para | Integración simple, decisiones en el frontend | Decisiones en el backend, lógica personalizada, requisitos de auditoría |
Flujo de verify()
App → SDK.verify() → Surt backend → risk decision → App
El SDK llama a Surt directamente y devuelve allowed: true/false. Su aplicación actúa sobre la decisión inmediatamente.
Flujo de collect()
App → SDK.collect() → encrypted payload → App → Your backend → Surt /evaluate → Your backend → App
El SDK recopila datos del dispositivo y los cifra localmente. Cero llamadas de red a Surt. Su backend envía el payload al endpoint de evaluación de Surt, recibe la evaluación de riesgo completa y decide qué devolver a su aplicación.
Cuándo usar collect()
- Su backend necesita los datos de riesgo antes de responder al cliente
- Desea combinar el riesgo del dispositivo con su propia lógica de negocio del lado del servidor
- Necesita control total sobre lo que el cliente ve
- El cumplimiento requiere que todas las llamadas a terceros se originen desde su infraestructura
Uso
1. Recopilar en el 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. Llamar a Surt desde su backend
Su backend recibe el payload cifrado de la aplicación y luego llama al endpoint de evaluación de 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 de la solicitud
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
customer_id | string | Sí | Su identificador de usuario |
transaction_type | string | Sí | login, sign_up, deposit o withdrawal |
transaction_name | string | No | Etiqueta legible para humanos |
payload.type | string | Sí | Siempre "encrypted" |
payload.data | string | Sí | El payload cifrado de collect() |
config.response.address | object | No | { "type": "include" } para obtener la dirección completa, omitido por defecto |
Config
El objeto config controla qué datos opcionales se incluyen en la respuesta.
{
"config": {
"response": {
"address": { "type": "include" }
}
}
}
| Campo | Valores | Por defecto | Descripción |
|---|---|---|---|
config.response.address.type | "include" o "omit" | "omit" | Si se incluye la dirección completa de geocodificación inversa (calle, ciudad, estado, país, código postal) en la respuesta. Requiere datos GPS en el payload. |
Cuando address se omite (por defecto), el campo address en la respuesta será null incluso si se recopilaron datos de ubicación. Establezca en "include" si su backend necesita la dirección física para cumplimiento, revisión de fraude o visualización.
Autenticación
Use su clave de API de Surt en el encabezado Authorization como un token Bearer. Esta es una llamada servidor a servidor - la clave de API nunca toca el dispositivo.
3. Manejar la respuesta
El endpoint de evaluación devuelve los mismos datos que verify(), pero con detalle completo:
{
"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"
}
}
}
Su backend puede usar status.risk_level, status.result.status, signals, triggered_scenarios y address para tomar su propia decisión antes de responder al cliente.
Anulación de ubicación
Igual que verify(), puede anular la recopilación de ubicación por llamada:
// Recopilar con ubicación
const { payload } = await collect('login', 'Login', { collectLocation: true });
// Recopilar sin ubicación
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;
}
El payload está cifrado y solo puede ser descifrado por el backend de Surt. Contiene la huella digital del dispositivo, datos de atestación, señales de seguridad y opcionalmente ubicación.
Ejemplo 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}