value('value'), true) ?: []; return [ 'enabled' => (bool) ($value['enabled'] ?? false), 'site_key' => (string) ($value['site_key'] ?? ''), 'secret_key' => (string) ($value['secret_key'] ?? ''), ]; } public static function siteKey(): ?string { $config = self::stored(); return $config['enabled'] && $config['site_key'] !== '' && $config['secret_key'] !== '' ? $config['site_key'] : null; } public static function verify(?string $token, ?string $ip): bool { if (self::siteKey() === null) { return true; } if ($token === null || $token === '' || strlen($token) > 2048) { return false; } try { $response = Http::asForm()->timeout(10)->post(self::VERIFY_URL, array_filter([ 'secret' => SignRSA::DecryptFunc(self::stored()['secret_key']), 'response' => $token, 'remoteip' => $ip, ])); } catch (Throwable $e) { Log::warning('Turnstile siteverify failed', ['error' => $e->getMessage()]); return false; } if ($response->json('success') !== true) { Log::info('Turnstile rejected', ['codes' => $response->json('error-codes')]); return false; } return true; } /** * @param array $input */ public static function save(array $input): void { $current = self::stored(); $secret = (string) ($input['secret_key'] ?? self::SECRET_MASK); $config = [ 'enabled' => (bool) ($input['enabled'] ?? $current['enabled']), 'site_key' => trim((string) ($input['site_key'] ?? $current['site_key'])), 'secret_key' => match (true) { $secret === self::SECRET_MASK => $current['secret_key'], trim($secret) === '' => '', default => SignRSA::EncryptFunc(trim($secret)), }, ]; if ($config['enabled'] && ($config['site_key'] === '' || $config['secret_key'] === '')) { throw new InvalidArgumentException('Cần nhập Site key và Secret key trước khi bật Turnstile.'); } Setting::updateOrCreate(['key' => self::SETTING_KEY], ['value' => json_encode($config)]); } /** * @return array{enabled: bool, site_key: string, secret_key: string} */ public static function forAdmin(): array { $config = self::stored(); $config['secret_key'] = $config['secret_key'] === '' ? '' : self::SECRET_MASK; return $config; } }