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.
This commit is contained in:
parent
e6c823570c
commit
2de018b8d4
@ -95,7 +95,7 @@ err-limit-exceeded = 🚫 Слишком много запросов! Подож
|
||||
|
||||
# Сообщения о доступе
|
||||
msg-subscribe =
|
||||
👑 Pro доступ:
|
||||
👑 Pro доступ
|
||||
• Разблокирует неограниченное количество заказов
|
||||
msg-subscribe-success = ✅ Платеж успешно обработан!
|
||||
msg-subscribe-error = ❌ Произошла ошибка при обработке платежа
|
||||
|
||||
@ -18,7 +18,12 @@ export async function subscription(conversation: Conversation<Context, Context>,
|
||||
|
||||
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<Context, Context>,
|
||||
|
||||
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<Context, Context>,
|
||||
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<Context, Context>,
|
||||
|
||||
// --- 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)})`,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ const composer = new Composer<Context>();
|
||||
|
||||
// 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');
|
||||
});
|
||||
|
||||
// успешная оплата
|
||||
|
||||
@ -3,7 +3,7 @@ import { I18n } from '@grammyjs/i18n';
|
||||
import path from 'node:path';
|
||||
|
||||
export const i18n = new I18n<Context>({
|
||||
defaultLocale: 'en',
|
||||
defaultLocale: 'ru',
|
||||
directory: path.resolve(process.cwd(), 'locales'),
|
||||
fluentBundleOptions: {
|
||||
useIsolating: false,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user