How I Integrated reCAPTCHA v3 with AJAX in my Laravel 11 project

Are you adding reCAPTCHA validation to your website forms and Google for the first time and google’s guide is going right above your head?

That was me, today morning! But I finally figured it out and obviously I’d like to share what errors I faced and how I solved it.

I am working on Laravel 11, and this is my step by step guide of how i integrated google reCAPTCHA v3 in my project!

First, We’ll make a simple form that is submitting correctly which will be our view page. (in my case I’m using AJAX to submit form)

Second, We’ll go to https://www.google.com/recaptcha/  to get two reCAPTCHA keys– Site key and secret key. Go to the Admin Console at the top right and register your website. You’ll have to choose between v2 and v3 according to your need and preference. This guide is for v3. I’m gonna fast-forward to you completing the registration and now you have 2 keys.

(In my case i was already given the keys by my senior)

Now, this is where the fun begins!

Third, We’re gonna be working on 3 files– the controller, the form view, and the JavaScript file (you can have your script included in your view file only, then you’ll have mainly 2 files to play with)

  • In your html <head> tag, add this and replace with your site key —
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>

  • Inside your form, above your submit button, add a hidden input field that will carry your recaptcha’s token.
<input type="hidden" name="g-recaptcha-response" id="grecaptcha">

NOTE- If you’re working on local environment then you have to enable localhost in your recaptcha settings.

  • In your JavaScript file, add this code and modify fields and elements names according to your project.
$(document).ready(function() {

//im using ajax for form submission
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});

$('#my-form').on('submit', function(e) {
    e.preventDefault(); // this will prevent the form from submitting normally

    // perform the reCAPTCHA verification and you will get a token
    grecaptcha.ready(function() {
        grecaptcha.execute('YOU_SITE_KEY', { action: 'submit'  }).then(function(token) {

   // add that reCAPTCHA token in the hidden input that we created in the form view
        var recaptchaResponse = document.getElementById('grecaptcha');
        recaptchaResponse.value = token;

// Send the form data via AJAX
        $.ajax({
            url: $('#my-form').attr('action'), // The form's action URL
            type: 'POST',
            data: $('#my-form').serialize(),
            success: function(response) {
                if (response.status === 'success') {
                    $('#success-message').text(response.message).show();
                }
                else{
                    $('#error-message').text(response.message).show();
                }
            },
            error: function(xhr) {
                        var errorMessage = xhr.responseJSON ? xhr.responseJSON.message : "An error occurred. Please try again later.";
                        $('#error-message').text(errorMessage).show();
           }
        });
     });
  });
});
});

  • Fifth, in your controller, where the form submission / POST request is being handled, add this code
$recaptchaSecret = 'YOUR_SECRET_KEY';
$recaptchaResponse = $_POST['g-recaptcha-response'];
if (empty($recaptchaResponse)) {
die('reCAPTCHA response is missing!');
}
else{
$responseFromCaptcha = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
            'secret' => $recaptchaSecret,
            'response' => $recaptchaResponse,
 ]);
        $responseKeys = json_decode($responseFromCaptcha, true);
if (intval($responseKeys["success"]) !== 1) {
            return response()->json([
                'status' => 'error',
                'message' => 'reCAPTCHA verification failed. Please try again.'
            ], 422);
        }
            try {

                // ADD YOUR SUBMITTING LOGIC--- FOR EXAMPLE HERE IM ADDING A NEW USER

                $user = new Users;
                
                // extract all related data from request 
                $users->fill($validator->validated());

                $users->save();
                
               // set default status with form submitted message
		return response()->json([
			    	'status' => 'success',
			    	'message' => "Form submitted successfully!"
			    ]);
            }catch (\Exception $e) {
                $errorM = 'Form submission error: ' . $e->getMessage();
                return response()->json([
                    'status' => 'error',
                    'message' => $errorM
                ], 500);
            }
 }

  • In your form view, add these divs to display the success or error messages
<div id="error-message" class="alert alert-danger" style="display: none;"></div>
<div id="success-message" class="alert alert-success text-center m-3" style="display: none;"></div>

And— Voila! My code was working as smooth as butter <3

Bonus, I’m gonna list the mistakes I was making and how I solved them (don’t worry the code above is totally correct and working.)

I’m using Chromium Browser, by the way.

  • First Mistake: I wasn’t passing the recaptcha token from the JS file to my form’s hidden input field correctly.
  • Solve: I clicked Ctrl+shift+i , then opened the Network tab, then clicked on my form submission function (submit_leads_form), then checked Payload, there I could see that my token field was empty and that means the issue was with how I was passing the token.

  • Second Mistake: After I fixed the token passing thing, the payload was showing that correct and complete data was passing still i couldn’t see the correct ajax success message.
  • Solve: Then I checked the Response tab beside Payload and checked the json response status being passed to ajax. And along with the json response, i was mistakenly passing an echo statement  — echo (“reCAPTCHA verification success <br>”); — which was meant for testing but I forgot to remove it.

I hope this helped you, have a calm and beautiful day ahead!

Toodles, Pratibha <3

Scroll to top