13

I have added two invisible recaptcha divs but when i saw the code in inspect element then only one invisible recaptcha added in my single page. My code is:

 <div id="captcha1" class="g-recaptcha"
      data-sitekey="your_site_key"
      data-callback="onSubmit"
      data-size="invisible"></div>
<div id="captcha2" class="g-recaptcha"
      data-sitekey="your_site_key"
      data-callback="onSubmit"
     ></div>

Get reference from Programmatically invoke recaptcha

Can you help me what am i doing wrong?

Jacob
  • 3,598
  • 4
  • 35
  • 56
Versha Gupta
  • 265
  • 2
  • 3
  • 10

7 Answers7

19

Below a more reliable solution to Peter and Alessandro answers when nesting elements.

<script>
$(".g-recaptcha").each(function() {
    var object = $(this);
    grecaptcha.render(object.attr("id"), {
        "sitekey" : "6LdwRC0UAAAAAK0hjA8O4y1tViGPk9ypXEH_LU22",
        "callback" : function(token) {
            object.parents('form').find(".g-recaptcha-response").val(token);
            object.parents('form').submit();
        }
    });
});
</script>

<form>
    <input type="text" name="example"/>
    <button id="captcha1" class="g-recaptcha">submit form 1</button>
</form>

<form>
    <input type="text" name="example"/>
    <button id="captcha2" class="g-recaptcha">submit form 2</button>
</form>

<script src='https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit' async defer></script>
user1788978
  • 306
  • 2
  • 4
  • 7
    Don't you need to wrap that first script in function onloadCallback() { ... } to match the onload=onloadCallback in the reCAPTCHA api.js URL? I ended up having to do that. Other than that, this worked beautifully. Thanks! – Bemisawa Jan 26 '18 at 17:39
  • This does not answer the question since you don't specify size: 'invisible' as paramenter for grecaptcha.render method – ZalemCitizen Feb 18 '21 at 17:35
6

Had the same problem. After some puzzling I got it to work.

Used the idea Alessandro provided, and made it so the form would automatically be submit on success.

<script type="text/javascript">
    var onloadCallback = function() {
        $(".g-recaptcha").each(function() {
            var el = $(this);
            grecaptcha.render($(el).attr("id"), {
                "sitekey" : SITE_KEY,
                "callback" : function(token) {
                    $(el).parent().find(".g-recaptcha-response").val(token);
                    $(el).parent().submit();
                }
            });
        });
    };
</script>

<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
Peter Bode
  • 222
  • 1
  • 9
  • This does not answer the question since you don't specify the size: 'invisible' in options when calling grecaptcha.render method. – ZalemCitizen Feb 18 '21 at 17:45
2

You must do explicit render on each submit button

<form>
    <button id="captcha1" class="g-recaptcha invisible-recaptcha">submit form 1</button>
</form>

<form>
    <button id="captcha2" class="g-recaptcha invisible-recaptcha">submit form 2</button>
</form>

<script>
    function verifyCaptcha(token){
        console.log('success!');
    };

    var onloadCallback = function() {
        $( ".invisible-recaptcha" ).each(function() {
            grecaptcha.render($( this ).attr('id'), {
                'sitekey' : $key,
                'callback' : verifyCaptcha
            });
        });
    };
</script>

<script src='https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit' async defer></script>
1

Multiple Invisible reCaptcha V2 on Single Page Dynamically

Github Code: https://github.com/prathameshsawant7/multiple-invisible-recaptcha

Step 1>

Add below 2 Js library on page

<!--  reCaptcha Library -->
<script type="text/javascript" src="https://www.google.com/recaptcha/api.js?render=explicit"></script>

<!--  Customized Init for invisible reCaptcha  -->
<script src="js/init_recaptcha.js" async defer></script>

Step 2>

Add below div's in respective forms.

<div id="recaptcha-form-1" style="display:none;"></div> <!--for Form 1-->
<div id="recaptcha-form-2" style="display:none;"></div> <!--for Form 2-->
<div id="recaptcha-form-3" style="display:none;"></div> <!--for Form 3-->

Step 3>

Create init_recaptcha.js

  • Step 1 - Initialize reCaptcha Site Key and Widget eg: widget_1 for Form 1
  • Step 2 - In init function add code to create form submit callback action.
  • Step 3 - Call renderInvisibleReCaptcha function by passing reCaptcha ID and createCallbackFn Response.

    "use strict";
    
    var PS = PS || {};
    var widget_1;var widget_2;var widget_3;
    var recaptcha_site_key = 'RECAPTCHA_SITE_KEY';
    
    if( typeof PS.RECAPTCHA === 'undefined' ) {
        (function (a, $) {
            var retryTime = 300;
            var x = {
                init: function(){
                    if(typeof grecaptcha != 'undefined'){
    
                        //For Form 1 Initialization
                        if($('#form1 #recaptcha-form-1').length > 0){
                            var callbackFn = {
                                action : function(){
                                    saveData('1'); //Here Callback Function
                                }
                            }
                            /*--- 'recaptcha-form-1' - reCaptcha div ID | 'form1' - Form ID ---*/
                            widget_1 = x.renderInvisibleReCaptcha('recaptcha-form-1',x.createCallbackFn(widget_1,'form1',callbackFn));
                        }
    
                                               //For Form 2 Initialization
                        if($('#form2 #recaptcha-form-2').length > 0){
                            var callbackFn = {
                                action : function(){
                                    saveData('2'); //Here Callback Function
                                }
                            }
                            /*--- 'recaptcha-form-2' - reCaptcha div ID | 'form2' - Form ID ---*/
                            widget_2 = x.renderInvisibleReCaptcha('recaptcha-form-2',x.createCallbackFn(widget_2,'form2',callbackFn));
                        }
    
                                                //For Form 3 Initialization
                        if($('#form3 #recaptcha-form-3').length > 0){
                            var callbackFn = {
                                action : function(){
                                    saveData('3'); //Here Callback Function
                                }
                            }
                            /*--- 'recaptcha-form-3' - reCaptcha div ID | 'form3' - Form ID ---*/
                            widget_3 = x.renderInvisibleReCaptcha('recaptcha-form-3',x.createCallbackFn(widget_3,'form3',callbackFn));
                        }
    
                    }else{
                        setTimeout(function(){ x.init();} , retryTime);
                    }
                },
                renderInvisibleReCaptcha: function(recaptchaID,callbackFunction){
                        return grecaptcha.render(recaptchaID, {
                                'sitekey'   : recaptcha_site_key,
                                "theme" : "light",
                                'size'      : 'invisible',
                                'badge' : 'inline',
                                'callback'  : callbackFunction
                            });
                },
                createCallbackFn: function (widget,formID,callbackFn) {
                    return function(token) {
                        $('#'+formID+' .g-recaptcha-response').val(token);
                        if($.trim(token) == ''){
                            grecaptcha.reset(widget);
                        }else{
                            callbackFn.action();
                        }
                    }
                }
            }
            a.RECAPTCHA = x;
        })( PS, $ );
    }
    
    $(window).load(function(){
        PS.RECAPTCHA.init();
    });
    

Step 4> Changes in Form Validation JS -

/* Execute respective Widget on form submit after form Validations  */
function formSubmit(form){
    var text = $.trim($('#text'+form).val());
    if(text != ''){
        switch(form){
            case '1' : grecaptcha.execute(widget_1); break;
            case '2' : grecaptcha.execute(widget_2); break;
            case '3' : grecaptcha.execute(widget_3); break;
        }
    }
}

Step 5> Validate reCaptcha from Server Side -

<?php
    define('RECAPTCHA_SECRET_KEY','KEY');
    /**
    *  @Desc:   To Validate invisible recaptcha from server-side
    *  @Param:  g-recaptcha-response value
    *  @Return: True/False
    **/
    if(!function_exists('check_recaptcha')){
        function check_recaptcha($recaptcha_response){
            $test = array ('secret' => RECAPTCHA_SECRET_KEY,'remoteip' => $_SERVER["REMOTE_ADDR"],'response' => $recaptcha_response);
            foreach ($test as $key => $value) {
                $req .= $key . '=' . urlencode(stripslashes($value)) . '&';
            }
            $req=substr($req, 0, strlen($req)-1);
            $path = 'https://www.google.com/recaptcha/api/siteverify?';
            $response = file_get_contents($path . $req);
            $responseData = json_decode($response);
            if($responseData->success){
                return true;            
            }else{
                return false;
            }
        }
    }

    // Validate reCaptcha
    if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == "POST" && !empty($_POST)) {
        $checkCapcha = false;
            $recaptcha = $_POST['g-recaptcha-response'];
                $checkCapcha = check_recaptcha($recaptcha);
                if($checkCapcha){
                    echo $_POST['textmsg']; exit;
                    /** Perform Actions Here (Add,Update,Delete etc) 
**/
                }
    else{
            echo “reCaptcha Error”;
        }
    }
    echo "failed";exit;
?>

Step 6> Reset Widget after Server Call -

// saveData will be automatically get called on grecaptacha.execute 
function saveData(form){
$.ajax( {
    type: 'POST',
    url:  $("#form"+form).attr( 'action' ),
    data: $("#form"+form).serialize(),
    success: function( response ) {
                switch(form){
            case '1' : grecaptcha.reset(widget_1); break;
            case '2' : grecaptcha.reset(widget_2); break;
            case '3' : grecaptcha.reset(widget_3); break;
            }
        }
    } );
}
1

You could use invisible recaptcha. On your button use a tag like " formname='rcaptchaformname' " to specify which form is to be submitted and hide a submit form input.

This allows you to keep the html5 form validation intact, one recaptcha, but multiple button interfaces. Just capture the "captcha" input value for the token key generated by recaptcha.

<script src="https://www.google.com/recaptcha/api.js" async defer ></script>

<div class="g-recaptcha" data-sitekey="yours" data-callback="onSubmit" data-size="invisible"></div>
<script>

$('button').on('click', function () { formname = '#'+$(this).attr('formname');
    if ( $(formname)[0].checkValidity() == true) { grecaptcha.execute(); }
    else { $(formname).find('input[type="submit"]').click() }
    });

var onSubmit = function(token) {
    $(formname).append("<input type='hidden' name='captcha' value='"+token+"' />");
    $(formname).find('input[type="submit"]').click()
    };
</script>
eatmeimadanish
  • 3,809
  • 1
  • 14
  • 20
1

@Prathamesh Sawant solution is complete but if you don't need this to be dynamical you can simplify the process :

1. Load recaptcha library

<script src='https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit' async defer></script>

2. Add your placeholder in the HTML of each form

<form ...>
...
<div class="g-recaptcha"></div>
...
</form>

3. In your JS, write the callback you called as parameter when loading reCaptcha script :

window.onloadCallback = function() {
      $('.g-recaptcha').each(function(i, v) {
        const $placeholder = $(this)

        // Define a widget id that will be used by every grecaptcha method 
        // to keep track of which form is being used
        $placeholder.data('widget-id', i)

        grecaptcha.render( this, {
          callback: function( token ) {

            return new Promise(function(resolve, reject) {
              if( grecaptcha === undefined ) {
                console.log( 'reCaptcha not defined' )
                reject()
              }

              var response = grecaptcha.getResponse( $placeholder.data('widget-id') )
              if( !response ) {
                console.log( 'Could not get reCaptcha response' )
                reject()
              }

              const $form = $placeholder.closest('form')

              $form.find('.g-recaptcha-response').val( token )
              $form
              // Add a class that will be used to bypass the prevented submit event
              .addClass('recap-done')
              // submit by clicking the submit button of your form
              .find('[type="submit"]').trigger('click')
              resolve()
              grecaptcha.reset( $placeholder.data('widget-id') )
            })
          },
          sitekey: RECAPTCHA_KEY,
          size: 'invisible', // This makes the real reCaptcha V2 Invisible
        })
      })
    }

Note: I use a promise to prevent a Promise null issue, documented here.

4. Add a submit event handler for all forms

$('form').on('submit', function(e) {
      const $form = $(this)

      // 2nd pass (when the submit is triggered within the callback)
      // This bypasses the event to be prevented a new time -> form is really submitted
      if( $form.hasClass('recap-done') ) {
        return
      }

      // 1st pass: prevents the form to be submitted and do the reCaptcha process
      const $placeholder = $form.find('.g-recaptcha')
      if( $placeholder.length > 0 ) {
        e.preventDefault()

        grecaptcha.execute( $placeholder.data('widget-id') )
      }
    })

5. Enjoy

ZalemCitizen
  • 474
  • 7
  • 13
0

For Google Recaptcha V3 Multiple Recpatchas on a Page:

HTML:

<script src="https://www.google.com/recaptcha/api.js?render=explicit&onload=grecaptcha_onload"></script>
<script>
    function grecaptcha_onload() {
        $('.g-recaptcha-response').each(function( k, v ) {
            var submit = $(v).closest("form").find('[type="submit"]');
            grecaptcha.render( submit[0], {
                'sitekey' : SITE_KEY,
                'callback' : function( token ) {
                    $(v).closest("form").find('.g-recaptcha-response').val( token );
                    $(v).closest("form").submit();
                },
                'size' : 'invisible',
            });
        });
    }
</script>
<form>
    <input type="hidden" value="" name="g-recaptcha-response" class="g-recaptcha-response" />
    <button type="submit">Submit</button>
</form>
<form>
    <input type="hidden" value="" name="g-recaptcha-response" class="g-recaptcha-response" />
    <button type="submit">Submit</button>
</form>

PHP:

    function validate()
    {
        if( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
            // Build POST request:
            $recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
            $recaptcha_response = $_POST[ 'g-recaptcha-response' ];
            $recaptcha = file_get_contents( $recaptcha_url . '?secret=' . self::SECRET_KEY . '&response=' . $recaptcha_response );
            $recaptcha = json_decode( $recaptcha );

            if( $recaptcha->success == true ){
                // Take action based on the score returned:
                if( $recaptcha->score >= 0.1 ) {
                    return true;
                } else {
                    $this->errors[] = 'Something went wrong with sumbitting the form.<br />Please try again!';

                    foreach( (array) $recaptcha as $key => $value ) {
                        $this->errors[] = 'Key: ' . $key . ' Value: ' . $value;
                    }

                    return false;
                }
            } else { // there is an error /
                switch ( $recaptcha->{'error-codes'}[0] ) {
                    case 'missing-input-secret':
                        $this->errors[] = 'The secret parameter is missing';
                        break;
                    case 'invalid-input-secret':
                        $this->errors[] = 'The secret parameter is invalid or malformed';
                        break;
                    case 'missing-input-response':
                        $this->errors[] = 'The response parameter is missing';
                        break;
                    case 'invalid-input-response':
                        $this->errors[] = 'The response parameter is invalid or malformed';
                        break;
                    case 'bad-request':
                        $this->errors[] = 'The request is invalid or malformed';
                        break;
                    case 'timeout-or-duplicate':
                        $this->errors[] = 'The response is no longer valid: either is too old or has been used previously';
                        break;
                    default:
                        break;
                }

                return false;
            }
        }
    }