feat: SMS verification через n8n webhook
- Перенесена проверка SMS кода в n8n webhook (N8N_SMS_VERIFY_WEBHOOK) - Упрощен формат ответа: убран токен, только success/message - sms-verify.php теперь проксирует запросы на n8n - Обновлен JS код: убрано использование токена - Обновлена документация с упрощенным форматом ответа - Протестировано: верный и неверный коды работают корректно
This commit is contained in:
166
sms-verify.php
166
sms-verify.php
@@ -353,11 +353,10 @@ try {
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
} elseif ($action === 'verify') {
|
||||
// Проверка кода
|
||||
// Проверка кода через n8n webhook
|
||||
log_message("=== НАЧАЛО ПРОВЕРКИ КОДА ===");
|
||||
log_message("REQUEST_METHOD: " . ($_SERVER['REQUEST_METHOD'] ?? 'не установлен'));
|
||||
log_message("POST данные: " . json_encode($_POST, JSON_UNESCAPED_UNICODE));
|
||||
log_message("GET данные: " . json_encode($_GET, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$phone = $_POST['phonenumber'] ?? '';
|
||||
$code = $_POST['code'] ?? '';
|
||||
@@ -370,108 +369,89 @@ try {
|
||||
throw new Exception("Номер телефона или код не указаны");
|
||||
}
|
||||
|
||||
$phone_cleaned = clear_phone($phone);
|
||||
log_message("Номер после очистки: '$phone_cleaned'");
|
||||
// Получаем URL webhook из .env
|
||||
$webhook_url = env('N8N_SMS_VERIFY_WEBHOOK', '');
|
||||
|
||||
// Проверка rate limiting - пытаемся переподключиться при необходимости
|
||||
log_message("Попытка подключения к Redis для проверки кода...");
|
||||
$redis = getRedis(true); // Принудительно пытаемся переподключиться
|
||||
|
||||
$stored_code = false;
|
||||
|
||||
$stored_code = false;
|
||||
|
||||
// Пытаемся получить код из Redis
|
||||
if ($redis) {
|
||||
log_message("Redis подключен успешно для проверки кода");
|
||||
try {
|
||||
// Проверяем количество попыток
|
||||
$key_attempts = "sms:ratelimit:attempts:$phone_cleaned";
|
||||
$attempts = $redis->get($key_attempts) ?: 0;
|
||||
|
||||
if ($attempts >= 10) {
|
||||
log_message("Превышено количество попыток для номера $phone_cleaned");
|
||||
throw new Exception("Превышено количество попыток. Попробуйте позже.");
|
||||
}
|
||||
|
||||
// Увеличиваем счетчик попыток
|
||||
$redis->setex($key_attempts, 900, $attempts + 1); // 15 минут
|
||||
|
||||
// Проверяем код
|
||||
$key = "sms:code:$phone_cleaned";
|
||||
log_message("Проверка кода для номера $phone_cleaned, ключ Redis: $key, введенный код: $code");
|
||||
|
||||
$stored_code = $redis->get($key);
|
||||
|
||||
if ($stored_code !== null) {
|
||||
log_message("Код найден в Redis: $stored_code");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Пробрасываем исключение, если это ошибка rate limiting
|
||||
if (strpos($e->getMessage(), 'Превышено количество попыток') !== false) {
|
||||
throw $e;
|
||||
}
|
||||
log_message("Ошибка при работе с Redis: " . $e->getMessage());
|
||||
}
|
||||
if (empty($webhook_url)) {
|
||||
log_message("Ошибка: не указан N8N_SMS_VERIFY_WEBHOOK в .env");
|
||||
throw new Exception("Сервис временно недоступен. Попробуйте позже.");
|
||||
}
|
||||
|
||||
// Если код не найден в Redis, пытаемся получить из файла (fallback)
|
||||
if ($stored_code === null) {
|
||||
log_message("Код не найден в Redis, проверяем файловое хранилище...");
|
||||
$stored_code = getCodeFromFile($phone_cleaned);
|
||||
if ($stored_code !== false) {
|
||||
log_message("Код найден в файловом хранилище для номера $phone_cleaned");
|
||||
}
|
||||
log_message("Отправка запроса на n8n webhook: $webhook_url");
|
||||
|
||||
// Отправляем запрос на n8n webhook
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $webhook_url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'phonenumber' => $phone,
|
||||
'code' => $code
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json; charset=utf-8'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_error($ch);
|
||||
$curl_errno = curl_errno($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($curl_error) {
|
||||
log_message("Ошибка CURL при проверке кода через n8n: $curl_error (код: $curl_errno)");
|
||||
throw new Exception("Ошибка соединения с сервисом. Попробуйте позже.");
|
||||
}
|
||||
|
||||
// Если код все еще не найден, выдаем ошибку
|
||||
if ($stored_code === null || $stored_code === false) {
|
||||
log_message("КРИТИЧЕСКАЯ ОШИБКА: Код не найден ни в Redis, ни в файловом хранилище для номера $phone_cleaned");
|
||||
throw new Exception("Код не найден или истек. Запросите новый код.");
|
||||
// Логируем ответ от n8n
|
||||
log_message("Ответ от n8n: HTTP $http_code, ответ: " . substr($response, 0, 200));
|
||||
|
||||
// Парсим ответ
|
||||
$response_data = json_decode($response, true);
|
||||
|
||||
if ($response_data === null) {
|
||||
log_message("Ошибка: не удалось распарсить ответ от n8n: " . substr($response, 0, 200));
|
||||
throw new Exception("Ошибка обработки ответа. Попробуйте позже.");
|
||||
}
|
||||
|
||||
log_message("Проверка кода: сохраненный=$stored_code, введенный=$code");
|
||||
|
||||
if ($stored_code !== $code) {
|
||||
log_message("Неверный код для номера $phone_cleaned. Введен: $code, ожидался: $stored_code");
|
||||
throw new Exception("Неверный код");
|
||||
}
|
||||
|
||||
// Код верный - удаляем его из Redis и файла, создаем сессию подтверждения
|
||||
if ($redis) {
|
||||
try {
|
||||
$key = "sms:code:$phone_cleaned";
|
||||
$redis->del($key);
|
||||
$key_attempts = "sms:ratelimit:attempts:$phone_cleaned";
|
||||
$redis->del($key_attempts); // Сбрасываем счетчик попыток
|
||||
} catch (Exception $e) {
|
||||
log_message("Ошибка при удалении кода из Redis: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
deleteCodeFromFile($phone_cleaned); // Удаляем из файла тоже
|
||||
|
||||
// Создаем токен подтверждения (действует 1 час) - только если Redis доступен
|
||||
$verify_token = bin2hex(random_bytes(32));
|
||||
if ($redis) {
|
||||
try {
|
||||
$verify_key = "sms:verified:$phone_cleaned";
|
||||
$redis->setex($verify_key, 3600, $verify_token);
|
||||
log_message("Токен верификации сохранен в Redis для номера $phone_cleaned");
|
||||
} catch (Exception $e) {
|
||||
log_message("Ошибка при создании токена верификации: " . $e->getMessage());
|
||||
// Проверяем успешность
|
||||
if ($http_code >= 200 && $http_code < 300) {
|
||||
// Успешный ответ от n8n
|
||||
if (isset($response_data['success']) && $response_data['success'] === true) {
|
||||
log_message("Код подтвержден через n8n для номера: " . ($phone ?: 'не указан'));
|
||||
|
||||
// Возвращаем упрощенный ответ (без токена)
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => $response_data['message'] ?? 'Код подтвержден'
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
} else {
|
||||
// Ошибка от n8n
|
||||
$error_message = $response_data['message'] ?? 'Неверный код';
|
||||
log_message("Ошибка проверки кода через n8n: $error_message");
|
||||
|
||||
http_response_code(400);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $error_message
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
} else {
|
||||
log_message("Токен верификации сгенерирован, но не сохранен (Redis недоступен)");
|
||||
// HTTP ошибка
|
||||
$error_message = $response_data['message'] ?? "Ошибка сервиса (HTTP $http_code)";
|
||||
log_message("HTTP ошибка от n8n: $http_code, сообщение: $error_message");
|
||||
|
||||
http_response_code($http_code >= 500 ? 500 : 400);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $error_message
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
log_message("Код подтвержден для номера: $phone_cleaned");
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Код подтвержден',
|
||||
'token' => $verify_token // Токен для последующей проверки
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
} elseif ($action === 'check_verified') {
|
||||
// Проверка статуса верификации (для проверки перед отправкой формы)
|
||||
$phone = $_POST['phonenumber'] ?? '';
|
||||
|
||||
Reference in New Issue
Block a user