Вступление
Эта статья — продолжение статьи «Как создать ajax-форму в WordPress без плагинов» — там мы научились создавать простую форму ajax для WordPress, но не научились защищать ее от СПАМ-ботов.
Защита от СПАМа важна, иначе вы рискуете в один момент обнаружить полнейший завал почты бесполезными письмами, который будет трудно разгрести. Благо, есть эффективные и доступные всем способы. Рассмотрим вариант с Google reCaptcha, и вариант с Yandex Smart Captcha.
Yandex Smart Captcha
Для начала необходимо создать саму капчу. О том, как это сделать я написал в этой статье, в разделе «Регистрация и подготовка».
Далее я предполагаю, что у вас уже есть ключ клиента и ключ сервера.
Далее нам нужно подключить JavaScript яндекс капчи. Добавим в functions.php
следующий код:
<?php
//Добавим тег defer для подключаемых JS-скриптов - без этого не будет работать
function add_defer_tag_script( $tag, $handle, $source ) {
$path = parse_url( $_SERVER['REQUEST_URI'] );
if ( strripos( $path['path'], 'wp-admin/' ) ) {
return $tag;
}
return str_replace( "\">", "\" defer>", $tag );
}
add_filter( 'script_loader_tag', 'add_defer_tag_script', 1, 3 );
//Подключим JS-скрипт яндекс капчи
function feodoraxis_enqueue_scripts() {
wp_enqueue_script(
'ya-smart-captcha',
'https://smartcaptcha.yandexcloud.net/captcha.js',
[],
'1.0'
);
}
add_action( 'wp_enqueue_scripts', 'feodoraxis_enqueue_scripts', 100 );
Далее в нашу форму нужно добавить такой код:
<div id="captcha-container" class="smart-captcha" data-sitekey="<ключ_клиента>" ></div>
Должно получиться так:
<?php
//Зафиксируем публичный ключ в константе
define( "CLIENT_KEY", "ysc1_YIrsc28ZIwCNtdzCF6WbOax3XTMtHl1S8MZsbrXf30e89098" );
function feodoraxis_recall_form() {
return '<form method="POST" data-admin-ajax="' . admin_url( "admin-ajax.php" ) . '" action="' . $_SERVER['REQUEST_URI'] . '" id="recall_form">
<input type="text" name="your-name" value="">
<input type="email" name="your-email" value="">
<button>Отправить</button>
<div id="captcha-container" class="smart-captcha" data-sitekey="' . CLIENT_KEY . '" ></div>
</form>';
}
add_shortcode( 'feodoraxis_recall_form', 'feodoraxis_recall_form' );
Если вы всё сделали правильно, то капча должна отобразиться под кнопкой «Отправить».
Но пока капча не работает полноценно, и форма отправляет любые запросы — независимо от того, прошел юзер капчу или нет.
Чтобы капча заработала полноценно, необходимо добавить функцию проверки входящего токена капчи, а также модифицировать функцию отправки сообщения feodoraxis_recall_form()
. В этом листинге я оставлю комментарии только в тех местах, которые мы изменили:
<?php
//Добавим секретный ключ
define( "SERVER_KEY", "ysc2_YIrsc28ZIwCNtdzCF6WbijyIbqngEOt0gUnavHEYfdcdc897" );
//Опишем функцию проверки токена
function token_validate( string $token ):bool {
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://smartcaptcha.yandexcloud.net/validate?secret=' . SERVER_KEY . '&ip=' . $_SERVER['REMOTE_ADDR'] . '&token=' . $token,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'GET',
] );
$json_validation = curl_exec( $curl );
curl_close( $curl );
$response = json_decode( $json_validation, true );
if ( json_last_error() || $response['status'] !== 'ok' ) {
return false;
}
return true;
}
//Модифицируем эту функцию
function feodoraxis_recall_form_request() {
if ( $_SERVER['REQUEST_METHOD'] != 'POST' || ! wp_doing_ajax() )
wp_send_json_error();
}
//Если токен капчи не передан, то сообщение не отправляем
if ( ! isset( $_POST['smart-token'] ) || empty( trim( $_POST['smart-token'] ) ) ) {
wp_send_json_error( [
'error' => "Необходимо пройти капчу"
] );
}
//Если токен есть - то проведем его валидацию
$smart_token = $_POST['smart-token'];
if ( ! token_validate( $smart_token ) ) {
wp_send_json_error( [
'error' => "Ошибка: необходимо пройти капчу"
] );
}
if ( ! isset( $_POST['your-name'] ) || ! isset( $_POST['your-email'] ) || empty( trim( $_POST['your-name'] ) ) || empty( trim( $_POST['your-email'] ) ) ) {
wp_send_json_error( [
'error' => "Необходимо заполнить все поля формы"
] );
}
if ( ! is_email( $_POST['your-email'] ) ) {
wp_send_json_error( [
'error' => "Указан некорректный email"
] );
}
$your_name = sanitize_text_field( $_POST['your-name'] );
$your_email = sanitize_text_field( $_POST['your-email'] );
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-type: text/html; charset=utf-8\r\n";
$headers .= "From: Андрей Смородин <no-reply@" . $_SERVER["HTTP_HOST"] . ">\r\n";
$message = '<b>Имя:</b> ' . $your_name . '<br>';
$message .= '<b>Email:</b> ' . $your_email . '<br>';
$result = wp_mail( 'info@feodoraxis.ru', 'Заявка из формы обратной связи', $message, $headers )
if ( $result ) {
wp_send_json_success( [
'message' => "Сообщение отправлено. Мы свяжемся с вами в ближайшее время."
] );
}
wp_send_json_error( [
'error' => "Письмо не было отправлено"
] );
}
add_action( 'wp_ajax_' . 'recall_form', 'feodoraxis_recall_form_request' );
add_action( 'wp_ajax_nopriv_' . 'recall_form', 'feodoraxis_recall_form_request' );
Отлично! Если вы все сделали правильно, то теперь капча будет работать и защищать вашу форму от спама!
Еще у яндекс капчи есть невидимая версия. Но у меня так и не вышло ее внедрить.
Листинг кода для Yandex Smart Captcha
<?php
//Зафиксируем публичный ключ в константе
const CLIENT_KEY = "ysc1_YIrsc28ZIwCNtdzCF6WbOax3XTMtHl1S8MZsbrXf30e89098";
//Добавим секретный ключ
const SERVER_KEY = "ysc2_YIrsc28ZIwCNtdzCF6WbijyIbqngEOt0gUnavHEYfdcdc897";
//Добавим тег defer для подключаемых JS-скриптов - без этого не будет работать
function add_defer_tag_script( $tag, $handle, $source ) {
$path = parse_url( $_SERVER['REQUEST_URI'] );
if ( strripos( $path['path'], 'wp-admin/' ) ) {
return $tag;
}
return str_replace( "\">", "\" defer>", $tag );
}
add_filter( 'script_loader_tag', 'add_defer_tag_script', 1, 3 );
//Подключим JS-скрипт яндекс капчи
function feodoraxis_enqueue_scripts() {
wp_enqueue_script(
'ya-smart-captcha',
'https://smartcaptcha.yandexcloud.net/captcha.js',
[],
'1.0'
);
}
add_action( 'wp_enqueue_scripts', 'feodoraxis_enqueue_scripts', 100 );
function feodoraxis_recall_form() {
return '<form method="POST" data-admin-ajax="' . admin_url( "admin-ajax.php" ) . '" action="' . $_SERVER['REQUEST_URI'] . '" id="recall_form">
<input type="text" name="your-name" value="">
<input type="email" name="your-email" value="">
<button>Отправить</button>
<div id="captcha-container" class="smart-captcha" data-sitekey="' . CLIENT_KEY . '" ></div>
</form>';
}
add_shortcode( 'feodoraxis_recall_form', 'feodoraxis_recall_form' );
function feodoraxis_show_js_for_form() {
?>
<script>
document.addEventListener("DOMContentLoaded", () => { //Исполняем скрипт только когда загрузится страница, заодно оборачиваем скрипт в свое пространство
const recallForm = document.querySelector('#recall_form');
//Если формы нет - то дальше ничего не делаем
if (!recallForm) {
return;
}
recallForm.addEventListener('submit', (event) => {
//Отменим стандартное поведение формы
event.preventDefault();
// Получим данные формы
const formData = new FormData(recallForm);
//Обязательно добавляем значение action, которое мы будем использовать для обработки запроса на стороне сервера
formData.append('action', 'recall_form');
//Создадим объект запроса
const request = new XMLHttpRequest();
//Откроем запрос, определим метод и адрес куда его отправить, из атрибута data-admin-ajax формы
request.open("POST", recallForm.getAttribute("data-admin-ajax"));
//Отпрвим запрос
request.send(formData);
//При получении ответа от сервера обработаем его
request.onload = () => {
//Получим ответ в виде JSON и преобразуем его в объект для удобства
const resultJson = request.responseText;
const result = JSON.parse(resultJson);
//При успешной обработке запроса выведем сообщение сервера
if (result.success === true) {
alert(result.data.message);
} else { //В случае неудачи выведем информацию об ошибке
alert(result.data.error);
}
};
});
});
</script>
<?php
}
add_action( "wp_footer", "feodoraxis_show_js_for_form", 1000 );
//Опишем функцию проверки токена
function token_validate( string $token ):bool {
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://smartcaptcha.yandexcloud.net/validate?secret=' . SERVER_KEY . '&ip=' . $_SERVER['REMOTE_ADDR'] . '&token=' . $token,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'GET',
] );
$json_validation = curl_exec( $curl );
curl_close( $curl );
$response = json_decode( $json_validation, true );
if ( json_last_error() || $response['status'] !== 'ok' ) {
return false;
}
return true;
}
//Модифицируем эту функцию
function feodoraxis_recall_form_request() {
if ( $_SERVER['REQUEST_METHOD'] != 'POST' || ! wp_doing_ajax() )
wp_send_json_error();
}
//Если токен капчи не передан, то сообщение не отправляем
if ( ! isset( $_POST['smart-token'] ) || empty( trim( $_POST['smart-token'] ) ) ) {
wp_send_json_error( [
'error' => "Необходимо пройти капчу"
] );
}
//Если токен есть - то проведем его валидацию
$smart_token = $_POST['smart-token'];
if ( ! token_validate( $smart_token ) ) {
wp_send_json_error( [
'error' => "Ошибка: необходимо пройти капчу"
] );
}
if ( ! isset( $_POST['your-name'] ) || ! isset( $_POST['your-email'] ) || empty( trim( $_POST['your-name'] ) ) || empty( trim( $_POST['your-email'] ) ) ) {
wp_send_json_error( [
'error' => "Необходимо заполнить все поля формы"
] );
}
if ( ! is_email( $_POST['your-email'] ) ) {
wp_send_json_error( [
'error' => "Указан некорректный email"
] );
}
$your_name = sanitize_text_field( $_POST['your-name'] );
$your_email = sanitize_text_field( $_POST['your-email'] );
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-type: text/html; charset=utf-8\r\n";
$headers .= "From: Андрей Смородин <no-reply@" . $_SERVER["HTTP_HOST"] . ">\r\n";
$message = '<b>Имя:</b> ' . $your_name . '<br>';
$message .= '<b>Email:</b> ' . $your_email . '<br>';
$result = wp_mail( 'info@feodoraxis.ru', 'Заявка из формы обратной связи', $message, $headers )
if ( $result ) {
wp_send_json_success( [
'message' => "Сообщение отправлено. Мы свяжемся с вами в ближайшее время."
] );
}
wp_send_json_error( [
'error' => "Письмо не было отправлено"
] );
}
add_action( 'wp_ajax_' . 'recall_form', 'feodoraxis_recall_form_request' );
add_action( 'wp_ajax_nopriv_' . 'recall_form', 'feodoraxis_recall_form_request' );
Google reCaptcha
Google reCaptcha — аналогичный сервис как вариант от яндекса. Рассмотрим его интеграцию, хотя в целом процесс похож на интеграцию капчи яндекса. Но здесь мы будем интегрировать невидимую версию — т.е. reCaptcha v3.
Сначала нужно получить публичный и серверный ключ. Для этого нужно авторизоваться в гугле, а потом перейти Админ-панель Google reCaptcha.
Дальше выбираем reCAPTCHA V3 и указываем домен
После регистрации вы получите ключ сайта и секретный ключ для сайта. Скопируйте и сохраните их.

