Ограничение запросов и повторы
ShopStory не публикует универсальные числовые лимиты для всех клиентов, API-методов и окружений. Согласуйте гарантированную квоту или SLA с ShopStory и зафиксируйте параметры в интеграционном контракте проекта.
Клиент должен обрабатывать HTTP 429, временные 5xx, timeout и сетевые ошибки.
Правила повторов
- Автоматически повторяйте только безопасные идемпотентные чтения.
- Учитывайте
Retry-After, если сервер его прислал. - Без
Retry-Afterиспользуйте экспоненциальную задержку со случайным разбросом (jitter). - Ограничивайте число попыток и общую продолжительность запросов (
deadline). - Не повторяйте ошибки конфигурации, валидации и идентификации.
- Не повторяйте изменение корзины автоматически без idempotency-защиты на стороне ритейлера.
Для ShopStory API проверяйте и транспортный HTTP-код, и status внутри JSON-конверта. Бизнес-ошибка внутри HTTP 200 не является временным сетевым сбоем.
Пример для GET
function retryDelay(response, attempt) {
const value = response?.headers.get('Retry-After');
if (value) {
const seconds = Number(value);
if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);
const date = Date.parse(value);
if (Number.isFinite(date)) return Math.max(0, date - Date.now());
}
const backoff = Math.min(8000, 500 * 2 ** attempt);
return backoff + Math.floor(Math.random() * 300);
}
function wait(delay, signal) {
return new Promise((resolve, reject) => {
if (signal?.aborted) return reject(signal.reason);
const onAbort = () => {
clearTimeout(timer);
reject(signal.reason);
};
const timer = setTimeout(() => {
signal?.removeEventListener('abort', onAbort);
resolve();
}, delay);
signal?.addEventListener('abort', onAbort, { once: true });
});
}
async function getWithRetry(url, { attempts = 3, signal } = {}) {
for (let attempt = 0; attempt < attempts; attempt += 1) {
try {
const response = await fetch(url, {
headers: { Accept: 'application/json' },
signal,
});
const retryable = response.status === 429 || response.status >= 500;
if (!retryable) return response;
if (attempt === attempts - 1) return response;
await wait(retryDelay(response, attempt), signal);
} catch (error) {
if (signal?.aborted || attempt === attempts - 1) throw error;
await wait(retryDelay(null, attempt), signal);
}
}
}
Если попытки закончились, обработайте это до разбора JSON. Не показывайте устаревшие данные как подтверждение покупки.
Опрос API
- Не запускайте отдельный бесконечный polling для каждой карточки товара.
- Останавливайте запросы, когда экран скрыт или пользователь покинул страницу.
- Увеличивайте интервал после ошибок и восстанавливайте его постепенно.
- Объединяйте одинаковые одновременные запросы.
- Для стандартного плеера используйте SDK: он сам управляет сетевыми запросами.
- Частоту запросов кастомного live UI выбирайте с учётом допустимой задержки и квоты проекта.
Каталог и состояние плеера можно кратковременно кэшировать по правилам раздела Кэширование. При покупке merchant-система проверяет актуальный остаток заново.
Метрики
Отслеживайте:
- долю
429,5xxи timeout; - число попыток на исходный запрос;
- время от первого запроса до завершения всех попыток;
- возраст данных, показанных пользователю;
- число переходов интерфейса в деградированный режим.
Не записывайте в метрики полный URL, cookies, токены и другие учётные данные.