Макет магазина легко спутать с работающей торговлей: карточки товаров выглядят правдоподобно, корзина меняет сумму, кнопка открывает красивую форму. Чтобы понять, что реально готово, разложи путь заказа на отдельные подсистемы: каталог, цена, складской остаток, корзина, оформление, платёж, подтверждение и обработка заказа. ИИ может ускорить интерфейс и черновую логику, но зелёная кнопка «Оплатить» не подтверждает ни списание, ни создание заказа.
Каталог: какие данные задают товар
Для учебного магазина кофе достаточно сущностей product, variant, inventory и order. Товар описывает название и контент; вариант — конкретный размер/помол и цену; склад — доступный остаток в конкретной точке; заказ фиксирует выбранные варианты и цену на момент оформления. Не храни остаток как текст на карточке, если его нужно уменьшать после заказа.
Заранее реши, как поступить, когда товар распродан, скрыт, цена изменилась или пользователь положил последний экземпляр в корзину. Корзина не является резервированием склада сама по себе. Если временно резервируешь товары, нужен срок резерва и правило освобождения при неоплате; это отдельная серверная логика.
Попроси ИИ описать модель, прежде чем рисовать витрину:
Сделай учебный магазин кофе с каталогом, вариантами помола, корзиной и checkout-переходом. Сначала предложи таблицы и статусы заказа. Отдели цену из каталога от цены, сохранённой в созданном заказе. Покажи, где проверяется остаток и какие состояния остаются учебной имитацией. Не утверждай, что платёж подключён. До согласования модели используй только тестовые данные.
Корзина не должна доверять цене браузера
Браузер удобен для изменения количества, но его значение можно изменить через инструменты разработчика или собственный запрос. Поэтому перед созданием заказа сервер заново загружает товар/вариант, проверяет доступность и рассчитывает сумму из своего источника данных. Пример: пользователь отправил variant_id=coffee-250g, quantity=2; сервер проверяет текущую цену и наличие двух единиц. Он не принимает от браузера итог 100 ₽ — условное демонстрационное значение — как доказанную сумму.
Цена должна фиксироваться в заказе на момент его создания, чтобы последующее изменение каталога не переписало историю уже оформленного заказа. PostgreSQL constraints документируют CHECK, NOT NULL, уникальные и внешние ключи; ими можно закрепить часть инвариантов данных, например положительное количество и существование выбранного варианта (PostgreSQL constraints). Конкретные ограничения определяются схемой проекта и не заменяют проверку прав.
Путь заказа: по шагам
- Посетитель открывает карточку и выбирает вариант.
- Корзина показывает позиции и расчёт, но считает цену предварительной до повторной проверки.
- Сервер создаёт заказ со статусом ожидания и сохранёнными позициями.
- Если оплата подключена, сервер создаёт checkout у выбранного провайдера; браузер переходит на его страницу или встроенную форму согласно выбранной интеграции.
- Сервер получает и проверяет подтверждение провайдера, затем обновляет заказ и запускает выдачу/доставку.
- Если подтверждение не пришло или платёж отклонён, состояние остаётся неоплаченным/ошибочным, а посетитель видит понятный следующий шаг.
Это целевой маршрут для проектирования, а не доказательство, что он собран в конкретном приложении. Stripe описывает hosted checkout как готовую страницу провайдера, которая может открываться через redirect или встраиваться; документация отдельно показывает, что интеграция включает серверную часть (Stripe Checkout quickstart, Checkout overview). Для проверки интеграции Stripe описывает sandbox и симулированные тестовые платежи, которые не перемещают деньги (Stripe testing). Детали другого провайдера сверяй с его официальными документами.
Что входит в прототип, а что требует настоящего checkout
Прототип может показывать каталог, фильтры, корзину на демонстрационных данных и черновой заказ со статусом «тестовый». Его интерфейс нужно маркировать как демонстрационный, если реальная оплата и обработка заказа не подключены.
Для фактической торговли отдельно проверяются: создание заказа только на сервере; актуальная цена и остаток; провайдерское подтверждение; обработка повторного события; отмена/ошибка; сообщение покупателю; дальнейшая обработка товара и заказа. Нельзя переводить заказ в «оплачен» по возврату браузера на страницу благодарности. Факт просмотра checkout также не подтверждает успешное получение денег.
В Stripe sandbox можно проверять сценарии без движения денег, но результаты sandbox не доказывают успешность live-интеграции и реальные настройки магазина. Не включай реальные секреты в клиентскую сборку. Перед запуском торговли проверь права, валюты, налоги, условия доставки и применимые требования с компетентными специалистами; эта статья не заменяет юридическую и бухгалтерскую консультацию.
Проверки, которые отделяют демо от торговли
| Событие | Что должно остаться в системе |
|---|---|
| Количество превышает остаток или цена изменилась | Сервер повторно проверяет товар; заказ не подтверждается по старому расчёту |
| Соединение оборвалось после отправки формы | Перед повтором клиент узнаёт, создался ли заказ; дубликат заказа не появляется |
| Браузер вернулся с checkout раньше уведомления провайдера | Заказ остаётся ожидающим, пока сервер не подтвердит платёж |
| Провайдер прислал одно событие повторно | Повторная обработка не создаёт вторую оплату/выдачу |
| Sandbox показал успех | Запись помечена как тестовая; live-оплата не заявляется |
Проверь эти сценарии в тестовой среде, сверяя записи заказа и фактические ответы endpoint, а не только экран. Реальная торговля дополнительно зависит от провайдера, товара, возвратов, доставки и применимых правил; учебный макет сам их не подключает.
