Четыре правки 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.
Пока PR был на ревью, в main влились 228_scrape_proxies_browser_health.sql
и 230_house_merge_log.sql — номер 228 занят, 229/231 параллельно занимает
PR #2547. Переименовано в 232_payments.sql (git mv, история файла сохранена),
manifest обновлён (228_payments.sql -> 232_payments.sql), шапка миграции
объясняет причину переименования и актуализирован "Apply after".
Смержен свежий forgejo/main (обычный merge, без rebase/force-push) —
конфликтов не было, config.py не задет. test_new_files_do_not_reuse_prefix
теперь зелёный. Полный pytest после merge: 3858 passed, 10 skipped, 0 failed.
HOLD-правки ревью PR #2732 (миграция ни разу не применялась на проде,
manifest не трогаем):
- Блокер: UNIQUE(payment_id, kind, ref_id) и UNIQUE(tbank_payment_id, status,
amount_kopecks, token) не защищали при NULL (NULL != NULL в Postgres) —
на проде воспроизведено 3 одинаковых INSERT -> 3 строки. Оба ключа теперь
UNIQUE NULLS NOT DISTINCT (PG15+, прод на 16.4).
- payment_notifications.processed_at — контракт "выдача состоялась" для PR-D,
закрывает окно at-most-once (падение между INSERT нотификации и выдачей
товара при захолдированных деньгах).
- payments_lead_idx — FK lead_id ON DELETE SET NULL без индекса это seq scan
на каждый DELETE FROM trade_in_leads, а #2547 вводит пакетное удаление.
- payment_entitlements: убраны amount/consumed — модель кредитов/пакетов
явно отвергнута в mera-b2c-paid-flow-decision.md §1 (capability-URL вместо
кошелька).
- payments.pd_erased_at — покрытие purge-контура #2547, который не знает про
ПДн в payments (customer_email/customer_phone), появляющиеся в PR-D.
- payments.order_id CHECK char_length <= 50 — лимит T-Bank OrderId, падать
у себя, а не на /v2/Init.
- payments.status CHECK сверен с github.com/nikita-vanyasin/tinkoff/status.go
(developer.tbank.ru рендерит enum клиентским JS, прямого доступа нет):
убраны неподтверждённые AUTHORIZED_AND_CHARGED/RECEIPT_REGISTERED, добавлены
подтверждённые 3DS_CHECKING/3DS_CHECKED. ATTEMPTS_EXPIRED/PAY_CHECKING НЕ
добавлены — не подтверждены ни одним источником.
- config.py: комментарий TBANK_PAY_TYPE ссылался на recon-док (выбирает "O"),
переставлен на mera-b2c-paid-flow-decision.md §1 (выбирает "T", источник
реального дефолта).
Полный pytest: 3755 passed, 9 skipped, 0 failed.