// User types export interface User { id: number; telegram_id: number | null; // Nullable для email-only пользователей username: string | null; first_name: string | null; last_name: string | null; email: string | null; email_verified: boolean; balance_kopeks: number; balance_rubles: number; referral_code: string | null; language: string; created_at: string; auth_type: 'telegram' | 'email' | 'google' | 'yandex' | 'discord' | 'vk'; // Тип аутентификации } // OAuth types export interface OAuthProvider { name: string; display_name: string; } // Campaign bonus info (returned during auth) export interface CampaignBonusInfo { campaign_name: string; bonus_type: 'balance' | 'subscription' | 'tariff' | 'none'; balance_kopeks: number; subscription_days: number | null; tariff_name: string | null; } // Auth types export interface AuthResponse { access_token: string; refresh_token: string; token_type: string; expires_in: number; user: User; campaign_bonus?: CampaignBonusInfo | null; } export interface TokenResponse { access_token: string; refresh_token: string; token_type: string; expires_in: number; } export interface RegisterResponse { message: string; email: string; requires_verification: boolean; } // Subscription types export interface ServerInfo { uuid: string; name: string; country_code: string | null; } export interface TrafficPurchase { id: number; traffic_gb: number; expires_at: string; created_at: string; days_remaining: number; progress_percent: number; } export interface Subscription { id: number; status: string; is_trial: boolean; start_date: string; end_date: string; days_left: number; hours_left: number; minutes_left: number; time_left_display: string; traffic_limit_gb: number; traffic_used_gb: number; traffic_used_percent: number; device_limit: number; connected_squads: string[]; servers: ServerInfo[]; autopay_enabled: boolean; autopay_days_before: number; subscription_url: string | null; hide_subscription_link: boolean; is_active: boolean; is_expired: boolean; is_limited: boolean; traffic_purchases?: TrafficPurchase[]; // Daily tariff fields is_daily?: boolean; is_daily_paused?: boolean; daily_price_kopeks?: number; next_daily_charge_at?: string; // ISO datetime string // Tariff info tariff_id?: number; tariff_name?: string; traffic_reset_mode?: string; } // Response wrapper for subscription status endpoint export interface SubscriptionStatusResponse { has_subscription: boolean; subscription: Subscription | null; } // Multi-tariff subscription list item (from GET /cabinet/subscriptions) export interface SubscriptionListItem { id: number; status: string; tariff_id: number | null; tariff_name: string | null; traffic_limit_gb: number; traffic_used_gb: number; device_limit: number; end_date: string | null; subscription_url: string | null; subscription_crypto_link: string | null; is_trial: boolean; is_daily?: boolean; is_daily_paused?: boolean; autopay_enabled: boolean; connected_squads: string[] | null; } // Response from GET /cabinet/subscriptions (multi-tariff) export interface SubscriptionsListResponse { subscriptions: SubscriptionListItem[]; multi_tariff_enabled: boolean; } // Device types export interface Device { hwid: string; platform: string; device_model: string; created_at: string | null; /** * User-set local alias persisted in the bot DB (`user_device_aliases`). * `null` when the user hasn't renamed the device — clients fall back * to `device_model` / `platform` for display. */ local_name?: string | null; } export interface DevicesResponse { devices: Device[]; total: number; device_limit: number; } // Tariff switch preview export interface TariffSwitchPreview { can_switch: boolean; current_tariff_id: number | null; current_tariff_name: string | null; new_tariff_id: number; new_tariff_name: string; remaining_days: number; upgrade_cost_kopeks: number; upgrade_cost_label: string; balance_kopeks: number; balance_label: string; has_enough_balance: boolean; missing_amount_kopeks: number; missing_amount_label: string; is_upgrade: boolean; // Discount fields (from promo group) base_upgrade_cost_kopeks?: number; discount_percent?: number; discount_kopeks?: number; } export interface RenewalOption { period_days: number; price_kopeks: number; price_rubles: number; discount_percent: number; original_price_kopeks: number | null; } export interface TrafficPackage { gb: number; price_kopeks: number; price_rubles: number; is_unlimited: boolean; // Discount fields (from promo group) base_price_kopeks?: number; discount_percent?: number; discount_kopeks?: number; } export interface TrialInfo { is_available: boolean; duration_days: number; traffic_limit_gb: number; device_limit: number; requires_payment: boolean; price_kopeks: number; price_rubles: number; reason_unavailable: string | null; } // Purchase options types export interface TrafficOption { value: number; label: string; price_kopeks: number; price_label: string; original_price_kopeks?: number; original_price_label?: string; discount_percent?: number; is_available: boolean; is_default?: boolean; } export interface ServerOption { uuid: string; name: string; price_kopeks: number; price_label: string; original_price_kopeks?: number; original_price_label?: string; discount_percent?: number; is_available: boolean; } export interface DevicesConfig { min: number; max: number; default: number; current: number; price_per_device_kopeks: number; price_per_device_label: string; price_per_device_original_kopeks?: number; discount_percent?: number; } export interface TrafficConfig { selectable: boolean; mode: string; options: TrafficOption[]; default?: number; current?: number; } export interface ServersConfig { options: ServerOption[]; min: number; max: number; default: string[]; selected: string[]; } export interface PeriodOption { id: string; period_days: number; months: number; label: string; price_kopeks: number; price_label: string; per_month_price_kopeks: number; per_month_price_label: string; discount_percent?: number; original_price_kopeks?: number; original_price_label?: string; is_available: boolean; traffic: TrafficConfig; servers: ServersConfig; devices: DevicesConfig; } // Tariff types for tariffs mode export interface TariffPeriod { days: number; months: number; label: string; price_kopeks: number; price_label: string; price_per_month_kopeks: number; price_per_month_label: string; // Discount info (if promo group discount applied) original_price_kopeks?: number; original_price_label?: string; original_per_month_kopeks?: number; original_per_month_label?: string; discount_percent?: number; discount_amount_kopeks?: number; discount_label?: string; // Extra devices info (additional devices beyond tariff base) extra_devices_count?: number; extra_devices_cost_kopeks?: number; extra_devices_cost_label?: string; base_tariff_price_kopeks?: number; base_tariff_price_label?: string; } export interface TariffServer { uuid: string; name: string; } export interface Tariff { id: number; name: string; description: string | null; tier_level: number; traffic_limit_gb: number; traffic_limit_label: string; is_unlimited_traffic: boolean; device_limit: number; base_device_limit?: number; extra_devices_count: number; servers_count: number; servers: TariffServer[]; periods: TariffPeriod[]; is_current: boolean; is_available: boolean; // Custom days options custom_days_enabled?: boolean; price_per_day_kopeks?: number; min_days?: number; max_days?: number; // Custom traffic options custom_traffic_enabled?: boolean; traffic_price_per_gb_kopeks?: number; min_traffic_gb?: number; max_traffic_gb?: number; // Device price device_price_kopeks?: number; // Traffic topup options traffic_topup_enabled?: boolean; traffic_topup_packages?: number[]; max_topup_traffic_gb?: number; // Daily tariff options is_daily?: boolean; daily_price_kopeks?: number; // Promo group discount info promo_group_name?: string; original_device_price_kopeks?: number; device_discount_percent?: number; original_daily_price_kopeks?: number; daily_discount_percent?: number; original_price_per_day_kopeks?: number; custom_days_discount_percent?: number; // Traffic reset traffic_reset_mode?: string; // Multi-tariff: already purchased by user is_purchased?: boolean; } export interface TariffsPurchaseOptions { sales_mode: 'tariffs'; tariffs: Tariff[]; current_tariff_id: number | null; balance_kopeks: number; balance_label: string; // New fields for expired subscription handling subscription_status?: string; subscription_is_expired?: boolean; has_subscription?: boolean; // Multi-tariff: all available tariffs already purchased all_tariffs_purchased?: boolean; } export interface ClassicPurchaseOptions { sales_mode: 'classic'; currency: string; balance_kopeks: number; balance_label: string; subscription_id: number | null; periods: PeriodOption[]; traffic: TrafficConfig; servers: ServersConfig; devices: DevicesConfig; selection: { period_id: string; period_days: number; traffic_value: number; servers: string[]; devices: number; }; } export type PurchaseOptions = TariffsPurchaseOptions | ClassicPurchaseOptions; export interface PurchaseSelection { period_id?: string; period_days?: number; traffic_value?: number; servers?: string[]; devices?: number; } export interface PurchasePreview { total_price_kopeks: number; total_price_label: string; original_price_kopeks?: number; original_price_label?: string; discount_percent?: number; discount_label?: string; per_month_price_kopeks: number; per_month_price_label: string; breakdown: { label: string; value: string }[]; balance_kopeks: number; balance_label: string; missing_amount_kopeks: number; missing_amount_label?: string; can_purchase: boolean; status_message?: string; } // Balance types export interface Balance { balance_kopeks: number; balance_rubles: number; } export interface Transaction { id: number; type: string; amount_kopeks: number; amount_rubles: number; description: string | null; payment_method: string | null; is_completed: boolean; created_at: string; completed_at: string | null; } export interface PaymentMethodOption { id: string; name: string; description?: string | null; } export interface PaymentMethod { id: string; name: string; description: string | null; min_amount_kopeks: number; max_amount_kopeks: number; is_available: boolean; options?: PaymentMethodOption[] | null; // Если true — после получения payment_url кабинет сразу делает // window.location.href вместо показа панели с кнопкой "Открыть". open_url_direct?: boolean; } // Referral types export interface ReferralInfo { referral_code: string; referral_link: string; bot_referral_link?: string; total_referrals: number; active_referrals: number; total_earnings_kopeks: number; total_earnings_rubles: number; commission_percent: number; available_balance_kopeks: number; available_balance_rubles: number; withdrawn_kopeks: number; } export interface ReferralTerms { is_enabled: boolean; commission_percent: number; minimum_topup_kopeks: number; minimum_topup_rubles: number; first_topup_bonus_kopeks: number; first_topup_bonus_rubles: number; inviter_bonus_kopeks: number; inviter_bonus_rubles: number; max_commission_payments: number; partner_section_visible?: boolean; } // Ticket types export interface TicketMediaItem { type: 'photo' | 'video' | 'document'; file_id: string; caption?: string | null; } export interface TicketMessage { id: number; message_text: string; is_from_admin: boolean; has_media: boolean; media_type: string | null; media_file_id: string | null; media_caption: string | null; media_items?: TicketMediaItem[] | null; created_at: string; } export interface Ticket { id: number; title: string; status: string; priority: string; created_at: string; updated_at: string; closed_at: string | null; messages_count: number; last_message: TicketMessage | null; } export interface TicketDetail extends Omit { is_reply_blocked: boolean; messages: TicketMessage[]; } export interface SupportConfig { tickets_enabled: boolean; support_type: 'tickets' | 'profile' | 'url' | 'both'; support_url?: string | null; support_username?: string | null; } // Paginated response export interface PaginatedResponse { items: T[]; total: number; page: number; per_page: number; pages: number; } // App config types (for connection setup) export interface LocalizedText { [key: string]: string; } // RemnaWave format types export interface RemnawaveButtonClient { url?: string; link?: string; text: LocalizedText; type?: 'external' | 'subscriptionLink' | 'copyButton'; svgIconKey?: string; resolvedUrl?: string; } export interface RemnawaveBlockClient { title: LocalizedText; description: LocalizedText; buttons?: RemnawaveButtonClient[]; svgIconKey?: string; svgIconColor?: string; } export interface RemnawaveAppClient { name: string; featured?: boolean; deepLink?: string | null; svgIconKey?: string; blocks: RemnawaveBlockClient[]; } export interface RemnawavePlatformData { svgIconKey?: string; displayName?: LocalizedText; apps: RemnawaveAppClient[]; } export interface AppConfig { platformNames: Record; hasSubscription: boolean; subscriptionUrl: string | null; hideLink?: boolean; branding?: { name?: string; logoUrl?: string; supportUrl?: string; }; // RemnaWave isRemnawave?: boolean; svgLibrary?: Record; baseTranslations?: Record; baseSettings?: { isShowTutorialButton: boolean; tutorialUrl: string }; uiConfig?: { installationGuidesBlockType?: 'cards' | 'timeline' | 'accordion' | 'minimal'; }; platforms: Record; } // Pending payment types export interface PendingPayment { id: number; method: string; method_display: string; identifier: string; amount_kopeks: number; amount_rubles: number; status: string; status_emoji: string; status_text: string; is_paid: boolean; is_checkable: boolean; created_at: string; expires_at: string | null; payment_url: string | null; user_id?: number; user_telegram_id?: number; user_username?: string | null; user_email?: string | null; } export interface ManualCheckResponse { success: boolean; message: string; payment: PendingPayment | null; status_changed: boolean; old_status: string | null; new_status: string | null; } // Saved payment method (card) for recurrent payments export interface SavedCard { id: number; method_type: string; card_last4: string | null; card_type: string | null; title: string | null; created_at: string; } export interface SavedCardsResponse { cards: SavedCard[]; recurrent_enabled: boolean; } // Ticket notifications types export interface TicketNotification { id: number; ticket_id: number; notification_type: 'new_ticket' | 'admin_reply' | 'user_reply'; message: string | null; is_read: boolean; created_at: string; read_at: string | null; } export interface TicketNotificationList { items: TicketNotification[]; unread_count: number; } export interface UnreadCountResponse { unread_count: number; } export interface TicketSettings { sla_enabled: boolean; sla_minutes: number; sla_check_interval_seconds: number; sla_reminder_cooldown_minutes: number; support_system_mode: string; cabinet_user_notifications_enabled: boolean; cabinet_admin_notifications_enabled: boolean; } // Payment method config types (admin) export interface PaymentMethodSubOptionInfo { id: string; name: string; } export interface PaymentMethodConfig { method_id: string; sort_order: number; is_enabled: boolean; display_name: string | null; default_display_name: string; sub_options: Record | null; available_sub_options: PaymentMethodSubOptionInfo[] | null; min_amount_kopeks: number | null; max_amount_kopeks: number | null; default_min_amount_kopeks: number; default_max_amount_kopeks: number; user_type_filter: 'all' | 'telegram' | 'email'; first_topup_filter: 'any' | 'yes' | 'no'; promo_group_filter_mode: 'all' | 'selected'; allowed_promo_group_ids: number[]; open_url_direct: boolean; is_provider_configured: boolean; created_at: string | null; updated_at: string | null; } export interface PromoGroupSimple { id: number; name: string; } // Account Linking export interface LinkedProvider { provider: string; linked: boolean; identifier: string | null; } export interface LinkedProvidersResponse { providers: LinkedProvider[]; } export interface LinkCallbackResponse { success: boolean; message: string | null; merge_required: boolean; merge_token: string | null; } export interface ServerCompleteResponse extends LinkCallbackResponse { provider: string; } // Account Merge export interface MergeSubscriptionPreview { status: string; is_trial: boolean; end_date: string | null; traffic_limit_gb: number; traffic_used_gb: number; device_limit: number; tariff_name: string | null; autopay_enabled: boolean; } export interface MergeAccountPreview { id: number; username: string | null; first_name: string | null; email: string | null; auth_methods: string[]; balance_kopeks: number; subscription: MergeSubscriptionPreview | null; created_at: string | null; } export interface MergePreviewResponse { primary: MergeAccountPreview; secondary: MergeAccountPreview; expires_in_seconds: number; } export interface MergeResponse { success: boolean; access_token: string | null; refresh_token: string | null; user: User | null; }