Tested and maintained, so you don't have to. $99/year.
reCAPTCHA
Google reCAPTCHA v3 integration with configurable score validation and automatic form handling.
Why Use This Service?
What you'd build yourself: Google API integration, score-based validation, JavaScript widget injection, action/hostname verification, and proper error handling for API failures.
Maintained for you: when Google changes the API or scoring behavior, the service gets updated and your forms keep working.
What Pro provides:
- Automatic form integration with
scripts()method - Configurable score thresholds (0.0-1.0)
- Optional action and hostname verification
- Works with
file_get_contentsor cURL - Clean
passed()method - one line to validate
Installation
Flick Pro requires a license. See Pro installation for setup instructions.
Configuration
Get your reCAPTCHA v3 keys from the Google reCAPTCHA Admin Console.
$config = [ 'services' => [ 'recaptcha' => [ 'siteKey' => 'your-site-key', 'secretKey' => 'your-secret-key' ] ] ]; $form = new Flick($config);
Full Configuration
$config = [ 'services' => [ 'recaptcha' => [ 'siteKey' => 'your-site-key', 'secretKey' => 'your-secret-key', 'score' => 0.5, // Minimum score threshold (0.0-1.0) 'timeout' => 10, // API request timeout in seconds 'curl' => false, // Use cURL instead of file_get_contents 'expectedAction' => 'submit',// Reject tokens minted for a different action 'expectedHostname' => 'example.com' // Optional: reject tokens from another host ] ] ];
Score Threshold
reCAPTCHA v3 returns a score between 0.0 and 1.0:
- 1.0 - Very likely a human
- 0.0 - Very likely a bot
The default threshold is 0.5. Adjust based on your needs:
// Strict - good for sensitive forms 'score' => 0.7 // Lenient - good for public forms 'score' => 0.3
The threshold has to be a number between 0 and 1. Anything else is rejected when the form is built — a threshold Flick can't compare against would otherwise either wave every bot through or turn every visitor away, with nothing to see in the logs.
Basic Usage
1. Add Scripts to Your Page
Include the reCAPTCHA scripts in your HTML:
<?php require 'vendor/autoload.php'; $form = new Flick\Flick([ 'services' => [ 'recaptcha' => [ 'siteKey' => 'your-site-key', 'secretKey' => 'your-secret-key' ] ] ]); ?> <!DOCTYPE html> <html> <head> <title>Contact Form</title> </head> <body> <?php $form->create('Name, Email, Message|textarea'); ?> <!-- Add reCAPTCHA scripts before closing body tag --> <?= $form->recaptcha->scripts() ?> </body> </html>
2. Validate on Submission
Check if the reCAPTCHA validation passed:
if ($form->submitted()) { if (!$form->recaptcha->passed()) { $form->addError('recaptcha', 'reCAPTCHA validation failed. Please try again.'); } if ($form->ok()) { $request = $form->request('Name[required], Email[required, email], Message[required]'); if ($form->ok()) { // Process form... } } }
Methods
scripts()
Generate the reCAPTCHA JavaScript code for form integration.
$form->recaptcha->scripts(): string
Returns: HTML script tags that:
- Load the Google reCAPTCHA v3 library
- Intercept form submissions
- Execute reCAPTCHA and add the response token
- Submit the form with the token
Tip
Place the scripts just before the closing </body> tag for best performance.
passed()
Validate the reCAPTCHA response from form submission.
$form->recaptcha->passed(): bool
Returns: true if validation passed and score meets threshold, false otherwise.
Validation checks:
- Request method is POST
- reCAPTCHA response token is present
- Google API verification succeeds
- Hostname matches
expectedHostname(if configured) - Action matches
expectedAction(if configured) - Score meets configured threshold
When any check fails, passed() also records the reason in Flick's error bag under
the key recaptcha — for example Missing reCAPTCHA response token. Adding your own
addError('recaptcha', ...) replaces that message with yours; use a different key if
you'd rather keep both.
getScore()
The score Google returned for the most recent passed() call.
$form->recaptcha->getScore(): ?float
Returns: the score as a float, or null if passed() hasn't run yet or the
verification never reached Google. Handy for logging borderline submissions:
if (! $form->recaptcha->passed()) { error_log('reCAPTCHA rejected, score: '.var_export($form->recaptcha->getScore(), true)); }
Complete Example
<?php require 'vendor/autoload.php'; $form = new Flick\Flick([ 'services' => [ 'recaptcha' => [ 'siteKey' => $_ENV['RECAPTCHA_SITE_KEY'], 'secretKey' => $_ENV['RECAPTCHA_SECRET_KEY'], 'score' => 0.5 ], 'mail' => [ 'fromAddress' => 'noreply@example.com', 'mailer' => [ 'transport' => 'smtp', 'host' => 'smtp.example.com', 'port' => 587, 'username' => 'user', 'password' => 'pass' ] ] ] ]); // Handle form submission if ($form->submitted()) { // Validate reCAPTCHA first if (!$form->recaptcha->passed()) { $form->addError('form', 'Security check failed. Please try again.'); } if ($form->ok()) { $request = $form->request(' Name[required, min:2], Email[required, email], Message[required, min:10] '); if ($form->ok()) { // Send email $form->mail->sendFormData( 'admin@example.com', 'New Contact Form Submission', $request ); $form->successMessage('Thank you! Your message has been sent.'); } } } ?> <!DOCTYPE html> <html> <head> <title>Contact Us</title> </head> <body> <h1>Contact Us</h1> <?php if ($form->errorsIsNotEmpty()): ?> <div class="errors"> <ul> <?php foreach ($form->getErrors() as $error): ?> <li><?= htmlspecialchars($error) ?></li> <?php endforeach; ?> </ul> </div> <?php endif; ?> <?php $form->create('Name, Email, Message|textarea'); ?> <?= $form->recaptcha->scripts() ?> </body> </html>
How It Works
Client Side
When scripts() is called, it generates JavaScript that:
- Loads Google's reCAPTCHA v3 library with your site key
- Listens for form submissions
- Prevents the default form submit
- Executes reCAPTCHA to get a token
- Adds the token as a hidden field (
g-recaptcha-response) - Submits the form with the token
Server Side
When passed() is called, it:
- Checks the request is a POST request
- Extracts the
g-recaptcha-responsetoken - Sends the token to Google's verification API
- Parses the response and checks the score
- Returns true if score >= configured threshold
Action & Hostname Verification
Because the site key is shared across your whole site, a token minted for a
different action (or on a different host) could otherwise be replayed here. When
you configure expectedAction and/or expectedHostname, passed() rejects a
token whose action/hostname doesn't match:
'recaptcha' => [ 'siteKey' => 'your-site-key', 'secretKey' => 'your-secret-key', 'expectedAction' => 'submit', 'expectedHostname' => 'example.com' ]
Google only echoes back actions made of letters, numbers, slashes and
underscores, so expectedAction is limited to those characters — note that
hyphens are out, even though Turnstile accepts them. Anything else is rejected
when the form is built, rather than silently failing every submission.
Using cURL
By default, Flick uses file_get_contents for API requests. To use cURL instead:
'recaptcha' => [ 'siteKey' => 'your-site-key', 'secretKey' => 'your-secret-key', 'curl' => true ]
Tip
Use cURL if allow_url_fopen is disabled in your PHP configuration.
Security Features
- Score-Based Validation: Only accepts submissions meeting your score threshold
- Action & Hostname Verification: Optionally rejects tokens minted for another action or host
- Fail-Closed: Any verification or transport error returns
false - Secure API Communication: SSL verification enabled for API requests
- Configurable Timeouts: Prevents hanging requests