E2E Test Scenarios
Живой список ручных сценариев которые надо прогнать после новой реализации, прежде чем считать фичу принятой. Каждый сценарий привязан к коммиту/фазе. Когда сценарий пройден — отмечаем ✅ verified <date> рядом с заголовком, не удаляя сам сценарий.
Зачем
Локально/в CI юнит-тесты ловят регрессии в коде, но интеграция с PayKeeper / реальным VPS-окружением требует ручной проверки. Этот файл — единая шпаргалка где смотреть «что мы обещали проверить и не проверили».
Окружение
- Тестовый VPS:
erp-test.nirbi.ru(185.152.93.77) - ЛК PayKeeper sandbox:
koala-test(см.reference_paykeeper_sandbox.mdв memory) - БД access:
sshpass -p '<vps-pass>' ssh root@185.152.93.77 'docker exec -it erp-postgres psql -U erp -d <db-name>'
PayKeeper — возвраты, Фаза 1 (полный возврат e2e)
Готово в коде: order-service c2ded1a, paykeeper-adapter b9dd569 (deployed 2026-04-27)
Что проверяем: при нажатии «Вернуть» в админке для PK-оплаченного заказа цепочка идёт до самого PK, статус закрывается webhook’ом или (после Фазы 2) poll’ом.
Сценарий 1.1 — Полный возврат успешный путь
- Создать тестовый PK-заказ в
paykeeper_adapter_db/order_db:- Через UI POS-кассу с PK-ТТ (если работает) — нормальный flow.
- Или SQL-инсерт +
POST /internal/orders/{id}/request-payment(форсированно, как раньше).
- Оплатить через invoice URL (или эмулировать оплату в koala-test ЛК PK).
- Убедиться что
orders.pk_payment_idиorders.paid_atзаполнены,orders.status='paid'. - В админке открыть карточку заказа → нажать «Запросить возврат» → ввести сумму =
order.total→ подтвердить. - Ожидаем (Order Service):
- В Kafka топике
order.refund_requestedесть событие сis_full_refund=true,pk_payment_id=<...>. - В БД:
SELECT status FROM refund_records WHERE order_id='...';→started(неdone).
- В Kafka топике
- Ожидаем (paykeeper-adapter):
- В логах:
OrderEventsConsumer received order.refund_requested→enqueued reverse_payment→Reverse payment success. - В БД:
SELECT * FROM pk_outbox WHERE op_type='reverse_payment' ORDER BY id DESC LIMIT 1;→status='done'.
- В логах:
- Ожидаем (PK ЛК koala-test):
- Платёж переходит в статус
refunding→refunded. - Если refund webhook от PK включён → у нас в
paykeeper_refunds.status='done'иRefundRecord.status='done'. - Если webhook НЕ включён (текущее состояние тестового ЛК) → останется
startedдо Фазы 2.
- Платёж переходит в статус
Сценарий 1.2 — Частичный возврат для PK-заказа должен блокироваться
- Тот же PK-оплаченный заказ.
- В админке нажать «Запросить возврат» → ввести сумму меньше
order.total. - Ожидаем: ошибка
422 PARTIAL_REFUND_NOT_SUPPORTED. Никаких записей в БД, никаких событий.
Сценарий 1.3 — Частичный возврат для не-PK заказа должен работать
- Заказ оплачен локально (cash/card в POS),
orders.pk_payment_id IS NULL. - Запросить возврат с суммой меньше
order.total. - Ожидаем:
RefundRecord.status='done'сразу,paykeeper_refundsне создаётся,order.refundedсобытие.
Сценарий 1.5 — Поведение при отсутствии refund webhook (Фаза 2)
Готово в коде: paykeeper-adapter 03684ba (deployed 2026-04-27)
Что проверяем: poll-fallback RefundCheckScheduler подхватывает started-возвраты и закрывает их без webhook’а.
- Тестовый PK-заказ (как в 1.1).
- Webhook URL для refund’а в ЛК PK не настроен (это и есть текущее состояние koala-test).
- Нажать «Вернуть» в админке с полной суммой.
- Ожидаем сразу:
paykeeper_refunds— новая запись сstatus='started',initiated_by='admin',payment_idзаполнено (создаётся вPkOutboxWorker.executeReversePayment).- В ЛК PK платёж ушёл в
refunding.
- Ждём 3-5 минут (poll-delay 3 мин + один тик scheduler’а):
- Логи адаптера:
Reconciled refund <id> via poll → done (pk_refund_id=...). - В БД
paykeeper_refunds— наша запись теперьstatus='done',pk_refund_idзаполнен. - В Kafka
paykeeper.payment.refunded— событие. - В Order Service
RefundRecord.status='done'.
- Логи адаптера:
- Сценарий timeout (negative): если PK по какой-то причине не подтвердил возврат, через 30 минут scheduler помечает запись
status='failed'с reason “timeout after 30 min”.
Сценарий 1.6 — Webhook и poll одновременно — нет дублей
Что проверяем: если refund webhook от PK прилетит к нам когда уже есть started-запись, обновляется именно она, а не создаётся вторая.
- Тестовый PK-заказ + возврат запущен (
paykeeper_refundsимеет одну started-запись). - Эмулировать вручную refund webhook (или включить его у PK):
curl -X POST -d 'id=<pk_payment_id>&sum=...&sign=<correct_md5>' https://erp-test.nirbi.ru/pk-webhooks/refund/<account_id>.
- Ожидаем:
- В
paykeeper_refundsдля этого payment всё ещё одна запись (а не две). - Её
statusобновлён наdone,initiated_byосталсяadmin(не перезаписан вpk_external). - Если scheduler следом проверит — он не должен ничего делать (запись уже не started).
- В
Сценарий 1.7 — Частичный возврат с item picker (Фаза 3)
Готово в коде: order-service cec31f7, admin 2e6bf47 (deployed 2026-04-27)
Что проверяем: при частичном PK-возврате админка собирает корзину выбранных позиций, и она доходит до PK как refund_cart для корректного 54-ФЗ чека.
- Тестовый PK-заказ из ≥2 позиций (например, 2× «Латте», 1× «Круассан»).
- Оплачен через PK, статус
paid. - В админке открыть карточку → «Запросить возврат» → переключить на «Частичный».
- Ожидаем UI:
- Появляется список позиций с qty-инпутами (вместо свободного ввода суммы).
- Под списком — running total «Итого к возврату».
- Кнопка submit отключена пока не выбрана хотя бы одна позиция.
- Выбрать 1× Круассан (qty=1). Сумма автоматически = unit_price круассана.
- Submit.
- Ожидаем (Order Service):
- В БД
refund_requests— новая запись соstatus='pending',refund_cartJSONB c одним элементом (name=“Круассан”, qty=1, vat_rate=“vat20”, payment_subject=“goods”, payment_type=“full”).
- В БД
- Ожидаем (после POS confirm):
- В БД
refund_records.status='started'. - В Kafka
order.refund_requestedpayload содержитis_full_refund=falseи непустойrefund_cart. - В Adapter:
pk_outboxзаписьop_type='reverse_payment', в payload — тот же cart. - В PK API: запрос
POST /change/payment/reverse/сpartial=trueи сериализованным cart.
- В БД
- Ожидаем (PK ЛК):
- Платёж переходит в
partially_refunded. - В разделе «Чеки» появляется чек возврата с одной позицией = «Круассан».
- Платёж переходит в
- Дальше (poll или webhook):
paykeeper_refunds.status='done',is_full_refund=false.refund_records.status='done'.
Сценарий 1.8 — Частичный возврат PK без cart блокируется
Что проверяем: backend ловит partial-PK без cart.
- PK-оплаченный заказ.
- Дёрнуть BFF curl-ом без
refund_cart:curl -X POST .../api/v1/orders/{id}/refund-requests \ -d '{"amount": <half_total>, "reason": "..."}' - Ожидаем: 422
REFUND_CART_REQUIRED. Никаких записей в БД.
Сценарий 1.4 — Внешний возврат через ЛК PK (initiated_by=pk_external)
Проверить что isFullRefund теперь считается корректно (не хардкод).
- PK-оплаченный заказ. Зайти в ЛК PK koala-test → сделать частичный возврат руками через интерфейс PK.
- PK шлёт refund webhook к нам.
- Ожидаем:
- В
paykeeper_refundsзапись сis_full_refund=false(раньше всегда былоtrue). - В Kafka
paykeeper.payment.refundedpayload:is_full_refund=false.
- В
Шаблон для будущих сценариев
## <Фича> — <Фаза>
**Готово в коде**: <repo> `<commit>`, <repo> `<commit>` (deployed YYYY-MM-DD)
**Что проверяем**: <одно предложение>.
### Сценарий N.1 — <короткое название>
1. <шаг>
2. <шаг>
3. **Ожидаем:** <что должно произойти>Что устарело
(сюда переносить сценарии после того как фичу отрефакторили и они стали неактуальны)