Как интегрировать reCAPTCHA в форму, использующую PHPMailer?

Пожалуйста, помогите мне интегрировать reCaptcha на сайт. При отправке формы возникает ошибка "Ошибка обработки. Ответ не JSON". Я полагаю, что это связано с частью кода, которая проверяет успешность прохождения reCaptcha. 

Вот фрагмент кода:

```php
if($responseKeys["success"] && $responseKeys["score"] >= 0.5) {
    echo json_encode(array('success' => 'true', 'om_score' => $responseKeys["score"], 'token' => $_POST['token']));
} else {
    echo json_encode(array('success' => 'false', 'om_score' => $responseKeys["score"], 'token' => $_POST['token']));
}
```

Если я удаляю эту часть, форма отправляется успешно, но тогда, по всей видимости, функция капчи не работает. 

Вот полный код:

```php
<?php
// Файлы phpmailer
require 'phpmailer/PHPMailer.php';
require 'phpmailer/SMTP.php';
require 'phpmailer/Exception.php';

# проверка, что ошибки нет
if (!error_get_last()) {

    // Переменные, которые отправляет пользователь
    $name = $_POST['name'];
    $tel = $_POST['tel'];
    $email = $_POST['email'];
    $number = $_POST['number'];
    $text = $_POST['text'];

    $captcha = isset($_POST['token']) ? $_POST['token'] : '';

    $secretKey = '***';
    $ip = $_SERVER['REMOTE_ADDR'];

    $url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . $secretKey . '&response=' . $captcha;

    $response = file_get_contents($url);
    $responseKeys = json_decode($response, true);

    if($responseKeys["success"] && $responseKeys["score"] >= 0.5) {
        echo json_encode(array('success' => 'true', 'om_score' => $responseKeys["score"], 'token' => $captcha));
    } else {
        echo json_encode(array('success' => 'false', 'om_score' => $responseKeys["score"], 'token' => $captcha));
    }
}
```

Можете ли вы подсказать, что не так и как это исправить?
  • 19 марта 2025 г. 16:34
Ответы на вопрос 2
Ваша проблема заключается в том, что вы неправильно обрабатываете ответ от Google reCAPTCHA, поэтому возникает ошибка "Ошибка обработки. Ответ не JSON". Давайте пройдемся по основным шагам для корректной интеграции reCAPTCHA с PHPMailer и убедимся, что все работает хорошо.

### Основные шаги для интеграции

1. **Убедитесь, что вы получили правильные ключи от Google reCAPTCHA**: Вам нужно следить за тем, чтобы вы использовали правильные ключи в своем коде. В вашем случае замените `***` в `$secretKey` на ваш секретный ключ.

2. **Проверка на стороне клиента**: Перед отправкой формы с помощью JavaScript убедитесь, что вы правильно настроили reCAPTCHA, и форма не отправляется без успешной проверки.

    Пример JavaScript для инициализации reCAPTCHA v3:
    ```html
    <script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
    <script>
    grecaptcha.ready(function() {
        grecaptcha.execute('YOUR_SITE_KEY', {action: 'submit'}).then(function(token) {
            document.getElementById('token').value = token;
        });
    });
    </script>
    ```

3. **Обработка ответа в PHP**: Убедитесь, что вы корректно обрабатываете ответ от reCAPTCHA на сервере.

### Обновленный код

Вот обновленный код с учетом указанных выше рекомендаций:

