Collect (Server-zu-Server)
Zwei Verifizierungswege
Das Guardian SDK bietet zwei Möglichkeiten zur Geräteverifizierung. Wählen Sie basierend auf Ihrer Architektur:
verify() | collect() | |
|---|---|---|
| Wer Surt aufruft | Das SDK (vom Gerät) | Ihr Backend (Server-zu-Server) |
| Netzwerkaufrufe vom SDK | Ja | Nein |
| Ihr Backend beteiligt | Nein | Ja |
| Antwort | VerificationResult (erlaubt/abgelehnt) | CollectResult (verschlüsseltes Payload) |
| Am besten für | Einfache Integration, Frontend-gesteuerte Entscheidungen | Backend-gesteuerte Entscheidungen, individuelle Logik, Audit-Anforderungen |
Ablauf von verify()
App → SDK.verify() → Surt backend → risk decision → App
Das SDK ruft Surt direkt auf und gibt allowed: true/false zurück. Ihre App reagiert sofort auf die Entscheidung.
Ablauf von collect()
App → SDK.collect() → encrypted payload → App → Your backend → Surt /evaluate → Your backend → App
Das SDK sammelt Gerätedaten und verschlüsselt sie lokal. Null Netzwerkaufrufe an Surt. Ihr Backend sendet das Payload an Surts Evaluate-Endpoint, erhält die vollständige Risikobewertung und entscheidet, was an Ihre App zurückgegeben wird.
Wann collect() verwenden
- Ihr Backend benötigt die Risikodaten, bevor es dem Client antwortet
- Sie möchten das Geräterisiko serverseitig mit Ihrer eigenen Geschäftslogik kombinieren
- Sie benötigen volle Kontrolle darüber, was der Client sieht
- Compliance erfordert, dass alle Drittanbieter-Aufrufe von Ihrer Infrastruktur ausgehen
Verwendung
1. Auf dem Gerät sammeln
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. Surt von Ihrem Backend aufrufen
Ihr Backend empfängt das verschlüsselte Payload von der App und ruft dann Surts Evaluate-Endpoint auf:
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" }
}
}
}
Anforderungsfelder
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
customer_id | string | Ja | Ihr Benutzerbezeichner |
transaction_type | string | Ja | login, sign_up, deposit oder withdrawal |
transaction_name | string | Nein | Menschenlesbares Label |
payload.type | string | Ja | Immer "encrypted" |
payload.data | string | Ja | Das verschlüsselte Payload von collect() |
config.response.address | object | Nein | { "type": "include" } um die vollständige Adresse zu erhalten, standardmässig ausgelassen |
Config
Das config-Objekt steuert, welche optionalen Daten in der Antwort enthalten sind.
{
"config": {
"response": {
"address": { "type": "include" }
}
}
}
| Feld | Werte | Standard | Beschreibung |
|---|---|---|---|
config.response.address.type | "include" oder "omit" | "omit" | Ob die vollständige reverse-geokodierte Adresse (Strasse, Stadt, Bundesland, Land, Postleitzahl) in der Antwort enthalten sein soll. Erfordert GPS-Daten im Payload. |
Wenn address ausgelassen wird (Standard), ist das address-Feld in der Antwort null, auch wenn Standortdaten erfasst wurden. Setzen Sie es auf "include", wenn Ihr Backend die physische Adresse für Compliance, Betrugsüberprüfung oder Anzeige benötigt.
Authentifizierung
Verwenden Sie Ihren Surt-API-Schlüssel im Authorization-Header als Bearer-Token. Dies ist ein Server-zu-Server-Aufruf - der API-Schlüssel berührt niemals das Gerät.
3. Antwort verarbeiten
Der Evaluate-Endpoint gibt dieselben Daten wie verify() zurück, jedoch mit vollständigen Details:
{
"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"
}
}
}
Ihr Backend kann status.risk_level, status.result.status, signals, triggered_scenarios und address verwenden, um eine eigene Entscheidung zu treffen, bevor es dem Client antwortet.
Standortüberschreibung
Wie bei verify() können Sie die Standorterfassung pro Aufruf überschreiben:
// Mit Standort sammeln
const { payload } = await collect('login', 'Login', { collectLocation: true });
// Ohne Standort sammeln
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;
}
Das Payload ist verschlüsselt und kann nur vom Surt-Backend entschlüsselt werden. Es enthält den Gerätefingerabdruck, Attestierungsdaten, Sicherheitssignale und optional den Standort.
Backend-Beispiel
- 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}