Перейти к содержанию
Innovialabs

Как избежать продажи отсутствующего товара: стратегии резервирования

Продвинутое4 мин

Коротко

Для большинства магазинов надежный вариант по умолчанию — резервировать товар при оформлении заказа с коротким таймаутом, списывать остаток атомарно в базе и держать один учет остатков для всех каналов продаж.

Короткий ответ

Продажа отсутствующего товара (overselling) случается, когда двое покупателей получают одну последнюю единицу или маркетплейс показывает товар, который уже продан на сайте. Защищают от этого три вещи:

  • Модель резервирования — определяет, в какой момент единица перестает быть доступной другим.
  • Атомарные операции с остатками — два параллельных запроса не могут одновременно списать одну единицу.
  • Единый учет остатков — все каналы читают и пишут в одно место.

Для большинства магазинов сбалансированный вариант — резерв при оформлении с таймаутом 10-20 минут. Точное значение зависит от того, сколько реально длится ваша оплата.

Три модели резервирования

МодельКогда товар удерживаетсяПлюсыМинусы
Резерв в корзинеПри добавлении в корзинуПокупатель не потеряет товар на оплатеБрошенные корзины блокируют остаток, легко злоупотребить
Резерв при оформленииПри начале checkoutБаланс: держим товар только для серьезных покупателейНужны таймауты и задача освобождения
Резерв после оплатыПосле подтверждения оплатыНет заблокированных остатков, простая логикаДвое могут оплатить одну единицу, одному придется вернуть деньги

Резерв в корзине подходит для флеш-распродаж и лимитированных дропов, где потерять товар на последнем шаге недопустимо. Используйте короткий таймаут корзины и лимит количества на покупателя.

Резерв при оформлении подходит большинству каталожных магазинов: товар держится, пока покупатель вводит адрес и платит.

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

Таймауты резерва

Резерв без срока действия — это медленная утечка остатков. Каждому резерву нужны:

  • Поле expires_at, которое задается при создании резерва.
  • Механизм освобождения: фоновая задача, снимающая просроченные резервы, или ленивая очистка при чтении остатка.
  • Правила продления: если покупателя перенаправили на платежный шлюз, продлите резерв так, чтобы он пережил сессию шлюза.
  • Правило поздней оплаты: что делать, если подтверждение оплаты пришло после истечения резерва. Обычно пробуют зарезервировать заново, а если товара нет — делают автоматический возврат.

Храните резервы отдельными записями, а не как счетчик, который уменьшили и забыли. Тогда видно, кто что держит, и снимать резерв можно точечно.

Конкурентный доступ

Классическая ошибка: прочитать остаток (осталась 1 штука), проверить, потом записать. Два запроса одновременно читают «1» и оба продают. Решать это нужно на уровне базы данных.

Самый простой надежный способ — условное атомарное обновление:

UPDATE stock
SET available = available - :qty,
    reserved  = reserved + :qty
WHERE sku = :sku AND available >= :qty;
-- 0 затронутых строк = товара недостаточно

Другие варианты:

  • Блокировка строк (SELECT ... FOR UPDATE) внутри транзакции, если перед записью нужно несколько проверок. Блокируйте строки в одном порядке, чтобы избежать дедлоков.
  • Оптимистичная блокировка через колонку версии: при изменении версии — повтор.
  • Атомарные операции в Redis (например, Lua-скрипт) для очень высокой нагрузки, а база остается надежным хранилищем.

Сделайте создание резерва и обработку платежных колбэков идемпотентными. Шлюзы повторяют вебхуки, и повторный колбэк не должен списать остаток дважды.

Остатки в нескольких каналах

Когда вы продаете на сайте, в офлайн-магазине и на маркетплейсах, у каждого канала своя копия остатков с задержкой синхронизации. Чтобы они не расходились:

  • Один учет — источник правды: ваша база, ERP или 1С, но не маркетплейс.
  • Отправляйте изменения при каждом движении, а не раз в сутки. Используйте API обновления остатков маркетплейсов и очередь.
  • Держите страховой буфер по каналам: на каналах с медленной синхронизацией показывайте чуть меньше реального остатка.
  • Распределяйте дефицит: для редких товаров выделяйте каналам фиксированные квоты вместо общего пула.
  • Быстро забирайте заказы: заказ с маркетплейса должен как можно скорее создавать резерв в вашем учете.

Типичные ошибки

  • Проверка остатка в коде приложения без атомарной записи.
  • Резервы без срока действия, которые постепенно «замораживают» каталог.
  • Учет товаров «в корзине» как проданных в отчетах.
  • Синхронизация остатков с маркетплейсами раз в день для быстро продающихся товаров.
  • Отсутствие сценария для оплаты, пришедшей после истечения резерва.

Частые вопросы

Начните с резерва при оформлении и таймаута, который покрывает реальное время оплаты. Так вы защищаете тех, кто уже платит, и не замораживаете товар в брошенных корзинах.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.