feat: SMS verification через n8n webhook
- Перенесена проверка SMS кода в n8n webhook (N8N_SMS_VERIFY_WEBHOOK) - Упрощен формат ответа: убран токен, только success/message - sms-verify.php теперь проксирует запросы на n8n - Обновлен JS код: убрано использование токена - Обновлена документация с упрощенным форматом ответа - Протестировано: верный и неверный коды работают корректно
This commit is contained in:
261
docs/n8n_migration_plan.md
Normal file
261
docs/n8n_migration_plan.md
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
# План миграции SMS верификации в n8n
|
||||||
|
|
||||||
|
## Текущая логика (PHP)
|
||||||
|
|
||||||
|
### 1. Отправка SMS (`sms-verify.php?action=send`)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────┐
|
||||||
|
│ Frontend │
|
||||||
|
│ (JS) │
|
||||||
|
└──────┬──────┘
|
||||||
|
│ POST: phonenumber
|
||||||
|
▼
|
||||||
|
┌─────────────────────┐
|
||||||
|
│ sms-verify.php │
|
||||||
|
│ action=send │
|
||||||
|
└──────┬──────────────┘
|
||||||
|
│
|
||||||
|
├─► Нормализация номера (clear_phone)
|
||||||
|
├─► Проверка rate limit (Redis)
|
||||||
|
├─► Генерация кода (generateCode)
|
||||||
|
├─► Сохранение в Redis (setex, 10 мин)
|
||||||
|
├─► Отправка SMS → n8n webhook
|
||||||
|
└─► Ответ: success
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Проверка кода (`sms-verify.php?action=verify`)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────┐
|
||||||
|
│ Frontend │
|
||||||
|
│ (JS) │
|
||||||
|
└──────┬──────┘
|
||||||
|
│ POST: phonenumber, code
|
||||||
|
▼
|
||||||
|
┌─────────────────────┐
|
||||||
|
│ sms-verify.php │
|
||||||
|
│ action=verify │
|
||||||
|
└──────┬──────────────┘
|
||||||
|
│
|
||||||
|
├─► Нормализация номера
|
||||||
|
├─► Проверка rate limit попыток
|
||||||
|
├─► Чтение кода из Redis
|
||||||
|
├─► Сравнение кодов
|
||||||
|
├─► Удаление кода из Redis
|
||||||
|
├─► Создание токена верификации
|
||||||
|
└─► Ответ: success, token
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Новая логика (n8n)
|
||||||
|
|
||||||
|
### Workflow 1: Отправка SMS
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Webhook │ POST /webhook/sms-send
|
||||||
|
│ Trigger │ Body: { phonenumber: "+79262306381" }
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Function │ Нормализация номера
|
||||||
|
│ (JS Code) │ phone.replace(/[() -+]/g, '').replace(/^(\+?7|8)/, '')
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Redis │ GET sms:ratelimit:send:9262306381
|
||||||
|
│ (Get) │ Если >= 5 → ошибка
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Function │ Генерация кода
|
||||||
|
│ (JS Code) │ Math.floor(100000 + Math.random() * 900000)
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Redis │ SETEX sms:code:9262306381 600 "106574"
|
||||||
|
│ (Set) │
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Redis │ INCR sms:ratelimit:send:9262306381
|
||||||
|
│ (Increment) │ EXPIRE 3600
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ HTTP Request │ POST к SMS API
|
||||||
|
│ (SMS Send) │ Body: { phone, text: "Код: 106574" }
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ IF (error) │ Если ошибка → удалить код из Redis
|
||||||
|
│ (Error Handle) │
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Respond to │ { success: true, message: "..." }
|
||||||
|
│ Webhook │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Workflow 2: Проверка кода
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Webhook │ POST /webhook/sms-verify
|
||||||
|
│ Trigger │ Body: { phonenumber: "+79262306381", code: "106574" }
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Function │ Нормализация номера
|
||||||
|
│ (JS Code) │
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Redis │ GET sms:ratelimit:attempts:9262306381
|
||||||
|
│ (Get) │ Если >= 10 → ошибка
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Redis │ INCR sms:ratelimit:attempts:9262306381
|
||||||
|
│ (Increment) │ EXPIRE 900
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Redis │ GET sms:code:9262306381
|
||||||
|
│ (Get) │ Если null → ошибка "Код не найден"
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ IF │ Если code !== stored_code → ошибка "Неверный код"
|
||||||
|
│ (Compare) │
|
||||||
|
└────────┬────────┘
|
||||||
|
│ (success)
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Redis │ DEL sms:code:9262306381
|
||||||
|
│ (Delete) │ DEL sms:ratelimit:attempts:9262306381
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Function │ Генерация токена
|
||||||
|
│ (JS Code) │ crypto.randomBytes(32).toString('hex')
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Redis │ SETEX sms:verified:9262306381 3600 "token"
|
||||||
|
│ (Set) │
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Respond to │ { success: true, token: "..." }
|
||||||
|
│ Webhook │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Пошаговая миграция
|
||||||
|
|
||||||
|
### Шаг 1: Создать workflows в n8n
|
||||||
|
|
||||||
|
1. Зайти в n8n: https://n8n.clientright.pro
|
||||||
|
2. Создать новый workflow "SMS Send"
|
||||||
|
3. Создать новый workflow "SMS Verify"
|
||||||
|
4. Создать новый workflow "SMS Check Verified" (опционально)
|
||||||
|
|
||||||
|
### Шаг 2: Настроить Redis в n8n
|
||||||
|
|
||||||
|
- Добавить Redis credentials в n8n
|
||||||
|
- Host: `crm.clientright.ru`
|
||||||
|
- Port: `6379`
|
||||||
|
- Password: (из .env)
|
||||||
|
|
||||||
|
### Шаг 3: Протестировать workflows
|
||||||
|
|
||||||
|
- Запустить тестовые запросы через Postman/curl
|
||||||
|
- Проверить, что коды сохраняются в Redis
|
||||||
|
- Проверить, что SMS отправляются
|
||||||
|
|
||||||
|
### Шаг 4: Изменить PHP код
|
||||||
|
|
||||||
|
**Вариант A: Прокси через PHP (проще)**
|
||||||
|
```php
|
||||||
|
// sms-verify.php просто перенаправляет на n8n
|
||||||
|
$n8n_url = 'https://n8n.clientright.pro/webhook/sms-send';
|
||||||
|
// ... curl запрос к n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
**Вариант B: Прямой вызов n8n из JS (лучше)**
|
||||||
|
```javascript
|
||||||
|
// js/common.js
|
||||||
|
$.ajax({
|
||||||
|
url: 'https://n8n.clientright.pro/webhook/sms-send',
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 5: Удалить старую логику из PHP
|
||||||
|
|
||||||
|
- Удалить генерацию кода
|
||||||
|
- Удалить работу с Redis (оставить только fallback на файлы, если нужно)
|
||||||
|
- Оставить только проксирование запросов
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Преимущества
|
||||||
|
|
||||||
|
✅ **Визуализация** - видно весь процесс в n8n
|
||||||
|
✅ **Логирование** - автоматические логи каждого шага
|
||||||
|
✅ **Мониторинг** - видно ошибки и задержки
|
||||||
|
✅ **Гибкость** - легко добавить новые шаги
|
||||||
|
✅ **Тестирование** - можно тестировать каждый шаг отдельно
|
||||||
|
✅ **Масштабируемость** - легко добавить несколько SMS провайдеров
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Примеры кода для n8n
|
||||||
|
|
||||||
|
### Нормализация номера (Function Node)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const phone = $input.item.json.phonenumber || '';
|
||||||
|
const cleaned = phone
|
||||||
|
.replace(/[() -]/g, '')
|
||||||
|
.replace(/^(\+?7|8)/, '');
|
||||||
|
|
||||||
|
return { json: { phone_cleaned: cleaned } };
|
||||||
|
```
|
||||||
|
|
||||||
|
### Генерация кода (Function Node)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const code = Math.floor(100000 + Math.random() * 900000).toString();
|
||||||
|
return { json: { code } };
|
||||||
|
```
|
||||||
|
|
||||||
|
### Генерация токена (Function Node)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const token = crypto.randomBytes(32).toString('hex');
|
||||||
|
return { json: { token } };
|
||||||
|
```
|
||||||
201
docs/n8n_redis_example.md
Normal file
201
docs/n8n_redis_example.md
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
# Пример использования Redis в n8n через Webhook
|
||||||
|
|
||||||
|
## Как это работает
|
||||||
|
|
||||||
|
```
|
||||||
|
Webhook (точка входа) → Redis Node (чтение/запись) → Ответ
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow: Проверка SMS кода
|
||||||
|
|
||||||
|
### Структура:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Webhook Trigger
|
||||||
|
↓
|
||||||
|
2. Function Node (нормализация номера)
|
||||||
|
↓
|
||||||
|
3. Redis Node (GET) - читаем код из Redis
|
||||||
|
↓
|
||||||
|
4. Function Node (сравнение кодов)
|
||||||
|
↓
|
||||||
|
5. Redis Node (DEL) - удаляем код после проверки
|
||||||
|
↓
|
||||||
|
6. Respond to Webhook
|
||||||
|
```
|
||||||
|
|
||||||
|
### Детализация:
|
||||||
|
|
||||||
|
#### Шаг 1: Webhook Trigger
|
||||||
|
- **URL:** `/webhook/sms-verify`
|
||||||
|
- **Method:** POST
|
||||||
|
- **Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"phonenumber": "+79262306381",
|
||||||
|
"code": "106574"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Шаг 2: Function Node - Нормализация
|
||||||
|
```javascript
|
||||||
|
// Код для Function Node
|
||||||
|
const phone = $input.item.json.phonenumber || '';
|
||||||
|
const cleaned = phone
|
||||||
|
.replace(/[() -]/g, '')
|
||||||
|
.replace(/^(\+?7|8)/, '');
|
||||||
|
|
||||||
|
return {
|
||||||
|
json: {
|
||||||
|
phone_cleaned: cleaned,
|
||||||
|
code: $input.item.json.code
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Шаг 3: Redis Node - Чтение кода
|
||||||
|
- **Operation:** Get
|
||||||
|
- **Key:** `sms:code:{{ $json.phone_cleaned }}`
|
||||||
|
- **Результат:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"phone_cleaned": "9262306381",
|
||||||
|
"code": "106574",
|
||||||
|
"stored_code": "106574" // из Redis
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Шаг 4: Function Node - Сравнение
|
||||||
|
```javascript
|
||||||
|
// Код для Function Node
|
||||||
|
const inputCode = $input.item.json.code;
|
||||||
|
const storedCode = $input.item.json.stored_code;
|
||||||
|
|
||||||
|
if (!storedCode) {
|
||||||
|
return {
|
||||||
|
json: {
|
||||||
|
success: false,
|
||||||
|
message: "Код не найден или истек"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputCode !== storedCode) {
|
||||||
|
return {
|
||||||
|
json: {
|
||||||
|
success: false,
|
||||||
|
message: "Неверный код"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Код верный - продолжаем
|
||||||
|
return {
|
||||||
|
json: {
|
||||||
|
success: true,
|
||||||
|
phone_cleaned: $input.item.json.phone_cleaned
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Шаг 5: Redis Node - Удаление кода
|
||||||
|
- **Operation:** Delete
|
||||||
|
- **Key:** `sms:code:{{ $json.phone_cleaned }}`
|
||||||
|
|
||||||
|
#### Шаг 6: Redis Node - Создание токена
|
||||||
|
- **Operation:** Set
|
||||||
|
- **Key:** `sms:verified:{{ $json.phone_cleaned }}`
|
||||||
|
- **Value:** `{{ $json.token }}` (генерируется в Function Node)
|
||||||
|
- **TTL:** 3600 секунд
|
||||||
|
|
||||||
|
#### Шаг 7: Respond to Webhook
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Код подтвержден",
|
||||||
|
"token": "abc123..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow: Отправка SMS кода
|
||||||
|
|
||||||
|
### Структура:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Webhook Trigger
|
||||||
|
↓
|
||||||
|
2. Function Node (нормализация)
|
||||||
|
↓
|
||||||
|
3. Redis Node (GET) - проверка rate limit
|
||||||
|
↓
|
||||||
|
4. Function Node (генерация кода)
|
||||||
|
↓
|
||||||
|
5. Redis Node (SETEX) - сохранение кода
|
||||||
|
↓
|
||||||
|
6. HTTP Request - отправка SMS
|
||||||
|
↓
|
||||||
|
7. Respond to Webhook
|
||||||
|
```
|
||||||
|
|
||||||
|
### Детализация:
|
||||||
|
|
||||||
|
#### Шаг 3: Redis Node - Проверка rate limit
|
||||||
|
- **Operation:** Get
|
||||||
|
- **Key:** `sms:ratelimit:send:{{ $json.phone_cleaned }}`
|
||||||
|
- **Если значение >= 5** → ошибка через IF Node
|
||||||
|
|
||||||
|
#### Шаг 5: Redis Node - Сохранение кода
|
||||||
|
- **Operation:** Set with Expiration
|
||||||
|
- **Key:** `sms:code:{{ $json.phone_cleaned }}`
|
||||||
|
- **Value:** `{{ $json.code }}`
|
||||||
|
- **TTL:** 600 секунд (10 минут)
|
||||||
|
|
||||||
|
#### Шаг 6: HTTP Request - Отправка SMS
|
||||||
|
- **Method:** POST
|
||||||
|
- **URL:** (SMS API провайдера)
|
||||||
|
- **Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"phone": "{{ $json.phone_cleaned }}",
|
||||||
|
"text": "Код подтверждения: {{ $json.code }}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Настройка Redis в n8n
|
||||||
|
|
||||||
|
### Credentials:
|
||||||
|
|
||||||
|
1. Зайти в n8n → Credentials → New
|
||||||
|
2. Выбрать "Redis"
|
||||||
|
3. Заполнить:
|
||||||
|
- **Host:** `crm.clientright.ru`
|
||||||
|
- **Port:** `6379`
|
||||||
|
- **Password:** (из .env или пусто)
|
||||||
|
- **Database:** `0` (по умолчанию)
|
||||||
|
|
||||||
|
### Использование в Node:
|
||||||
|
|
||||||
|
1. Добавить **Redis Node** в workflow
|
||||||
|
2. Выбрать созданные credentials
|
||||||
|
3. Выбрать операцию (Get, Set, Set with Expiration, Delete, etc.)
|
||||||
|
4. Указать ключ (можно использовать переменные: `sms:code:{{ $json.phone_cleaned }}`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Преимущества
|
||||||
|
|
||||||
|
✅ **Всё в одном месте** - webhook принимает запрос, Redis Node читает/пишет
|
||||||
|
✅ **Визуализация** - видно весь процесс на графике
|
||||||
|
✅ **Логирование** - n8n автоматически логирует все операции
|
||||||
|
✅ **Обработка ошибок** - можно добавить IF Nodes для проверок
|
||||||
|
✅ **Тестирование** - можно тестировать каждый шаг отдельно
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Пример полного workflow (JSON для импорта в n8n)
|
||||||
|
|
||||||
|
Можно создать workflow вручную или импортировать готовый JSON (если нужно, могу создать).
|
||||||
150
docs/n8n_sms_workflow.md
Normal file
150
docs/n8n_sms_workflow.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
# N8N Workflow для SMS верификации
|
||||||
|
|
||||||
|
## Текущая архитектура
|
||||||
|
|
||||||
|
```
|
||||||
|
Frontend (JS) → PHP (sms-verify.php) → Redis + n8n (только отправка SMS)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Предлагаемая архитектура
|
||||||
|
|
||||||
|
```
|
||||||
|
Frontend (JS) → n8n Webhook → Redis + SMS отправка (всё в n8n)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow 1: Отправка SMS кода
|
||||||
|
|
||||||
|
**Webhook URL:** `https://n8n.clientright.pro/webhook/sms-send`
|
||||||
|
|
||||||
|
### Шаги:
|
||||||
|
|
||||||
|
1. **Webhook Trigger** (POST)
|
||||||
|
- Принимает: `{ "phonenumber": "+79262306381" }`
|
||||||
|
|
||||||
|
2. **Нормализация номера телефона**
|
||||||
|
- Убрать пробелы, скобки, дефисы
|
||||||
|
- Убрать +7 или 8 в начале
|
||||||
|
- Результат: `9262306381`
|
||||||
|
|
||||||
|
3. **Проверка Rate Limit (Redis)**
|
||||||
|
- Ключ: `sms:ratelimit:send:9262306381`
|
||||||
|
- Если значение >= 5 → ошибка "Превышен лимит"
|
||||||
|
- Иначе → увеличить счетчик (TTL: 1 час)
|
||||||
|
|
||||||
|
4. **Генерация кода**
|
||||||
|
- 6-значный случайный код: `106574`
|
||||||
|
|
||||||
|
5. **Сохранение кода в Redis**
|
||||||
|
- Ключ: `sms:code:9262306381`
|
||||||
|
- Значение: `106574`
|
||||||
|
- TTL: 600 секунд (10 минут)
|
||||||
|
|
||||||
|
6. **Отправка SMS (HTTP Request)**
|
||||||
|
- URL: API SMS провайдера (SigmaSMS или другой)
|
||||||
|
- Метод: POST
|
||||||
|
- Body: `{ "phone": "9262306381", "text": "Код подтверждения: 106574" }`
|
||||||
|
|
||||||
|
7. **Обработка ошибок**
|
||||||
|
- Если SMS не отправилось → удалить код из Redis
|
||||||
|
- Вернуть ошибку
|
||||||
|
|
||||||
|
8. **Ответ**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Код отправлен на ваш номер телефона"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow 2: Проверка SMS кода
|
||||||
|
|
||||||
|
**Webhook URL:** `https://n8n.clientright.pro/webhook/sms-verify`
|
||||||
|
|
||||||
|
### Шаги:
|
||||||
|
|
||||||
|
1. **Webhook Trigger** (POST)
|
||||||
|
- Принимает: `{ "phonenumber": "+79262306381", "code": "106574" }`
|
||||||
|
|
||||||
|
2. **Нормализация номера телефона**
|
||||||
|
- Аналогично workflow 1
|
||||||
|
|
||||||
|
3. **Проверка Rate Limit для попыток (Redis)**
|
||||||
|
- Ключ: `sms:ratelimit:attempts:9262306381`
|
||||||
|
- Если значение >= 10 → ошибка "Превышено количество попыток"
|
||||||
|
- Иначе → увеличить счетчик (TTL: 15 минут)
|
||||||
|
|
||||||
|
4. **Чтение кода из Redis**
|
||||||
|
- Ключ: `sms:code:9262306381`
|
||||||
|
- Если не найден → ошибка "Код не найден или истек"
|
||||||
|
|
||||||
|
5. **Сравнение кодов**
|
||||||
|
- Если `введенный код !== сохраненный код` → ошибка "Неверный код"
|
||||||
|
|
||||||
|
6. **Успешная верификация**
|
||||||
|
- Удалить код из Redis: `sms:code:9262306381`
|
||||||
|
- Удалить счетчик попыток: `sms:ratelimit:attempts:9262306381`
|
||||||
|
- Создать токен верификации: `sms:verified:9262306381` = `random_token` (TTL: 1 час)
|
||||||
|
|
||||||
|
7. **Ответ**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Код подтвержден",
|
||||||
|
"token": "abc123def456..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow 3: Проверка статуса верификации
|
||||||
|
|
||||||
|
**Webhook URL:** `https://n8n.clientright.pro/webhook/sms-check-verified`
|
||||||
|
|
||||||
|
### Шаги:
|
||||||
|
|
||||||
|
1. **Webhook Trigger** (POST)
|
||||||
|
- Принимает: `{ "phonenumber": "+79262306381", "token": "abc123..." }`
|
||||||
|
|
||||||
|
2. **Нормализация номера**
|
||||||
|
|
||||||
|
3. **Проверка токена в Redis**
|
||||||
|
- Ключ: `sms:verified:9262306381`
|
||||||
|
- Если токен совпадает → `verified: true`
|
||||||
|
- Иначе → `verified: false`
|
||||||
|
|
||||||
|
4. **Ответ**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"verified": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Преимущества переноса в n8n
|
||||||
|
|
||||||
|
1. ✅ **Визуализация** - видно весь процесс на графике
|
||||||
|
2. ✅ **Логирование** - все шаги логируются автоматически
|
||||||
|
3. ✅ **Мониторинг** - видно ошибки и задержки
|
||||||
|
4. ✅ **Гибкость** - легко добавить новые шаги (например, уведомления в Telegram)
|
||||||
|
5. ✅ **Тестирование** - можно тестировать каждый шаг отдельно
|
||||||
|
6. ✅ **Масштабируемость** - легко добавить несколько SMS провайдеров с fallback
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Изменения в PHP коде
|
||||||
|
|
||||||
|
После переноса в n8n, `sms-verify.php` станет простым прокси:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// sms-verify.php?action=send
|
||||||
|
// Просто перенаправляет на n8n webhook
|
||||||
|
|
||||||
|
// sms-verify.php?action=verify
|
||||||
|
// Просто перенаправляет на n8n webhook
|
||||||
|
```
|
||||||
|
|
||||||
|
Или можно вообще убрать PHP и вызывать n8n напрямую из JS.
|
||||||
230
docs/n8n_webhook_response.md
Normal file
230
docs/n8n_webhook_response.md
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
# Формат ответа N8N Webhook для проверки SMS кода
|
||||||
|
|
||||||
|
## URL Webhook
|
||||||
|
`https://n8n.clientright.pro/webhook/erv_sms_verify`
|
||||||
|
|
||||||
|
## Входящие данные (POST)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"phonenumber": "+79262306381",
|
||||||
|
"code": "106574"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Формат ответа
|
||||||
|
|
||||||
|
### ✅ Успешная проверка (HTTP 200)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Код подтвержден"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Поля:**
|
||||||
|
- `success` (boolean, обязательное) - `true` при успехе
|
||||||
|
- `message` (string, обязательное) - сообщение для пользователя
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ❌ Ошибки (HTTP 400)
|
||||||
|
|
||||||
|
#### 1. Неверный код
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "invalid_code",
|
||||||
|
"message": "Неверный код"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Поля:**
|
||||||
|
- `success` (boolean) - `false`
|
||||||
|
- `error` (string) - код ошибки: `"invalid_code"`
|
||||||
|
- `message` (string) - сообщение для пользователя
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 2. Код не найден или истек
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "code_not_found",
|
||||||
|
"message": "Код не найден или истек. Запросите новый код."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Поля:**
|
||||||
|
- `success` (boolean) - `false`
|
||||||
|
- `error` (string) - код ошибки: `"code_not_found"`
|
||||||
|
- `message` (string) - сообщение для пользователя
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 3. Превышен лимит попыток
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "rate_limit_exceeded",
|
||||||
|
"message": "Превышено количество попыток. Попробуйте позже."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Поля:**
|
||||||
|
- `success` (boolean) - `false`
|
||||||
|
- `error` (string) - код ошибки: `"rate_limit_exceeded"`
|
||||||
|
- `message` (string) - сообщение для пользователя
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 4. Номер телефона или код не указаны
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "missing_data",
|
||||||
|
"message": "Номер телефона или код не указаны"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Поля:**
|
||||||
|
- `success` (boolean) - `false`
|
||||||
|
- `error` (string) - код ошибки: `"missing_data"`
|
||||||
|
- `message` (string) - сообщение для пользователя
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 5. Внутренняя ошибка сервера (HTTP 500)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "internal_error",
|
||||||
|
"message": "Сервис временно недоступен. Попробуйте позже."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Поля:**
|
||||||
|
- `success` (boolean) - `false`
|
||||||
|
- `error` (string) - код ошибки: `"internal_error"`
|
||||||
|
- `message` (string) - сообщение для пользователя
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Коды ошибок
|
||||||
|
|
||||||
|
| Код ошибки | HTTP Status | Описание |
|
||||||
|
|------------|-------------|----------|
|
||||||
|
| `invalid_code` | 400 | Введенный код не совпадает с сохраненным |
|
||||||
|
| `code_not_found` | 400 | Код не найден в Redis или истек (TTL) |
|
||||||
|
| `rate_limit_exceeded` | 400 | Превышен лимит попыток проверки (10 за 15 минут) |
|
||||||
|
| `missing_data` | 400 | Не указан номер телефона или код |
|
||||||
|
| `internal_error` | 500 | Внутренняя ошибка (Redis недоступен и т.д.) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Примеры использования в n8n
|
||||||
|
|
||||||
|
### Успешная проверка
|
||||||
|
|
||||||
|
**Workflow шаги:**
|
||||||
|
1. Webhook Trigger → получает `phonenumber` и `code`
|
||||||
|
2. Function Node → нормализация номера
|
||||||
|
3. Redis Node (GET) → чтение кода: `sms:code:9262306381`
|
||||||
|
4. Function Node → сравнение кодов
|
||||||
|
5. Redis Node (DEL) → удаление кода и счетчика попыток
|
||||||
|
6. **Respond to Webhook** → возвращает успешный ответ
|
||||||
|
|
||||||
|
**Код для Respond to Webhook:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Код подтвержден"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ошибка: Неверный код
|
||||||
|
|
||||||
|
**Workflow шаги:**
|
||||||
|
1. ... (аналогично успешной проверке)
|
||||||
|
2. Function Node → сравнение кодов
|
||||||
|
3. IF Node → если коды не совпадают
|
||||||
|
4. **Respond to Webhook** → возвращает ошибку
|
||||||
|
|
||||||
|
**Код для Respond to Webhook:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "invalid_code",
|
||||||
|
"message": "Неверный код"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ошибка: Код не найден
|
||||||
|
|
||||||
|
**Workflow шаги:**
|
||||||
|
1. ... (аналогично)
|
||||||
|
2. Redis Node (GET) → чтение кода
|
||||||
|
3. IF Node → если код = null
|
||||||
|
4. **Respond to Webhook** → возвращает ошибку
|
||||||
|
|
||||||
|
**Код для Respond to Webhook:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "code_not_found",
|
||||||
|
"message": "Код не найден или истек. Запросите новый код."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Совместимость с текущим кодом
|
||||||
|
|
||||||
|
Текущий JS код (`common.js`) ожидает:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
success: function(data) {
|
||||||
|
if (data.success) {
|
||||||
|
// Успех - сохраняем токен
|
||||||
|
sms_verify_token = data.token;
|
||||||
|
// ... показываем форму
|
||||||
|
} else {
|
||||||
|
// Ошибка - показываем сообщение
|
||||||
|
$('.modal .form-item__warning').text(data.message || "Неверный код");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Предложенный формат полностью совместим!** ✅
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Дополнительные рекомендации
|
||||||
|
|
||||||
|
1. **HTTP Status Codes:**
|
||||||
|
- Успех: `200 OK`
|
||||||
|
- Ошибки клиента: `400 Bad Request`
|
||||||
|
- Ошибки сервера: `500 Internal Server Error`
|
||||||
|
|
||||||
|
2. **Content-Type:**
|
||||||
|
- Всегда: `application/json; charset=utf-8`
|
||||||
|
|
||||||
|
3. **Логирование:**
|
||||||
|
- Логировать все попытки проверки (успешные и неуспешные)
|
||||||
|
- Логировать ошибки с деталями
|
||||||
|
|
||||||
|
4. **Безопасность:**
|
||||||
|
- Не возвращать детали внутренних ошибок в production
|
||||||
|
- Не возвращать реальные коды в ответах
|
||||||
@@ -687,8 +687,8 @@ $(function() {
|
|||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
// Сохраняем токен верификации
|
// Код подтвержден (токен больше не используется)
|
||||||
sms_verify_token = data.token;
|
// sms_verify_token = data.token; // Убрано - токен больше не нужен
|
||||||
|
|
||||||
$('.sms-success').removeClass('d-none');
|
$('.sms-success').removeClass('d-none');
|
||||||
|
|
||||||
@@ -729,7 +729,7 @@ $(function() {
|
|||||||
var $phoneField = $('.js-phone-mask');
|
var $phoneField = $('.js-phone-mask');
|
||||||
var confirmedPhone = $phoneField.val();
|
var confirmedPhone = $phoneField.val();
|
||||||
$phoneField.attr('data-confirmed-phone', confirmedPhone)
|
$phoneField.attr('data-confirmed-phone', confirmedPhone)
|
||||||
.attr('data-verify-token', sms_verify_token)
|
// .attr('data-verify-token', sms_verify_token) // Убрано - токен больше не нужен
|
||||||
.prop('readonly', true)
|
.prop('readonly', true)
|
||||||
.attr('autocomplete', 'off');
|
.attr('autocomplete', 'off');
|
||||||
|
|
||||||
|
|||||||
166
sms-verify.php
166
sms-verify.php
@@ -353,11 +353,10 @@ try {
|
|||||||
], JSON_UNESCAPED_UNICODE);
|
], JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
} elseif ($action === 'verify') {
|
} elseif ($action === 'verify') {
|
||||||
// Проверка кода
|
// Проверка кода через n8n webhook
|
||||||
log_message("=== НАЧАЛО ПРОВЕРКИ КОДА ===");
|
log_message("=== НАЧАЛО ПРОВЕРКИ КОДА ===");
|
||||||
log_message("REQUEST_METHOD: " . ($_SERVER['REQUEST_METHOD'] ?? 'не установлен'));
|
log_message("REQUEST_METHOD: " . ($_SERVER['REQUEST_METHOD'] ?? 'не установлен'));
|
||||||
log_message("POST данные: " . json_encode($_POST, JSON_UNESCAPED_UNICODE));
|
log_message("POST данные: " . json_encode($_POST, JSON_UNESCAPED_UNICODE));
|
||||||
log_message("GET данные: " . json_encode($_GET, JSON_UNESCAPED_UNICODE));
|
|
||||||
|
|
||||||
$phone = $_POST['phonenumber'] ?? '';
|
$phone = $_POST['phonenumber'] ?? '';
|
||||||
$code = $_POST['code'] ?? '';
|
$code = $_POST['code'] ?? '';
|
||||||
@@ -370,108 +369,89 @@ try {
|
|||||||
throw new Exception("Номер телефона или код не указаны");
|
throw new Exception("Номер телефона или код не указаны");
|
||||||
}
|
}
|
||||||
|
|
||||||
$phone_cleaned = clear_phone($phone);
|
// Получаем URL webhook из .env
|
||||||
log_message("Номер после очистки: '$phone_cleaned'");
|
$webhook_url = env('N8N_SMS_VERIFY_WEBHOOK', '');
|
||||||
|
|
||||||
// Проверка rate limiting - пытаемся переподключиться при необходимости
|
if (empty($webhook_url)) {
|
||||||
log_message("Попытка подключения к Redis для проверки кода...");
|
log_message("Ошибка: не указан N8N_SMS_VERIFY_WEBHOOK в .env");
|
||||||
$redis = getRedis(true); // Принудительно пытаемся переподключиться
|
throw new Exception("Сервис временно недоступен. Попробуйте позже.");
|
||||||
|
|
||||||
$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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если код не найден в Redis, пытаемся получить из файла (fallback)
|
log_message("Отправка запроса на n8n webhook: $webhook_url");
|
||||||
if ($stored_code === null) {
|
|
||||||
log_message("Код не найден в Redis, проверяем файловое хранилище...");
|
// Отправляем запрос на n8n webhook
|
||||||
$stored_code = getCodeFromFile($phone_cleaned);
|
$ch = curl_init();
|
||||||
if ($stored_code !== false) {
|
curl_setopt($ch, CURLOPT_URL, $webhook_url);
|
||||||
log_message("Код найден в файловом хранилище для номера $phone_cleaned");
|
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("Ошибка соединения с сервисом. Попробуйте позже.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если код все еще не найден, выдаем ошибку
|
// Логируем ответ от n8n
|
||||||
if ($stored_code === null || $stored_code === false) {
|
log_message("Ответ от n8n: HTTP $http_code, ответ: " . substr($response, 0, 200));
|
||||||
log_message("КРИТИЧЕСКАЯ ОШИБКА: Код не найден ни в Redis, ни в файловом хранилище для номера $phone_cleaned");
|
|
||||||
throw new Exception("Код не найден или истек. Запросите новый код.");
|
// Парсим ответ
|
||||||
|
$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 ($http_code >= 200 && $http_code < 300) {
|
||||||
if ($stored_code !== $code) {
|
// Успешный ответ от n8n
|
||||||
log_message("Неверный код для номера $phone_cleaned. Введен: $code, ожидался: $stored_code");
|
if (isset($response_data['success']) && $response_data['success'] === true) {
|
||||||
throw new Exception("Неверный код");
|
log_message("Код подтвержден через n8n для номера: " . ($phone ?: 'не указан'));
|
||||||
}
|
|
||||||
|
// Возвращаем упрощенный ответ (без токена)
|
||||||
// Код верный - удаляем его из Redis и файла, создаем сессию подтверждения
|
echo json_encode([
|
||||||
if ($redis) {
|
'success' => true,
|
||||||
try {
|
'message' => $response_data['message'] ?? 'Код подтвержден'
|
||||||
$key = "sms:code:$phone_cleaned";
|
], JSON_UNESCAPED_UNICODE);
|
||||||
$redis->del($key);
|
} else {
|
||||||
$key_attempts = "sms:ratelimit:attempts:$phone_cleaned";
|
// Ошибка от n8n
|
||||||
$redis->del($key_attempts); // Сбрасываем счетчик попыток
|
$error_message = $response_data['message'] ?? 'Неверный код';
|
||||||
} catch (Exception $e) {
|
log_message("Ошибка проверки кода через n8n: $error_message");
|
||||||
log_message("Ошибка при удалении кода из Redis: " . $e->getMessage());
|
|
||||||
}
|
http_response_code(400);
|
||||||
}
|
echo json_encode([
|
||||||
deleteCodeFromFile($phone_cleaned); // Удаляем из файла тоже
|
'success' => false,
|
||||||
|
'message' => $error_message
|
||||||
// Создаем токен подтверждения (действует 1 час) - только если Redis доступен
|
], JSON_UNESCAPED_UNICODE);
|
||||||
$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());
|
|
||||||
}
|
}
|
||||||
} else {
|
} 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') {
|
} elseif ($action === 'check_verified') {
|
||||||
// Проверка статуса верификации (для проверки перед отправкой формы)
|
// Проверка статуса верификации (для проверки перед отправкой формы)
|
||||||
$phone = $_POST['phonenumber'] ?? '';
|
$phone = $_POST['phonenumber'] ?? '';
|
||||||
|
|||||||
Reference in New Issue
Block a user