Plan Your Visit landing page for visit.waterschurch.org
Next.js 16 static export + PHP 8 API for cPanel/Apache hosting. - Location-aware campus finder (IP estimate, optional precise location, ZIP search) - Three-step Plan Your Visit form with separate email/SMS consent - PHP API: validation, anti-spam, rate limiting, SQLite lead store, Mailchimp and Sinch Engage adapters, idempotent retries via cron - Attribution capture (utm_*, gclid/gbraid/wbraid, fbclid, ttclid, twclid) and consent-aware analytics hooks - Vitest and PHP test suites, cPanel deployment script and docs Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/** Optional Cloudflare Turnstile verification. Only enforced when TURNSTILE_SECRET_KEY is set. */
|
||||
final class Turnstile
|
||||
{
|
||||
public static function enabled(): bool
|
||||
{
|
||||
return Config::get('TURNSTILE_SECRET_KEY') !== null;
|
||||
}
|
||||
|
||||
public static function verify(?string $token, string $ip): bool
|
||||
{
|
||||
if (!self::enabled()) {
|
||||
return true;
|
||||
}
|
||||
if (!$token) {
|
||||
return false;
|
||||
}
|
||||
$res = Http::request('POST', 'https://challenges.cloudflare.com/turnstile/v0/siteverify', http_build_query([
|
||||
'secret' => Config::require('TURNSTILE_SECRET_KEY'),
|
||||
'response' => $token,
|
||||
'remoteip' => $ip,
|
||||
]), ['Content-Type' => 'application/x-www-form-urlencoded'], 6);
|
||||
if ($res['error'] !== null || $res['status'] !== 200) {
|
||||
// Fail open on provider outage so real visitors are never blocked; honeypot + rate limit still apply.
|
||||
Log::warn('Turnstile verification unavailable', ['status' => $res['status'], 'error' => $res['error']]);
|
||||
return true;
|
||||
}
|
||||
return (bool)($res['json']['success'] ?? false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user