feat: интеграция платежей через Telegram Stars API
- обавлена интеграция с эндпоинтом /create-invoice-link для создания инвойсов
- бновлен paymentService для работы с новым API платежей
- обавлено получение информации о пользователе через /users/{user_id}
- бновлен интерфейс профиля для отображения актуальных данных после оплаты
This commit is contained in:
parent
c3e6a6b05d
commit
92424f6902
@ -38,9 +38,20 @@ const Profile: React.FC = () => {
|
||||
const pack = tokenPacks.find(p => p.id === packId);
|
||||
if (!pack) return;
|
||||
|
||||
paymentService.showBuyTokensPopup(pack, () => {
|
||||
// Обновляем данные после успешной оплаты
|
||||
paymentService.showBuyTokensPopup(pack, (userData) => {
|
||||
if (userData) {
|
||||
// Обновляем данные на основе полученной информации
|
||||
if (userData.stickers_count !== undefined) {
|
||||
setStickersCount(userData.stickers_count);
|
||||
}
|
||||
if (userData.packs_count !== undefined) {
|
||||
setPacksCount(userData.packs_count);
|
||||
}
|
||||
// Можно также обновить другие данные, если они есть в ответе
|
||||
} else {
|
||||
// Если данные не получены, просто перезагружаем страницу
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -63,26 +63,11 @@ export const trackButtonClick = (buttonName: string): void => {
|
||||
* @param prompt - Промпт для генерации
|
||||
*/
|
||||
export const trackStickerGeneration = (prompt: string): void => {
|
||||
// Создаем объект события с дополнительными данными
|
||||
const eventData: {
|
||||
event: string;
|
||||
category: string;
|
||||
value_num: number;
|
||||
prompt?: string;
|
||||
} = {
|
||||
event: 'Генерация стикера',
|
||||
category: 'generation',
|
||||
value_num: 1
|
||||
};
|
||||
|
||||
// Добавляем промпт в данные события, если он предоставлен
|
||||
if (prompt) {
|
||||
eventData.prompt = prompt;
|
||||
}
|
||||
|
||||
// Отправляем событие напрямую, чтобы включить дополнительные данные
|
||||
if (typeof window !== 'undefined' && window.graspil) {
|
||||
window.graspil.push(eventData);
|
||||
window.graspil.push({
|
||||
event: prompt, // Сам промпт как название события
|
||||
category: 'generation' // Категория для фильтрации
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -103,3 +88,16 @@ export const trackStickerPackCreation = (packName: string): void => {
|
||||
export const trackTokenPurchase = (amount: number, price: number, currency: string): void => {
|
||||
trackEvent('Покупка токенов', 'purchase', price, currency);
|
||||
};
|
||||
|
||||
/**
|
||||
* Отслеживает неудавшуюся генерацию стикера из-за непрошедшего фильтр промпта
|
||||
* @param originalPrompt - Оригинальный промпт пользователя
|
||||
*/
|
||||
export const trackRejectedPrompt = (originalPrompt: string): void => {
|
||||
if (typeof window !== 'undefined' && window.graspil) {
|
||||
window.graspil.push({
|
||||
event: `Не принятый промпт: ${originalPrompt}`, // Оригинальный промпт как название события
|
||||
category: 'rejected_generation' // Категория для фильтрации
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@ import { baseWorkflow } from '../constants/baseWorkflow';
|
||||
import { prompts } from '../assets/prompts';
|
||||
import translateService from './translateService';
|
||||
import { getCurrentUserId, isTelegramWebAppAvailable } from '../constants/user';
|
||||
import { trackStickerGeneration } from './analyticsService';
|
||||
import { trackStickerGeneration, trackRejectedPrompt } from './analyticsService';
|
||||
|
||||
const API_BASE_URL = 'https://stickerserver.gymnasticstuff.uk';
|
||||
|
||||
@ -50,32 +50,62 @@ class GenerationError extends Error {
|
||||
}
|
||||
|
||||
// Временное решение для работы с балансом токенов
|
||||
// В будущем будет заменено на API-запросы
|
||||
// Используется только если не удалось получить баланс с сервера
|
||||
let mockBalance = 50; // Начальное значение из MOCK_USER
|
||||
|
||||
const apiService = {
|
||||
// Метод для списания токенов
|
||||
async deductTokens(userId: number, amount: number): Promise<boolean> {
|
||||
// Метод для получения информации о пользователе
|
||||
async getUserInfo(userId: number = getCurrentUserId()): Promise<any> {
|
||||
try {
|
||||
// В будущем здесь будет API-запрос для списания токенов
|
||||
// Пока реализуем локальное списание
|
||||
mockBalance -= amount;
|
||||
return true;
|
||||
const response = await fetch(`${API_BASE_URL}/users/${userId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch user info');
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error deducting tokens:', error);
|
||||
return false;
|
||||
console.error('Error fetching user info:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Метод для создания ссылки на инвойс
|
||||
async createInvoiceLink(userId: number, starsAmount: number, tokens: number): Promise<string> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/create-invoice-link?user_id=${userId}&stars_amount=${starsAmount}&tokens=${tokens}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to create invoice link');
|
||||
}
|
||||
|
||||
// Предполагаем, что бэкенд возвращает строку с URL инвойса
|
||||
return await response.text();
|
||||
} catch (error) {
|
||||
console.error('Error creating invoice link:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Метод для получения текущего баланса
|
||||
async getBalance(userId: number): Promise<number> {
|
||||
async getBalance(userId: number = getCurrentUserId()): Promise<number> {
|
||||
try {
|
||||
// В будущем здесь будет API-запрос для получения баланса
|
||||
// Пока возвращаем локальное значение
|
||||
return mockBalance;
|
||||
const userInfo = await this.getUserInfo(userId);
|
||||
return userInfo.balance || 0;
|
||||
} catch (error) {
|
||||
console.error('Error getting balance:', error);
|
||||
throw error;
|
||||
// В случае ошибки возвращаем mockBalance для обратной совместимости
|
||||
return mockBalance;
|
||||
}
|
||||
},
|
||||
|
||||
@ -218,6 +248,9 @@ const apiService = {
|
||||
console.error('Не удалось перевести промпт:', translationResult.text);
|
||||
translationFailed = true;
|
||||
|
||||
// Отслеживаем событие неудавшейся генерации
|
||||
trackRejectedPrompt(userPrompt);
|
||||
|
||||
// Не продолжаем генерацию, возвращаем ошибку с сообщением
|
||||
return {
|
||||
translationFailed: true,
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { TokenPack } from '../constants/tokenPacks';
|
||||
import apiService from '../services/api';
|
||||
import { getCurrentUserId } from '../constants/user';
|
||||
|
||||
export const paymentService = {
|
||||
showBuyTokensPopup: (pack: TokenPack, onSuccess?: () => void) => {
|
||||
showBuyTokensPopup: async (pack: TokenPack, onSuccess?: (userData?: any) => void) => {
|
||||
// Проверяем наличие Telegram WebApp
|
||||
if (!window.Telegram?.WebApp) {
|
||||
console.error('Telegram WebApp не доступен');
|
||||
@ -9,6 +11,7 @@ export const paymentService = {
|
||||
}
|
||||
|
||||
const webApp = window.Telegram.WebApp;
|
||||
const userId = getCurrentUserId();
|
||||
|
||||
// Открываем окно оплаты Telegram
|
||||
webApp.showPopup({
|
||||
@ -25,14 +28,40 @@ export const paymentService = {
|
||||
text: 'Отмена'
|
||||
}
|
||||
]
|
||||
}, (buttonId: string) => {
|
||||
}, async (buttonId: string) => {
|
||||
if (buttonId === 'buy') {
|
||||
try {
|
||||
// Получаем ссылку на инвойс от бэкенда
|
||||
const invoiceLink = await apiService.createInvoiceLink(
|
||||
userId,
|
||||
pack.price,
|
||||
pack.tokens + pack.bonusTokens
|
||||
);
|
||||
|
||||
// Открываем встроенный платеж Telegram
|
||||
webApp.openInvoice(`sticker_tokens_${pack.id}`, (status: 'paid' | 'cancelled' | 'failed' | 'pending') => {
|
||||
if (status === 'paid' && onSuccess) {
|
||||
webApp.openInvoice(invoiceLink, async (status: 'paid' | 'cancelled' | 'failed' | 'pending') => {
|
||||
if (status === 'paid') {
|
||||
try {
|
||||
// Получаем обновленную информацию о пользователе
|
||||
const userData = await apiService.getUserInfo(userId);
|
||||
|
||||
if (onSuccess) {
|
||||
onSuccess(userData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка при получении данных пользователя:', error);
|
||||
|
||||
// Даже если не удалось получить данные, вызываем onSuccess
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Ошибка при создании инвойса:', error);
|
||||
webApp.showAlert('Произошла ошибка при создании платежа. Пожалуйста, попробуйте позже.');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user