2025-12-22 21:17:17 +03:00

275 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from aiogram import Router, types
from aiogram.filters import Command
from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
from decimal import Decimal
from tg_bot.config.settings import settings
from tg_bot.payment.yookassa.client import yookassa_client
from tg_bot.infrastructure.database.database import AsyncSessionLocal
from tg_bot.infrastructure.database.models import PaymentModel
from tg_bot.domain.services.user_service import UserService
from sqlalchemy import select
import uuid
from datetime import datetime, timedelta
router = Router()
@router.message(Command("buy"))
async def cmd_buy(message: Message):
user_id = message.from_user.id
username = message.from_user.username or f"user_{user_id}"
async with AsyncSessionLocal() as session:
try:
user_service = UserService(session)
user = await user_service.get_user_by_telegram_id(user_id)
if user and user.is_premium and user.premium_until and user.premium_until > datetime.now():
days_left = (user.premium_until - datetime.now()).days
await message.answer(
f"<b>У вас уже есть активная подписка!</b>\n\n"
f"• Статус: Premium активен\n"
f"• Действует до: {user.premium_until.strftime('%d.%m.%Y')}\n"
f"• Осталось дней: {days_left}\n\n"
f"Новая подписка будет добавлена к текущей.",
parse_mode="HTML"
)
except Exception:
pass
await message.answer(
"*Создаю ссылку для оплаты...*\n\n"
"Пожалуйста, подождите несколько секунд.",
parse_mode="Markdown"
)
try:
payment_data = await yookassa_client.create_payment(
amount=Decimal(str(settings.PAYMENT_AMOUNT)),
description=f"Подписка VibeLawyerBot для @{username}",
user_id=user_id
)
async with AsyncSessionLocal() as session:
try:
payment = PaymentModel(
payment_id=str(uuid.uuid4()),
user_id=user_id,
amount=str(settings.PAYMENT_AMOUNT),
currency="RUB",
status="pending",
yookassa_payment_id=payment_data["id"],
description="Оплата подписки VibeLawyerBot"
)
session.add(payment)
await session.commit()
print(f"Платёж сохранён в БД: {payment.payment_id}")
except Exception as e:
print(f"Ошибка сохранения платежа в БД: {e}")
await session.rollback()
keyboard = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(
text="Оплатить онлайн",
url=payment_data["confirmation_url"]
)
],
[
InlineKeyboardButton(
text="Проверить статус оплаты",
callback_data=f"check_payment:{payment_data['id']}"
)
]
]
)
response_text = (
f"<b>Оплата подписки VibeLawyerBot</b>\n\n"
f"<b>Детали платежа:</b>\n"
f"• Сумма: {settings.PAYMENT_AMOUNT} руб.\n"
f"• Описание: Подписка на 30 дней\n"
f"• ID платежа: <code>{payment_data['id'][:20]}...</code>\n\n"
f"<b>Что даёт подписка:</b>\n"
f"• Неограниченное число вопросов\n"
f"• Приоритетная обработка\n"
f"• Доступ ко всем функциям\n\n"
f"<i>После оплаты доступ активируется автоматически в течение 1-2 минут.</i>"
)
await message.answer(
response_text,
parse_mode="HTML",
reply_markup=keyboard,
disable_web_page_preview=True
)
await message.answer(
"<b>Инструкция по оплате:</b>\n\n"
"1. Нажмите кнопку 'Оплатить онлайн'\n"
"2. Введите данные банковской карты\n"
"3. Подтвердите оплату\n"
"4. После успешной оплаты нажмите 'Проверить статус оплаты'\n\n"
"<b>Тестовые карты для проверки:</b>\n"
"• <code>5555 5555 5555 4477</code> - успешная оплата\n"
" Срок: <b>любой будущий</b> (напр. 12/30)\n"
" CVV: <b>любые 3 цифры</b> (напр. 123)\n\n"
"<i>Это тестовые карты, реальные деньги не списываются!</i>",
parse_mode="HTML"
)
except Exception as e:
print(f"Ошибка создания платежа: {e}")
await message.answer(
"<b>Произошла ошибка при создании платежа</b>\n\n"
"Пожалуйста, попробуйте позже или обратитесь к администратору.\n\n"
f"<code>Ошибка: {str(e)[:100]}</code>",
parse_mode="HTML"
)
@router.callback_query(lambda c: c.data.startswith("check_payment:"))
async def check_payment_status(callback_query: types.CallbackQuery):
yookassa_id = callback_query.data.split(":")[1]
user_id = callback_query.from_user.id
await callback_query.answer("Проверяю статус оплаты...")
try:
from yookassa import Payment as YooPayment
payment = YooPayment.find_one(yookassa_id)
if payment.status == "succeeded":
async with AsyncSessionLocal() as session:
try:
result = await session.execute(
select(PaymentModel).filter_by(yookassa_payment_id=yookassa_id)
)
db_payment = result.scalar_one_or_none()
if db_payment:
db_payment.status = "succeeded"
user_service = UserService(session)
success = await user_service.activate_premium(user_id)
if success:
user = await user_service.get_user_by_telegram_id(user_id)
await session.commit()
if not user:
user = await user_service.get_user_by_telegram_id(user_id)
await callback_query.message.answer(
"<b>Оплата подтверждена!</b>\n\n"
f"Ваш premium-доступ активирован до: "
f"<b>{user.premium_until.strftime('%d.%m.%Y')}</b>\n\n"
"Теперь вы можете:\n"
"• Задавать неограниченное количество вопросов\n"
"• Получать приоритетные ответы\n"
"• Использовать все функции бота\n\n"
"<i>Спасибо за покупку!</i>",
parse_mode="HTML"
)
else:
await callback_query.message.answer(
"<b>Платёж найден в ЮKассе, но не в нашей БД</b>\n\n"
"Пожалуйста, обратитесь к администратору.",
parse_mode="HTML"
)
except Exception as e:
print(f"Ошибка обработки платежа: {e}")
elif payment.status == "pending":
await callback_query.message.answer(
"<b>Платёж ещё не завершён</b>\n\n"
"Если вы уже оплатили, пожалуйста, подождите 1-2 минуты "
"и проверьте статус снова.\n\n"
"<i>Проверьте правильность данных карты:</i>\n"
"• Срок действия должен быть <b>будущим</b>\n"
"• CVV - <b>3 цифры</b> на обратной стороне карты",
parse_mode="HTML"
)
else:
await callback_query.message.answer(
f"<b>Статус платежа: {payment.status}</b>\n\n"
"Попробуйте оплатить ещё раз или обратитесь в поддержку.\n\n"
"<i>Для теста используйте карту:</i>\n"
"<code>5555 5555 5555 4477</code>\n"
"Срок: <b>12/30</b>, CVV: <b>123</b>",
parse_mode="HTML"
)
except Exception as e:
print(f"Ошибка проверки статуса: {e}")
await callback_query.message.answer(
"<b>Не удалось проверить статус платежа</b>\n\n"
"Попробуйте позже или обратитесь к администратору.",
parse_mode="HTML"
)
@router.message(Command("mypayments"))
async def cmd_my_payments(message: Message):
user_id = message.from_user.id
async with AsyncSessionLocal() as session:
try:
result = await session.execute(
select(PaymentModel).filter_by(user_id=user_id).order_by(PaymentModel.created_at.desc()).limit(10)
)
payments = result.scalars().all()
if not payments:
await message.answer(
"<b>У вас пока нет платежей</b>\n\n"
"Используйте команду /buy чтобы оформить подписку.",
parse_mode="HTML"
)
return
response = ["<b>Ваши последние платежи:</b>\n"]
for i, payment in enumerate(payments, 1):
status_text = "Успешно" if payment.status == "succeeded" else "Ожидание" if payment.status == "pending" else "Ошибка"
response.append(
f"\n<b>{i}. {payment.amount} руб. ({status_text})</b>\n"
f"Статус: {payment.status}\n"
f"Дата: {payment.created_at.strftime('%d.%m.%Y %H:%M')}\n"
f"ID: <code>{payment.payment_id[:8]}...</code>"
)
response.append("\n\n<i>Полный доступ открывается после успешной оплаты</i>")
await message.answer(
"\n".join(response),
parse_mode="HTML"
)
except Exception as e:
print(f"Ошибка получения платежей: {e}")
@router.message(Command("testcards"))
async def cmd_testcards(message: Message):
testcards_text = (
f"<b>Тестовые банковские карты для оплаты</b>\n\n"
f"<b>Для тестирования оплаты используйте:</b>\n\n"
f"<b>Карта для успешной оплаты:</b>\n"
f"• Номер: <code>5555 5555 5555 4477</code>\n"
f"• Срок действия: <b>ЛЮБОЙ будущий</b> (например: 12/30)\n"
f"• CVV код: <b>ЛЮБЫЕ 3 цифры</b> (например: 123)\n"
f"• Результат: Оплата пройдёт успешно\n\n"
f"<b>Карта для отказа в оплате:</b>\n"
f"• Номер: <code>5555 5555 5555 4445</code>\n"
f"• Срок действия: <b>ЛЮБОЙ будущий</b>\n"
f"• CVV код: <b>ЛЮБЫЕ 3 цифры</b>\n"
f"• Результат: Оплата будет отклонена\n\n"
f"<b>Важно:</b>\n"
f"• Это тестовые карты, реальные деньги не списываются\n"
f"• Используются только для проверки работы оплаты\n"
f"• После успешной тестовой оплаты premium активируется\n\n"
f"Для оплаты подписки используйте команду /buy"
)
await message.answer(testcards_text, parse_mode="HTML")