```php
<?php
require 'phpmailer/PHPMailer.php';
require 'phpmailer/SMTP.php';
require 'phpmailer/Exception.php';

if (!error_get_last()) {
    // Переменные, которые отправляет пользователь
    $name = $_POST['name'];
    $tel = $_POST['tel'];
    $email = $_POST['email'];
    $number = $_POST['number'];
    $text = $_POST['text'];
    
    $captcha = isset($_POST['token']) ? $_POST['token'] : '';

    $secretKey = 'YOUR_SECRET_KEY'; // Замените на ваш секретный ключ.
    $ip = $_SERVER['REMOTE_ADDR'];

    $url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . $secretKey . '&response=' . $captcha;

    // Получение ответа от reCAPTCHA
    $response = file_get_contents($url);
    $responseKeys = json_decode($response, true);

    // Проверка успешности и наличия нужного балла
    if ($responseKeys['success'] && (isset($responseKeys['score']) && $responseKeys['score'] >= 0.5)) {
        // Здесь вы можете отправить почту, так как проверка прошла успешно
        // Пример:
        // $mail = new PHPMailer();
        // Настройка PHPMailer...
        // $mail->send();

        echo json_encode(array('success' => 'true', 'om_score' => $responseKeys["score"], 'token' => $captcha));
    } else {
        echo json_encode(array('success' => 'false', 'om_score' => isset($responseKeys['score']) ? $responseKeys['score'] : 'N/A', 'token' => $captcha));
    }
}
?>
```

### Примечания

1. Замените `YOUR_SECRET_KEY` на ваш фактический секретный ключ от reCAPTCHA.
2. Убедитесь, что вы правильно добавляете `token` в вашу форму HTML:
    ```html
    <input type="hidden" name="token" id="token">
    ```
3. Проверяйте наличие поля `score` в ответе от reCAPTCHA, поскольку в более ранних версиях может быть возвращено лишь поле `success`.

Теперь протестируйте свой код, и если будут ошибки, проверьте, какая именно информация возвращается от Google. Это поможет в диагностике возможных проблем.
Проблема в вашем коде заключается в том, что вы не объединяете проверку reCAPTCHA с процессом отправки письма. Вот исправленный вариант: 
<?php
// Файлы phpmailer
require 'phpmailer/PHPMailer.php';
require 'phpmailer/SMTP.php';
require 'phpmailer/Exception.php';

// Инициализация массива данных для ответа
$data = [];

