Товари на сайті показують неправильні залишки, менеджери правлять ціни в МойСклад, а на сайті вони не оновлюються. Замовлення доводиться переносити вручну — це призводить до 20–30% помилок відвантаження та втрат клієнтів. Двостороння синхронізація товарів вирішує проблему: товари, залишки, замовлення та статуси оновлюються автоматично. Результат — актуальні дані на сайті та в МойСклад без вашої участі. Економія на ручному введенні даних сягає 200 000 грн на рік. Синхронізація МойСклад дозволяє уникнути помилок.
Які проблеми вирішує двостороння синхронізація?
Розбіжність залишків — найпоширеніший біль. Клієнт бачить товар в наявності, а на складі його вже немає, або навпаки. Наша синхронізація оновлює залишки кожні 5 хвилин, знижуючи кількість помилок відвантаження на 80% порівняно з ручним управлінням. Друга проблема — ручний перенос замовлень. Співробітники витрачають до 2 годин на день на дублювання інформації. Двостороння синхронізація автоматично створює замовлення в МойСклад при оформленні на сайті та оновлює статус при відвантаженні. Третя — конфлікти даних. Якщо ціну змінили одночасно в двох системах, ми використовуємо чітке правило джерела істини: ціни та залишки — з МойСклад, SEO та зображення — з сайту.
Як ми реалізуємо інтеграцію
Архітектура та стек
Використовуємо REST API МойСклад версії 1.2, PHP 8.3 з фреймворком Laravel 11 та чергами через Redis. Для аутентифікації — Bearer-токен. Всі запити йдуть через HTTP-клієнт з повторними спробами при помилках. Webhook налаштовані на події зміни замовлень, щоб статуси оновлювалися миттєво. Архітектура враховує N+1 запити та використовує чанковане завантаження даних (пачки по 100 елементів). Бекенд інтеграція написана на Laravel з використанням API МойСклад.
Згідно з офіційною документацією API МойСклад, це забезпечує надійну інтеграцію.
Приклад реалізації клієнта API
class MoiSkladClient
{
private string $baseUrl = 'https://api.moysklad.ru/api/remap/1.2';
private function headers(): array
{
return [
'Authorization' => 'Bearer ' . config('services.moysklad.token'),
'Content-Type' => 'application/json;charset=utf-8',
'Accept-Encoding' => 'gzip',
];
}
public function get(string $path, array $params = []): array
{
return Http::withHeaders($this->headers())
->get("{$this->baseUrl}/{$path}", $params)
->throw()
->json();
}
public function post(string $path, array $data): array
{
return Http::withHeaders($this->headers())
->post("{$this->baseUrl}/{$path}", $data)
->throw()
->json();
}
}
| Сутність | Джерело істини | Примітка |
|---|---|---|
| Товари (назва, опис, ціна) | МойСклад | Менеджери правлять там |
| Залишки | МойСклад | Оновлюються при надходженні/продажу |
| Зображення товарів | Сайт | Завантажуються через CMS |
| SEO-поля (meta, slug) | Сайт | Не існують в МС |
| Замовлення | Сайт → МС | Створюються на сайті, йдуть в МС |
| Статуси замовлень | МС → Сайт | Менеджер змінює в МС |
Отримання товарів та залишків
Автоматичне оновлення залишків запобігає помилкам.
class ProductSyncService
{
public function fetchProducts(int $offset = 0, int $limit = 100): array
{
return $this->ms->get('entity/product', [
'offset' => $offset,
'limit' => $limit,
'expand' => 'productFolder,images',
'filter' => 'archived=false',
]);
}
public function fetchStocks(): array
{
return $this->ms->get('report/stock/all/current', [
'stockType' => 'stock',
'includeRelated' => false,
]);
}
public function syncToSite(): void
{
$offset = 0;
$limit = 100;
do {
$response = $this->fetchProducts($offset, $limit);
$products = $response['rows'];
foreach ($products as $msProduct) {
$this->upsertProduct($msProduct);
}
$offset += $limit;
} while ($offset < $response['meta']['size']);
$stocks = $this->fetchStocks();
foreach ($stocks as $stock) {
Product::whereExternalId($stock['assortmentId'])
->update(['stock' => max(0, (int)$stock['stock'])]);
}
}
private function upsertProduct(array $msProduct): void
{
$msId = $msProduct['id'];
$data = [
'external_id' => $msId,
'name' => $msProduct['name'],
'article' => $msProduct['article'] ?? null,
'price' => $this->parsePrice($msProduct['salePrices'][0]['value'] ?? 0),
'description' => $msProduct['description'] ?? '',
'ms_updated_at'=> Carbon::parse($msProduct['updated']),
];
$product = Product::updateOrCreate(['external_id' => $msId], $data);
}
private function parsePrice(int $msPrice): float
{
return $msPrice / 100;
}
}
Передача замовлень та webhook
public function pushOrder(Order $order): string
{
$positions = [];
foreach ($order->items as $item) {
$positions[] = [
'assortment' => [
'meta' => [
'href' => "{$this->baseUrl}/entity/product/{$item->product->external_id}",
'type' => 'product',
],
],
'quantity' => $item->quantity,
'price' => $item->price * 100,
];
}
$msOrder = $this->ms->post('entity/customerorder', [
'name' => "Замовлення #{$order->id}",
'organization' => [
'meta' => [
'href' => "{$this->baseUrl}/entity/organization/" . config('services.moysklad.org_id'),
'type' => 'organization',
],
],
'agent' => $this->getOrCreateCounterparty($order->customer),
'positions' => $positions,
'description' => "Джерело: сайт\nEmail: {$order->customer->email}",
'attributes' => [
[
'meta' => ['href' => $this->orderIdAttributeHref()],
'value' => (string)$order->id,
],
],
]);
$order->update(['ms_order_id' => $msOrder['id']]);
return $msOrder['id'];
}
Webhook МойСклад налаштовані на зміни замовлень. При зміні замовлення в МойСклад webhook повідомляє наш сервер, і статус автоматично оновлюється на сайті:
public function handleMsWebhook(Request $request): Response
{
$events = $request->json('events', []);
foreach ($events as $event) {
if ($event['meta']['type'] === 'customerorder') {
$msOrderId = basename($event['meta']['href']);
SyncOrderStatusJob::dispatch($msOrderId);
}
}
return response()->noContent();
}
class SyncOrderStatusJob implements ShouldQueue
{
public function handle(MoiSkladClient $ms): void
{
$msOrder = $ms->get("entity/customerorder/{$this->msOrderId}");
// маппінг статусів та оновлення замовлення
}
}
Частота оновлення даних та вирішення конфліктів
Залишки синхронізуються кожні 5 хвилин, товари та замовлення — кожні 10 хвилин. Це забезпечує актуальність без зайвого навантаження на API. За потреби частоту можна налаштувати індивідуально.
При двосторонній синхронізації конфлікти неминучі. Наш підхід: ціна, залишки, артикул — пріоритет МойСклад; SEO-поля та зображення — сайт; назва — за датою останньої зміни. Вирішення конфліктів синхронізації відбувається за цими правилами. Ми гарантуємо, що ви не втратите важливі зміни. Помилки відвантаження обходяться в середньому в 50 000 грн на місяць — синхронізація зводить їх до мінімуму.
Порівняння підходів
| Характеристика | Одностороння | Двостороння |
|---|---|---|
| Товари, ціни, залишки | ✅ | ✅ |
| Замовлення (сайт→МС) | ❌ | ✅ |
| Статуси (МС→сайт) | ❌ | ✅ |
| Вирішення конфліктів | не потрібно | ✅ |
| Ручна праця менеджерів | ~1 год/день | автоматично |
| Помилки відвантаження | 20-30% | <5% |
Двостороння синхронізація в 10 разів скорочує час на обробку замовлень і практично виключає помилки відвантаження.
Що входить в роботу
- Аналітика та проєктування інтеграції
- Написання коду модуля на Laravel
- Налаштування webhook МойСклад та черг
- Тестування на копії даних
- Документація API та інструкції
- Навчання менеджерів роботі з системою
- Підтримка після запуску
Чому варто обрати двосторонню синхронізацію?
Понад 5 років ми інтегруємо інтернет-магазини з МойСклад. За цей час реалізували 30+ проєктів, включаючи складні з тисячею SKU. Наші інженери мають підтверджену експертизу в Laravel та REST API. 5+ років на ринку, 30+ завершених проєктів. Ми даємо гарантію на працездатність інтеграції та оперативно виправляємо можливі неузгодженості. Економія на ручному введенні даних сягає 200 000 грн на рік, а витрати на виправлення помилок відвантаження знижуються до 50 000 грн на місяць. Ми пропонуємо інтеграцію під ключ за 6-8 днів. В роботу входить: налаштування webhook, код інтеграції, тестування, документація та навчання менеджерів.
Процес та терміни роботи
- Аналітика — вивчаємо структуру каталогу, визначаємо сутності та правила маппінгу.
- Проєктування — проєктуємо інтеграцію: які поля, як часто, обробка помилок.
- Реалізація — пишемо код, налаштовуємо webhook, черги.
- Тестування — запускаємо тестову синхронізацію на копії даних, звіряємо результати.
- Деплой — розгортаємо на бойовому сервері, проводимо моніторинг.
Орієнтовні терміни: одностороння синхронізація (МойСклад → сайт) — 2–3 робочі дні; двостороння (із замовленнями, статусами, webhook) — 6–8 робочих днів. Термін може збільшитися при складній структурі каталогу (варіації, комплекти). Проведемо безкоштовний аудит вашої поточної системи та запропонуємо оптимальне рішення. Пишіть нам для отримання консультації та оцінки вашого проекту. Отримайте консультацію вже сьогодні.







