feat: implement billing system with Pally integration

- Added billing models and repository for handling bills.
- Introduced BillingService to manage payment initiation and bill creation.
- Updated user service to remove ticket-related functionality.
- Refactored settings to include new billing-related fields.
- Removed ticket-related handlers and schemas.
- Added new billing handlers for processing payments.
- Updated database schema with new bills table and status enum.
- Enhanced utility functions to calculate prices considering discounts.
- Introduced prehandling for non-private messages.
- Updated main application to initialize new billing services and repositories.
This commit is contained in:
2026-04-23 19:55:51 +07:00
parent 8290d18235
commit d98383285e
29 changed files with 705 additions and 305 deletions

View File

@@ -4,18 +4,15 @@ from aiogram.types import Message
from remnawave import RemnawaveSDK
from sqlalchemy.ext.asyncio import AsyncSession
from db.models.tickets import TicketStatus
from db.models.users import User
from misc import utils
from repositories import UserRepository
from repositories.tickets import TicketRepository
from schemas.users import NewUserDTO, TicketCreator
from schemas.users import NewUserDTO
from services.rw import RWUserInfo, get_user_by_telegram_id
from texts import (
NO_SUBSCRIPTION,
NOTHING_PLACEHOLDER,
SUBSCRIPTION_INFORMATION,
TICKET_STATUS_EMOJIS,
)
logger = logging.getLogger(__name__)
@@ -25,24 +22,8 @@ class UserServiceError(Exception): ...
class UserService:
def __init__(
self, user_repository: UserRepository, ticket_repository: TicketRepository
) -> None:
def __init__(self, user_repository: UserRepository) -> None:
self.user_repository = user_repository
self.ticket_repository = ticket_repository
@staticmethod
def format_ticket_name(
creator: TicketCreator,
status: TicketStatus | None = TicketStatus.OPEN,
ticket_id: int | None = None,
):
return (
TICKET_STATUS_EMOJIS[status.value]
+ (f" T-{ticket_id:0>3}" if ticket_id else "")
+ " | "
+ (f"@{creator.username}" if creator.username else str(creator.id))
)
@staticmethod
def format_subscription_message(
@@ -90,7 +71,7 @@ class UserService:
async def update_user_link(
self, session: AsyncSession, user_id: int, rw_sdk: RemnawaveSDK
) -> User:
) -> User | None:
rw_user = await get_user_by_telegram_id(rw_sdk, user_id)
user = await self.user_repository.get_user_by_id(session, user_id)