Google reCAPTCHA v3 using PHP, AJAX and WordPress

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', () => {
    const form = document.getElementById('cf8-form');
    const responseEl = document.getElementById('cf8-response');

    if (form) {
        form.addEventListener('submit', (event) => {
            event.preventDefault();
            responseEl.textContent = 'Sending...';

            const to = document.getElementById('contact-to').value;
            const id = document.getElementById('contact-id').value;
            const reference = document.getElementById('contact-reference').value;
            const name = document.getElementById('contact-name').value;
            const email = document.getElementById('contact-email').value;
            const phone = document.getElementById('contact-phone').value;
            const notes = document.getElementById('contact-notes').value;

            if (name && email && phone && notes) {
                grecaptcha.ready(() => {
                    grecaptcha.execute('cy08Qu0dkGIsCDBaXKf1cy08Qu0dkGIsCDBaXKf1', { action: 'create_comment' })
                        .then((token) => {
                            const hiddenInput = document.createElement('input');
                            hiddenInput.type = 'hidden';
                            hiddenInput.name = 'g-recaptcha-response';
                            hiddenInput.value = token;
                            form.prepend(hiddenInput);

                            const params = new URLSearchParams();
                            params.append('action', 'wppd_action_cf8');
                            params.append('to', to);
                            params.append('id', id);
                            params.append('reference', reference);
                            params.append('name', name);
                            params.append('email', email);
                            params.append('phone', phone);
                            params.append('notes', notes);
                            params.append('token', token);

                            fetch(ajaxVar.ajaxUrl, {
                                method: 'POST',
                                headers: {
                                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
                                },
                                body: params.toString()
                            })
                            .then(response => response.json())
                            .then(data => {
                                if (data.success) {
                                    responseEl.textContent = 'Email sent successfully!';
                                } else {
                                    responseEl.textContent = 'An error has occurred!';
                                }
                            })
                            .catch(() => {
                                responseEl.textContent = 'An error has occurred!';
                            });
                        });
                });
            } else {
                responseEl.textContent = 'An error has occurred!';
            }
        });
    }
});

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.

on in Blog | Last modified on

Related Posts