Теперь подключим JavaScript reCaptcha:
<?php
const SITEKEY = '6KepjAsTFFFFRvMEDcY0Z3fqc3TEd3YVxo8cHsGX';
//Подключим JS-скрипт
add_action( 'wp_enqueue_scripts', 'feodoraxis_enqueue_scripts', 100 );
function feodoraxis_enqueue_scripts() {
wp_enqueue_script(
'recaptcha',
'https://www.google.com/recaptcha/api.js?render=' . SITE_KEY,
[],
'1.0'
);
}
Теперь нам нужно подключить PHP-библиотеку капчи гугл из этого репозитория — https://github.com/google/recaptcha
Для этого в активной теме или плагине подключаем ее через composer:
composer require google/recaptcha "^1.3"
О том, как пользоваться composer можно прочитать в этой статье
Далее нужно подключить автолоадер Composer. Если он у вас подключен, то этот пункт можно пропустить. Вариант подключения для темы:
<?php
add_action( 'after_setup_theme', 'feodoraxis_load' );
function feodoraxis_load() {
if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
require_once( __DIR__ . '/vendor/autoload.php' );
}
}
Вариант подключения для плагина:
<?php
if ( file_exists( __DIR__ . '/vendor/autoload.php' ) {
require_once( __DIR__ . '/vendor/autoload.php' );
}
Доработаем скрипт отправки:
<?php
function feodoraxis_show_js_for_form() {
?>
<script>
document.addEventListener("DOMContentLoaded", () => {
const recallForm = document.querySelector('#recall_form');
if (!recallForm) {
return;
}
recallForm.addEventListener('submit', (event) => {
event.preventDefault();
//Отправляем запрос только когда капча готова
grecaptcha.ready(function() {
//Получаем токен
grecaptcha.execute('<?php echo SITE_KEY; ?>', {action: 'submit'}).then(function(token) {
//Если токен пустой - значит пользователь не прошел тест
if (!token.length) {
alert("Вы не прошли капчу, форма не отправлена");
return;
}
const formData = new FormData(recallForm);
formData.append('action', 'recall_form');
//Добавим полученную строку капчи в форму, которую отправляем
formData.append('g-recaptcha-response', token);
const request = new XMLHttpRequest();
request.open("POST", recallForm.getAttribute("data-admin-ajax"));
request.send(formData);
request.onload = () => {
const resultJson = request.responseText;
const result = JSON.parse(resultJson);
if (result.success === true) {
alert(result.data.message);
} else {
alert(result.data.error);
}
};
});
});
});
});
</script>
<?php
}
add_action( "wp_footer", "feodoraxis_show_js_for_form", 1000 );
Доработаем обработку запроса на бекенде.
<?php
const SECRET_KEY = '6Le_LvwqAAAAAJLdFdNjaDeXkRYa9FHMsfQl0nWb';
add_action( 'wp_ajax_' . 'recall_form', 'feodoraxis_recall_form_request' );
add_action( 'wp_ajax_nopriv_' . 'recall_form', 'feodoraxis_recall_form_request' );
function feodoraxis_recall_form_request() {
if ( $_SERVER['REQUEST_METHOD'] != 'POST' || ! wp_doing_ajax() ) {
wp_send_json_error();
}
//Если нет токена капчи, то сообщение не отправляем
if ( ! isset( $_POST['g-recaptcha-response'] ) ) {
wp_send_json_error( [
'error' => "Вы не прошли капчу"
] );
}
//Если токен есть - то проверяем его
$recaptcha = new \ReCaptcha\ReCaptcha( SECRET_KEY );
$result = $recaptcha->verify( $_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR'] );
if ( !$result->isSuccess() ) {
wp_send_json_error( [
'error' => "Вы не прошли капчу"
] );
}
if ( ! isset( $_POST['your-name'] ) || ! isset( $_POST['your-email'] ) || empty( trim( $_POST['your-name'] ) ) || empty( trim( $_POST['your-email'] ) ) ) {
wp_send_json_error( [
'error' => "Необходимо заполнить все поля формы"
] );
}
if ( ! is_email( $_POST['your-email'] ) ) {
wp_send_json_error( [
'error' => "Указан некорректный email"
] );
}
$your_name = sanitize_text_field( $_POST['your-name'] );
$your_email = sanitize_text_field( $_POST['your-email'] );
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-type: text/html; charset=utf-8\r\n";
$headers .= "From: Андрей Смородин <no-reply@" . $_SERVER["HTTP_HOST"] . ">\r\n";
$message = '<b>Имя:</b> ' . $your_name . '<br>';
$message .= '<b>Email:</b> ' . $your_email . '<br>';
$result = wp_mail( 'info@feodoraxis.ru', 'Заявка из формы обратной связи', $message, $headers );
if ( $result ) {
wp_send_json_success( [
'message' => "Сообщение отправлено. Мы свяжемся с вами в ближайшее время."
] );
}
wp_send_json_error( [
'error' => "Письмо не было отправлено"
] );
}
Готово! Если вы все сделали правильно — то форма должна работать. По моему личному опыту, капча гугла версии 3 работает очень точно. Хотя в идеале ее лучше совмещать со второй версией, т.к. ошибки все-таки случаются и нужно давать пользователю возможность доказать, что он реальный человек.
Листинг кода для reCaptcha
<?php
add_action( 'after_setup_theme', 'feodoraxis_load' );
function feodoraxis_load() {
if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
require_once( __DIR__ . '/vendor/autoload.php' );
}
}
const SITE_KEY = '6Le_LvwqAAAAABqqkpyOqZuuDL1JmeFygdPlc_VA';
const SECRET_KEY = '6Le_LvwqAAAAAJLdFdNjaDeXkRYa9FHMsfQl0nWb';
//Подключим JS-скрипт
function feodoraxis_enqueue_scripts() {
wp_enqueue_script(
'recaptcha',
'https://www.google.com/recaptcha/api.js?render=' . SITE_KEY,
[],
'1.0'
);
}
add_action( 'wp_enqueue_scripts', 'feodoraxis_enqueue_scripts', 100 );
function feodoraxis_recall_form() {
return '<form method="POST" data-admin-ajax="' . admin_url( "admin-ajax.php" ) . '" action="' . $_SERVER['REQUEST_URI'] . '" id="recall_form">
<input type="text" name="your-name" value="">
<input type="email" name="your-email" value="">
<button>Отправить</button>
</form>';
}
add_shortcode( 'feodoraxis_recall_form', 'feodoraxis_recall_form' );
function feodoraxis_show_js_for_form() {
?>
<script>
document.addEventListener("DOMContentLoaded", () => {
const recallForm = document.querySelector('#recall_form');
if (!recallForm) {
return;
}
recallForm.addEventListener('submit', (event) => {
event.preventDefault();
//Отправляем запрос только когда капча готова
grecaptcha.ready(function() {
//Получаем токен
grecaptcha.execute('<?php echo SITE_KEY; ?>', {action: 'submit'}).then(function(token) {
//Если токен пустой - значит пользователь не прошел тест
if (!token.length) {
alert("Вы не прошли капчу, форма не отправлена");
return;
}
const formData = new FormData(recallForm);
formData.append('action', 'recall_form');
//Добавим полученную строку капчи в форму, которую отправляем
formData.append('g-recaptcha-response', token);
const request = new XMLHttpRequest();
request.open("POST", recallForm.getAttribute("data-admin-ajax"));
request.send(formData);
request.onload = () => {
const resultJson = request.responseText;
const result = JSON.parse(resultJson);
if (result.success === true) {
alert(result.data.message);
} else {
alert(result.data.error);
}
};
});
});
});
});
</script>
<?php
}
add_action( "wp_footer", "feodoraxis_show_js_for_form", 1000 );
function feodoraxis_recall_form_request() {
if ( $_SERVER['REQUEST_METHOD'] != 'POST' || ! wp_doing_ajax() ) {
wp_send_json_error();
}
//Если нет токена капчи, то сообщение не отправляем
if ( ! isset( $_POST['g-recaptcha-response'] ) ) {
wp_send_json_error( [
'error' => "Вы не прошли капчу"
] );
}
//Если токен есть - то проверяем его
$recaptcha = new \ReCaptcha\ReCaptcha( SECRET_KEY );
$result = $recaptcha->verify( $_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR'] );
if ( ! $result->isSuccess() ) {
wp_send_json_error( [
'error' => "Вы не прошли капчу"
] );
}
if ( ! isset( $_POST['your-name'] ) || ! isset( $_POST['your-email'] ) || empty( trim( $_POST['your-name'] ) ) || empty( trim( $_POST['your-email'] ) ) ) {
wp_send_json_error( [
'error' => "Необходимо заполнить все поля формы"
] );
}
if ( ! is_email( $_POST['your-email'] ) ) {
wp_send_json_error( [
'error' => "Указан некорректный email"
] );
}
$your_name = sanitize_text_field( $_POST['your-name'] );
$your_email = sanitize_text_field( $_POST['your-email'] );
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-type: text/html; charset=utf-8\r\n";
$headers .= "From: Андрей Смородин <no-reply@" . $_SERVER["HTTP_HOST"] . ">\r\n";
$message = '<b>Имя:</b> ' . $your_name . '<br>';
$message .= '<b>Email:</b> ' . $your_email . '<br>';
$result = wp_mail( 'info@feodoraxis.ru', 'Заявка из формы обратной связи', $message, $headers );
if ( $result ) {
wp_send_json_success( [
'message' => "Сообщение отправлено. Мы свяжемся с вами в ближайшее время."
] );
}
wp_send_json_error( [
'error' => "Письмо не было отправлено"
] );
}
add_action( 'wp_ajax_' . 'recall_form', 'feodoraxis_recall_form_request' );
add_action( 'wp_ajax_nopriv_' . 'recall_form', 'feodoraxis_recall_form_request' );