Files
WeechatPayBot/bot/handlers/start.py

168 lines
5.2 KiB
Python

# 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,
)