fix(tradein/payments): номер 232->233 + статусы T-Bank по официальной спеке
All checks were successful
CI / changes (pull_request) Successful in 9s
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 3m10s

Четыре правки HOLD-ревью PR #2732, форма таблиц не меняется:

1. Коллизия номера: 232_listings_observation_time_meaning.sql несёт открытый
   PR #2742 (не main, поэтому пропущено в прошлый раз). Переименовано 232 ->
   233 (git mv, история сохранена), manifest обновлён. Урок задокументирован
   в шапке файла: сверять номер нужно по main И по всем открытым PR-веткам.
   Перепроверено дважды (main + 5 остальных открытых веток) — 233 свободен.

2. payments_status_check дополнен пятью реальными in-flight статусами из
   канонического источника (OpenAPI-спека developer.tbank.ru/schemas/eacq/
   openapi.yaml, схема Confirm-2, v1.24): CHECKING, CHECKED, PROCESSING,
   COMPLETING, COMPLETED. Это статусы, которые GetState/CheckOrder вернёт по
   зависшему платежу — их читает реконсиляция PR-E; пропуск реального
   значения был единственным опасным направлением ошибки CHECK.

3. Блок про источник статусов переписан: убрана ссылка на сторонний Go-клиент
   и формулировка "enum рендерится клиентским JS" — источник истины теперь
   openapi.yaml. Отдельно зафиксировано: GetState/CheckOrder объявляют Status
   свободной строкой (maxLength: 20, без enum) — наш CHECK строже контракта
   поставщика, осознанно.

4. Контракт 'UNKNOWN' переадресован: теперь явно на обработчик нотификаций
   И задачу реконсиляции (GetState/CheckOrder, PR-E), а не только на вебхуки.

Побочно подтверждено спекой: AUTHORIZED_AND_CHARGED/RECEIPT_REGISTERED
отсутствуют (плюс maxLength:20 делает первое физически невозможным);
3DS_CHECKING/3DS_CHECKED и PREAUTHORIZING есть; ATTEMPTS_EXPIRED/PAY_CHECKING
нет; PARTIAL_REVERSED/REFUND_FAILED тоже нет в спеке — оставлены как
безвредный запас, комментарий перестал ложно утверждать обратное.

Полный pytest: 3858 passed, 10 skipped, 0 failed.
This commit is contained in:
bot-backend 2026-08-06 19:28:44 +03:00
parent 04b268ad3a
commit 95ed9c7811
2 changed files with 72 additions and 32 deletions

View file

