Development of connectors for integrating 1C-Bitrix with external systems

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

Developing Connectors for 1C-Bitrix Integration with External Systems

A connector is an isolated PHP module that encapsulates the interaction logic with a single external system. A well-designed connector is independent of the site's specific business logic, easy to test, and reusable across projects.

Connector Module Structure

A connector is implemented as a 1C-Bitrix module. Directory structure:

/local/modules/vendor.connector_name/
├── install/
│   ├── index.php          # Module installer
│   └── db/
│       └── install.sql    # Module tables
├── lib/
│   ├── Client.php         # HTTP client for the external system
│   ├── Mapper.php         # Data mapping: 1C-Bitrix ↔ external system
│   ├── Queue.php          # Task queue
│   └── EventHandler.php   # 1C-Bitrix event subscriptions
├── options.php             # Settings page in the admin panel
└── include.php

Base Client Class

namespace Vendor\ConnectorName;

use Bitrix\Main\Web\HttpClient;
use Bitrix\Main\Data\Cache;

abstract class BaseApiClient {
    protected string $baseUrl;
    protected array  $defaultHeaders = [];

    abstract protected function authenticate(HttpClient $http): void;

    public function call(string $method, string $endpoint, array $data = []): array {
        $http = new HttpClient(['socketTimeout' => 10, 'streamTimeout' => 30]);
        $this->authenticate($http);

        foreach ($this->defaultHeaders as $name => $value) {
            $http->setHeader($name, $value);
        }

        $url = rtrim($this->baseUrl, '/') . '/' . ltrim($endpoint, '/');

        $rawResponse = match(strtoupper($method)) {
            'GET'    => $http->get($url . '?' . http_build_query($data)),
            'POST'   => $http->post($url, json_encode($data)),
            'PUT'    => $http->query(HttpClient::HTTP_PUT, $url, json_encode($data)),
            'DELETE' => $http->query(HttpClient::HTTP_DELETE, $url),
            default  => throw new \InvalidArgumentException("Unknown method: $method"),
        };

        $statusCode = $http->getStatus();
        if ($statusCode >= 400) {
            $this->handleError($statusCode, $rawResponse, $endpoint);
        }

        return json_decode($rawResponse, true) ?? [];
    }

    protected function handleError(int $code, string $body, string $endpoint): void {
        $error = json_decode($body, true)['message'] ?? $body;
        \Bitrix\Main\Diag\Debug::writeToFile(
            date('Y-m-d H:i:s') . " [{$code}] {$endpoint}: {$error}\n",
            '', '/local/logs/connector_errors.log'
        );
        throw new \RuntimeException("API error {$code}: {$error}");
    }
}

Data Mapper

The mapper is a dedicated class responsible for transforming data structures. It is the primary point of change when the external system's contract changes:

class OrderMapper {
    // 1C-Bitrix order → external CRM format
    public function toExternal(\Bitrix\Sale\Order $order): array {
        $props = $order->getPropertyCollection();
        return [
            'external_id' => $order->getId(),
            'total'       => $order->getPrice(),
            'customer'    => [
                'email' => $props->getUserEmail(),
                'phone' => $props->getItemByOrderPropertyCode('PHONE')?->getValue(),
                'name'  => $props->getItemByOrderPropertyCode('NAME')?->getValue(),
            ],
            'items'       => $this->mapBasketItems($order->getBasket()),
            'status'      => StatusMap::toExternal($order->getField('STATUS_ID')),
        ];
    }

    // External CRM response → 1C-Bitrix order update
    public function applyToOrder(array $externalData, \Bitrix\Sale\Order $order): void {
        $newStatus = StatusMap::toBitrix($externalData['status']);
        if ($newStatus && $order->getField('STATUS_ID') !== $newStatus) {
            $order->setField('STATUS_ID', $newStatus);
        }
    }
}

Queue with Retry Logic

class Queue {
    private const TABLE = 'b_vendor_connector_queue';

    public static function push(string $type, array $payload): void {
        \Bitrix\Main\Application::getConnection()->query(
            "INSERT INTO " . self::TABLE . " (TYPE, PAYLOAD, STATUS, ATTEMPTS, DATE_CREATE)
             VALUES ('" . $type . "', '" . json_encode($payload) . "', 'pending', 0, NOW())"
        );
    }

    public static function process(int $batchSize = 20): void {
        $rows = \Bitrix\Main\Application::getConnection()->query(
            "SELECT * FROM " . self::TABLE . "
             WHERE STATUS = 'pending' AND ATTEMPTS < 5
             ORDER BY DATE_CREATE LIMIT " . $batchSize
        );

        while ($row = $rows->fetch()) {
            try {
                self::dispatch($row['TYPE'], json_decode($row['PAYLOAD'], true));
                self::markDone($row['ID']);
            } catch (\Throwable $e) {
                self::markFailed($row['ID'], $e->getMessage());
            }
        }
    }
}

Module Settings Page

options.php contains a form for entering connector parameters — external system URL, API key, mode (test/production), sync frequency. The standard CAdminTabControl component is used for integration with the 1C-Bitrix admin interface.

Sensitive data (API keys) is encrypted before being written to b_option using openssl_encrypt() with a key from .env.

Task Effort
Base module structure 4–6 h
HTTP client with error handling 4–6 h
Data mapper 4–8 h
Queue with retry logic 4–6 h
Settings page and tests 4–6 h