feat(rw): remnawave integration

This commit is contained in:
2026-08-03 00:01:00 +07:00
parent 97a4e819d6
commit 1e4cb43ac7
10 changed files with 640 additions and 18 deletions

View File

@@ -6,16 +6,21 @@ from sqlalchemy.ext.asyncio import AsyncSession
from db.models import Subscription, SubscriptionAddon, User
from db.models.orders import Order, OrderStatus
from db.models.transactions import BalanceTransaction, BalanceTxType
from external.rw import sync_subscription_by_telegram_id
from schemas.enums import SubscriptionStatus
from schemas.plans import PricingPlans
def calculate_plan_monthly_price(pricing: PricingPlans, devices: int, addon_ids: list[str]) -> float:
def calculate_plan_monthly_price(
pricing: PricingPlans, devices: int, addon_ids: list[str]
) -> float:
addon_prices = {addon.id: addon.price for addon in pricing.addons}
return devices * pricing.device_price + sum(addon_prices[addon_id] for addon_id in addon_ids)
def calculate_order_total(pricing: PricingPlans, devices: int, addon_ids: list[str], duration_days: int) -> float:
def calculate_order_total(
pricing: PricingPlans, devices: int, addon_ids: list[str], duration_days: int
) -> float:
monthly_price = calculate_plan_monthly_price(pricing, devices, addon_ids)
return monthly_price * duration_days / 30
@@ -29,10 +34,10 @@ def get_subscription_addon_ids(subscription: Subscription | None) -> list[str]:
def should_apply_immediately(
*,
subscription: Subscription | None,
order: Order,
pricing: PricingPlans,
now: datetime,
subscription: Subscription | None = None,
) -> bool:
if subscription is None or subscription.expires_at <= now:
return True
@@ -66,7 +71,9 @@ async def replace_subscription_addons(
session.add(SubscriptionAddon(subscription_id=subscription_id, addon_id=addon_id))
async def ensure_subscription(user: User, session: AsyncSession, starts_at: datetime) -> Subscription:
async def ensure_subscription(
user: User, session: AsyncSession, starts_at: datetime
) -> Subscription:
subscription = user.subscription
if subscription is not None:
return subscription
@@ -113,7 +120,7 @@ async def apply_order_now(
order: Order,
pricing: PricingPlans,
now: datetime,
) -> None:
) -> Subscription:
subscription = await ensure_subscription(user, session, now)
addon_ids = [addon.addon_id for addon in order.addons]
current_addons = [] if subscription.devices == 0 else get_subscription_addon_ids(subscription)
@@ -142,11 +149,10 @@ async def apply_order_now(
await replace_subscription_addons(session, subscription.id, addon_ids)
order.applies_at = now
order.applied_at = now
return subscription
async def queue_order_for_later(
*, order: Order, subscription: Subscription, now: datetime
) -> None:
async def queue_order_for_later(*, order: Order, subscription: Subscription, now: datetime) -> None:
order.applies_at = max(subscription.expires_at, now)
@@ -157,6 +163,7 @@ async def sync_user_subscription(
) -> None:
now = datetime.now(UTC)
subscription = user.subscription
applied_due_orders = False
if subscription is not None and subscription.expires_at <= now:
due_orders = [
@@ -181,5 +188,14 @@ async def sync_user_subscription(
[addon.addon_id for addon in order.addons],
)
order.applied_at = now
applied_due_orders = True
else:
subscription.status = SubscriptionStatus.EXPIRED
if applied_due_orders:
await sync_subscription_by_telegram_id(
expires_at=subscription.expires_at,
devices=subscription.devices,
telegram_id=user.telegram_id,
username=user.username,
)