Добавление в корзину (Short Flow)
Руководство по интеграции кнопки "Купить" с прямым добавлением товара в корзину на сайте.
Если ShopStory открывается в WebView нативного приложения (iOS/Android), используйте mobile bridge вместо callback'ов. Bridge-событие add-product передаёт feedProductId в нативный слой, где приложение само добавляет товар в корзину.
Что такое Short Flow
SDK поддерживает два режима кнопки "Купить":
| Режим | Поведение | Настройка |
|---|---|---|
| Redirect (по умолчанию) | Переход на страницу товара | Не требуется |
| Short Flow | Добавление в корзину без перехода | Передать actions при инициализации SDK |
При Short Flow: кнопка "Купить" → спиннер → "В корзине" → клик открывает страницу корзины.
Предварительные требования
- В YML-фиде у каждого
<offer>заполнен атрибутid. Это ID товара/оффера в вашей CMS, например<offer id="2858">. - SDK встроен на домене сайта как
<script>(same-origin для cookies). См. Установка SDK. - На сайте есть способ добавить товар в корзину программно (AJAX или JS API).
Интеграция
Ниже показаны только параметры Short Flow, которые нужно добавить к уже работающему ShopStorySDK.show(...).
Параметры Short Flow
type ShortFlowOptions = {
actions?: {
// Добавить товар по ID из фида (feedProductId)
addProductToCartById?: (id: string, callback: (result: boolean) => void) => void;
// Добавить товар по артикулу (vendorCode)
addProductToCart?: (vendorCode: string, callback: (result: boolean) => void) => void;
};
cartUrl?: string; // URL страницы корзины
};
Для YML-фида рекомендуемый путь интеграции: addProductToCartById.
Полный пример инициализации
ShopStorySDK.show({
containerElement: document.getElementById('shopstory'),
cartUrl: '/personal/cart/',
actions: {
addProductToCartById: function(id, callback) {
// Ваш код добавления товара в корзину
fetch('/your-cart-endpoint', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ productId: id, quantity: '1' }),
})
.then(function(r) { return r.json(); })
.then(function(data) { callback(data.success === true); })
.catch(function() { callback(false); });
},
},
});
Что передаётся в id
Параметр id — это значение атрибута id из тега <offer id="..."> в вашем YML-фиде. SDK передаёт его как строку, даже если значение числовое.
Пример: если в фиде <offer id="2858">, то в callback придёт id = "2858".
id приходит не артикулВ YML-фидах Битрикс у товара два разных идентификатора:
<offer id="1537" available="true">
<vendorCode>4009</vendorCode>
<name>Крем для ног Super Moisture</name>
</offer>
| Поле в фиде | Пример | Что это | В callback? |
|---|---|---|---|
<offer id="1537"> | 1537 | iblock element ID (внутренний ID в БД Битрикс) | Да — передаётся как id |
<vendorCode>4009</vendorCode> | 4009 | Артикул (виден покупателю на сайте) | Нет |
SDK передаёт <offer id> (= iblock element ID), потому что именно его ожидает Basket::addProduct(['PRODUCT_ID' => ...]) в Битрикс.
Артикул (vendorCode) используется в ShopStory для отображения стримерам, но для добавления в корзину не нужен.
Контракт callback
| Вызов | Результат в UI |
|---|---|
callback(true) | Кнопка меняется на "В корзине", клик открывает cartUrl |
callback(false) | Кнопка меняется на "Нет в наличии" (disabled) |
Важно: callback нужно вызвать ровно один раз. Если callback не вызван, кнопка останется в состоянии загрузки (спиннер) навсегда.
callback(false) означает только то, что товар не удалось добавить. Это может быть как бизнес-ошибка, так и проблема в вашем HTTP/JS-обработчике.
Параметр cartUrl
- Относительный путь (
/personal/cart/илиpersonal/cart/) — SDK добавитlocation.originавтоматически, ведущий/необязателен. - Абсолютный URL (
https://example.com/cart/) — используется как есть. - Если
cartUrlне указан, кнопка "В корзине" не будет кликабельной.
Примеры по платформам
1С Битрикс
PHP-обработчик. Создайте на сайте файл /local/ajax/shopstory-cart.php:
<?php
require_once($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php');
use Bitrix\Main\Loader;
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
die();
}
Loader::includeModule('sale');
Loader::includeModule('catalog');
$productId = intval($_POST['productId'] ?? 0);
$quantity = intval($_POST['quantity'] ?? 1);
if ($productId <= 0) {
echo json_encode(['success' => false, 'error' => 'Invalid product ID']);
die();
}
$result = \Bitrix\Catalog\Product\Basket::addProduct([
'PRODUCT_ID' => $productId,
'QUANTITY' => $quantity,
]);
if ($result->isSuccess()) {
echo json_encode(['success' => true]);
} else {
echo json_encode([
'success' => false,
'error' => implode(', ', $result->getErrorMessages()),
]);
}
require_once($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_after.php');
JS callback:
ShopStorySDK.show({
containerElement: document.getElementById('shopstory'),
cartUrl: '/personal/cart/',
actions: {
addProductToCartById: function(id, callback) {
fetch('/local/ajax/shopstory-cart.php', {
method: 'POST',
credentials: 'include', // Обязательно: отправляет cookies сессии корзины
headers: {
'Content-Type': 'application/x-www-form-urlencoded', // Не JSON
},
body: new URLSearchParams({
productId: id,
quantity: '1',
}),
})
.then(function(r) { return r.json(); })
.then(function(data) { callback(data.success === true); })
.catch(function() { callback(false); });
},
},
});
Важные нюансы Битрикс:
- В callback приходит iblock element ID из атрибута
<offer id="...">, а не<vendorCode>. Именно этот ID нужен дляBasket::addProduct. - Для товаров с вариантами в фиде должен быть ID торгового предложения (offer/SKU), а не родительского товара. Если у
<offer>есть атрибутgroup_id, для корзины нужен именноidэтого предложения. Content-Type: application/x-www-form-urlencodedобязателен, потому что PHP$_POSTне парситapplication/json.credentials: 'include'обязателен, иначе корзина создастся в другой сессии.sessid(CSRF) не нужен. Сессионной cookie достаточно для аутентификации.- URL обработчика может быть любым, если он создаётся на сайте.
Shopify
ShopStorySDK.show({
containerElement: document.getElementById('shopstory'),
cartUrl: '/cart',
actions: {
addProductToCartById: function(id, callback) {
fetch('/cart/add.js', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
items: [{ id: Number(id), quantity: 1 }],
}),
})
.then(function(r) {
callback(r.ok);
})
.catch(function() { callback(false); });
},
},
});
Универсальный пример
Контракт простой: любая функция, которая вызывает callback(true) при успехе и callback(false) при ошибке. Это может быть HTTP POST, JS-вызов глобальной функции или любой другой способ.
ShopStorySDK.show({
containerElement: document.getElementById('shopstory'),
cartUrl: '/cart/',
actions: {
addProductToCartById: function(id, callback) {
fetch('/api/cart/add', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ id: id }),
})
.then(function(r) { callback(r.ok); })
.catch(function() { callback(false); });
// Если CMS предоставляет глобальный JS API:
// window.yourCMS.addToCart(Number(id), {
// onSuccess: function() { callback(true); },
// onError: function() { callback(false); },
// });
},
},
});
Чек-лист интеграции
- AJAX или JS-добавление в корзину работает независимо.
- В XML-фиде у
<offer>заполнен атрибутid. - SDK загружен как
<script>на домене сайта, не в iframe. -
callback(true)илиcallback(false)вызывается ровно один раз. -
cartUrlуказан и ведёт на страницу корзины. - После клика "Купить" кнопка показывает спиннер, затем "В корзине".
- При ошибке
callback(false)кнопка показывает "Нет в наличии". - Клик по "В корзине" открывает страницу корзины в новой вкладке.
Частые проблемы
| Симптом | Причина | Решение |
|---|---|---|
| Кнопка "Купить" переходит на страницу товара вместо добавления в корзину | actions не передан при sdk.show() | Добавить actions.addProductToCartById в конфиг |
| Кнопка не реагирует | Атрибут id пустой в фиде | Заполнить атрибут id в XML-фиде |
| 403/401 от сервера | Cookies не отправляются | Добавить credentials: 'include' в fetch() |
| Товар не в корзине (Bitrix) | Отправлен parent product ID вместо offer ID | В фиде указать ID торгового предложения (SKU) |
Пустой $_POST на PHP | Отправлен Content-Type: application/json | Использовать application/x-www-form-urlencoded |
| "В корзине" не кликается | cartUrl не указан | Добавить cartUrl при инициализации SDK |
| Корзина "пустая" после добавления | Запрос создал новую сессию | SDK должен быть на same-origin, не в iframe |
| Добавляется не тот товар (Bitrix) | В обработчик передан артикул вместо iblock element ID | В callback приходит <offer id>, а не <vendorCode> |
| Кнопка "зависла" на спиннере | callback() не вызван в вашей функции | Вызвать callback(true) или callback(false) во всех ветках, включая .catch() |
Быстрый старт: 1С Битрикс
Типовой сценарий для сайта на 1С Битрикс.
Шаг 1. Создайте файл /local/ajax/shopstory-cart.php на вашем сайте по примеру выше.
Шаг 2. Протестируйте обработчик из консоли браузера:
fetch('/local/ajax/shopstory-cart.php', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ productId: '1537', quantity: '1' }),
})
.then(function(r) { return r.json(); })
.then(function(data) { console.log('Результат:', data); });
Ожидаемый ответ: { success: true }.
Здесь productId должен быть равен iblock element ID из <offer id="1537">, а не артикулу 4009 из <vendorCode>. Подставьте значение из вашего фида.
Шаг 3. Добавьте actions и cartUrl в инициализацию SDK:
ShopStorySDK.show({
containerElement: document.getElementById('shopstory'),
cartUrl: '/personal/cart/',
actions: {
addProductToCartById: function(id, callback) {
fetch('/local/ajax/shopstory-cart.php', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ productId: id, quantity: '1' }),
})
.then(function(r) { return r.json(); })
.then(function(data) { callback(data.success === true); })
.catch(function() { callback(false); });
},
},
});
Кнопка "Купить" после этого будет добавлять товар в корзину Битрикс, а кнопка "В корзине" будет открывать /personal/cart/.