WebJesus — баланс и платежи
Разбор на реальном коде google_ai_scrapper/handlers/admin.py — списание баланса, пополнение, два платёжных провайдера.
Архитектурная схема (archify) →
Баланс списывается ДО обработки, за весь батч разом
check_and_deduct_balance(user_id, amount) — атомарная проверка-и-списание: читает user.balance_rub, если хватает — сразу вычитает processing_cost (цена парсера × число строк в файле). Это происходит ДО вызова process_file_with_parser(), то есть до того, как хоть одна строка реально ушла в парсер.
# Deduct balance
success = await check_and_deduct_balance(user_id, processing_cost)
if not success:
await message.answer(...) # баланс не хватило — обработка не начинается
return
# Start processing
await process_file_with_parser(message, state, bot, parser_type)
Ключевой факт, который нашёлся при поиске по коду: возврата средств за отдельные строки, вернувшие "ERROR" внутри батча, в коде НЕТ — ни одного вхождения refund/возврата баланса по всему репозиторию. Пользователь платит за весь батч по факту отправки на обработку, а не по факту успеха каждой строки.
Что стоит сказать на собесе про этот выбор: списание постфактум (по числу реально успешных строк) было бы честнее к пользователю, но сильно усложнило бы модель — пришлось бы держать баланс "замороженным" на время обработки, обрабатывать частичный возврат, защищаться от гонки между несколькими одновременными батчами одного пользователя. Списание авансом — осознанно более простая модель ценой риска для пользователя при нестабильном внешнем API (капча, блокировка) — компромисс, который стоит уметь явно назвать и объяснить, а не выдавать за случайный недосмотр.
Два независимых платёжных провайдера
Пополнение доступно суммами 499/1499/2999/4999₽, выбор провайдера — отдельным шагом ПОСЛЕ выбора суммы: пользователь видит две кнопки, pay_tbank:{amount} или pay_lava:{amount}, оба ведут на разные callback-хендлеры (callback_pay_tbank/аналогичный для Lava). T-Bank и Lava.top работают независимо — это не fallback друг друга, а равноправный выбор пользователя, вероятно продиктованный тем, что у разных провайдеров разные ограничения на юрлицо/сумму/комиссию.
Начисление баланса после оплаты (user.balance_rub += transaction.amount) происходит в обработчике вебхука провайдера, а не сразу при создании счёта — то есть баланс реально приходит только после подтверждения оплаты снаружи, не по факту нажатия кнопки "оплатить" пользователем.
Тарифы одной строкой
Yandex — 5₽/запрос, Google — 3₽/запрос (комментарий в самом коде: "User tops up balance, then pays per query"), бонус за подписку на канал и разовый приветственный бонус — оба защищены флагами welcome_bonus_received/channel_bonus_received в модели User, чтобы не начислялись повторно при повторном вызове того же хендлера.