Разработка оформления заказа на React для 1С-Битрикс
Оформление заказа — самое уязвимое место воронки. Стандартный компонент bitrix:sale.order.ajax в Битрикс работает на jQuery и шаблонной системе, обновляет части страницы через AJAX-подмену HTML-блоков. Это работало в 2015 году. Сейчас — сложно кастомизировать, медленно отрисовывается, плохо масштабируется при нестандартных сценариях: многошаговое оформление с условной логикой, доставка по нескольким адресам, B2B-поля (реквизиты, ИНН), интеграция с картами.
React-чекаут решает проблему на уровне архитектуры: весь UI живёт в компонентах, логика сосредоточена в хуках и стейт-менеджере, взаимодействие с сервером — через чистый API.
Архитектура React-чекаута
Оформление заказа делится на два независимых уровня: UI-уровень (React) и бизнес-логика (Битрикс на сервере).
На фронте — React-приложение, которое управляет формой, показывает/скрывает шаги, рассчитывает итог в реальном времени. На сервере — Битрикс обрабатывает заказ через \Bitrix\Sale\Order, применяет скидки, рассчитывает стоимость доставки, проверяет остатки.
Ключевой API-метод для расчёта заказа без его создания:
// Расчёт итогов без сохранения заказа
$order = \Bitrix\Sale\Order::create(SITE_ID, $userId);
$basket = \Bitrix\Sale\Basket::loadSiteBasket(SITE_ID);
$order->setBasket($basket);
// Применяем параметры доставки
$shipment = $order->getShipmentCollection()->createItem(
\Bitrix\Sale\Delivery\Services\Manager::getById($deliveryId)
);
$shipment->setFields(['DELIVERY_ID' => $deliveryId, 'CURRENCY' => 'RUB']);
$shipment->calculateDelivery();
// Применяем купон
$order->getDiscountSystem()->calculate();
// Возвращаем итог без сохранения (без вызова $order->save())
return [
'subtotal' => $basket->getPrice(),
'delivery_price' => $shipment->getPrice(),
'discount' => $order->getDiscountPrice(),
'total' => $order->getPrice(),
];
Этот endpoint вызывается при каждом изменении полей: выбор службы доставки, ввод промокода, изменение количества. React получает актуальные цифры без перезагрузки.
Пошаговая форма и управление состоянием
Для сложного чекаута (3+ шагов с валидацией) оптимальна библиотека React Hook Form с Zod-схемами валидации:
const checkoutSchema = z.object({
contact: z.object({
name: z.string().min(2, 'Укажите имя'),
phone: z.string().regex(/^\+7\d{10}$/, 'Неверный формат'),
email: z.string().email('Неверный email'),
}),
delivery: z.object({
type: z.enum(['courier', 'pickup', 'cdek']),
address: z.string().optional(),
pickupId: z.number().optional(),
}),
payment: z.object({
method: z.enum(['online', 'cash', 'invoice']),
}),
});
Стейт чекаута хранится в Zustand: шаги, текущий шаг, данные каждого шага, результат расчёта. При переходе между шагами данные не теряются, пользователь может вернуться назад.
Интеграция с картами для курьерской доставки
Яндекс.Карты или DaData для автодополнения адреса — стандартная задача для React-чекаута.
// Хук для автодополнения адреса через DaData
function useAddressSuggest(query: string) {
return useQuery({
queryKey: ['address-suggest', query],
queryFn: () => fetchDaDataSuggestions(query),
enabled: query.length > 3,
staleTime: 60_000,
});
}
При выборе адреса через DaData структурированные данные (город, улица, индекс) передаются в Битрикс отдельными полями — это упрощает последующую обработку заказа и передачу в службы доставки.
Кейс: чекаут для мебельного ритейлера
Интернет-магазин мебели. Специфика: товары с разными сроками изготовления, возможность заказать доставку на конкретную дату, обязательный замер для ряда товаров, B2B-оформление с реквизитами. Штатный sale.order.ajax не поддерживал ни выбор даты доставки, ни условное отображение блока замера, ни реквизиты компании в одном потоке.
Реализация:
-
Шаг 1 — контакты. Форма с телефоном и именем. Телефон валидируется через libphonenumber-js, подсказка через СМС-верификацию (опционально).
-
Шаг 2 — доставка. Динамическое отображение: если в заказе есть товары с замером — появляется блок «Запись на замер» с datepicker. Доступные даты загружаются с сервера (из CRM Битрикс, занятые слоты закрыты). Выбор даты доставки с учётом срока изготовления — минимальная дата рассчитывается на сервере по
max(PRODUCTION_DAYS)в корзине. -
Шаг 3 — оплата. Переключатель «Физическое лицо / Юридическое лицо». При выборе юрлица разворачивается блок реквизитов (ИНН → автозаполнение через DaData → подтягивание КПП, названия, адреса). Безналичный счёт для B2B генерируется автоматически после создания заказа через
\Bitrix\Sale\PaySystem\Manager. -
Создание заказа. Финальный POST отправляет все данные на сервер. Bitrix создаёт заказ, привязывает кастомные свойства (дата доставки, тип клиента, реквизиты), отправляет уведомления. React получает ID заказа и переводит пользователя на страницу «Спасибо».
| Шаг | Штатный Битрикс | React-чекаут |
|---|---|---|
| Выбор даты доставки | Невозможно | Datepicker с занятыми слотами |
| B2B-реквизиты | Отдельная форма | Inline, в том же потоке |
| Валидация в реальном времени | Только при сабмите | Мгновенно, по blur |
| Расчёт итогов при смене доставки | Перезагрузка блока | Без перезагрузки, <200ms |
Конверсия чекаута выросла с 62% до 79% за первые 6 недель после запуска.
Создание заказа на сервере
public function createOrderAction(array $data): array
{
$order = \Bitrix\Sale\Order::create(SITE_ID, $this->getCurrentUserId());
$basket = \Bitrix\Sale\Basket::loadSiteBasket(SITE_ID);
$order->setBasket($basket);
// Контакт
$order->setField('USER_DESCRIPTION', $data['comment'] ?? '');
// Доставка
$shipmentCollection = $order->getShipmentCollection();
$shipment = $shipmentCollection->createItem(
\Bitrix\Sale\Delivery\Services\Manager::getById($data['delivery_id'])
);
$shipment->setField('DELIVERY_ID', $data['delivery_id']);
// Оплата
$paymentCollection = $order->getPaymentCollection();
$payment = $paymentCollection->createItem(
\Bitrix\Sale\PaySystem\Manager::getObjectById($data['payment_id'])
);
$payment->setField('PAY_SYSTEM_ID', $data['payment_id']);
$payment->setField('SUM', $order->getPrice());
// Свойства заказа (адрес, телефон, ИНН и т.д.)
$propertyCollection = $order->getPropertyCollection();
foreach ($data['properties'] as $code => $value) {
$prop = $propertyCollection->getItemByOrderPropertyCode($code);
if ($prop) {
$prop->setValue($value);
}
}
$result = $order->save();
if (!$result->isSuccess()) {
throw new \Exception(implode(', ', $result->getErrorMessages()));
}
return ['order_id' => $order->getId()];
}
Обработка ошибок и edge cases
Недостаточный остаток товара при оформлении — обрабатывается на финальном шаге сохранения. React показывает модальное окно с перечнем недоступных позиций, предлагает удалить их или сохранить заказ без них.
Потеря соединения во время оформления — React Query с retry: 3 и уведомлением пользователю. Данные формы сохраняются в sessionStorage и восстанавливаются при перезагрузке.
Состав работ
- Проектирование шагов чекаута, условной логики, валидации
- API-контроллеры: расчёт заказа, создание, получение служб доставки и ПВЗ
- Разработка React-приложения: форма, стейт, интеграция с картами/DaData
- Создание заказа в Битрикс с полным набором полей, свойств, платёжных систем
- Тестирование edge cases: пустая корзина, нехватка остатка, таймаут сессии







