botProtection->assess($request, 'comment_create'); if ((bool) ($assessment['blocked'] ?? false)) { return response()->json(['message' => 'Suspicious activity detected.', 'errors' => ['bot' => ['Suspicious activity detected.']]], 429); } $threshold = (int) config('comment_spam.captcha.threshold', 40); if ((int) ($assessment['risk_score'] ?? 0) < $threshold) { return $next($request); } $token = (string) ($request->input('comment-turnstile-response') ?: $request->header('X-Turnstile-Token', '')); $valid = false; if ($token !== '') { try { $valid = (bool) Http::asForm()->timeout(5)->post( (string) config('comment_spam.captcha.verify_url'), ['secret' => (string) config('comment_spam.captcha.secret_key'), 'response' => $token, 'remoteip' => $request->ip()], )->json('success', false); } catch (\Throwable) { $valid = (bool) config('comment_spam.captcha.fail_open', false); } } if ($valid) { return $next($request); } $payload = [ 'message' => 'Complete the captcha challenge to continue.', 'errors' => ['captcha' => ['Complete the captcha challenge to continue.']], 'requires_captcha' => true, 'captcha' => [ 'provider' => 'turnstile', 'siteKey' => (string) config('comment_spam.captcha.site_key'), 'inputName' => 'comment-turnstile-response', 'scriptUrl' => (string) config('comment_spam.captcha.script_url'), ], 'captcha_provider' => 'turnstile', 'captcha_site_key' => (string) config('comment_spam.captcha.site_key'), 'captcha_input' => 'comment-turnstile-response', 'captcha_script_url' => (string) config('comment_spam.captcha.script_url'), ]; return response()->json($payload, 422); } }