Приложение обкатывается на боевом контуре, поэтому интерфейс надо проверять на
живых данных — а живые данные лежат у настоящих водителей.
* «Вход для сотрудника» (/dev) — логин учётки CRM, поддержан второй шаг 2FA;
дальше поиск водителя по ФИО/телефону/id и просмотр его экранов.
* Плашка «Смотрю как …» над всеми экранами: интерфейс в этом режиме неотличим
от водительского, и чужой баланс легко принять за свой.
* Вкладка «Отладка» (/debug, водителю недоступна): смена водителя, driver_id,
режим, срок токена, адрес API, коммит и время сборки, журнал последних
40 ответивших запросов, сброс кэша сервис-воркера.
* Пополнение под просмотром идёт в учебный контур: экран /mock-pay/:orderId
выбирает, что «ответит» банк, а настоящий экран оплаты разбирает эти статусы
своим боевым кодом. Ни рубля, ни чека 54-ФЗ.
Заодно доезжает незакоммиченная работа прошлой сессии: дев-прокси в
vite.config.ts, его тест и разобранный .env.example.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- pairing-сессия хранит poll_secret и confirm_code (lib/pairing), опрос ушёл
на POST: pair_session уезжал в query-параметре и оседал в access-логах, а
это половина ключа к JWT. Вторая половина в deep-link не уходит;
- на экране ожидания видно число, которое обязан назвать бот. По чужой ссылке
человек его не увидит — подтверждать нечего, токен не выдаётся;
- онбординг больше не просит вернуться и запросить код: Telegram подтверждает
номер шарингом контакта, приложение забирает токен опросом;
- суммы с копейками. formatMoney округлял до рубля, и «к оплате 350 ₽»
расходилось и со списанием банка, и с чеком (комиссия почти всегда даёт
копейки). Клавиша разделителя, нарисованная на клавиатуре, наконец работает;
- категорию долга выбирает водитель. Хардкод «Долг аренда» отправлял платёж
в аренду даже тем, у кого её нет; список приходит с сервера и там же
проверяется;
- ошибки бэкенда показываются словами (минимум СБП, потолок суммы) вместо
молчаливого «не удалось», статусы истории переведены полностью, возврат
денег отличим от отказа.
Тесты: 33 зелёных, tsc чист.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Экран успеха рисовался только при PAID. При любом другом финальном статусе
опрос останавливался, а человек навсегда оставался на «Ожидаем оплату…» со
спиннером — то есть два из трёх тестовых сценариев СБП глазами не проверить.
- ветки отказа (в том числе исчерпанных попыток), истёкшего срока и возврата
средств: у возврата свой текст, новая попытка там ни при чём;
- ошибка запроса больше не выглядит как честное ожидание: после серии неудач
подряд экран говорит, что не может проверить оплату, и даёт повторить.
Раньше 404, 500 и рестарт бэкенда были неотличимы от «ждём оплату»;
- плашка способа оплаты показывала «СБП» и при оплате картой;
- после перезагрузки страницы сумма бралась из пустого router state и
показывалась как 0 ₽ — лучше не показать ничего, чем показать неправду.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
window.location.href=deep_link navigated the whole PWA to t.me, destroying the
/tg-wait polling screen -> the authenticated token was never fetched. Prefetch
the pairing session and open Telegram via an <a href> (OS app-switch, PWA stays
mounted polling); add an Open-Telegram fallback link on the wait screen.
Co-Authored-By: claude-flow <ruv@ruv.net>
Backend omits channels without a configured username; strict zod schema
threw on missing 'max', surfacing as 'Не удалось отправить код' instead of
routing to the onboarding screen. Make both optional + regression test.
Co-Authored-By: claude-flow <ruv@ruv.net>