Розробка оформлення замовлення на 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' => 'UAH']);
$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(/^\+380\d{9}$/, 'Невірний формат'),
email: z.string().email('Невірний email'),
}),
delivery: z.object({
type: z.enum(['courier', 'pickup', 'nova_poshta']),
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, підказка через SMS-верифікацію (опціонально).
-
Крок 2 — доставка. Динамічне відображення: якщо в замовленні є товари із заміром — з'являється блок «Запис на замір» з datepicker. Доступні дати завантажуються з сервера (з CRM Бітрікс, зайняті слоти закриті). Вибір дати доставки з урахуванням терміну виготовлення — мінімальна дата розраховується на сервері за
max(PRODUCTION_DAYS)у кошику. -
Крок 3 — оплата. Перемикач «Фізична особа / Юридична особа». При виборі юрособи розгортається блок реквізитів (ІПН → автозаповнення через DaData → підтягування КПП, назви, адреси). Безготівковий рахунок для B2B генерується автоматично після створення замовлення через
\Bitrix\Sale\PaySystem\Manager. -
Створення замовлення. Фінальний POST надсилає всі дані на сервер. Бітрікс створює замовлення, прив'язує кастомні властивості (дата доставки, тип клієнта, реквізити), надсилає сповіщення. React отримує ID замовлення і переводить користувача на сторінку «Дякуємо».
| Крок | Штатний Бітрікс | React-чекаут |
|---|---|---|
| Вибір дати доставки | Неможливо | Datepicker із зайнятими слотами |
| B2B-реквізити | Окрема форма | Inline, в тому самому потоці |
| Валідація в реальному часі | Тільки при сабміті | Миттєво, по blur |
| Розрахунок підсумків при зміні доставки | Перезавантаження блоку | Без перезавантаження, <200 мс |
Конверсія чекауту зросла з 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: порожній кошик, нестача залишку, таймаут сесії







