From 806b668a6deb4adade075b4a1c74f58dd7c0077e Mon Sep 17 00:00:00 2001 From: Umbrix Dev Date: Mon, 9 Feb 2026 06:38:35 +0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=20=D0=91=D0=BB=D0=BE=D0=BA=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20trial=20=D0=B1=D0=B5=D0=B7=20T?= =?UTF-8?q?elegram=20ID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ Что сделано: - app/page.tsx: Добавлена проверка tgUser?.id перед созданием trial - app/plans/page.tsx: Добавлена валидация user?.id в createUser() - app/api/create-user/route.ts: Серверная валидация telegramId (400 если отсутствует) - types/telegram.ts: Расширен TelegramWebApp интерфейс (MainButton, BackButton, HapticFeedback) - types/telegram.d.ts: Глобальная декларация Window.Telegram - hooks/useTelegramWebApp.ts: Упрощено - использует (window as any) ❌ ПРОБЛЕМА (user_... имена): Имена вида user_1770605873419 появляются когда Telegram WebApp не передает данные пользователя. Теперь система блокирует создание trial/подписки если нет tgUser.id ✅ РЕШЕНИЕ: Приложение должно быть открыто ВНУТРИ Telegram Mini App, а не в браузере! Если пользователь видит user_..., значит открыто не через бота. --- app/api/create-user/route.ts | 12 +++ app/page.tsx | 21 ++++- app/plans/page.tsx | 11 ++- hooks/useTelegramWebApp.ts | 143 +---------------------------------- types/telegram.d.ts | 16 ++++ types/telegram.ts | 33 ++++++++ 6 files changed, 92 insertions(+), 144 deletions(-) create mode 100644 types/telegram.d.ts diff --git a/app/api/create-user/route.ts b/app/api/create-user/route.ts index 0dcf8e7..085a63c 100644 --- a/app/api/create-user/route.ts +++ b/app/api/create-user/route.ts @@ -43,6 +43,18 @@ export async function POST(request: NextRequest) { referrerId }); + // ⚠️ ВАЛИДАЦИЯ: telegramId обязателен! + if (!telegramId) { + logger.error('❌ VALIDATION FAILED: No telegramId provided'); + return NextResponse.json( + { + success: false, + error: 'Telegram ID is required. Please open the app through Telegram bot.' + }, + { status: 400 } + ); + } + // 1. Получаем токен админа const tokenResponse = await fetch(`${MARZBAN_API}/api/admin/token`, { method: 'POST', diff --git a/app/page.tsx b/app/page.tsx index a6d73c5..d34b9a9 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -202,13 +202,32 @@ export default function Home() { // Получаем реальные данные Telegram const tgUser = telegramWebApp?.initDataUnsafe?.user; + // 🔍 DEBUG: Проверяем данные Telegram при TRIAL подписке + console.log('🎁 TRIAL ACTIVATION - Telegram data:', { + hasWebApp: !!telegramWebApp, + hasInitData: !!telegramWebApp?.initDataUnsafe, + hasUser: !!tgUser, + userId: tgUser?.id, + username: tgUser?.username, + firstName: tgUser?.first_name, + fullData: tgUser + }); + + // ⚠️ ВАЖНО: Если Telegram не передал данные - НЕ создаем trial! + if (!tgUser?.id) { + console.error('❌ TRIAL BLOCKED: No Telegram user data!'); + showToastNotification('❌ Откройте приложение через Telegram бота'); + setIsLoading(false); + return; + } + // Создаем trial подписку через API const response = await fetch('/api/create-user', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ planType: 'trial', - telegramId: tgUser?.id || Date.now(), + telegramId: tgUser.id, // Теперь 100% есть telegramUsername: tgUser?.username || undefined, firstName: tgUser?.first_name || undefined, referrerId: referrerId || undefined, diff --git a/app/plans/page.tsx b/app/plans/page.tsx index 24566f1..d1e0fa3 100644 --- a/app/plans/page.tsx +++ b/app/plans/page.tsx @@ -187,12 +187,21 @@ export default function PlansNew() { last_name: user.last_name, } : null }); + + // ⚠️ ВАЖНО: Если Telegram не передал user.id - блокируем создание + if (!user?.id) { + console.error('❌ USER CREATION BLOCKED: No Telegram user ID!'); + alert('❌ Откройте приложение через Telegram бота\n\nПриложение должно быть открыто в Telegram Mini App, а не в браузере.'); + setStep('plan'); + setIsCreatingUser(false); + return; + } const requestBody = { planType, period, locationIds, - telegramId: user?.id || null, + telegramId: user.id, // Теперь 100% есть telegramUsername: user?.username || null, firstName: user?.first_name || null, lastName: user?.last_name || null, diff --git a/hooks/useTelegramWebApp.ts b/hooks/useTelegramWebApp.ts index 4b008ea..6999a67 100644 --- a/hooks/useTelegramWebApp.ts +++ b/hooks/useTelegramWebApp.ts @@ -3,147 +3,6 @@ import { useEffect, useState } from 'react'; -declare global { - interface Window { - Telegram?: { - WebApp: { - ready: () => void; - expand: () => void; - close: () => void; - isExpanded: boolean; - viewportHeight: number; - viewportStableHeight: number; - headerColor: string; - backgroundColor: string; - isClosingConfirmationEnabled: boolean; - platform: string; - version: string; - - MainButton: { - text: string; - color: string; - textColor: string; - isVisible: boolean; - isActive: boolean; - isProgressVisible: boolean; - setText: (text: string) => void; - onClick: (callback: () => void) => void; - offClick: (callback: () => void) => void; - show: () => void; - hide: () => void; - enable: () => void; - disable: () => void; - showProgress: (leaveActive?: boolean) => void; - hideProgress: () => void; - setParams: (params: { text?: string; color?: string; text_color?: string; is_active?: boolean; is_visible?: boolean }) => void; - }; - - BackButton: { - isVisible: boolean; - onClick: (callback: () => void) => void; - offClick: (callback: () => void) => void; - show: () => void; - hide: () => void; - }; - - HapticFeedback: { - impactOccurred: (style: 'light' | 'medium' | 'heavy' | 'rigid' | 'soft') => void; - notificationOccurred: (type: 'error' | 'success' | 'warning') => void; - selectionChanged: () => void; - }; - - setHeaderColor: (color: string) => void; - setBackgroundColor: (color: string) => void; - enableClosingConfirmation: () => void; - disableClosingConfirmation: () => void; - - showPopup: (params: { - title?: string; - message: string; - buttons: Array<{ id?: string; type: string; text?: string }>; - }, callback?: (buttonId: string) => void) => void; - - showAlert: (message: string, callback?: () => void) => void; - showConfirm: (message: string, callback?: (confirmed: boolean) => void) => void; - - openLink: (url: string, options?: { try_instant_view?: boolean }) => void; - openTelegramLink: (url: string) => void; - openInvoice: (url: string, callback?: (status: string) => void) => void; - - shareToStory: (media_url: string, params?: { - text?: string; - widget_link?: { - url: string; - name?: string; - }; - }) => void; - - switchInlineQuery: (query: string, choose_chat_types?: string[]) => void; - - sendData: (data: string) => void; - - initData: string; - initDataUnsafe: { - query_id?: string; - user?: { - id: number; - first_name: string; - last_name?: string; - username?: string; - language_code?: string; - is_premium?: boolean; - photo_url?: string; - }; - receiver?: { - id: number; - first_name: string; - last_name?: string; - username?: string; - language_code?: string; - is_premium?: boolean; - }; - chat?: { - id: number; - type: string; - title: string; - username?: string; - photo_url?: string; - }; - chat_type?: string; - chat_instance?: string; - start_param?: string; - can_send_after?: number; - auth_date: number; - hash: string; - }; - - themeParams: { - bg_color?: string; - text_color?: string; - hint_color?: string; - link_color?: string; - button_color?: string; - button_text_color?: string; - secondary_bg_color?: string; - header_bg_color?: string; - accent_text_color?: string; - section_bg_color?: string; - section_header_text_color?: string; - subtitle_text_color?: string; - destructive_text_color?: string; - }; - - colorScheme: 'light' | 'dark'; - - isVersionAtLeast: (version: string) => boolean; - - onEvent: (eventType: string, eventHandler: () => void) => void; - offEvent: (eventType: string, eventHandler: () => void) => void; - }; - }; - } -} - export function useTelegramWebApp() { const [webApp, setWebApp] = useState(null); const [isReady, setIsReady] = useState(false); @@ -152,7 +11,7 @@ export function useTelegramWebApp() { // Проверяем доступность Telegram WebApp только на клиенте if (typeof window === 'undefined') return; - const tg = window.Telegram?.WebApp; + const tg = (window as any).Telegram?.WebApp; if (!tg) { console.warn('[TelegramWebApp] Not running in Telegram'); setIsReady(true); // Всё равно помечаем как ready для dev diff --git a/types/telegram.d.ts b/types/telegram.d.ts new file mode 100644 index 0000000..9c2a9fc --- /dev/null +++ b/types/telegram.d.ts @@ -0,0 +1,16 @@ +/** + * Global Telegram WebApp type declarations + * Расширяет Window interface для доступа к Telegram WebApp API + */ + +import { TelegramWebApp } from './telegram'; + +declare global { + interface Window { + Telegram?: { + WebApp: TelegramWebApp; + }; + } +} + +export {}; diff --git a/types/telegram.ts b/types/telegram.ts index e2f6123..de61208 100644 --- a/types/telegram.ts +++ b/types/telegram.ts @@ -46,6 +46,39 @@ export interface TelegramWebApp { showPopup(params: any, callback?: () => void): void; onEvent(eventType: string, callback: () => void): void; offEvent(eventType: string, callback: () => void): void; + + // Additional methods used in useTelegramWebApp + setHeaderColor(color: string): void; + setBackgroundColor(color: string): void; + enableClosingConfirmation(): void; + disableClosingConfirmation(): void; + + MainButton: { + text: string; + isVisible: boolean; + isActive: boolean; + setText(text: string): void; + onClick(callback: () => void): void; + offClick(callback: () => void): void; + show(): void; + hide(): void; + enable(): void; + disable(): void; + }; + + BackButton: { + isVisible: boolean; + onClick(callback: () => void): void; + offClick(callback: () => void): void; + show(): void; + hide(): void; + }; + + HapticFeedback: { + impactOccurred(style: 'light' | 'medium' | 'heavy' | 'rigid' | 'soft'): void; + notificationOccurred(type: 'error' | 'success' | 'warning'): void; + selectionChanged(): void; + }; } export interface TelegramUserData {