О проекте
Двойной клик и повторный webhook не должны создавать второй заказ или выдавать ещё один код. Если поставщик не ответил, нужно разобраться со старым запросом, прежде чем отправлять новый.
Каталог на Angular, API на NestJS, заказы в PostgreSQL. Оплату и выдачу обрабатывает воркер. Поставщики в демке — HTTP-моки. Есть сценарии с таймаутом, повторным webhook и восстановлением заказа.
- Оплата и поставщики — явные моки; деньги не списываются, коды учебные.
- До 20 заказов на сессию и 1000 новых демонстрационных заказов суммарно. TTL один час, очистка каждые пять минут.
- Пример восстановления намеренно завершает первую попытку одного заказа ошибкой. Он не меняет глобальные режимы поставщиков.
Скриншоты, схемы и исходные материалы сохранены на языке оригинала; подписи и описание переведены.
Архитектура и схемы
- Цена и скидка вычисляются на сервере и фиксируются в заказе.
- Idempotency-Key защищён уникальным индексом; входные данные повтора сверяются с исходным намерением.
- Webhook сначала сохраняется в durable inbox. Отдельный worker обрабатывает платёж и очередь выдачи.
- Стабильный requestId поставщика сохраняется при повторе. Неоднозначный timeout не разрешает немедленно выдать второй код.
Code-map: файлы и связи
Карта привязана к версии 767226bf3c2f. В таблице — экран или API, связанный модуль и данные.
| Функция | Экран / API | Модуль | Данные / проверка |
|---|---|---|---|
| Покупка | Storefront / POST orders | storefront.component; ShopService.createOrder | orders, products, promocodes; race tests |
| Оплата | POST payments/simulate → webhooks/payment | PaymentsController; acceptWebhook | payment_events; повтор event_id |
| Выдача | Order / GET orders/:id | worker.service processDeliveryJob | delivery_jobs, provider_requests, fulfillments; recovery |
| Сессия | Guard всех публичных заказов | portfolio-demo PortfolioGuard | HMAC cookie, prefix owner; tests/portfolio/run.ts |
| Сброс / TTL | POST demo/reset / timer | PortfolioCleanup.clear | только portfolio orders; ownership / expiry tests |
Как работать с картой
- Перед изменением найти функцию и пройти связи до API, состояния и хранилища.
- Проверить входы, ошибки, повторные события и зависимые сценарии.
- После изменения обновить карту и затронутые схемы вместе с кодом.
- Выполнить связанные проверки и привязать новые материалы к версии исходников.
Сценарии и API
- Выберите товар и выполните успешную тестовую оплату. Дождитесь кода и просмотрите историю.
- Повторите то же платёжное событие: код и выдача должны остаться единственными.
- Создайте заказ через сценарий сбоя, оплатите и нажмите «Восстановить мой заказ».
- Проверьте отказ оплаты, чужой UUID в другой сессии и сброс своего примера.
Проверка опубликованной версии
CI: CI ↗ · 767226bf · версия этого демо · 2026-09-07T00:49:05Z
CI: Portfolio immutable image ↗ · 767226bf · версия этого демо · 2026-09-07T00:49:05Z
- Real HTTP webhook, durable inbox, worker, single fulfillment preserved on event replay
- Foreign GET/recover return 404; other session reset preserves first order
- Payment failure; isolated first-attempt delivery fault; own recovery succeeds
Эти проверки относятся к указанной версии. Коммерческие показатели и качество моделей на независимой выборке здесь не измерялись.
Измерения
- Сохранено 14 содержательных кадров фактического интерфейса.
- Дата, сценарий и версия указаны у каждого кадра.
- Максимум RAM контейнеров в коротком тесте: 218.56 MiB. Общие службы учитываются отдельно; магазин включает свою БД.
- Измерено 6 повторений сценария; медиана 129.0 мс, максимум 1939 мс. Время включает браузер и HTTPS; методика и ограничения приложены.
Запуск и эксплуатация
У демки и исходного приложения одна модель заказов. Демо-guard включается через PORTFOLIO_DEMO=true. Без этого флага действуют исходные контракты API.
Сборка выполняется GitHub Actions из закреплённого commit SHA. На VPS загружается готовый GHCR-образ. База магазина остаётся отдельной от новых приложений.
Запуск: docker compose --env-file .env.production -f compose.production.yaml up -d. До переключения выполнить prisma migrate deploy. Секреты хранятся только на сервере. Seed нельзя использовать как безусловный сброс данных работающего магазина.
Readiness: /api/health/ready проверяет PostgreSQL. Во время инцидента сравнить историю заказа, inbox и delivery job; повторять выдачу по исходному orderId, не создавать новую покупку.
Сборка вне VPS, на сервер уходят готовые файлы и образы с фиксированной версией. Секреты отдельно. Логи ротируются; после изменения log options контейнер нужно пересоздать. Общий docker prune не используется.
Code-map связывает экран или API с модулем и данными. При правке нужно проверить эти связи, обновить карту и прогнать затронутые сценарии: обычный проход, ошибку, повтор, сброс. Схема может устареть, поэтому её версия указана рядом.
Откат проверен 2026-09-07T01:07:46.255650+00:00: 20260907-verified → 20260907 → 20260907-verified. После каждого переключения подтверждены HTTPS, readiness и SHA. Это проверка конфигурации и артефактов на одном проверенном SHA; обратная миграция БД не выполнялась.
Стек
| Angular / TypeScript | Витрина, сигналы состояния, HTTP-клиент и polling заказа. |
|---|---|
| NestJS | Контракты, валидация, webhook, операции восстановления и границы демо. |
| PostgreSQL / Prisma | Снимки денег, inbox, очередь с lease, уникальные индексы и транзакции. |
| Node.js worker | HTTP-вызовы поставщиков вне короткой транзакции, retry и финальная выдача. |
Материалы и исходники
История реализации: 1–3 сентября 2026. Подготовка портфолио: 7 сентября 2026.
Архив материалов на языке оригинала.