diff --git a/app/webapi/routes/miniapp.py b/app/webapi/routes/miniapp.py index f4937461..0ae6c531 100644 --- a/app/webapi/routes/miniapp.py +++ b/app/webapi/routes/miniapp.py @@ -6,7 +6,7 @@ import math from decimal import Decimal, InvalidOperation, ROUND_HALF_UP, ROUND_FLOOR from datetime import datetime, timedelta, timezone from uuid import uuid4 -from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple, Union +from typing import Any, Awaitable, Callable, Collection, Dict, List, Optional, Tuple, Union from aiogram import Bot from fastapi import APIRouter, Depends, HTTPException, status @@ -4108,7 +4108,11 @@ async def _authorize_miniapp_user( return user -def _ensure_paid_subscription(user: User) -> Subscription: +def _ensure_paid_subscription( + user: User, + *, + allowed_statuses: Optional[Collection[str]] = None, +) -> Subscription: subscription = getattr(user, "subscription", None) if not subscription: raise HTTPException( @@ -4116,7 +4120,9 @@ def _ensure_paid_subscription(user: User) -> Subscription: detail={"code": "subscription_not_found", "message": "Subscription not found"}, ) - if getattr(subscription, "is_trial", False): + normalized_allowed_statuses = set(allowed_statuses or {"active"}) + + if getattr(subscription, "is_trial", False) and "trial" not in normalized_allowed_statuses: raise HTTPException( status.HTTP_403_FORBIDDEN, detail={ @@ -4125,7 +4131,28 @@ def _ensure_paid_subscription(user: User) -> Subscription: }, ) - if not getattr(subscription, "is_active", False): + actual_status = getattr(subscription, "actual_status", None) or "" + + if actual_status not in normalized_allowed_statuses: + if actual_status == "trial": + detail = { + "code": "paid_subscription_required", + "message": "This action is available only for paid subscriptions", + } + elif actual_status == "disabled": + detail = { + "code": "subscription_disabled", + "message": "Subscription is disabled", + } + else: + detail = { + "code": "subscription_inactive", + "message": "Subscription must be active to manage settings", + } + + raise HTTPException(status.HTTP_403_FORBIDDEN, detail=detail) + + if not getattr(subscription, "is_active", False) and "expired" not in normalized_allowed_statuses: raise HTTPException( status.HTTP_403_FORBIDDEN, detail={ @@ -4398,7 +4425,10 @@ async def get_subscription_renewal_options_endpoint( db: AsyncSession = Depends(get_db_session), ) -> MiniAppSubscriptionRenewalOptionsResponse: user = await _authorize_miniapp_user(payload.init_data, db) - subscription = _ensure_paid_subscription(user) + subscription = _ensure_paid_subscription( + user, + allowed_statuses={"active", "trial", "expired"}, + ) _validate_subscription_id(payload.subscription_id, subscription) periods, pricing_map, default_period_id = await _prepare_subscription_renewal_options( @@ -4477,7 +4507,10 @@ async def submit_subscription_renewal_endpoint( db: AsyncSession = Depends(get_db_session), ) -> MiniAppSubscriptionRenewalResponse: user = await _authorize_miniapp_user(payload.init_data, db) - subscription = _ensure_paid_subscription(user) + subscription = _ensure_paid_subscription( + user, + allowed_statuses={"active", "trial", "expired"}, + ) _validate_subscription_id(payload.subscription_id, subscription) period_days: Optional[int] = None