Flick
Guide Validation Services API Pro Examples GitHub
Docs / Services
Flick Pro Service

Tested and maintained, so you don't have to. $99/year.

Get Flick Pro

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_contents or 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:

  1. Load the Google reCAPTCHA v3 library
  2. Intercept form submissions
  3. Execute reCAPTCHA and add the response token
  4. 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:

  1. Request method is POST
  2. reCAPTCHA response token is present
  3. Google API verification succeeds
  4. Hostname matches expectedHostname (if configured)
  5. Action matches expectedAction (if configured)
  6. 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:

  1. Loads Google's reCAPTCHA v3 library with your site key
  2. Listens for form submissions
  3. Prevents the default form submit
  4. Executes reCAPTCHA to get a token
  5. Adds the token as a hidden field (g-recaptcha-response)
  6. Submits the form with the token

Server Side

When passed() is called, it:

  1. Checks the request is a POST request
  2. Extracts the g-recaptcha-response token
  3. Sends the token to Google's verification API
  4. Parses the response and checks the score
  5. 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