'Parsley + recaptcha v3 promise

I'm not able to get the following code to work:

$(function() {
    function verifyRecaptcha() {
        return new Promise((resolve, reject) => {
            grecaptcha.execute('RECAPTCHA_SITE_KEY', {action: 'submit'}).then(function(token) {
                postJson('frontend/verify_recaptcha/'+token, null, function(d) {
                    if (d.pass) {
                        resolve('Verified');
                    } else {
                        reject('Not verified');
                    }
                });
            });
        });
    };

    var parsleyForm = $('#enquiry-form').parsley().on('form:submit', function(formInstance) {
        var validationResult = false;

        verifyRecaptcha().then(function() {
            console.log('VERIFIED');
            validationResult = true
        }).catch(function() {
            console.log('NOT VERIFIED');
            validationResult = false;
        });

        console.log('Resultat: '+validationResult);

        return validationResult;
    });
});

I already tried a lot with await/async, but it always submits the form.

I think I cannot be the only one who needs to implement Parsley with recaptcha v3? Any ideas? :)



Solution 1:[1]

I use this:

// Returns the current form that the recaptcha widget is appended on
const form = getRecaptchaForm();
// I use a different input from the standard 'g-recaptcha-response', but you can use the one appended by recaptcha
const input = form.elements.namedItem('g-recaptcha-response');
// Keep track of validation time
let lastToken, lastTokenDate;

// Use a polyfilled version of form.requestSubmit (https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit)
// See Safari bug: https://webkit.org/b/197958
const requestSubmit = function () {
    let submitter = form.querySelector('input[type=\"submit\"],button[type=\"submit\"]');
    if (!submitter) {
        submitter = document.createElement('input');
        submitter.type = 'submit';
        submitter.hidden = true;
        form.appendChild(submitter);
        submitter.click();
        form.removeChild(submitter);
    }
    else {
        submitter.click();
    }
};

// This submits the form as if the user clicked the submit button
const submitRecaptcha = function (token, error) {
    lastToken = input.value = token;
    lastTokenDate = Date.now();
    if (error) {
        console.warn('Recaptcha error', error);
    }
    requestSubmit();
};

// The submit handler must be bound on the form itself (not as a delegate from a parent)
form.addEventListener('submit', function handleRecaptcha(e) {
    // Validation token is still valid
    if ((input.value && (input.value === lastToken) && (lastTokenDate > (Date.now() - (2 * 60 * 1000)))) || (e.defaultPrevented === true) || (e.target !== form)) {
        return;
    }

    try {
        // Stop form submission and also prevent other handlers from triggering until the recaptcha promise is resolved
        e.preventDefault();
        e.stopPropagation();

        // Validate the recaptcha
        grecaptcha.ready(function () {
            grecaptcha.execute('RECAPTCHA_SITE_KEY', { action: 'submit' })
            .then(function (token) {
                // Do something else with the token before submitting the form
                postJson('frontend/verify_recaptcha/' + token, null, function(d) {
                    if (d.pass) {
                        // Now we trigger the other submit handlers (will also trigger our `handleRecaptcha` handler, but will return because the lastTokenDate is still valid)
                        submitRecaptcha(token);
                    }
                    else {
                        // Ensure the other submit handlers get triggered
                        submitRecaptcha('error', d);
                    }
                });
            })
            .catch(function (e) {
                // Ensure the other submit handlers get triggered
                submitRecaptcha('error', e);
            });
        });
    }
    catch (e) {
        // Ensure the other submit handlers get triggered
        submitRecaptcha('error', e);
    }
}, /* Must use capture phase to prevent other custom events from triggering ahead of this */ true);

The implementation is not parsley.js aware, you can theoretically use any validation library, but I tested it on a form that had parsley.js bound to it. The recaptcha checkbox decoration is handled by google, so there is no need to create a custom parsley.js async validator to handle it yourself.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1