Developing a React-based order form for 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1173
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    745
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

React Checkout Development for 1C-Bitrix

Checkout is the most vulnerable point in the funnel. The standard Bitrix bitrix:sale.order.ajax component runs on jQuery and a template system, updating page sections through AJAX HTML-block replacement. That worked in 2015. Today it is difficult to customize, slow to render, and scales poorly for non-standard scenarios: multi-step checkout with conditional logic, delivery to multiple addresses, B2B fields (company details, tax IDs), map integration.

A React checkout addresses the problem at the architecture level: all UI lives in components, logic is concentrated in hooks and a state manager, and server communication goes through a clean API.

React Checkout Architecture

The checkout is split into two independent layers: the UI layer (React) and business logic (Bitrix on the server).

On the frontend — a React application that manages the form, shows/hides steps, and calculates totals in real time. On the server — Bitrix processes the order via \Bitrix\Sale\Order, applies discounts, calculates delivery costs, and checks stock.

The key API method for calculating the order without creating it:

// Calculate totals without saving the order
$order = \Bitrix\Sale\Order::create(SITE_ID, $userId);
$basket = \Bitrix\Sale\Basket::loadSiteBasket(SITE_ID);
$order->setBasket($basket);

// Apply delivery settings
$shipment = $order->getShipmentCollection()->createItem(
    \Bitrix\Sale\Delivery\Services\Manager::getById($deliveryId)
);
$shipment->setFields(['DELIVERY_ID' => $deliveryId, 'CURRENCY' => 'RUB']);
$shipment->calculateDelivery();

// Apply coupon
$order->getDiscountSystem()->calculate();

// Return totals without saving (no $order->save() call)
return [
    'subtotal'       => $basket->getPrice(),
    'delivery_price' => $shipment->getPrice(),
    'discount'       => $order->getDiscountPrice(),
    'total'          => $order->getPrice(),
];

This endpoint is called on every field change: selecting a delivery service, entering a promo code, changing a quantity. React receives up-to-date figures without a page reload.

Multi-Step Form and State Management

For a complex checkout (3+ steps with validation), React Hook Form with Zod validation schemas is the optimal choice:

const checkoutSchema = z.object({
  contact: z.object({
    name:  z.string().min(2, 'Name is required'),
    phone: z.string().regex(/^\+7\d{10}$/, 'Invalid format'),
    email: z.string().email('Invalid 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']),
  }),
});

Checkout state is stored in Zustand: steps, current step, data for each step, calculation result. Data is not lost when moving between steps; the user can go back.

Map Integration for Courier Delivery

Yandex Maps or DaData for address autocomplete is a standard checkout requirement in React.

// Hook for address autocomplete via DaData
function useAddressSuggest(query: string) {
  return useQuery({
    queryKey: ['address-suggest', query],
    queryFn:  () => fetchDaDataSuggestions(query),
    enabled:  query.length > 3,
    staleTime: 60_000,
  });
}

When an address is selected via DaData, structured data (city, street, postal code) is passed to Bitrix as separate fields — this simplifies subsequent order processing and handoff to delivery services.

Case Study: Furniture Retailer Checkout

Online furniture store. Specifics: products with different production lead times, ability to book delivery for a specific date, mandatory measurement service for certain products, B2B checkout with company details. The standard sale.order.ajax supported none of these: no delivery date selection, no conditional measurement block, no company details in a single flow.

Implementation:

  1. Step 1 — Contact details. Form with phone and name. Phone validated with libphonenumber-js; SMS verification as an optional enhancement.

  2. Step 2 — Delivery. Dynamic display: if the order contains products requiring measurement, a "Schedule Measurement" block with a datepicker appears. Available dates are loaded from the server (from Bitrix CRM; occupied slots are closed). Delivery date selection accounts for production lead time — the minimum date is calculated server-side using max(PRODUCTION_DAYS) across cart items.

  3. Step 3 — Payment. A toggle between "Individual" and "Business." Selecting "Business" expands a company details block (tax ID → autocomplete via DaData → populating registration number, company name, address). An invoice for B2B is generated automatically after order creation via \Bitrix\Sale\PaySystem\Manager.

  4. Order creation. The final POST sends all data to the server. Bitrix creates the order, attaches custom properties (delivery date, client type, company details), and sends notifications. React receives the order ID and redirects the user to the "Thank You" page.

Step Standard Bitrix React Checkout
Delivery date selection Not possible Datepicker with occupied slots
B2B company details Separate form Inline, in the same flow
Real-time validation On submit only Instant, on blur
Totals recalculation on delivery change Block reload No reload, <200 ms

Checkout conversion increased from 62% to 79% in the first 6 weeks after launch.

Server-Side Order Creation

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);

    // Contact
    $order->setField('USER_DESCRIPTION', $data['comment'] ?? '');

    // Delivery
    $shipmentCollection = $order->getShipmentCollection();
    $shipment = $shipmentCollection->createItem(
        \Bitrix\Sale\Delivery\Services\Manager::getById($data['delivery_id'])
    );
    $shipment->setField('DELIVERY_ID', $data['delivery_id']);

    // Payment
    $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());

    // Order properties (address, phone, tax ID, etc.)
    $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()];
}

Error Handling and Edge Cases

Insufficient stock at checkout — handled at the final save step. React shows a modal listing unavailable items and offers to remove them or save the order without them.

Connection lost during checkout — React Query with retry: 3 and a user notification. Form data is saved to sessionStorage and restored on page reload.

Scope of Work

  • Designing checkout steps, conditional logic, and validation rules
  • API controllers: order calculation, order creation, delivery services and pickup points
  • React application development: form, state, map/DaData integration
  • Order creation in Bitrix with the full set of fields, properties, and payment systems
  • Edge case testing: empty cart, insufficient stock, session timeout