Інтеграція 1С-Бітрікс із PIM-системою Salsify
Salsify — американська SaaS PIM-платформа, орієнтована на великої електронної комерції та мультиканальні продажи. Використовується переважно брендами та дистрибюторами, які керують тисячами SKU із синдикацією на маркетплейси (Amazon, Walmart, Target). Якщо на Бітрікс перенесено західний бренд або партнерство передбачає використання Salsify як джерела даних — інтеграція будується через REST API Salsify.
Salsify API: основні ендпоінти
Salsify надає REST API із аутентифікацією за токеном:
Base URL: https://app.salsify.com/api/v1/orgs/{org_id}/
Authorization: Bearer {api_key}
Ключові ендпоінти:
GET products — список продуктів з атрибутами
GET products/{product_id} — конкретний продукт
GET products?filter[updated_at][gte]={timestamp} — змінені з дати
GET property_groups — групи атрибутів (аналог розділів властивостей)
GET assets?filter[product_id]={id} — медіафайли продукту
GET digital_assets/{id}/download — завантажити файл
Salsify використовує плоску структуру атрибутів: кожний атрибут — пара «імя» : «значення», де імя може бути довільним UUID або рядком. Перед маппінгом отримайте довідник атрибутів:
GET properties — повертає всі атрибути організації з ID, name, data_type
Клієнт Salsify
class SalsifyClient
{
private string $baseUrl;
private string $apiKey;
public function __construct(string $orgId, string $apiKey)
{
$this->baseUrl = "https://app.salsify.com/api/v1/orgs/{$orgId}/";
$this->apiKey = $apiKey;
}
private function request(string $endpoint, array $params = []): array
{
$http = new \Bitrix\Main\Web\HttpClient();
$http->setHeader('Authorization', 'Bearer ' . $this->apiKey);
$http->setHeader('Content-Type', 'application/json');
$url = $this->baseUrl . $endpoint;
if ($params) {
$url .= '?' . http_build_query($params);
}
$response = $http->get($url);
return json_decode($response, true) ?? [];
}
public function getProducts(int $page = 1, int $perPage = 100, ?string $updatedAfter = null): array
{
$params = ['page' => $page, 'per_page' => $perPage];
if ($updatedAfter) {
$params['filter[updated_at][gte]'] = $updatedAfter;
}
return $this->request('products', $params);
}
public function getProperties(): array
{
return $this->request('properties');
}
public function downloadAsset(string $assetId): string
{
$http = new \Bitrix\Main\Web\HttpClient();
$http->setHeader('Authorization', 'Bearer ' . $this->apiKey);
return $http->get($this->baseUrl . 'digital_assets/' . $assetId . '/download');
}
}
Маппінг атрибутів Salsify
Атрибути Salsify ідентифікуються за salsify:id (UUID) або за name. Конфігурація маппінгу:
// /local/config/salsify-mapping.php
// 'salsify_property_id_or_name' => ['target' => 'bitrix_field', 'type' => 'field|prop']
return [
'Product Name' => ['target' => 'NAME', 'type' => 'field'],
'Long Description' => ['target' => 'DETAIL_TEXT', 'type' => 'field'],
'Short Description' => ['target' => 'PREVIEW_TEXT', 'type' => 'field'],
'Brand' => ['target' => 'BRAND', 'type' => 'prop'],
'Net Weight (kg)' => ['target' => 'WEIGHT', 'type' => 'prop'],
'Color' => ['target' => 'COLOR', 'type' => 'prop'],
'Country of Origin' => ['target' => 'COUNTRY_ORIGIN','type' => 'prop'],
'GTIN' => ['target' => 'CML2_BAR_CODE', 'type' => 'prop'],
'Manufacturer SKU' => ['target' => 'CML2_ARTICLE', 'type' => 'prop'],
];
Синхронізація
function syncSalsifyAgent(): string
{
$lastSync = \Bitrix\Main\Config\Option::get('salsify_sync', 'last_run', '');
$client = new SalsifyClient(SALSIFY_ORG_ID, SALSIFY_API_KEY);
$mapping = include '/local/config/salsify-mapping.php';
$page = 1;
$newSync = date('c');
do {
$response = $client->getProducts($page, 100, $lastSync ?: null);
$products = $response['products'] ?? [];
foreach ($products as $product) {
importSalsifyProduct($product, $mapping, $client);
}
$page++;
$meta = $response['meta'] ?? [];
} while (($meta['current_page'] ?? 1) < ($meta['total_pages'] ?? 1));
\Bitrix\Main\Config\Option::set('salsify_sync', 'last_run', $newSync);
return __FUNCTION__ . '();';
}
function importSalsifyProduct(array $product, array $mapping, SalsifyClient $client): void
{
$attributes = $product['attributes'] ?? [];
$sku = $product['salsify:id'];
// Отримуємо значення атрибутів
$getValue = static function (array $attrs, string $key): mixed {
return $attrs[$key] ?? null;
};
$fields = ['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'ACTIVE' => 'Y'];
$props = [];
foreach ($mapping as $salsifyKey => $config) {
$value = $getValue($attributes, $salsifyKey);
if ($value === null) continue;
// Масиви перетворюємо в рядок через розділювач
if (is_array($value)) {
$value = implode(', ', $value);
}
if ($config['type'] === 'field') {
$fields[$config['target']] = $value;
} else {
$props[$config['target']] = $value;
}
}
$existing = CIBlockElement::GetList(
[], ['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'PROPERTY_CML2_ARTICLE' => $sku]
)->Fetch();
$el = new CIBlockElement();
if ($existing) {
$productId = $existing['ID'];
$el->Update($productId, $fields);
} else {
$productId = $el->Add($fields);
}
if ($productId) {
CIBlockElement::SetPropertyValuesEx($productId, CATALOG_IBLOCK_ID, $props);
// Медіафайли
$primaryImage = $product['salsify:primary_image'] ?? null;
if ($primaryImage) {
importSalsifyAsset($productId, $primaryImage, $client);
}
}
}
Особливості Salsify
Складні значення атрибутів. Salsify повертає атрибути як масив, де кожний елемент може бути скалярним значенням, об'єктом або списком. Enum-атрибути повертають {"salsify:id": "...", "salsify:name": "Red"} — потрібно вилучати salsify:name.
Ціни. Salsify не є системою ціноутворення. Ціни, як правило, не синхронізуються з Salsify — вони надходять з 1С. Перевірте з клієнтом, чи є Salsify джерелом цін або лише контенту.
Медіафайли. Salsify зберігає медіафайли із CDN-посиланнями. Можна не завантажувати файли, а зберігати зовнішні URL у властивості EXTERNAL_IMAGE_URL та підтягувати на льоту через <img src="...">. Це економить місце на сервері Бітрікс.
Webhook. Salsify підтримує вебхуки при зміні продукту. Налаштовується в Salsify → Integrations → Webhooks. URL обробника отримує POST-запит з product_id, потім запитує актуальні дані через API.
Тривалість реалізації
| Обсяг | Склад | Тривалість |
|---|---|---|
| До 5 000 SKU, базовий контент | Клієнт + маппінг + агент | 1–2 тижня |
| 10 000–100 000 SKU + медіа + вебхуки | + обробка складних атрибутів + оптимізація | 3–4 тижня |
| Мультирегіональний каталог (різні ціни, контент по регіонам) | + логіка каналів Salsify + мультисайт Бітрікс | 5–7 тижнів |







