From 2de018b8d4fceedcdc3737f40925dfe06b94314d Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Wed, 17 Sep 2025 13:22:02 +0300 Subject: [PATCH] feat(subscriptions): enhance subscription flow and localization updates - Updated default locale to Russian for improved user experience. - Refactored subscription messages to include expiration dates and active subscription status. - Enhanced keyboard display for subscription options with clear expiration information. - Improved handling of subscription-related queries and responses for better clarity. --- apps/bot/locales/ru.ftl | 2 +- .../bot/src/bot/conversations/subscription.ts | 74 +++++++++++++++---- apps/bot/src/bot/features/subscription.ts | 5 +- apps/bot/src/bot/i18n.ts | 2 +- 4 files changed, 64 insertions(+), 19 deletions(-) diff --git a/apps/bot/locales/ru.ftl b/apps/bot/locales/ru.ftl index b28e5f8..4182aca 100644 --- a/apps/bot/locales/ru.ftl +++ b/apps/bot/locales/ru.ftl @@ -95,7 +95,7 @@ err-limit-exceeded = 🚫 Слишком много запросов! Подож # Сообщения о доступе msg-subscribe = - 👑 Pro доступ: + 👑 Pro доступ • Разблокирует неограниченное количество заказов msg-subscribe-success = ✅ Платеж успешно обработан! msg-subscribe-error = ❌ Произошла ошибка при обработке платежа diff --git a/apps/bot/src/bot/conversations/subscription.ts b/apps/bot/src/bot/conversations/subscription.ts index 5ae2f6b..e55c209 100644 --- a/apps/bot/src/bot/conversations/subscription.ts +++ b/apps/bot/src/bot/conversations/subscription.ts @@ -18,7 +18,12 @@ export async function subscription(conversation: Conversation, const subscriptionsService = new SubscriptionsService({ telegramId }); - const { remainingDays, usedTrialSubscription } = await subscriptionsService.getSubscription({ + const { + hasActiveSubscription, + remainingDays, + subscription: currentSubscription, + usedTrialSubscription, + } = await subscriptionsService.getSubscription({ telegramId, }); @@ -35,19 +40,28 @@ export async function subscription(conversation: Conversation, const prices = sift(subscriptionPrices); - // строим клавиатуру - const keyboard = buildPricesKeyboard(prices); + // строим клавиатуру с указанием даты окончания после покупки + const keyboard = buildPricesKeyboard(prices, currentSubscription?.expiresAt); // сообщение с выбором плана const messageWithPrices = await ctx.reply( combine( - await conversation.external(({ t }) => - combine( - t('msg-subscribe'), - remainingDays ? t('msg-subscription-active-days', { days: remainingDays }) : '', - fmt`${i}${t('msg-cancel-operation')}${i}`.text, - ), - ), + await conversation.external(({ t }) => { + let statusLine = t('msg-subscribe'); + if (hasActiveSubscription && currentSubscription?.expiresAt) { + statusLine = t('msg-subscription-active-until', { + date: new Date(currentSubscription.expiresAt).toLocaleDateString('ru-RU', { + day: '2-digit', + month: '2-digit', + year: 'numeric', + }), + }); + } else if (remainingDays) { + statusLine = t('msg-subscription-active-days', { days: remainingDays }); + } + + return combine(statusLine, fmt`${i}${t('msg-cancel-operation')}${i}`.text); + }), ), { reply_markup: keyboard }, ); @@ -68,16 +82,28 @@ export async function subscription(conversation: Conversation, const selectedPrice = prices.find((price) => price?.period === selectedPeriod); if (!selectedPrice) return replyError(ctx, conversation); - // создаём invoice + // создаём invoice (с указанием даты, до которой будет доступ) + const baseDate = currentSubscription?.expiresAt + ? new Date(Math.max(Date.now(), new Date(currentSubscription.expiresAt).getTime())) + : new Date(); + const targetDate = addDays(baseDate, selectedPrice.days ?? 0); + const targetDateRu = targetDate.toLocaleDateString('ru-RU', { + day: '2-digit', + month: '2-digit', + year: 'numeric', + }); return ctx.replyWithInvoice( 'Оплата Pro доступа', - selectedPrice.description || '', + combine( + `${selectedPrice.description || 'Pro доступ'} — до ${targetDateRu}`, + '(Автопродление отключено)', + ), JSON.stringify({ period: selectedPrice.period }), 'RUB', [ { amount: selectedPrice.amount * 100, // Telegram ждёт в копейках - label: selectedPrice.description || 'К оплате', + label: `${selectedPrice.description || 'К оплате'} — до ${targetDateRu}`, }, ], { @@ -89,13 +115,31 @@ export async function subscription(conversation: Conversation, // --- helpers --- -function buildPricesKeyboard(prices: GQL.SubscriptionPriceFieldsFragment[]) { +function addDays(date: Date, days: number) { + const d = new Date(date); + d.setDate(d.getDate() + days); + return d; +} + +function buildPricesKeyboard( + prices: GQL.SubscriptionPriceFieldsFragment[], + currentExpiresAt?: string, +) { const keyboard = new InlineKeyboard(); + const baseTime = currentExpiresAt + ? Math.max(Date.now(), new Date(currentExpiresAt).getTime()) + : Date.now(); for (const price of prices) { + const targetDate = addDays(new Date(baseTime), price.days ?? 0); + const targetDateRu = targetDate.toLocaleDateString('ru-RU', { + day: '2-digit', + month: '2-digit', + year: 'numeric', + }); keyboard.row({ callback_data: price.period, pay: true, - text: `${price.description} (${formatMoney(price.amount)})`, + text: `Продлить до ${targetDateRu} (${formatMoney(price.amount)})`, }); } diff --git a/apps/bot/src/bot/features/subscription.ts b/apps/bot/src/bot/features/subscription.ts index 910d543..36585ee 100644 --- a/apps/bot/src/bot/features/subscription.ts +++ b/apps/bot/src/bot/features/subscription.ts @@ -8,6 +8,7 @@ const composer = new Composer(); // Telegram требует отвечать на pre_checkout_query composer.on('pre_checkout_query', logHandle('pre-checkout-query'), async (ctx) => { + console.log('🚀 ~ ctx:', ctx); await ctx.answerPreCheckoutQuery(true); }); @@ -22,9 +23,9 @@ feature.command('subscribe', logHandle('command-subscribe'), async (ctx) => { const proEnabled = subscriptionSetting?.proEnabled; - if (!proEnabled) return await ctx.reply(ctx.t('msg-subscribe-disabled')); + if (!proEnabled) return ctx.reply(ctx.t('msg-subscribe-disabled')); - await ctx.conversation.enter('subscription'); + return ctx.conversation.enter('subscription'); }); // успешная оплата diff --git a/apps/bot/src/bot/i18n.ts b/apps/bot/src/bot/i18n.ts index d33ca01..5c05c52 100644 --- a/apps/bot/src/bot/i18n.ts +++ b/apps/bot/src/bot/i18n.ts @@ -3,7 +3,7 @@ import { I18n } from '@grammyjs/i18n'; import path from 'node:path'; export const i18n = new I18n({ - defaultLocale: 'en', + defaultLocale: 'ru', directory: path.resolve(process.cwd(), 'locales'), fluentBundleOptions: { useIsolating: false,