✅ Что сделано: - 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_..., значит открыто не через бота.
133 lines
3.3 KiB
TypeScript
133 lines
3.3 KiB
TypeScript
// hooks/useTelegramWebApp.ts
|
||
// Hook для интеграции с Telegram WebApp API
|
||
|
||
import { useEffect, useState } from 'react';
|
||
|
||
export function useTelegramWebApp() {
|
||
const [webApp, setWebApp] = useState<any>(null);
|
||
const [isReady, setIsReady] = useState(false);
|
||
|
||
useEffect(() => {
|
||
// Проверяем доступность Telegram WebApp только на клиенте
|
||
if (typeof window === 'undefined') return;
|
||
|
||
const tg = (window as any).Telegram?.WebApp;
|
||
if (!tg) {
|
||
console.warn('[TelegramWebApp] Not running in Telegram');
|
||
setIsReady(true); // Всё равно помечаем как ready для dev
|
||
return;
|
||
}
|
||
|
||
console.log('[TelegramWebApp] Initializing...', {
|
||
platform: tg.platform,
|
||
version: tg.version,
|
||
colorScheme: tg.colorScheme,
|
||
user: tg.initDataUnsafe.user,
|
||
});
|
||
|
||
// Инициализация Telegram WebApp
|
||
tg.ready();
|
||
tg.expand();
|
||
|
||
// Настройка темы
|
||
tg.setHeaderColor('bg_color');
|
||
tg.setBackgroundColor('#18222d');
|
||
|
||
// Отключить подтверждение закрытия (можно включить при необходимости)
|
||
tg.disableClosingConfirmation();
|
||
|
||
setWebApp(tg);
|
||
setIsReady(true);
|
||
|
||
// Cleanup
|
||
return () => {
|
||
tg.MainButton.hide();
|
||
tg.BackButton.hide();
|
||
};
|
||
}, []);
|
||
|
||
return {
|
||
webApp,
|
||
isReady,
|
||
isTelegram: !!webApp,
|
||
user: webApp?.initDataUnsafe.user,
|
||
platform: webApp?.platform,
|
||
colorScheme: webApp?.colorScheme,
|
||
themeParams: webApp?.themeParams,
|
||
};
|
||
}
|
||
|
||
// Helper функции для удобства
|
||
|
||
export function useTelegramMainButton(
|
||
text: string,
|
||
onClick: () => void,
|
||
options?: {
|
||
color?: string;
|
||
textColor?: string;
|
||
enabled?: boolean;
|
||
}
|
||
) {
|
||
const { webApp } = useTelegramWebApp();
|
||
|
||
useEffect(() => {
|
||
if (!webApp) return;
|
||
|
||
webApp.MainButton.setText(text);
|
||
webApp.MainButton.onClick(onClick);
|
||
|
||
if (options?.color) {
|
||
webApp.MainButton.color = options.color;
|
||
}
|
||
if (options?.textColor) {
|
||
webApp.MainButton.textColor = options.textColor;
|
||
}
|
||
if (options?.enabled !== undefined) {
|
||
if (options.enabled) {
|
||
webApp.MainButton.enable();
|
||
} else {
|
||
webApp.MainButton.disable();
|
||
}
|
||
}
|
||
|
||
webApp.MainButton.show();
|
||
|
||
return () => {
|
||
webApp.MainButton.offClick(onClick);
|
||
webApp.MainButton.hide();
|
||
};
|
||
}, [webApp, text, onClick, options]);
|
||
}
|
||
|
||
export function useTelegramBackButton(onClick: () => void) {
|
||
const { webApp } = useTelegramWebApp();
|
||
|
||
useEffect(() => {
|
||
if (!webApp) return;
|
||
|
||
webApp.BackButton.onClick(onClick);
|
||
webApp.BackButton.show();
|
||
|
||
return () => {
|
||
webApp.BackButton.offClick(onClick);
|
||
webApp.BackButton.hide();
|
||
};
|
||
}, [webApp, onClick]);
|
||
}
|
||
|
||
export function useTelegramHaptic() {
|
||
const { webApp } = useTelegramWebApp();
|
||
|
||
return {
|
||
impact: (style: 'light' | 'medium' | 'heavy' | 'rigid' | 'soft' = 'medium') => {
|
||
webApp?.HapticFeedback.impactOccurred(style);
|
||
},
|
||
notification: (type: 'error' | 'success' | 'warning') => {
|
||
webApp?.HapticFeedback.notificationOccurred(type);
|
||
},
|
||
selection: () => {
|
||
webApp?.HapticFeedback.selectionChanged();
|
||
},
|
||
};
|
||
}
|