Development of a custom exchange between 1C and 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
    1175
  • 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
    747
  • 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

Custom 1C and 1C-Bitrix Exchange Development

The standard CommerceML covers typical tasks: products, prices, stock, orders. When requirements go beyond that scope — custom exchange development is the answer. There are plenty of reasons: non-standard document structures in 1C, multiple data sources, business logic that cannot be expressed in XML.

When the Standard Exchange Falls Short

  • Non-standard 1C configuration without CommerceML support (industry-specific or custom-built configurations)
  • Data not present in CommerceML: applications, tenders, service requests
  • Real-time requirements: stock updates immediately upon changes in 1C
  • Complex data transformation: data from multiple 1C reference books consolidated into a single 1C-Bitrix object
  • Multi-system integration: 1C + CRM + 1C-Bitrix via a unified gateway

Custom Exchange Architecture Options

Option 1: 1C HTTP Services (REST API)

Modern configurations (UT 11.4+, ERP 2.5+, KA 2.5+) support publishing HTTP services. 1C is published on a web server (Apache/nginx), and 1C-Bitrix calls the endpoints via REST.

class OneCApiClient
{
    private string $baseUrl;
    private string $token;

    public function getProducts(array $filters = [], int $limit = 100, int $offset = 0): array
    {
        return $this->request('GET', '/hs/exchange/products', [
            'modified_since' => $filters['modified_since'] ?? null,
            'limit' => $limit,
            'offset' => $offset,
        ]);
    }

    public function createOrder(array $orderData): array
    {
        return $this->request('POST', '/hs/exchange/orders', $orderData);
    }

    public function updateOrderStatus(string $orderId, string $status): bool
    {
        $result = $this->request('PUT', "/hs/exchange/orders/{$orderId}/status", [
            'status' => $status,
        ]);
        return $result['success'] ?? false;
    }

    private function request(string $method, string $path, array $data = []): array
    {
        $ch = curl_init();
        $url = $this->baseUrl . $path;

        if ($method === 'GET' && $data) {
            $url .= '?' . http_build_query($data);
        }

        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $this->token,
                'Content-Type: application/json',
            ],
            CURLOPT_CUSTOMREQUEST => $method,
        ]);

        if ($method !== 'GET') {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200 && $httpCode !== 201) {
            throw new \RuntimeException("1C API error: HTTP {$httpCode}. Response: {$response}");
        }

        return json_decode($response, true);
    }
}

Option 2: Event-Driven Exchange (Webhooks from 1C)

When data changes in 1C, it sends an HTTP request to 1C-Bitrix. This is implemented by adding event subscriptions in the 1C configuration (stock changes, order status changes) and triggering an HTTP request on each event.

On the 1C-Bitrix side — an endpoint to receive events:

// /local/api/1c/webhook.php
$payload = json_decode(file_get_contents('php://input'), true);
$eventType = $payload['event_type'] ?? '';

switch ($eventType) {
    case 'stock_changed':
        StockSyncHandler::handle($payload['products']);
        break;
    case 'order_status_changed':
        OrderStatusHandler::handle($payload['order_id'], $payload['status']);
        break;
    case 'price_changed':
        PriceSyncHandler::handle($payload['prices']);
        break;
}

http_response_code(200);
echo json_encode(['ok' => true]);

Event-driven exchange delivers real-time data accuracy — without polling on a schedule.

Data Transformation

A custom exchange requires explicit mapping logic. A typical complex case: in 1C a product is stored across three related reference books (Items, Characteristics, Series), while in 1C-Bitrix it must be a single infoblock element with trade offers (SKUs).

class ProductTransformer
{
    public function transform(array $oneCNomenclature): array
    {
        $product = [
            'NAME' => $oneCNomenclature['name'],
            'CODE' => \CUtil::translit($oneCNomenclature['name'], 'ru'),
            'XML_ID' => $oneCNomenclature['guid'],
            'ACTIVE' => $oneCNomenclature['active'] ? 'Y' : 'N',
            'DETAIL_TEXT' => $oneCNomenclature['description'],
            'PROPERTY_ARTICLE' => $oneCNomenclature['article'],
            'PROPERTY_BRAND' => $this->getBrandId($oneCNomenclature['manufacturer_guid']),
        ];

        // Build trade offers from characteristics
        $offers = [];
        foreach ($oneCNomenclature['characteristics'] as $char) {
            $offers[] = [
                'XML_ID' => $char['guid'],
                'NAME' => $oneCNomenclature['name'] . ' / ' . $char['value'],
                'PROPERTY_COLOR' => $this->getColorId($char['color_guid']),
                'PROPERTY_SIZE' => $char['size'],
                'CATALOG_PRICE_1' => $char['price'],
                'CATALOG_QUANTITY' => $char['stock'],
            ];
        }

        $product['OFFERS'] = $offers;
        return $product;
    }
}

Queues for Reliable Delivery

Direct synchronous exchange breaks when one of the systems is unavailable. The reliable approach uses a queue:

// When an order changes in 1C-Bitrix — push a task to the queue
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
    'sale',
    'OnSaleOrderSaved',
    function(\Bitrix\Main\Event $event) {
        $order = $event->getParameter('ENTITY');
        if ($order->isNew() || $order->getFields()->isChanged('STATUS_ID')) {
            // Add to the queue for transmission to 1C
            \MyProject\Queue\ExchangeQueue::push([
                'type' => 'order_sync',
                'order_id' => $order->getId(),
                'created_at' => time(),
            ]);
        }
    }
);

A worker processes the queue and retries on temporary 1C unavailability.

Versioning and Backward Compatibility

A custom exchange lives for years. 1C updates change the API structure. The right approach: versioned endpoints (/hs/exchange/v2/products) and explicit documentation of the exchange contract. When the 1C-side API changes — a new endpoint version is introduced, while the old one remains active until an agreed-upon deprecation date.

Development Timelines

Complexity Examples Duration
Simple REST client for standard objects Syncing 1–2 reference books 3–5 days
Bidirectional exchange with transformation Non-standard item structure 2–4 weeks
Event-driven exchange + queues Real-time, high reliability 3–6 weeks
Full integration gateway Multiple systems, complex business logic 1–3 months