Google reCAPTCHA v3 using PHP, AJAX and WordPress

on in Blog
Last modified on

After having issues with Google reCAPTCHA v2 and a custom WordPress implementation, I started coding an optimized version using the invisible version of Google reCAPTCHA v3.

Based on the post ID variable, I have added several extra fields to the form (not shown here).

Optionally, there’s a handy [cf8] shortcode.

I have named this form feature CF8.

Prerequisites

The prerequisites for this contact form are, obviously, a Google reCAPTCHA v3 (the invisible one). Create one here.

The WordPress Function:

/**
 * CF8: Quick contact form with Google reCAPTCHA v3
 * 
 * @return HTML string
 */
function wppd_cf8() {
    global $post;

    $postId = $post->ID;
    $emailTo = 'email@example.com';

    $out = '';

    $out = '<form id="cf8-form" method="post">
        <p>
            <input type="hidden" name="contact_to" id="contact-to" value="' . $emailTo . '" readonly>
            <input type="hidden" name="contact_id" id="contact-id" value="' . $postId . '">
            <input type="text" name="contact_name" id="contact-name" placeholder="Full Name *">
            <input type="email" name="contact_email" id="contact-email" placeholder="Email *">
            <input type="text" name="contact_phone" id="contact-phone" placeholder="Phone">
            <textarea name="contact_notes" id="contact-notes" rows="3" placeholder="Your Enquiry *"></textarea>
        </p>

        <p>
            <input type="submit" name="cf8_send" id="cf8-send" value="Send" data-ip="1.2.3.4">
        </p>

        <div id="cf8-response"></div>
    </form>';

    return $out;
}
add_shortcode( 'cf8', 'wppd_cf8' );

The WordPress Action

function wppd_action_cf8() {
    $to = filter_input(INPUT_POST, 'to', FILTER_VALIDATE_EMAIL);
    $pid = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
    $name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING);
    $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
    $phone = filter_input(INPUT_POST, 'phone', FILTER_SANITIZE_STRING);
    $notes = filter_input(INPUT_POST, 'notes', FILTER_SANITIZE_STRING);
    $captcha = filter_input(INPUT_POST, 'token', FILTER_SANITIZE_STRING);

    if (!$captcha) {
        echo '<h2>Please check the the captcha form.</h2>';
        exit;
    }

    $secretKey = "BAXtnHN2u5rGtplZ4n5gBAXtnHN2u5rGtplZ4n5g";
    $ip = $_SERVER['REMOTE_ADDR'];

    $url = 'https://www.google.com/recaptcha/api/siteverify';
    $data = [
        'secret' => $secretKey,
        'response' => $captcha
    ];

    $options = [
        'http' => [
            'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
            'method'  => 'POST',
            'content' => http_build_query($data)
        ]
    ];

    $context  = stream_context_create($options);
    $response = file_get_contents($url, false, $context);
    $responseKeys = json_decode($response,true);

    header('Content-type: application/json');

    if ($responseKeys["success"] && $responseKeys["score"] >= 0.5) {
        $body = '<h3>A new quick contact has been sent from <a href="' . get_permalink($pid) . '">' . get_the_title($pid) . '</a>!</h3>
        <p>Contact details:</p>
        <p>
            <b>Name:</b> ' . $name . '<br>
            <b>Email:</b> ' . $email . '<br>
            <b>Telephone:</b> ' . $phone . '<br><br>
            <b>Sent From:</b> <a href="' . get_permalink($pid) . '">' . get_the_title($pid) . '</a><br>
            <b>Post ID:</b> ' . $pid . '<br><br>
            <b>Notes:</b> ' . $notes . '
        </p>

        <p>
            <small>Email sent to ' . $to . ' on ' . date('Y-m-d, H:i') . '</small><br>
            <small>Google reCAPTCHA score: ' . $responseKeys['score'] . '</small>
        </p>';

        $headers[] = "Content-Type: text/html;";

        $subjectLine = 'CF8 Contact Request - ' . get_the_title($pid);

        wp_mail($to, 'CF8 Contact Request', $body, $headers);

        // Return response
        echo json_encode([
            'success' => 'true',
            'score' => $responseKeys["score"]
        ]);
    } else {
        echo json_encode([
            'success' => 'false',
            'score' => $responseKeys["score"]
        ]);
    }

    wp_die();
}
add_action('wp_ajax_wppd_action_cf8', 'wppd_action_cf8');
add_action('wp_ajax_nopriv_wppd_action_cf8', 'wppd_action_cf8');

The CSS

#cf8-response {
    font-size: 14px;
}

The JavaScript

document.addEventListener('DOMContentLoaded', () => {
    /**
     * CF8: Quick contact form with Google reCAPTCHA v3
     */
    if (document.getElementById('cf8-form')) {
        document.getElementById('cf8-form').addEventListener('submit', (event) => {
            event.preventDefault();

            document.getElementById('cf8-response').innerHTML = 'Sending...';

            let to = jQuery("#contact-to").val(),
                id = jQuery("#contact-id").val(),
                reference = jQuery("#contact-reference").val(),
                name = jQuery("#contact-name").val(),
                email = jQuery("#contact-email").val(),
                phone = jQuery("#contact-phone").val(),
                notes = jQuery("#contact-notes").val();

            if (name !== '' && email !== '' && phone !== '' && notes !== '') {
                grecaptcha.ready(() => {
                    grecaptcha.execute("cy08Qu0dkGIsCDBaXKf1cy08Qu0dkGIsCDBaXKf1", {
                        action: "create_comment"
                    }).then((token) => {
                        document.getElementById('cf8-form').insertAdjacentHTML('afterBegin', '<input type="hidden" name="g-recaptcha-response" value="' + token + '">');

                        let request = new XMLHttpRequest(),
                            requestString = '';

                        requestString += '&to=' + to;
                        requestString += '&id=' + id;
                        requestString += '&reference=' + reference;
                        requestString += '&name=' + name;
                        requestString += '&email=' + email;
                        requestString += '&phone=' + phone;
                        requestString += '&notes=' + notes;
                        requestString += '&token=' + token;

                        request.open('POST', ajaxVar.ajaxUrl, true);
                        request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
                        request.onload = () => {
                            response = JSON.parse(request.response);

                            if (response.success) {
                                document.getElementById("cf8-response").innerHTML = "Email sent successfully!";
                            } else {
                                document.getElementById("cf8-response").innerHTML = "An error has occured!";
                            }
                        };
                        request.send('action=wppd_action_cf8' + requestString);
                    });;
                });
            } else {
                document.getElementById("cf8-response").innerHTML = "An error has occured!";
            }
        });
    }
});

And that is all!

Make sure you create a custom plugin or integrate it into your existing plugin. Dumping the code in functions.php is always a bad idea.

Related posts