@ -1,11 +1,16 @@
-- 232_payments.sql
-- 233_payments.sql
-- Платёжный контур МЕРЫ (Т-Банк интернет-эквайринг) — схема БД, PR-B из серии
-- A..F (см. корень репо `mera-tbank-acquiring-recon.md`, §9 «Разбивка на PR»).
-- Ни разу не применялась на проде (см. `_manifest_applied.txt`) — правится на
-- месте по итогам review (статус HOLD), без ребейза номера. Переименована из
-- 228_payments.sql в 232_payments.sql (git mv, история сохранена): номер 228
-- заняли 228_scrape_proxies_browser_health.sql, 230 — 230_house_merge_log.sql
-- (влились в main, пока шло ревью); 229 и 231 параллельно занимает PR #2547.
-- месте по итогам review (статус HOLD), без ребейза номера. Дважды
-- переименована (git mv, история сохранена): 228 → 232 → 233. Номер 228
-- заняли 228_scrape_proxies_browser_health.sql и 230_house_merge_log.sql
-- (влились в main); 229 и 231 занимает открытый PR #2547; 232 занял открытый
-- PR #2742 (`232_listings_observation_time_meaning.sql`). Урок: сверять номер
-- нужно не только по `forgejo/main`, но и по ВСЕМ открытым PR-веткам — ни один
-- из этих файлов сам себя в `_manifest_applied.txt` не пишет (мы пишем), из-за
-- чего коллизия обнаруживается только тестом `test_new_files_do_not_reuse_prefix`
-- уже после того, как чей-то PR смержен первым.
--
-- ── WHY ──────────────────────────────────────────────────────────────────────
-- Этот PR — ТОЛЬКО схема + конфиг + kill-switch (`PAYMENTS_ENABLED=false` в
@ -49,32 +54,62 @@
-- DO UPDATE). Применено к обоим дедуп-ключам ниже.
--
-- ── СТАТУСЫ T-BANK (payments.status CHECK) ──────────────────────────────────
-- Список сверен с публичным Status-enum T-Bank Acquiring API. Источник
-- developer.tbank.ru рендерит enum клиентским JS (правая панель схемы ответа
-- динамически подгружается) — прямого текстового доступа к разделу GetState
-- не получено; сверка выполнена по независимому активно поддерживаемому
-- Go-клиенту (github.com/nikita-vanyasin/tinkoff, файл status.go), который
-- явно комментирует каждый статус. Изменения относительно первой версии:
-- - УБРАНЫ 'AUTHORIZED_AND_CHARGED' и 'RECEIPT_REGISTERED' — не найдены ни
-- в одном сверенном источнике; RECEIPT_REGISTERED похоже на статус
-- отдельного объекта «чек» (SendClosingReceipt), не платежа.
-- - ДОБАВЛЕНЫ '3DS_CHECKING' и '3DS_CHECKED' — подтверждены сверкой.
-- - НЕ добавлены 'ATTEMPTS_EXPIRED' и 'PAY_CHECKING' (гипотеза из ревью) —
-- не нашлись ни в одном источнике, которым удалось свериться; если реально
-- существуют — расширить CHECK отдельной миграцией по факту документа.
-- - 'PARTIAL_REVERSED' оставлен: та же сверка его подтверждает (реально
-- существующий статус частичной отмены холда), а не «оставлен из
-- осторожности», как было сформулировано раньше.
-- - 'PREAUTHORIZING' оставлен как есть (не проверялся под вопрос ревью):
-- тот же Go-клиент помечает его комментарием "deprecated / removed from
-- API", но это не запрошенная часть проверки — трогать не стал, инертное
-- значение в CHECK безвредно, если банк его больше не шлёт.
-- Контракт для PR-D: если банк присылает статус вне списка ниже, обработчик
-- нотификаций обязан писать в payments.status значение 'UNKNOWN' (не поднимать
-- исключение, не терять запись) — сырое тело в любом случае лежит целиком в
-- payment_notifications.body. INSERT/UPDATE payments с любым другим
-- незнакомым значением упадёт на CHECK — это специально: тихое искажение
-- статуса хуже, чем громкий сбой одной записи.
-- Источник истины — официальная OpenAPI-спека:
-- https://developer.tbank.ru/schemas/eacq/openapi.yaml (OpenAPI 3.0.2, v1.24),
-- схема `Confirm-2`, 24 значения. На странице /eacq/intro/developer/openapi
-- прямо сказано: при расхождении прозы и спеки приоритет у спеки — поэтому
-- ссылка на спеку, а не на человекочитаемые доки.
--
-- ВАЖНО про GetState/CheckOrder (ручки, которыми реконсиляция PR-E читает
-- статус): в спеке их поле `Status` объявлено СВОБОДНОЙ строкой
-- (`maxLength: 20`, БЕЗ enum). То есть на ручках, которыми фактически питается
-- реконсиляция, банк словарь значений не фиксирует контрактно — наш CHECK
-- здесь строже, чем контракт поставщика. Это осознанный выбор (закрытый
-- список читается и валидируется проще, чем произвольная строка), а не
-- недосмотр; следующий читатель должен видеть, что этот CHECK может однажды
-- отвергнуть легитимный, но недокументированный `Confirm-2`-строкой статус —
-- см. контракт 'UNKNOWN' ниже.
--
-- Сверка по `Confirm-2` относительно первой версии миграции:
-- - УБРАНЫ 'AUTHORIZED_AND_CHARGED' и 'RECEIPT_REGISTERED' — отсутствуют в
-- `Confirm-2`. Дополнительно у поля `Status` в спеке `maxLength: 20`, а
-- 'AUTHORIZED_AND_CHARGED' — 22 символа: физически не может быть значением
-- этого поля, не только "не найдено", а невозможно по контракту.
-- - ДОБАВЛЕНЫ '3DS_CHECKING' и '3DS_CHECKED' — есть в `Confirm-2`.
-- - НЕ добавлены 'ATTEMPTS_EXPIRED' и 'PAY_CHECKING' — отсутствуют в
-- `Confirm-2`, гипотеза не подтвердилась.
-- - 'PREAUTHORIZING' оставлен и подтверждён: есть в `Confirm-2`. (Ранее
-- редакция ссылалась на комментарий стороннего Go-клиента о том, что этот
-- статус будто бы убран из API — спекой это не подтверждается, комментарий
-- был неточным источником и снят.)
-- - ДОБАВЛЕНЫ пять пропущенных in-flight значений из `Confirm-2`:
-- 'CHECKING', 'CHECKED', 'PROCESSING', 'COMPLETING', 'COMPLETED'. Это
-- ровно те статусы, которые GetState/CheckOrder вернёт по зависшему
-- платежу — их читает реконсиляция (PR-E). Пропуск реального значения —
-- единственное опасное направление ошибки CHECK: не "лишний" статус
-- проскочит, а свой же CHECK отвергнет то, что банк реально прислал.
-- Порядок в списке ниже — по смысловой близости к соседним стадиям
-- (CHECKING/CHECKED рядом с 3DS_CHECKING/3DS_CHECKED, COMPLETING/COMPLETED
-- рядом с CONFIRMED), а не порядок из спеки — `Confirm-2` не гарантирует
-- порядок enum, для CHECK-констрейнта (проверка принадлежности множеству)
-- порядок значения не имеет.
-- - 'PARTIAL_REVERSED' и 'REFUND_FAILED' в `Confirm-2` ОТСУТСТВУЮТ. Оставлены
-- в CHECK как безвредный запас на случай появления в будущей версии API
-- (сам факт лишнего разрешённого значения в CHECK ничего не ломает — в
-- отличие от отсутствующего). Это отличается от предыдущей редакции
-- комментария, которая ошибочно утверждала, что сверка их "подтверждает":
-- не подтверждает, они не найдены в источнике истины.
--
-- Контракт: если банк присылает статус вне списка ниже, ОБРАБОТЧИК
-- НОТИФИКАЦИЙ И ЗАДАЧА РЕКОНСИЛЯЦИИ (GetState/CheckOrder, PR-E) обязаны
-- писать в payments.status значение 'UNKNOWN' (не поднимать исключение, не
-- терять запись) — сырое тело нотификации в любом случае лежит целиком в
-- payment_notifications.body (у GetState/CheckOrder своего append-only лога
-- нет — если реконсиляция сама не сохранит сырой ответ, факт неизвестного
-- статуса останется только в payments.status='UNKNOWN' и её собственных логах).
-- INSERT/UPDATE payments с любым другим незнакомым значением упадёт на
-- CHECK — это специально: тихое искажение статуса хуже, чем громкий сбой
-- одной записи.
--
-- ── payment_entitlements: без кредитно-кошельковой семантики ────────────────
-- Первая версия несла amount/consumed (модель «кредиты/пакеты»). Явно
@ -173,8 +208,13 @@ ALTER TABLE payments
'REJECTED',
'3DS_CHECKING',
'3DS_CHECKED',
'CHECKING',
'CHECKED',
'PROCESSING',
'CONFIRMING',
'CONFIRMED',
'COMPLETING',
'COMPLETED',
'REVERSING',
'PARTIAL_REVERSED',
'REVERSED',

View file

@ -230,4 +230,4 @@
# Тем самым снято отложенное условие из прошлой редакции: 187/188 (веб-чат
# поддержки, #2532/#2533) откладывались до подтверждения, что они осели на
# проде в финальном виде. Они в _schema_migrations — условие выполнено.
232_payments.sql
233_payments.sql