Настройка подтверждения заказа через SMS-код 1С-Битрикс
На крупных магазинах с высоким средним чеком встречается задача: перед финальным оформлением заказа подтвердить намерение покупателя по SMS. Это снижает число «случайных» заказов и верифицирует номер телефона, который потом используется в программе лояльности. Стандартный чекаут 1С-Битрикс такого механизма не имеет — нужно вмешиваться в процесс оформления.
Архитектура подтверждения
Процесс: покупатель заполнил форму заказа → нажал «Оформить» → система отправляет SMS с кодом на указанный телефон → покупатель вводит код → заказ создаётся. Заказ в Битрикс создаётся только после успешной верификации кода, не раньше.
Хранение кодов — в таблице local_order_confirmations:
CREATE TABLE local_order_confirmations (
ID INT AUTO_INCREMENT PRIMARY KEY,
PHONE VARCHAR(20) NOT NULL,
CODE VARCHAR(6) NOT NULL,
SESSION_ID VARCHAR(128),
ATTEMPTS TINYINT DEFAULT 0,
CONFIRMED CHAR(1) DEFAULT 'N',
CREATED_AT DATETIME NOT NULL,
EXPIRES_AT DATETIME NOT NULL,
INDEX idx_phone_code (PHONE, CODE),
INDEX idx_session (SESSION_ID)
);
Срок жизни кода — 5 минут. Максимум попыток — 3. После исчерпания — новый запрос кода с задержкой (rate limit).
Интеграция в чекаут
Если используется sale.order.ajax — вмешиваемся в его JavaScript. Перехватываем событие отправки формы:
// В result_modifier.php компонента или подключаемом JS
BX.addCustomEvent('onSaleComponentOrderSuccess', function(order) {
// Стандартная обработка — отключаем
});
document.querySelector('.order-confirm-btn').addEventListener('click', async (e) => {
e.preventDefault();
const phone = document.querySelector('[name="ORDER_PROP_PHONE"]').value;
// Запрашиваем SMS
const res = await fetch('/local/api/order-confirm/send', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'X-Bitrix-Csrf-Token': BX.bitrix_sessid()},
body: JSON.stringify({phone})
});
if (res.ok) {
showCodeInputModal(phone);
}
});
После успешного подтверждения кода фронтенд отправляет токен подтверждения вместе с данными заказа, сервер проверяет токен до создания заказа.
Серверная часть
class OrderConfirmController
{
public function sendCode(): void
{
$phone = $this->normalizePhone($_POST['phone'] ?? '');
if (!$phone) { $this->error('Неверный номер телефона'); }
// Rate limit: не более 3 отправок в 15 минут
if ($this->isRateLimited($phone)) {
$this->error('Слишком много запросов. Подождите 15 минут.');
}
$code = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
$expiresAt = (new \DateTime())->modify('+5 minutes');
OrderConfirmationTable::add([
'PHONE' => $phone,
'CODE' => password_hash($code, PASSWORD_DEFAULT), // не храним открытый код
'SESSION_ID' => session_id(),
'EXPIRES_AT' => \Bitrix\Main\Type\DateTime::createFromTimestamp($expiresAt->getTimestamp()),
]);
SmsService::send($phone, "Код подтверждения заказа: {$code}. Действует 5 минут.");
$this->success(['expires_in' => 300]);
}
public function verifyCode(): void
{
$phone = $this->normalizePhone($_POST['phone'] ?? '');
$code = $_POST['code'] ?? '';
$record = OrderConfirmationTable::getActiveRecord($phone, session_id());
if (!$record) {
$this->error('Код не найден или истёк срок действия');
}
// Increment attempts
OrderConfirmationTable::incrementAttempts($record['ID']);
if ($record['ATTEMPTS'] >= 3) {
$this->error('Превышено число попыток. Запросите новый код.');
}
if (!password_verify($code, $record['CODE'])) {
$this->error('Неверный код');
}
// Код верный — выдаём одноразовый токен для создания заказа
$token = bin2hex(random_bytes(32));
OrderConfirmationTable::markConfirmed($record['ID'], $token);
$this->success(['token' => $token]);
}
}
Код хранится в базе в хешированном виде — password_hash(). Даже при утечке базы коды не скомпрометированы.
Проверка токена при создании заказа
Перед стандартным созданием заказа через OnBeforeSaleOrderSaved или переопределение контроллера:
AddEventHandler('sale', 'OnBeforeSaleOrderSaved', function(\Bitrix\Sale\Order $order, array $data) {
if (!ConfirmConfig::isRequiredForOrder($order)) {
return; // не все заказы требуют подтверждения
}
$token = $_POST['confirm_token'] ?? '';
if (!OrderConfirmationTable::isValidToken($token, session_id())) {
throw new \Exception('Заказ не подтверждён по SMS');
}
});
SMS-провайдер
Интеграция с провайдером через абстракцию:
interface SmsProviderInterface {
public function send(string $phone, string $message): bool;
}
Реализации: SMS.ru, SMSC.ru, МТС Коммуникатор, интеграция через Битрикс24 SMS. Переключение провайдера без изменения бизнес-логики.
Состав работ
- Таблица подтверждений, логика rate limit и попыток
- API-контроллер: отправка кода, верификация, выдача токена
- Интеграция с чекаутом (перехват JS, передача токена)
- Проверка токена перед созданием заказа
- Подключение SMS-провайдера
Сроки: 5–7 дней с одним провайдером SMS. 1.5–2 недели при кастомном чекауте.







