Development of Marketplace Integration Module for 1C-Bitrix
The task sounds simple: synchronize catalog and orders between a Bitrix-based site and a marketplace — Ozon, Wildberries, Yandex Market or Aliexpress. In practice this results in hundreds of edge cases: the marketplace changes API schema without warning, products are rejected due to attribute mismatch, stock diverges due to race conditions between multiple warehouses.
What Really Needs to be Implemented in the Module
A standard integration module for 1C-Bitrix operates within the module system (/bitrix/modules/). It registers via RegisterModule(), adds agents via CAgent::AddAgent() and hooks handlers to info block events (OnAfterIBlockElementAdd, OnAfterIBlockElementUpdate, OnAfterIBlockElementDelete).
Minimum set of functions:
-
Product export — mapping info block fields to marketplace schema, image upload by URL, attribute transmission (for Ozon it's
attributes[], for WB —addin[]) -
Stock synchronization — updating
CATALOG_QUANTITYinb_iblock_element_proptable and transmitting to marketplace; critical to do fast, separate agent with 5–15 minute interval -
Order receipt — creating orders in
b_sale_order,b_sale_basket,b_sale_order_propsviaCSaleOrder::Add()or Sale module API -
Status handling — bidirectional: status change in Bitrix → push to marketplace; updates from marketplace → update via
CSaleOrder::StatusOrder()
Architecture: Why Queue is Mandatory
Direct API call from event handler — antipattern. Ozon allows max 10 RPS on most methods, WB has rate limit 1 request/second on /api/v3/orders. If 5000+ products, OnAfterIBlockElementUpdate handler during bulk edit in admin creates request flood and gets 429.
Correct scheme:
Bitrix event → write task to queue (b_highload_element or separate table)
↓
Agent every N minutes → read queue in batches → call marketplace API
↓
Result → log + update task status in queue
Queue table conveniently implemented via Highload info block (module highloadblock) or via direct queries to own table created on install in DoInstall() method.
Queue table structure:
| Field | Type | Description |
|---|---|---|
| ID | int, AI | |
| ENTITY_TYPE | varchar(50) | product / order / stock |
| ENTITY_ID | int | Element/order ID |
| ACTION | varchar(50) | create / update / delete |
| MARKETPLACE | varchar(30) | ozon / wb / yandex |
| STATUS | varchar(20) | pending / processing / done / error |
| ATTEMPTS | int | attempt counter |
| LAST_ERROR | text | last error text |
| CREATED_AT | datetime | |
| PROCESSED_AT | datetime |
Attribute Mapping — Most Labor-Intensive Part
Each marketplace has its own category system and required attributes. For Ozon you must get category attributes via /v3/category/attribute, match with info block fields and save mapping. For WB — similarly via /content/v2/object/charcs/{subjectId}.
In module implemented via admin interface: settings page in /bitrix/admin/, where for each marketplace you can set:
-
Category correspondence —
b_iblock_section↔ marketplace category ID -
Property mapping —
UF_*orPROPERTY_*info block fields ↔ marketplaceattribute_id -
Store mapping —
CATALOG_STORE↔ warehouse_id -
Value transformation rules — e.g., numeric WB characteristics transmitted as strings
"180"not180
Without proper UI for mapping, module will be used with pain: you'll have to edit code each time catalog structure changes.
Trade Offers (SKU) and Variable Products
WB and Ozon handle variability differently. On WB nomenclature (nmId) contains size array sizes[], each has its own skuId. On Ozon base product has offer_id, variants transmitted via color_image.
In Bitrix trade offers stored as child info block elements, linked to main via IBLOCK_ID in b_catalog_iblock. On export:
- Get all TOs via
CCatalogSKU::GetOffersList() - Assemble structure matching specific marketplace format
- On stock update update each SKU separately, as marketplace stores stock at SKU/size level
Order Receipt and Processing
Orders from marketplaces come either via polling (agent requests new orders every N minutes) or via webhook (marketplace POST's to your URL). WB supports both, Ozon — webhook via account settings.
When creating order in Bitrix important details:
- Customer created as anonymous or linked to existing via email — via
CSaleOrder::DoFinalAction() - Delivery method and payment system must be registered in system beforehand (
b_sale_delivery_service,b_sale_pay_system) and specified in module settings - Marketplace SKU (
offer_id/nmId) must matchCATALOG_ARTICLEorXML_IDof info block element — key for matching - Marketplace external order ID must be saved in
b_sale_order_propsfor reverse status synchronization
Error Handling and Monitoring
Module without logging — black box. Minimal log written to b_event_log via CEventLog::Add(). For production better to write own logs table with fields: level, marketplace, action, entity ID, HTTP code, response body (truncate to 4KB).
Separate agent hourly should check tasks with error status and ATTEMPTS < 3, retry them. Tasks with ATTEMPTS >= 3 — alert via CEvent::Send() or Telegram webhook.
Development Timeline
| Integration Scope | Timeline |
|---|---|
| One marketplace, product export + stock only | 3–5 weeks |
| One marketplace, full cycle (products + orders + statuses) | 6–9 weeks |
| Two marketplaces, full cycle | 10–14 weeks |
| Three+ marketplaces with common queue and mapping UI | 16–24 weeks |
Timelines for development from scratch. Using ready-made queue core and API adapter reuse — can reduce by 25–30%.
Testing and Acceptance
Each marketplace adapter must have unit tests for data mapping and integration tests with sandbox environment (Ozon and Yandex Market provide sandbox, WB — no, testing only via live battle with test SKUs). Before release check scenarios: mass price update (500+ items at once), 50+ orders arriving simultaneously, stock update to zero.







