""" Subscription and User models for the monetization system """ from pydantic import BaseModel, EmailStr, Field from typing import Optional, List, Dict, Any from datetime import datetime from enum import Enum class PlanType(str, Enum): FREE = "free" STARTER = "starter" PRO = "pro" BUSINESS = "business" ENTERPRISE = "enterprise" class SubscriptionStatus(str, Enum): ACTIVE = "active" CANCELED = "canceled" PAST_DUE = "past_due" TRIALING = "trialing" PAUSED = "paused" import os # Plan definitions with limits # NOTE: Stripe price IDs should be set via environment variables in production # Create products and prices in Stripe Dashboard: https://dashboard.stripe.com/products PLANS = { PlanType.FREE: { "name": "Free", "price_monthly": 0, "price_yearly": 0, "docs_per_month": 3, "max_pages_per_doc": 10, "max_file_size_mb": 5, "providers": ["ollama"], # Only self-hosted "features": [ "3 documents per day", "Up to 10 pages per document", "Ollama (self-hosted) only", "Basic support via community", ], "api_access": False, "priority_processing": False, "stripe_price_id_monthly": None, "stripe_price_id_yearly": None, }, PlanType.STARTER: { "name": "Starter", "price_monthly": 12, # Updated pricing "price_yearly": 120, # 2 months free "docs_per_month": 50, "max_pages_per_doc": 50, "max_file_size_mb": 25, "providers": ["ollama", "google", "libre"], "features": [ "50 documents per month", "Up to 50 pages per document", "Google Translate included", "LibreTranslate included", "Email support", ], "api_access": False, "priority_processing": False, "stripe_price_id_monthly": os.getenv("STRIPE_PRICE_STARTER_MONTHLY", ""), "stripe_price_id_yearly": os.getenv("STRIPE_PRICE_STARTER_YEARLY", ""), }, PlanType.PRO: { "name": "Pro", "price_monthly": 39, # Updated pricing "price_yearly": 390, # 2 months free "docs_per_month": 200, "max_pages_per_doc": 200, "max_file_size_mb": 100, "providers": ["ollama", "google", "deepl", "openai", "libre", "openrouter"], "features": [ "200 documents per month", "Up to 200 pages per document", "All translation providers", "DeepL & OpenAI included", "API access (1000 calls/month)", "Priority email support", ], "api_access": True, "api_calls_per_month": 1000, "priority_processing": True, "stripe_price_id_monthly": os.getenv("STRIPE_PRICE_PRO_MONTHLY", ""), "stripe_price_id_yearly": os.getenv("STRIPE_PRICE_PRO_YEARLY", ""), }, PlanType.BUSINESS: { "name": "Business", "price_monthly": 99, # Updated pricing "price_yearly": 990, # 2 months free "docs_per_month": 1000, "max_pages_per_doc": 500, "max_file_size_mb": 250, "providers": ["ollama", "google", "deepl", "openai", "libre", "openrouter", "azure"], "features": [ "1000 documents per month", "Up to 500 pages per document", "All translation providers", "Azure Translator included", "Unlimited API access", "Priority processing queue", "Dedicated support", "Team management (up to 5 users)", ], "api_access": True, "api_calls_per_month": -1, # Unlimited "priority_processing": True, "team_seats": 5, "stripe_price_id_monthly": os.getenv("STRIPE_PRICE_BUSINESS_MONTHLY", ""), "stripe_price_id_yearly": os.getenv("STRIPE_PRICE_BUSINESS_YEARLY", ""), }, PlanType.ENTERPRISE: { "name": "Enterprise", "price_monthly": -1, # Custom "price_yearly": -1, "docs_per_month": -1, # Unlimited "max_pages_per_doc": -1, "max_file_size_mb": -1, "providers": ["ollama", "google", "deepl", "openai", "libre", "openrouter", "azure", "custom"], "features": [ "Unlimited documents", "Unlimited pages", "Custom integrations", "On-premise deployment", "SLA guarantee", "24/7 dedicated support", "Custom AI models", "White-label option", ], "api_access": True, "api_calls_per_month": -1, "priority_processing": True, "team_seats": -1, # Unlimited "stripe_price_id_monthly": None, # Contact sales "stripe_price_id_yearly": None, }, } class User(BaseModel): id: str email: EmailStr name: str password_hash: str created_at: datetime = Field(default_factory=datetime.utcnow) updated_at: datetime = Field(default_factory=datetime.utcnow) email_verified: bool = False avatar_url: Optional[str] = None # Subscription info plan: PlanType = PlanType.FREE subscription_status: SubscriptionStatus = SubscriptionStatus.ACTIVE stripe_customer_id: Optional[str] = None stripe_subscription_id: Optional[str] = None subscription_ends_at: Optional[datetime] = None # Usage tracking docs_translated_this_month: int = 0 pages_translated_this_month: int = 0 api_calls_this_month: int = 0 usage_reset_date: datetime = Field(default_factory=datetime.utcnow) # Extra credits (purchased separately) extra_credits: int = 0 # Each credit = 1 page # Settings default_source_lang: str = "auto" default_target_lang: str = "en" default_provider: str = "google" # Ollama self-hosted config ollama_endpoint: Optional[str] = None ollama_model: Optional[str] = None class UserCreate(BaseModel): email: EmailStr name: str password: str class UserLogin(BaseModel): email: EmailStr password: str class UserResponse(BaseModel): id: str email: EmailStr name: str avatar_url: Optional[str] = None plan: PlanType subscription_status: SubscriptionStatus docs_translated_this_month: int pages_translated_this_month: int api_calls_this_month: int extra_credits: int created_at: datetime # Plan limits for display plan_limits: Dict[str, Any] = {} class Subscription(BaseModel): id: str user_id: str plan: PlanType status: SubscriptionStatus stripe_subscription_id: Optional[str] = None stripe_customer_id: Optional[str] = None current_period_start: datetime current_period_end: datetime cancel_at_period_end: bool = False created_at: datetime = Field(default_factory=datetime.utcnow) class UsageRecord(BaseModel): id: str user_id: str document_name: str document_type: str # excel, word, pptx pages_count: int source_lang: str target_lang: str provider: str processing_time_seconds: float credits_used: int created_at: datetime = Field(default_factory=datetime.utcnow) class CreditPurchase(BaseModel): """For buying extra credits (pay-per-use)""" id: str user_id: str credits_amount: int price_paid: float # in cents stripe_payment_id: str created_at: datetime = Field(default_factory=datetime.utcnow) # Credit packages for purchase CREDIT_PACKAGES = [ {"credits": 50, "price": 5.00, "price_per_credit": 0.10, "stripe_price_id": "price_credits_50"}, {"credits": 100, "price": 9.00, "price_per_credit": 0.09, "stripe_price_id": "price_credits_100", "popular": True}, {"credits": 250, "price": 20.00, "price_per_credit": 0.08, "stripe_price_id": "price_credits_250"}, {"credits": 500, "price": 35.00, "price_per_credit": 0.07, "stripe_price_id": "price_credits_500"}, {"credits": 1000, "price": 60.00, "price_per_credit": 0.06, "stripe_price_id": "price_credits_1000"}, ]