# проверка, что ошибки нет
if (!error_get_last()) {
    // Проверка reCAPTCHA
    if(isset($_POST['token'])){
        $secretKey = '***'; // Ваш секретный ключ reCAPTCHA
        $token = $_POST['token'];
        $url = 'https://www.google.com/recaptcha/api/siteverify';
        
        // Используем cURL вместо file_get_contents для лучшей обработки ошибок
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
            'secret' => $secretKey,
            'response' => $token,
            'remoteip' => $_SERVER['REMOTE_ADDR']
        ));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $response = curl_exec($ch);
        curl_close($ch);
        
        $responseKeys = json_decode($response, true);
        
        // Если reCAPTCHA не пройдена, возвращаем ошибку
        if(!$responseKeys["success"] || $responseKeys["score"] < 0.5) {
            header('Content-Type: application/json');
            echo json_encode([
                'result' => 'error',
                'info' => 'Ошибка проверки reCAPTCHA',
                'desc' => 'Возможная попытка спама'
            ]);
            exit;
        }
    } else {
        header('Content-Type: application/json');
        echo json_encode([
            'result' => 'error',
            'info' => 'Отсутствует токен reCAPTCHA'
        ]);
        exit;
    }

    // Переменные, которые отправляет пользователь
    $name = $_POST['name'] ?? '';
    $tel = $_POST['tel'] ?? '';
    $email = $_POST['email'] ?? '';
    $number = $_POST['number'] ?? '';
    $text = $_POST['text'] ?? '';

    // Формирование самого письма
    $title = "Заявка с сайта";
    $body = "
    <h2>Заявка с сайта</h2>
    <b>Имя:</b> $name<br>
    <b>Телефон:</b> $tel<br>
    <b>Почта:</b> $email<br>
    <b>Количество изделий:</b> $number<br>
    <b>Сообщение:</b><br>$text
    ";

    // Настройки PHPMailer
    $mail = new PHPMailer\PHPMailer\PHPMailer();

    try {
        $mail->isSMTP();
        $mail->CharSet = "UTF-8";
        $mail->SMTPAuth = true;
        //$mail->SMTPDebug = 2;
        $mail->Debugoutput = function($str, $level) use (&$data) {
            $data['debug'][] = $str;
        };

        // Настройки вашей почты
        $mail->Host = 'smtp.yandex.com'; // SMTP сервера вашей почты
        $mail->Username = '***'; // Логин на почте
        $mail->Password = '***'; // Пароль на почте
        $mail->SMTPSecure = 'ssl';
        $mail->Port = 465;
        $mail->setFrom('***', 'Заявка с сайта'); // Адрес самой почты и имя отправителя

        // Получатель письма
        $mail->addAddress('***');

        // Прикрипление файлов к письму
        if (!empty($_FILES['file']['name'][0])) {
            for ($i = 0; $i < count($_FILES['file']['tmp_name']); $i++) {
                if ($_FILES['file']['error'][$i] === 0) {
                    $mail->addAttachment($_FILES['file']['tmp_name'][$i], $_FILES['file']['name'][$i]);
                }
            }
        }
        
        // Отправка сообщения
        $mail->isHTML(true);
        $mail->Subject = $title;
        $mail->Body = $body;

        // Проверяем отправленность сообщения
        if ($mail->send()) {
            $data['result'] = "success";
            $data['info'] = "Сообщение успешно отправлено!";
        } else {
            $data['result'] = "error";
            $data['info'] = "Сообщение не было отправлено. Ошибка при отправке письма";
            $data['desc'] = "Причина ошибки: {$mail->ErrorInfo}";
        }
    } catch (Exception $e) {
        $data['result'] = "error";
        $data['info'] = "Ошибка при отправке письма";
        $data['desc'] = $e->getMessage();
    }
} else {
    $data['result'] = "error";
    $data['info'] = "В коде присутствует ошибка";
    $data['desc'] = error_get_last();
}

// Отправка результата
header('Content-Type: application/json');
echo json_encode($data);
?>

Основные изменения:
Правильная обработка reCAPTCHA:

Использовал cURL вместо file_get_contents для лучшей обработки ошибок

Добавил проверку до обработки формы

При ошибке reCAPTCHA скрипт завершается с ошибкой

Улучшенная обработка ошибок:

Добавил try-catch для обработки исключений PHPMailer

Исправил обработку файлов (используйте $_FILES вместо $file)

JSON ответ:

Теперь скрипт возвращает один JSON ответ, а не несколько

Все сообщения об ошибках стандартизированы

Безопасность:

Добавил проверку наличия токена reCAPTCHA

Использовал оператор объединения ?? для переменных формы

На стороне клиента (HTML/JS):
Убедитесь, что вы правильно реализовали reCAPTCHA v3 на клиентской стороне. Вот пример:
<script src="https://www.google.com/recaptcha/api.js?render=ВАШ_КЛЮЧ_САЙТА"></script>
<script>
document.querySelector('form').addEventListener('submit', function(e) {
    e.preventDefault();
    
    grecaptcha.ready(function() {
        grecaptcha.execute('ВАШ_КЛЮЧ_САЙТА', {action: 'submit'}).then(function(token) {
            // Добавляем token к данным формы
            let formData = new FormData(document.querySelector('form'));
            formData.append('token', token);
            
            // Отправка формы
            fetch('ваш_обработчик.php', {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                if(data.result === 'success') {
                    alert('Сообщение отправлено!');
                } else {
                    alert('Ошибка: ' + data.info);
                }
            })
            .catch(error => console.error('Error:', error));
        });
    });
});
</script>

Замените ВАШ_КЛЮЧ_САЙТА на ваш ключ reCAPTCHA (site key) и ваш_обработчик.php на путь к вашему PHP-скрипту.
Похожие вопросы