> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Обзор

> Как работает Aftersell Cart SDK: глобальная точка входа, четыре части API, когда он загружается и как безопасно выполнять код с его использованием.

**Cart SDK** — это JavaScript API для корзины Aftersell на вашей витрине. Он позволяет менять поведение корзины, реагировать на действия покупателей, а также читать и изменять содержимое корзины из кода.

Код SDK запускается через [Пользовательские скрипты](/ru/aftersell/cart/custom-scripts) или через режим React блока [Custom code](/ru/aftersell/cart/custom-code-blocks) для блока, который отображает собственный интерфейс.

<Note>
  Многое из того, что мерчанты просят у SDK, уже доступно в виде настройки. Прежде чем писать скрипт, проверьте, не решает ли задачу [блок корзины](/ru/aftersell/cart/blocks-overview), [условия по рынку/стране/валюте](/ru/aftersell/cart/blocks-overview#show-or-hide-by-market-country-or-currency) или [настройка корзины](/ru/aftersell/cart/cart-settings). Они продолжают работать при редизайне корзины, а ваш скрипт может перестать.
</Note>

<div id="the-global-entry-point">
  ## Глобальная точка входа
</div>

Всё держится на одном глобальном объекте:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart
```

<Note>
  **Каждый фрагмент кода в этой документации пишет `window.aftersell.cart` полностью**, поэтому любой из них работает сам по себе при вставке. Создать псевдоним один раз (`const cart = window.aftersell.cart;`) и дальше использовать `cart` тоже совершенно корректно и безопасно даже до загрузки корзины. Только не забудьте включить эту строку, если сокращаете фрагмент, поскольку одиночный `cart` сам по себе выбрасывает ошибку `cart is not defined`.
</Note>

Работу выполняют четыре части:

<Columns cols={2}>
  <Card title="Configure" icon="sliders" href="/ru/aftersell/cart/sdk-configure">
    Настройте поведение корзины: когда открывается панель, как форматируются суммы, перехватывает ли Aftersell добавление в корзину.
  </Card>

  <Card title="Events" icon="tower-broadcast" href="/ru/aftersell/cart/sdk-events">
    Реагируйте на происходящее: корзина загрузилась, товар добавлен, панель открылась, нажата кнопка оформления заказа.
  </Card>

  <Card title="Actions" icon="wand-magic-sparkles" href="/ru/aftersell/cart/sdk-actions">
    Читайте и изменяйте корзину: откройте её, добавьте товар, обновите количество, прочитайте текущее состояние.
  </Card>

  <Card title="Hooks" icon="plug" href="/ru/aftersell/cart/sdk-hooks">
    Меняйте работу самой корзины: скрывайте или переименовывайте строки, меняйте их порядок, прикрепляйте дополнительные данные, управляйте добавлением в корзину.
  </Card>
</Columns>

<Note>
  Если ваш скрипт перестал срабатывать при добавлении в корзину, начните с [Перехвата добавления в корзину](/ru/aftersell/cart/add-to-cart-interception). Там объясняется, почему Aftersell берёт добавление на себя, и все способы исключить форму.
</Note>

Плюс три небольших элемента:

| Элемент      | Для чего он                                                                     |
| ------------ | ------------------------------------------------------------------------------- |
| `ready()`    | Promise, который разрешается после первой загрузки корзины.                     |
| `context`    | Контекст покупателя, отрендеренный на сервере, доступен для синхронного чтения. |
| `shadowRoot` | Shadow root корзины, для поиска элементов внутри панели.                        |

<div id="events-actions-or-hooks">
  ## События, действия или хуки?
</div>

Эти три понятия легко перепутать, и выбор неправильного — самая частая причина того, что скрипт не делает то, что ожидал его автор:

| Вы хотите…                                       | Используйте  | Пример                                                  |
| ------------------------------------------------ | ------------ | ------------------------------------------------------- |
| Выполнить код, *когда что-то происходит*         | **Событие**  | Отправить событие аналитики при добавлении товара.      |
| *Изменить содержимое* корзины                    | **Действие** | Добавить бесплатный подарок, когда сумма превысит \$50. |
| Изменить *как корзина работает или отображается* | **Хук**      | Скрыть строки бесплатных подарков в панели.             |

Самое важное различие: **действие изменяет фактическую корзину покупателя** (и его итоговую сумму), тогда как **хук меняет только отображение**. Скрытие строки хуком оставляет её в корзине и в итоговой сумме; удаление её действием убирает её по-настоящему.

<div id="how-and-when-it-loads">
  ## Как и когда он загружается
</div>

Корзина загружается в два этапа, и SDK устроен так, чтобы вам не приходилось думать о порядке:

1. Небольшая **заглушка** сразу создаёт `window.aftersell.cart`, поэтому он всегда доступен.
2. Полный SDK загружается чуть позже и берёт управление на себя, обновляя заглушку на месте, поэтому захваченная ранее ссылка продолжает работать.

Это даёт две категории вызовов:

<Columns cols={2}>
  <Card title="Вызовы настройки: безопасны сразу" icon="circle-check">
    `configure(...)`, `events.on(...)` и все вызовы `hooks.register*`. Буферизуются до загрузки и воспроизводятся по порядку после загрузки SDK. Размещайте их в начале скрипта.
  </Card>

  <Card title="Действия: дождитесь ready()" icon="clock">
    Всё под `actions.*`. Запускайте их внутри `ready()` или обработчика события. При слишком раннем вызове они выводят предупреждение в консоль и безопасно ничего не делают: асинхронные всё равно разрешаются, поэтому цепочка `.then()` не сломается.
  </Card>
</Columns>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Set-up: fine at the top level, before the cart has loaded.
window.aftersell.cart.configure({ open_on_add_to_cart: 'always' });

window.aftersell.cart.events.on('item_added', (payload) => {
  console.log('Added', payload.item.title);
});

// Actions: wait until the cart is ready.
window.aftersell.cart.ready().then(() => {
  const state = window.aftersell.cart.actions.getCart();
  console.log(state.itemCount, 'items');
});
```

<div id="ready">
  ### ready()
</div>

`ready()` возвращает Promise, который разрешается после того, как первая загрузка корзины **завершится**. Он разрешается как при неудаче, так и при успехе, поэтому у покупателя с нестабильным соединением ваш скрипт никогда не зависнет. Проверяйте `getCart()` на `null`, а не предполагайте, что корзина получена.

Вызов `ready()` после того, как корзина уже загружена, разрешается немедленно, поэтому его можно безопасно использовать как общий шлюз «корзина теперь существует» в любом месте вашего кода.

<Tip>
  Внутри обработчика события `ready()` не нужен. К моменту срабатывания `cart_loaded`, `cart_updated` или `item_added` корзина уже загружена, и действия можно вызывать безопасно.
</Tip>

<div id="context">
  ## context
</div>

`window.aftersell.cart.context` содержит данные покупателя, отрендеренные сервером, доступные для синхронного чтения без `ready()`. Используйте его для ветвления по рынку или стране, которое должно произойти до загрузки корзины.

| Поле                      | Описание                                                                                      | Доступно до загрузки                       |
| ------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------ |
| `shopify_market`          | Рынок Shopify покупателя.                                                                     | Да                                         |
| `customer_country`        | Двухбуквенный код страны.                                                                     | Да                                         |
| `customer_currency`       | Код активной валюты.                                                                          | Да                                         |
| `money_format`            | Формат денег Shopify для магазина.                                                            | Да                                         |
| `backend_url`             | Прямой хост бэкенда, используется как резервный вариант, когда прокси приложения не настроен. | Да                                         |
| `storefront_access_token` | Токен для вызовов Storefront API.                                                             | **Нет** — добавляется при загрузке корзины |

<Warning>
  `storefront_access_token` — единственное поле `context`, которое сервер не рендерит в `cart.context`. Оно добавляется в `context` при загрузке корзины, поэтому чтение его в начале скрипта даст `undefined`. Сначала дождитесь `window.aftersell.cart.ready()`.
</Warning>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
if (window.aftersell.cart.context.customer_country === 'CA') {
  // Canada-only behavior, decided before the cart loads.
}
```

<Note>
  Чтобы показывать разные настройки блоков по рынку, стране или валюте, используйте вместо этого [условия в редакторе корзины](/ru/aftersell/cart/blocks-overview#show-or-hide-by-market-country-or-currency). Скрипт не требуется. Полный интерфейс Conditions сегодня доступен в [Rewards](/ru/aftersell/cart/rewards-block#per-market-rewards).
</Note>

<div id="shadowroot">
  ## shadowRoot
</div>

Корзина отображается внутри shadow root, поэтому `document.querySelector` **не видит ничего внутри панели**. Чтобы добраться до элемента в корзине, выполняйте запрос к shadow root:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const root = window.aftersell.cart.shadowRoot;
const button = root?.querySelector('.cart-external-checkout-button');
```

Обращайтесь к тем же **публичным классам `cart-external-*`**, которые использует [Custom CSS](/ru/aftersell/cart/custom-css). Это поддерживаемые точки входа. Двойники `cart-internal-*` — внутренняя механика корзины, поэтому используйте внешние классы.

<Warning>
  Обращайтесь к shadow root только тогда, когда задачу не решает ни блок, ни настройка, ни хук. Хук переживёт редизайн корзины; поддержка DOM-запроса — проблема вашего кода.
</Warning>

Shadow root появляется только после загрузки корзины, поэтому читайте его внутри `ready()` или обработчика события, а не в начале скрипта.

<div id="debugging">
  ## Отладка
</div>

Сломанный скрипт никогда не должен выводить из строя добавление в корзину или панель, поэтому SDK локализует сбои, а не позволяет им всплывать. Где проявится сбой, зависит от того, что сломалось:

| Что сломалось                                                            | Где это проявится                                                |
| ------------------------------------------------------------------------ | ---------------------------------------------------------------- |
| Ваш скрипт выбросил ошибку на верхнем уровне                             | `console.error` с указанием строки и того, что не было выполнено |
| Обработчик [события](/ru/aftersell/cart/sdk-events) выбросил ошибку      | `console.error`; остальные обработчики продолжают работать       |
| [Хук](/ru/aftersell/cart/sdk-hooks) выбросил ошибку                      | Молча. Уходит в канал отладки, описанный ниже                    |
| [Действие](/ru/aftersell/cart/sdk-actions) выполнено до загрузки корзины | `console.warn`; вызов ничего не делает                           |

<div id="when-your-script-throws">
  ### Когда ваш скрипт выбрасывает ошибку
</div>

Пользовательский скрипт **останавливается на первой ошибке**, поэтому каждый вызов `configure`, `events.on` и `hooks.register*` ниже этой строки никогда не выполняется. Корзина явно сообщает об этом:

```
[aftersell-cart] Initialization script error on line 12 — 4 more line(s) did not run;
any configure/events/hooks below are unregistered.
```

Именно это сообщение стоит искать, когда обработчик, который вы точно зарегистрировали, не срабатывает: скорее всего, до него просто не дошло выполнение. Номер строки — это оператор верхнего уровня, на котором остановилось выполнение, а не внутренняя функция, выбросившая ошибку; он опускается, а не угадывается, если стек браузера непригоден.

Ваши скрипты также выполняются под собственными именами файлов, поэтому в DevTools они отображаются как `aftersell-cart-init.js` и `aftersell-cart-cart-update.js`. Вы можете открыть их на панели Sources и ставить точки останова, как в любом другом файле.

<div id="the-debug-channel">
  ### Канал отладки
</div>

Сбои хуков намеренно не выводятся в консоль, чтобы покупатели их никогда не видели. Вместо этого они попадают сюда:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// After reproducing the problem, inspect what was swallowed:
window.aftersellCartDebugEvents.filter((entry) => entry.level === 'ERROR');

// Or watch them live:
window.addEventListener('aftersell-cart-debug', (event) => console.log(event.detail));
```

<div id="where-to-go-next">
  ## Что дальше
</div>

<Columns cols={2}>
  <Card title="Configure" icon="sliders" href="/ru/aftersell/cart/sdk-configure">
    Каждая опция с примером.
  </Card>

  <Card title="Events" icon="tower-broadcast" href="/ru/aftersell/cart/sdk-events">
    Каждое событие, когда оно срабатывает и чего не следует делать в обработчике.
  </Card>

  <Card title="Actions" icon="wand-magic-sparkles" href="/ru/aftersell/cart/sdk-actions">
    Каждое действие с примером кода.
  </Card>

  <Card title="Hooks" icon="plug" href="/ru/aftersell/cart/sdk-hooks">
    Каждый хук и как компонуются регистрации.
  </Card>

  <Card title="Cart object" icon="table-list" href="/ru/aftersell/cart/sdk-cart-object">
    Структура корзины и её строк.
  </Card>

  <Card title="Use cases" icon="book-open" href="/ru/aftersell/cart/sdk-use-cases">
    Полные, готовые к запуску решения типичных запросов.
  </Card>
</Columns>
