feat: implement subscription management and billing enhancements

This commit is contained in:
2026-04-29 20:10:26 +07:00
parent 120e19e667
commit db2dabed58
12 changed files with 223 additions and 28 deletions

View File

@@ -1,15 +1,12 @@
import datetime
import logging
import math
from aiogram.types import Message, User as TelegramUser
from remnawave import RemnawaveSDK
from sqlalchemy.ext.asyncio import AsyncSession
from bot.texts import (
NO_SUBSCRIPTION,
NOTHING_PLACEHOLDER,
SUBSCRIPTION_INFORMATION,
)
from bot.texts import NO_SUBSCRIPTION, NOTHING_PLACEHOLDER, SUBSCRIPTION_INFORMATION
from db.models.users import User
from misc import utils
from repositories import UserRepository
@@ -33,8 +30,7 @@ class UserService:
@staticmethod
def format_subscription_message(
rw_user: rw.RWUserInfo | None,
link: str | None = None,
rw_user: rw.RWUserInfo | None, link: str | None = None, autorenew: bool = False
):
if rw_user is None:
# Пользователь не найден в Remnawave
@@ -46,6 +42,7 @@ class UserService:
used_traffic = utils.format_traffic(rw_user.used_traffic_bytes, rw_user.traffic_limit_bytes)
hwid_limit = utils.format_hwid_limit(rw_user.hwid_device_limit)
link = link or NOTHING_PLACEHOLDER
autorenew_text = "🟢" if autorenew else "🔴"
return SUBSCRIPTION_INFORMATION.format(
status_icon=status_icon,
@@ -55,6 +52,7 @@ class UserService:
used_traffic=used_traffic,
hwid_limit=hwid_limit,
link=link,
autorenew=autorenew_text,
)
async def add_user(
@@ -147,7 +145,13 @@ class UserService:
duration_days=duration.days,
)
await rw.add_days(rw_sdk, rw_user.uuid, duration.days)
if not utils.is_same_plan(rw_user, subscription):
new_expire = datetime.datetime.now(datetime.UTC) + duration
delta_days = (new_expire - rw_user.expire_at).days
await rw.add_days(rw_sdk, rw_user.uuid, delta_days)
else:
await rw.add_days(rw_sdk, rw_user.uuid, duration.days)
await rw.update_squads(rw_sdk, rw_user.uuid, squads)
await rw.set_hwid_limit(rw_sdk, rw_user.uuid, subscription.devices)
@@ -198,3 +202,25 @@ class UserService:
await self.update_user_link(session, user.id, rw_sdk)
return db_subscription
async def calculate_plan_credit(
self,
session: AsyncSession,
user_id: int,
new_plan: SubscriptionPlan,
rw_sdk: RemnawaveSDK,
) -> int:
rw_user = await rw.get_user_by_telegram_id(rw_sdk, user_id)
if not rw_user or rw_user.expire_at <= datetime.datetime.now(datetime.UTC):
return 0
if utils.is_same_plan(rw_user, new_plan):
return 0
db_sub = await self.subscription_repository.get_subscription_by_user_id(session, user_id)
if not db_sub or not db_sub.plan_price_paid or not db_sub.duration_days:
return 0
remaining_days = max(0, (rw_user.expire_at - datetime.datetime.now(datetime.UTC)).days)
return math.floor(db_sub.plan_price_paid * remaining_days / db_sub.duration_days)