Token: {{ captchaToken }}
``` --- ## Laravel + Inertia.js integration ### Front-end (ContactForm.vue) See [`examples/inertia/ContactForm.vue`](examples/inertia/ContactForm.vue) for a full working example using `useForm` from `@inertiajs/vue3`. Key points: 1. Store the verified token in `form.recaptcha_token` 2. Always reset the widget after a successful **or** failed submission **Walkthrough: server rejects an expired or reused token.** This is the scenario that actually breaks in production, so here's exactly what happens, step by step, with the example above and [`ContactController.php`](examples/laravel/ContactController.php): 1. The token expires (idle too long) or was already used in a prior request. The checkbox still looks checked; nothing in the UI has changed yet. 2. The user submits. Your controller's `Http::asForm()->post(...)` call to Google comes back with `success: false`, so it throws a `ValidationException` on the `recaptcha_token` key. Laravel turns that into a 422 with `errors: { recaptcha_token: [...] }`. 3. Inertia's `form.post()` sees the 422 and calls `onError`, populating `form.errors.recaptcha_token` (which the template renders) - it does **not** touch `form.name`, `form.email`, or `form.message`. Inertia only clears form data when you explicitly call `form.reset()`, and this example deliberately doesn't call it here. 4. Our `onError` handler calls `recaptchaRef.value?.reset()`, which reloads the widget and clears the local `token`/`isVerified` state. The checkbox goes back to unchecked. 5. The user sees their name/email/message exactly as they left them, plus the recaptcha error, re-checks the box, and resubmits. Nothing they already typed is lost. The one thing to get right: don't call `form.reset()` in the same handler that resets the widget on error. That's what wipes the rest of the form along with the stale token. ### Back-end (ContactController.php) See [`examples/laravel/ContactController.php`](examples/laravel/ContactController.php). Add to **`config/services.php`**: ```php 'recaptcha' => [ 'site_key' => env('RECAPTCHA_SITE_KEY'), 'secret_key' => env('RECAPTCHA_SECRET_KEY'), ], ``` Add to **`.env`** (server-side): ```ini RECAPTCHA_SITE_KEY=your_site_key RECAPTCHA_SECRET_KEY=your_secret_key # Expose site key to Vite: VITE_RECAPTCHA_SITE_KEY="${RECAPTCHA_SITE_KEY}" ``` Verify the token inside your controller: ```php $response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [ 'secret' => config('services.recaptcha.secret_key'), 'response' => $request->input('recaptcha_token'), 'remoteip' => $request->ip(), ]); if (! $response->json('success')) { throw ValidationException::withMessages([ 'recaptcha_token' => 'reCAPTCHA verification failed. Please try again.', ]); } ``` --- ## Dark theme example ```vue