<?php
require __DIR__ . '/vendor/autoload.php';
use ShieldLabs\Exception\ShieldLabsException;
use ShieldLabs\Exception\ValidationException;
use ShieldLabs\Risk;
use ShieldLabs\ShieldLabs;
function respond(int $status, array $body): never
{
http_response_code($status);
header('Content-Type: application/json');
echo json_encode($body);
exit;
}
// One identification authorizes one action, so remember the request IDs you accepted.
// Marker files keep this snippet self-contained; see "Storing used request IDs" below
// for a database version.
function markRequestIdUsed(string $requestId): bool
{
$marker = sys_get_temp_dir() . '/shieldlabs-used-' . hash('sha256', $requestId);
return @fopen($marker, 'x') !== false; // mode "x" fails when the file already exists
}
// The requestId arrives as a form field or in a JSON body.
$json = json_decode((string) file_get_contents('php://input'), true);
$requestId = $_POST['requestId'] ?? (is_array($json) ? ($json['requestId'] ?? null) : null);
$shieldlabs = new ShieldLabs(['api_key' => 'sec_your_private_key']); // or new ShieldLabs() to read SHIELDLABS_API_KEY
try {
// Polls the History API until the verdict is stored (usually 1-3 seconds after
// the browser call), for up to 10 seconds by default.
$identification = $shieldlabs->identifications->get(is_string($requestId) ? $requestId : '');
} catch (ValidationException) {
respond(400, ['error' => 'requestId must be a UUID']);
} catch (ShieldLabsException) {
respond(503, ['error' => 'verification unavailable']); // unverified is never "clean"
}
$evaluation = Risk::evaluate($identification, [
'is_replay' => fn(string $id): bool => !markRequestIdUsed($id),
]);
if (!$evaluation->ok) {
respond(403, ['error' => 'signup refused', 'reason' => $evaluation->reason?->value]);
}
// Create the account here.
respond(200, ['ok' => true]);