# Aiogram import aiogram.types as types from aiogram.fsm.context import FSMContext from aiogram.filters import CommandObject, CommandStart, StateFilter from aiogram import Router # Utils from utils.text_tools import to_html from utils.amount_parser import format_rub_amount # Const from create_bot import orm from services.wata import WataAPIError, WataClient # States from states.client_states import MainStates # Another from datetime import datetime, timezone import logging # Init start_router = Router() logger = logging.getLogger(__name__) wata_client = WataClient() @start_router.message(CommandStart(), StateFilter("*")) async def cmd_start( message: types.Message, state: FSMContext, command: CommandObject | None = None ): if message.chat.type != "private": return await register_user(message) command_args = command.args if command else None if command_args and command_args.startswith("payment_failed_"): order_id = command_args.removeprefix("payment_failed_") await show_payment_result(message, state, order_id) return if command_args and command_args.startswith("payment_"): order_id = command_args.removeprefix("payment_") await show_payment_result(message, state, order_id) return await state.set_state(MainStates.waiting_amount) await message.answer( text=( "Для продолжения работы укажите сумму оплаты в рублях, " "и я отправлю ссылку для безопасной оплаты.\n\n" "Отправьте сумму сообщением." ), reply_markup=types.ReplyKeyboardRemove(), ) async def register_user(message: types.Message) -> None: user_id = message.from_user.id username = ( "@" + message.from_user.username if message.from_user.username is not None else None ) fullname = to_html(message.from_user.full_name) await orm.create_user( user_id=user_id, username=username, fullname=fullname, register_date=datetime.now(timezone.utc), ) async def show_payment_result( message: types.Message, state: FSMContext, order_id: str ) -> None: payment = await orm.get_payment_by_order_id(order_id) if payment is None: await state.set_state(MainStates.waiting_amount) await message.answer( text=( "Платёж не найден.\n\n" "Отправьте сумму сообщением, чтобы создать новую ссылку." ), reply_markup=types.ReplyKeyboardRemove(), ) return if payment.status not in {"paid", "declined"}: await sync_payment_status(order_id) payment = await orm.get_payment_by_order_id(order_id) await state.set_state(MainStates.main) if payment.status == "paid": await message.answer( text=( f"Спасибо за оплату!\n\n" f"Платёж на сумму {format_rub_amount(payment.amount)} подтверждён." ), reply_markup=types.ReplyKeyboardRemove(), ) return if payment.status == "declined": error_text = "" if payment.error_description: error_text = f"\nПричина: {payment.error_description}" await message.answer( text=( "Оплата не была подтверждена платёжным сервисом." f"{error_text}\n\n" "Отправьте новую сумму сообщением, чтобы попробовать ещё раз." ), reply_markup=types.ReplyKeyboardRemove(), ) return await message.answer( text=( "Платёж ещё обрабатывается платёжным сервисом. " "Если вы уже оплатили, подождите несколько секунд и снова откройте бота." ), reply_markup=types.ReplyKeyboardRemove(), ) async def sync_payment_status(order_id: str) -> None: try: transaction = await wata_client.find_transaction_by_order_id(order_id) except WataAPIError: logger.exception("WATA returned an API error while syncing order %s", order_id) return except Exception: logger.exception("Unexpected error while syncing order %s", order_id) return if transaction is None: return paid_at = None if transaction.payment_time: paid_at = datetime.fromisoformat( transaction.payment_time.strip().replace("Z", "+00:00") ) local_status = "pending" if transaction.status == "Paid": local_status = "paid" elif transaction.status == "Declined": local_status = "declined" await orm.update_payment_status( order_id=order_id, status=local_status, transaction_status=transaction.status, transaction_id=transaction.id, error_code=transaction.error_code, error_description=transaction.error_description, updated_at=datetime.now(timezone.utc), paid_at=paid_at, )