AI_Virtual_Wardrobe / app /api /v1 /subscription.py
SuZeAI
feat: update subscription response to track AI credits instead of tokens
437ffc0
Raw
History Blame Contribute Delete
3.06 kB
from fastapi import APIRouter, Depends
from sqlalchemy import func
from sqlalchemy.orm import Session
from datetime import datetime, timedelta, timezone
from app.core.database import get_db
from app.api.deps import get_current_user
from app.models.user import User
from app.models.wardrobe import WardrobeItem
from app.models.chat import ChatMessage
from app.models.tryon import TryOnTask
from app.schemas.subscription import SubscriptionResponse
router = APIRouter()
# Free-tier caps advertised on the landing page's pricing table.
FREE_WARDROBE_ITEM_LIMIT = 30
FREE_AI_RECOMMENDATIONS_PER_WEEK = 5
# AI Stylist usage is metered like an LLM API quota, expressed in "credits"
# (1 credit = 1,000 tokens) over a rolling 30-day window.
TOKENS_PER_CREDIT = 1_000
FREE_AI_CREDIT_LIMIT_PER_30D = 500
def _build_response(db: Session, user: User) -> SubscriptionResponse:
is_premium = user.plan == "premium"
wardrobe_used = (
db.query(WardrobeItem).filter(WardrobeItem.user_id == user.id).count()
)
# "AI outfits" = virtual try-on generations (each one produces one AI-styled outfit).
week_ago = datetime.now(timezone.utc) - timedelta(days=7)
ai_used = (
db.query(TryOnTask)
.filter(
TryOnTask.user_id == user.id,
TryOnTask.created_at >= week_ago,
)
.count()
)
month_ago = datetime.now(timezone.utc) - timedelta(days=30)
tokens_used = (
db.query(func.coalesce(func.sum(ChatMessage.tokens_used), 0))
.filter(
ChatMessage.user_id == user.id,
ChatMessage.role == "ai",
ChatMessage.created_at >= month_ago,
)
.scalar()
)
# Round up: any partial usage still consumes a whole credit, like real LLM billing.
credits_used = -(-int(tokens_used or 0) // TOKENS_PER_CREDIT)
return SubscriptionResponse(
plan=user.plan,
renews_at=user.premium_renews_at,
wardrobe_items_used=wardrobe_used,
wardrobe_items_limit=None if is_premium else FREE_WARDROBE_ITEM_LIMIT,
ai_recommendations_used=ai_used,
ai_recommendations_limit=None if is_premium else FREE_AI_RECOMMENDATIONS_PER_WEEK,
ai_credits_used=credits_used,
ai_credits_limit=None if is_premium else FREE_AI_CREDIT_LIMIT_PER_30D,
)
@router.get("/me", response_model=SubscriptionResponse)
def get_my_subscription(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
return _build_response(db, current_user)
# There is no self-serve payment flow yet, so upgrading to Premium is not exposed here —
# only accounts granted Premium manually (direct DB update) can downgrade themselves back.
@router.post("/cancel", response_model=SubscriptionResponse)
def cancel_my_subscription(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
current_user.plan = "free"
current_user.premium_renews_at = None
db.commit()
db.refresh(current_user)
return _build_response(db, current_user)