Notify users after Tribute payment
This commit is contained in:
@@ -1,19 +1,20 @@
|
||||
import logging
|
||||
from aiogram import Router, F, types, Bot
|
||||
from aiogram.filters import Command
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, LabeledPrice
|
||||
from typing import Optional, Dict, Any, Union
|
||||
from datetime import datetime, timezone
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from config.settings import Settings
|
||||
from db.dal import payment_dal
|
||||
from db.dal import payment_dal, user_dal
|
||||
from bot.keyboards.inline.user_keyboards import (
|
||||
get_subscription_options_keyboard, get_confirm_subscription_keyboard,
|
||||
get_subscription_options_keyboard, get_payment_method_keyboard,
|
||||
get_payment_url_keyboard, get_back_to_main_menu_markup)
|
||||
from bot.services.payment_service import YooKassaService
|
||||
from bot.services.subscription_service import SubscriptionService
|
||||
from bot.services.panel_api_service import PanelApiService
|
||||
from bot.services.referral_service import ReferralService
|
||||
from bot.middlewares.i18n import JsonI18n
|
||||
|
||||
router = Router(name="user_subscription_router")
|
||||
@@ -99,28 +100,101 @@ async def select_subscription_period_callback_handler(
|
||||
return
|
||||
|
||||
currency_symbol_val = settings.DEFAULT_CURRENCY_SYMBOL
|
||||
confirmation_text_content = get_text("confirm_subscription_prompt",
|
||||
months=months,
|
||||
price=f"{price_rub:.2f}",
|
||||
currency_symbol=currency_symbol_val)
|
||||
reply_markup = get_confirm_subscription_keyboard(months, price_rub,
|
||||
currency_symbol_val,
|
||||
current_lang, i18n)
|
||||
text_content = get_text("choose_payment_method")
|
||||
tribute_url = settings.tribute_payment_links.get(months)
|
||||
stars_price = settings.stars_subscription_options.get(months)
|
||||
reply_markup = get_payment_method_keyboard(
|
||||
months,
|
||||
price_rub,
|
||||
tribute_url,
|
||||
stars_price,
|
||||
currency_symbol_val,
|
||||
current_lang,
|
||||
i18n,
|
||||
)
|
||||
|
||||
try:
|
||||
await callback.message.edit_text(confirmation_text_content,
|
||||
await callback.message.edit_text(text_content,
|
||||
reply_markup=reply_markup)
|
||||
except Exception as e_edit:
|
||||
logging.warning(
|
||||
f"Edit message for subscription confirmation failed: {e_edit}. Sending new one."
|
||||
f"Edit message for payment method selection failed: {e_edit}. Sending new one."
|
||||
)
|
||||
await callback.message.answer(confirmation_text_content,
|
||||
await callback.message.answer(text_content,
|
||||
reply_markup=reply_markup)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("confirm_sub:"))
|
||||
async def confirm_subscription_callback_handler(
|
||||
@router.callback_query(F.data.startswith("pay_stars:"))
|
||||
async def pay_stars_callback_handler(
|
||||
callback: types.CallbackQuery, settings: Settings, i18n_data: dict,
|
||||
session: AsyncSession, bot: Bot):
|
||||
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
||||
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
|
||||
|
||||
get_text = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) if i18n else key
|
||||
|
||||
if not i18n or not callback.message:
|
||||
await callback.answer(get_text("error_occurred_try_again"), show_alert=True)
|
||||
return
|
||||
|
||||
try:
|
||||
_, data_payload = callback.data.split(":", 1)
|
||||
months_str, price_str = data_payload.split(":")
|
||||
months = int(months_str)
|
||||
stars_price = int(price_str)
|
||||
except (ValueError, IndexError):
|
||||
logging.error(f"Invalid pay_stars data in callback: {callback.data}")
|
||||
await callback.answer(get_text("error_try_again"), show_alert=True)
|
||||
return
|
||||
|
||||
user_id = callback.from_user.id
|
||||
payment_description = get_text("payment_description_subscription", months=months)
|
||||
|
||||
payment_record_data = {
|
||||
"user_id": user_id,
|
||||
"amount": float(stars_price),
|
||||
"currency": "XTR",
|
||||
"status": "pending_stars",
|
||||
"description": payment_description,
|
||||
"subscription_duration_months": months,
|
||||
"provider": "telegram_stars",
|
||||
}
|
||||
|
||||
try:
|
||||
db_payment_record = await payment_dal.create_payment_record(session, payment_record_data)
|
||||
await session.commit()
|
||||
except Exception as e_db_payment:
|
||||
await session.rollback()
|
||||
logging.error(f"Failed to create stars payment record: {e_db_payment}", exc_info=True)
|
||||
await callback.message.edit_text(get_text("error_creating_payment_record"))
|
||||
await callback.answer(get_text("error_try_again"), show_alert=True)
|
||||
return
|
||||
|
||||
payload = f"{db_payment_record.payment_id}:{months}"
|
||||
prices = [LabeledPrice(label=payment_description, amount=stars_price)]
|
||||
|
||||
try:
|
||||
await bot.send_invoice(
|
||||
chat_id=user_id,
|
||||
title=payment_description,
|
||||
description=payment_description,
|
||||
payload=payload,
|
||||
provider_token="",
|
||||
currency="XTR",
|
||||
prices=prices,
|
||||
)
|
||||
except Exception as e_inv:
|
||||
logging.error(f"Failed to send Telegram Stars invoice: {e_inv}", exc_info=True)
|
||||
await callback.message.edit_text(get_text("error_payment_gateway"))
|
||||
await callback.answer(get_text("error_try_again"), show_alert=True)
|
||||
return
|
||||
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("pay_yk:"))
|
||||
async def pay_yk_callback_handler(
|
||||
callback: types.CallbackQuery, settings: Settings, i18n_data: dict,
|
||||
yookassa_service: YooKassaService, session: AsyncSession):
|
||||
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
||||
@@ -151,7 +225,7 @@ async def confirm_subscription_callback_handler(
|
||||
price_rub = float(price_str)
|
||||
except (ValueError, IndexError):
|
||||
logging.error(
|
||||
f"Invalid confirmation data in callback: {callback.data}")
|
||||
f"Invalid pay_yk data in callback: {callback.data}")
|
||||
await callback.answer(get_text("error_try_again"), show_alert=True)
|
||||
return
|
||||
|
||||
@@ -342,6 +416,89 @@ async def my_subscription_command_handler(
|
||||
await target.answer(text, reply_markup=markup, parse_mode="HTML", disable_web_page_preview=True)
|
||||
|
||||
|
||||
@router.pre_checkout_query()
|
||||
async def stars_pre_checkout_handler(pre_checkout_query: types.PreCheckoutQuery):
|
||||
await pre_checkout_query.answer(ok=True)
|
||||
|
||||
|
||||
@router.message(F.successful_payment)
|
||||
async def stars_successful_payment_handler(
|
||||
message: types.Message, settings: Settings, i18n_data: dict,
|
||||
session: AsyncSession, bot: Bot, panel_service: PanelApiService,
|
||||
subscription_service: SubscriptionService, referral_service: ReferralService):
|
||||
sp = message.successful_payment
|
||||
if not sp or sp.currency != "XTR":
|
||||
return
|
||||
|
||||
payload = sp.invoice_payload or ""
|
||||
try:
|
||||
payment_id_str, months_str = payload.split(":")
|
||||
payment_db_id = int(payment_id_str)
|
||||
months = int(months_str)
|
||||
except (ValueError, IndexError):
|
||||
logging.error(f"Invalid invoice payload for stars payment: {payload}")
|
||||
return
|
||||
|
||||
provider_payment_id = sp.provider_payment_charge_id
|
||||
stars_amount = sp.total_amount
|
||||
try:
|
||||
await payment_dal.update_provider_payment_and_status(
|
||||
session, payment_db_id, provider_payment_id, "succeeded")
|
||||
await session.commit()
|
||||
except Exception as e_upd:
|
||||
await session.rollback()
|
||||
logging.error(f"Failed to update stars payment record {payment_db_id}: {e_upd}", exc_info=True)
|
||||
return
|
||||
|
||||
activation_details = await subscription_service.activate_subscription(
|
||||
session,
|
||||
message.from_user.id,
|
||||
months,
|
||||
float(stars_amount),
|
||||
payment_db_id,
|
||||
provider="telegram_stars",
|
||||
)
|
||||
|
||||
if not activation_details or not activation_details.get("end_date"):
|
||||
logging.error(f"Failed to activate subscription after stars payment for user {message.from_user.id}")
|
||||
return
|
||||
|
||||
referral_bonus_info = await referral_service.apply_referral_bonuses_for_payment(
|
||||
session, message.from_user.id, months)
|
||||
await session.commit()
|
||||
|
||||
applied_referee_days = referral_bonus_info.get("referee_bonus_applied_days") if referral_bonus_info else None
|
||||
final_end = referral_bonus_info.get("referee_new_end_date") if referral_bonus_info else activation_details["end_date"]
|
||||
|
||||
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
||||
i18n: JsonI18n = i18n_data.get("i18n_instance")
|
||||
_ = lambda k, **kw: i18n.gettext(current_lang, k, **kw) if i18n else k
|
||||
|
||||
if applied_referee_days:
|
||||
inviter_name_display = _("friend_placeholder")
|
||||
db_user = await user_dal.get_user_by_id(session, message.from_user.id)
|
||||
if db_user and db_user.referred_by_id:
|
||||
inviter = await user_dal.get_user_by_id(session, db_user.referred_by_id)
|
||||
if inviter and inviter.first_name:
|
||||
inviter_name_display = inviter.first_name
|
||||
elif inviter and inviter.username:
|
||||
inviter_name_display = f"@{inviter.username}"
|
||||
success_msg = _("payment_successful_with_referral_bonus",
|
||||
months=months,
|
||||
base_end_date=activation_details["end_date"].strftime('%Y-%m-%d'),
|
||||
bonus_days=applied_referee_days,
|
||||
final_end_date=final_end.strftime('%Y-%m-%d'),
|
||||
inviter_name=inviter_name_display)
|
||||
else:
|
||||
success_msg = _("payment_successful", months=months,
|
||||
end_date=final_end.strftime('%Y-%m-%d'))
|
||||
|
||||
try:
|
||||
await bot.send_message(message.from_user.id, success_msg)
|
||||
except Exception as e_send:
|
||||
logging.error(f"Failed to send stars payment success message: {e_send}")
|
||||
|
||||
|
||||
@router.message(Command("connect"))
|
||||
async def connect_command_handler(message: types.Message, i18n_data: dict,
|
||||
settings: Settings,
|
||||
|
||||
Reference in New Issue
Block a user