Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Excelwebzonerecaptcha Bundle Laravel Package

lethak/excelwebzonerecaptcha-bundle

Integrates Google reCAPTCHA into PHP/Symfony forms via the ExcelWebZone bundle, offering configurable widget rendering and server-side verification to help protect applications from spam, bots, and automated form abuse.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the framework reality check. lethak/excelwebzonerecaptcha-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no config/recaptcha.php, no extra.laravel auto-discovery, and no Blade/Twig interoperability in this codebase. composer require will pull in Symfony components (symfony/form, symfony/http-kernel, symfony/validator, symfony/dependency-injection) that will sit unused in a Laravel app, and its EWZRecaptchaType references Symfony\Component\Form\AbstractType — nothing in Laravel's Illuminate stack knows what to do with that.

Three realistic paths for a Laravel developer:

  1. Do not install it. Use google/recaptcha (the official client) directly, or a Laravel-native package such as anhskohbo/no-captcha. You get the same siteverify call with a fraction of the dependency surface.
  2. Read it, port it. Treat the bundle as a small, readable reference implementation. The two files worth reading are Validator/Constraints/RecaptchaValidator.php (server-side verify) and the widget rendering template (client-side grecaptcha.render/execute). That's ~100–150 lines to reimplement idiomatically.
  3. Ignore it unless you are running a Symfony sub-app alongside a Laravel app during a migration.

If you genuinely need a working captcha today, the minimal Laravel path is:

composer require google/recaptcha
// config/services.php
'recaptcha' => [
    'site_key'   => env('RECAPTCHA_SITE_KEY'),
    'secret_key' => env('RECAPTCHA_SECRET_KEY'),
],

Then a Rule object wrapping ReCaptcha::verifyResponse() — see the next section. First use case: protect registration or contact forms where a spam bot would create rows in your DB or trigger transactional email.

Implementation Patterns

Interface-first, so the vendor is swappable. Define a contract and bind a Google implementation. This is the single most important pattern — it's what makes the choice reversible.

// app/Contracts/CaptchaVerifier.php
interface CaptchaVerifier {
    public function verify(string $token, ?string $action = null, ?string $ip = null): bool;
}
// app/Services/GoogleCaptchaVerifier.php
class GoogleCaptchaVerifier implements CaptchaVerifier
{
    public function __construct(
        private string $secret,
        private float $minScore = 0.5,
    ) {}

    public function verify(string $token, ?string $action = null, ?string $ip = null): bool
    {
        $res = (new ReCaptcha($this->secret))->setExpectedHostname(request()->getHost())->verify($token, $ip ?? request()->ip());
        if (!$res->isSuccess()) return false;
        if ($action !== null && $res->getAction() !== $action) return false;
        return $res->getScore() === null || $res->getScore() >= $this->minScore;
    }
}

Wrap in a Validation Rule, not middleware. Laravel's validation pipeline is where per-form captcha belongs, because captcha is form-scoped (its token is single-use) and middleware cannot easily distinguish "user submitted form" from "user submitted form with a token to check."

class ValidRecaptcha implements ValidationRule {
    public function __construct(private string $action = 'submit') {}
    public function validate($attribute, $value, $fail): void {
        if (!app(CaptchaVerifier::class)->verify((string) $value, $this->action, request()->ip())) {
            $fail('Captcha verification failed.');
        }
    }
}

Blade rendering. The bundle's Twig helper translates to a Blade component. Load Google's script once, render the widget by site_key, and put the token into a hidden input that matches the rule above.

{{-- resources/views/components/captcha.blade.php --}}
@once @push('scripts')
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
@endpush @endonce
<div class="g-recaptcha" data-sitekey="{{ config('services.recaptcha.site_key') }}"></div>

For v3, use grecaptcha.execute on submit and inject the resulting token into a hidden field; verify the action string server-side to prevent token reuse across forms.

Feature-flag the rollout. Captcha failures block submissions — that's the highest-severity failure mode on a conversion-critical path. Ship behind config('features.captcha') with a bypass for internal/staging traffic, canary on one low-traffic form, then widen.

Fail-open vs. fail-closed per form. Contact form → fail-open (log, let it through). Registration/checkout guest → fail-closed. Put the policy on the verifier, not in each rule.

Testing. Stub CaptchaVerifier via $this->app->instance(...) in feature tests. Never hit Google from tests — they'll rate-limit you and slow the suite. Add one integration test that runs against real keys on a nightly build to catch broken siteverify contracts.

Gotchas and Tips

  • Do not try to boot the Symfony DI container inside Laravel. Even if you get EWZRecaptchaBundle registered via a Symfony kernel shim, its form type expects Symfony\Component\Form\FormBuilderInterface. Laravel request objects won't satisfy the type hints. You'll spend more time on the adapter than on writing the 100 lines from scratch.
  • Tokens are single-use and short-lived (~2 minutes). If you verify a token and then re-verify after a validation redirect (e.g., flashing old input back to the form), the second call fails. Verify as the last rule in the pipeline, or re-issue a fresh token on the redirect.
  • Never let the secret key reach rendered output. The bundle's default config is safe (secret used only server-side), but any custom blade you write will be tempting to pass both keys to the frontend — only site_key belongs in HTML. Audit for {{ config('services.recaptcha.secret_key') }} in views and for Log::info($response) in the verifier catching raw response bodies.
  • siteverify is a synchronous HTTPS call (50–200 ms typical). Set explicit timeouts on the Guzzle client (connect_timeout: 2, timeout: 3) and decide what happens on timeout — google/recaptcha returns false from isSuccess() on network failure by default, which means your form fails-closed. Log and alert on the error rate so you notice a Google outage rather than only noticing "registration is down."
  • v2 vs v3 semantics are not interchangeable. This bundle was written in the v2 era (g-recaptcha widget). v2 returns a boolean; v3 returns a score and an action. If you're porting, don't copy the boolean-only logic and expose it as v3-capable — getScore() and getAction() are mandatory checks for v3 or tokens can be replayed across forms. Anchor v3 thresholds in config (services.recaptcha.min_score) and monitor rejection rate as a product metric, because threshold drift silently blocks legitimate users.
  • The bundle's verify_url is hard-coded to https://www.google.com/recaptcha/api/siteverify. If you're building from the bundle's reference, keep that URL configurable (a RECAPTCHA_VERIFY_URL env var) so you can swap to a regional endpoint or a mock server in tests without code changes.
  • reCAPTCHA is not a rate limiter. It complements throttle:5,1 on the endpoint; it doesn't replace it. Bots that pass the checkbox (human farms, compromised browsers) will hammer your form at line rate. Run both.
  • Key rotation produces silent 100% rejection. Add a startup health check (or a scheduled job) that performs a known-bad verification against siteverify and asserts the response shape — if the secret rotates wrong, you'll find out in an alert instead of in the support inbox.
  • Accessibility and GDPR are the bundle's blind spot. reCAPTCHA v2 has documented screen-reader problems; the widget also sends user data to Google. If you serve EU traffic or have a11y commitments, capture consent and surface the alternative challenge path (Google provides an audio fallback; some EU deployments require a local captcha). The bundle has no hooks for this — you'll implement it yourself either way.
  • Extension points worth stealing from the bundle if you do port: the constraint carries a message option (match that for your $fail() string so translations work), and the validator accepts an injectable HttpClient interface (match that with an Http facade fake in tests so you can assert the outbound call shape).
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager