javascriptpromiserecaptchainvisible-recaptcha

How would I handle Google's Recaptcha with Promises?


What I want to do is make a link that when clicked opens Google's Invisible Recaptcha. That part is working fine. But I also want to catch errors, and that's where it fails. It is failing in both the latest versions of Firefox and Chrome with an error that says, "uncaught exception: undefined" (Firefox) "uncaught (in promise) undefined" (Chrome) and traces back to the line where reject() is called. The call to alert('Error') never fires.

What am I doing wrong? Is there another way to do this?

<script>
var onloadCallback, onerrorCallback;
var promise = new Promise(function(resolve, reject) {
    onerrorCallback = function() {
        reject();
    }
    onloadCallback = function() {
        var form = document.createElement('form'),
            recaptcha = document.createElement('div');
        form.method = 'post';
        resolve(grecaptcha.render(recaptcha, {
            sitekey: 'INVISIBLE RECAPTCHA SITE KEY',
            size: 'invisible',
            callback: function() {
                form.submit()
            }
        }));
        form.appendChild(recaptcha);
        document.body.appendChild(form);
    }
})

function userClick() {
    promise
        .then(grecaptcha.execute)
        .catch(function(){alert('Error')});
}
</script>
<script async defer src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit&hl=en" onerror="onerrorCallback()"></script>
<p>Please <a href="javascript:userClick()">click here</a> to solve the recaptcha</p>

Solution

  • I found the problem. Here is how I fixed it:

    <script>
    var onloadCallback, onerrorCallback;
    var promise = new Promise(function(resolve, reject) {
        onerrorCallback = function() {
            reject();
        }
        onloadCallback = function() {
            var form = document.createElement('form'),
                recaptcha = document.createElement('div');
            form.method = 'post';
            resolve(grecaptcha.render(recaptcha, {
                sitekey: 'INVISIBLE RECAPTCHA SITE KEY',
                size: 'invisible',
                callback: function() {
                    form.submit()
                }
            }));
            form.appendChild(recaptcha);
            document.body.appendChild(form);
        }
    })
    
    function userClick() {
        promise
            .then(function(id) {
                grecaptcha.execute(id);
            })
            .catch(function(){alert('Error')});
    }
    </script>
    <script async defer src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit&hl=en" onerror="onerrorCallback()"></script>
    <p>Please <a href="javascript:userClick()">click here</a> to solve the recaptcha</p>
    

    The problem was in the then() call. Due to grecaptcha being not defined, JavaScript halted and never made it to the catch() call. The error message printed on the console had nothing to do with that though.

    I fixed it by wrapping the call to grecaptcha.execute in an anonymous function.