Zum Hauptinhalt springen
Version: Guardian v0.1.0

Collect (Server-zu-Server)

v0.3.0+

Zwei Verifizierungswege​

Das Guardian SDK bietet zwei Möglichkeiten zur Geräteverifizierung. Wählen Sie basierend auf Ihrer Architektur:

verify()collect()
Wer Surt aufruftDas SDK (vom Gerät)Ihr Backend (Server-zu-Server)
JWT-AnforderungErforderlich, frisch pro AufrufOptional (nur für die Geräte-IP)
Netzwerkaufrufe vom SDKJaNur GET /geolocation/client-ip, wenn ein JWT übergeben wird
Ihr Backend beteiligtNeinJa
AntwortVerificationResult (erlaubt/abgelehnt)CollectResult (verschlüsseltes Payload)
Am besten fürEinfache Integration, Frontend-gesteuerte EntscheidungenBackend-gesteuerte Entscheidungen, individuelle Logik, Audit-Anforderungen

Ablauf von verify()​

App → frisches JWT vom Backend holen → SDK.verify(jwt) → 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. Für jeden Aufruf ist ein frisches JWT erforderlich.

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. Ohne Argumente macht collect() 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 } = useGuardian();

const handlePayment = async () => {
// Das JWT ist für collect() optional. Rufen Sie collect() ohne Argumente auf,
// oder übergeben Sie collect(jwt) nur, wenn die öffentliche IP des Geräts
// in das Payload aufgelöst werden soll.
const { payload } = await collect();

// Payload an IHR Backend senden
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();
// Ihr Backend hat die Risikoentscheidung bereits getroffen
};
}

Bei collect() übergeben Sie ein JWT an collect(jwt) nur, wenn die öffentliche IP des Geräts in das Payload aufgelöst werden soll; andernfalls rufen Sie collect() ohne Argumente auf. Anders als bei verify() darf ein collect()-JWT wiederverwendet oder weggelassen werden - collect() benötigt kein JWT pro Aufruf.

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​

FeldTypErforderlichBeschreibung
customer_idstringJaIhr Benutzerbezeichner
transaction_typestringJalogin, sign_up, deposit oder withdrawal
transaction_namestringNeinMenschenlesbares Label
payload.typestringJaImmer "encrypted"
payload.datastringJaDas verschlüsselte Payload von collect()
config.response.addressobjectNein{ "type": "include" } um die vollständige Adresse zu erhalten, standardmäßig ausgelassen

Config​

Das config-Objekt steuert, welche optionalen Daten in der Antwort enthalten sind.

{
"config": {
"response": {
"address": { "type": "include" }
}
}
}
FeldWerteStandardBeschreibung
config.response.address.type"include" oder "omit""omit"Ob die vollständige reverse-geokodierte Adresse (Straße, 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. Das address-Feld hängt von Ihrer Config ab.

Mit config.response.address.type: "include"​

{
"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"
}
}
}

Mit config.response.address.type: "omit" (Standard)​

Gleiche Antwort, aber address ist null:

{
"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": null,
"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 (und Geräte-IP über das JWT auflösen)
const { payload } = await collect(jwt, { collectLocation: true });

// Ohne Standort und ohne JWT sammeln (kein Surt-Netzwerkaufruf)
const { payload } = await collect(undefined, { 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, Geräteintegritätssignale, Sicherheitssignale und optional den Standort.

Das collect()-Ergebnis enthält außerdem ein optionales diagnostics-Objekt, das beschreibt, was das SDK lokal beobachtet hat - additiv, kann ignoriert werden:

const { payload, diagnostics } = await collect();
// diagnostics?.location: 'collected' | 'denied' | 'unavailable' | 'timeout' | 'not_requested'
// diagnostics?.networkIntel: 'collected' | 'unavailable'
// diagnostics?.warnings: { code, signal }[]
// diagnostics?.timings: { [phase]: ms } (nur Android, v0.5.2+)

Siehe Fehlercodes → Ergebnis-Diagnose.

Backend-Beispiel​

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 });
});