timehunter/laravel-google-recaptcha-v2
A Laravel package for integrating Google reCAPTCHA v2 into forms and requests. It provides configuration and validation support to help verify reCAPTCHA responses in Laravel applications.
composer require timehunter/laravel-google-recaptcha-v2. Because the last release was March 2021, expect a strict illuminate/* constraint — on Laravel 9+ you may need a fork, a replace/path repository, or --ignore-platform-reqs to install at all.config/recaptcha.php (key names and env vars), the package's ServiceProvider (what is bound and auto-discovered), and the validation rule class. The package is small; its entire surface area is the service provider, a config file, the rule, and usually a Blade helper/directive.php artisan vendor:publish --tag=recaptcha (or --provider="TimeHunter\LaravelGoogleReCaptchaV2\...ServiceProvider" — check the README for the exact provider/tag). Then add RECAPTCHA_SITE_KEY and RECAPTCHA_SECRET_KEY to .env.localhost as a separate key if you develop locally; otherwise the widget silently fails to render.<div class="g-recaptcha" data-sitekey="{{ config('recaptcha.site_key') }}"> plus https://www.google.com/recaptcha/api.js).'g-recaptcha-response' => 'required|recaptcha' to your FormRequest rules.ValidationException and that a good one passes.composer.json after the first successful install; this package is not actively maintained and a stray composer update is a real risk.public function rules(): array
{
return [
'email' => 'required|email',
'message' => 'required',
'g-recaptcha-response' => 'required|recaptcha',
];
}
'g-recaptcha-response.recaptcha' => __('Please confirm you are not a robot.')) so users don't see the raw rule key. Check whether the package ships translations you can publish; otherwise override in resources/lang.Validator::make(...) and return a JSON error on failure, rather than throwing a full-page redirect.data-callback and submit the form programmatically after the token resolves. The server never needs to know which variant you used, which makes switching modes a front-end-only change.grecaptcha.render(element, {...}) per form, keep the returned widget ID, and call grecaptcha.reset(widgetId) after an AJAX submit so the token isn't reused.g-recaptcha-response (Google's expected field name — the rule reads it), validate, and on failure reset the widget client-side. Tokens are single-use, so a failed non-recaptcha validation means the user must re-solve.6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI site / 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe secret), which always pass. In feature tests, assert the redirect/errors rather than the network call.config/recaptcha.php reading from env(), and reference config() everywhere else — env() calls break under config:cache.NOASSERTION license. Verify the actual license text in the repo before commercial use; "no assertion" often means the file is missing or non-standard. If it's unmaintained, you can replicate this package in ~60 lines (one rule, one Blade include, one config) and drop the dependency.timeout-or-duplicate. Call grecaptcha.reset() client-side on every failed submit.invalid-input-response with no hint. Double-check you didn't mix a localhost site key with a production secret.siteverify response body (success plus error-codes), not just the boolean. missing-input-secret / invalid-input-secret mean a config or .env/config:cache problem, not a user problem. If the package uses Guzzle directly, you may not get Laravel's Http::fake() for free — check composer.json and bind a mock handler in tests accordingly.config:cache traps: after changing keys in .env, run php artisan config:clear. A cached config with an empty secret is the most common "it worked yesterday" report.remoteip may be wrong behind a load balancer or proxy if the package sends request()->ip() without trusting proxy headers. Verify TrustProxies is configured; a mismatched IP doesn't usually break validation but pollutes Google's risk scoring.script-src and frame-src must allow https://www.google.com and https://www.gstatic.com, and the widget renders in an iframe — expect it to disappear entirely under a strict policy.timeout-or-duplicate. Pick one place to validate.data-size/data-theme attributes, not CSS.display:none container or hide it behind tabs that aren't rendered at load time, or you'll break the challenge.How can I help you explore Laravel packages today?