Developing a Middleware Exchange Service for 1C and 1C-Bitrix
A direct 1C and 1C-Bitrix integration works fine as long as there are only two systems. The moment a third one appears — a CRM, a warehouse system, a marketplace — direct connections turn into a tangled web. A middleware service resolves this architecturally: each system communicates only with the middleware.
When a Middleware Service Is Needed
- Multiple 1C-Bitrix websites connected to a single 1C instance
- 1C + 1C-Bitrix + CRM (AmoCRM, Bitrix24) — data must be synchronized across all systems
- Non-standard 1C configuration without HTTP services — an adapter is required
- Data transformation requirements too complex to handle in a CommerceML mapping
- Exchange business logic must be isolated and testable independently of 1C and 1C-Bitrix
Middleware Architecture
[1C] ←→ [Middleware] ←→ [1C-Bitrix]
←→ [CRM]
←→ [Marketplace]
Middleware is a standalone application (PHP, Node.js, Python, Go — depending on the team's stack). Its functions:
- Connection adapters: reads from and writes to each system via its API
- Data mapping: transforms objects from one system's format into another's
- Task queue: stores synchronization tasks and ensures retries on failure
- Exchange log: a history of all operations with their results
- Rule configuration: which fields to sync, in which direction, with what transformations
PHP Middleware Tech Stack
Laravel (or Symfony) + PostgreSQL + Redis (queue) + Horizon (queue monitoring)
Application structure:
app/
Adapters/
OneCAdapter.php # Reads/writes to 1C via HTTP services
BitrixAdapter.php # Reads/writes to 1C-Bitrix via REST API
CrmAdapter.php # AmoCRM / Bitrix24
Mappers/
ProductMapper.php # 1C Item → 1C-Bitrix infoblock element
OrderMapper.php # 1C-Bitrix order → 1C document
Jobs/
SyncProductsJob.php # Product sync job
SyncOrdersJob.php # Order sync job
Models/
SyncLog.php # Exchange log model
SyncQueue.php # Task queue table
1C-Bitrix REST API Adapter
class BitrixAdapter
{
private string $webhookUrl;
public function getOrders(array $filter = []): array
{
$response = $this->call('crm.deal.list', [
'filter' => $filter,
'select' => ['ID', 'TITLE', 'STAGE_ID', 'OPPORTUNITY', 'UF_ORDER_ID'],
]);
return $response['result'] ?? [];
}
public function updateProductStock(int $productId, int $quantity): bool
{
$response = $this->call('catalog.storeProduct.update', [
'id' => $productId,
'fields' => ['AMOUNT' => $quantity],
]);
return $response['result'] ?? false;
}
public function createOrder(array $fields): int
{
$response = $this->call('sale.order.add', ['fields' => $fields]);
return $response['result']['order']['id'] ?? 0;
}
private function call(string $method, array $params = []): array
{
$response = file_get_contents(
$this->webhookUrl . $method . '.json?' . http_build_query($params)
);
return json_decode($response, true);
}
}
Mapper with Transformation Rules
class ProductMapper
{
private array $categoryMapping; // 1C group → 1C-Bitrix section
private array $propertyMapping; // 1C attribute → 1C-Bitrix property
public function oneCToBitrix(array $oneCProduct): array
{
return [
'IBLOCK_ID' => config('bitrix.catalog_iblock_id'),
'XML_ID' => $oneCProduct['Ref_Key'],
'NAME' => $oneCProduct['Description'],
'ACTIVE' => $oneCProduct['DeletionMark'] ? 'N' : 'Y',
'DETAIL_TEXT' => $oneCProduct['КомментарийДляСайта'],
'IBLOCK_SECTION_ID' => $this->mapCategory($oneCProduct['ОсновнаяТоварнаяГруппа_Key']),
'PROPERTY_ARTICLE' => $oneCProduct['Артикул'],
'PROPERTY_WEIGHT' => $oneCProduct['Вес'] * 1000, // kg → g
'PROPERTY_BRAND' => $this->resolveBrand($oneCProduct['Производитель_Key']),
];
}
private function mapCategory(string $oneCGroupGuid): int
{
return $this->categoryMapping[$oneCGroupGuid] ?? config('bitrix.default_section_id');
}
}
Queue and Reliability
class SyncProductsJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $backoff = 300; // 5 minutes between retries
public function handle(OneCAdapter $oneC, BitrixAdapter $bitrix, ProductMapper $mapper): void
{
$products = $oneC->getModifiedProducts($this->sinceDate);
foreach ($products as $oneCProduct) {
try {
$bitrixFields = $mapper->oneCToBitrix($oneCProduct);
$bitrix->upsertProduct($bitrixFields);
SyncLog::create([
'entity' => 'product',
'external_id' => $oneCProduct['Ref_Key'],
'status' => 'success',
]);
} catch (\Exception $e) {
SyncLog::create([
'entity' => 'product',
'external_id' => $oneCProduct['Ref_Key'],
'status' => 'error',
'message' => $e->getMessage(),
]);
// Do not halt the entire batch because of one item
}
}
}
}
Monitoring and Operational Interface
A middleware without a management interface is a black box. Minimum dashboard requirements:
- Status of the latest exchange sessions (success / error / duration)
- Error list with the ability to retry individual items
- Statistics: how many objects were synchronized over the past day/week
- Manual trigger for synchronizing a specific object or data type
Implemented as a simple Laravel/Symfony web interface or via Laravel Horizon (queue monitoring).
Development Timelines
| Middleware Scope | Duration |
|---|---|
| Two adapters (1C + 1C-Bitrix) + 2–3 mappers + queue | 3–5 weeks |
| + 3rd system (CRM) + extended mapping | +2–3 weeks |
| + Monitoring dashboard + manual controls | +1–2 weeks |
| Full system for 5+ systems | 3–6 months |







