168 lines
5.2 KiB
Python
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,
|
|
)
|