diff --git a/.env.example b/.env.example
index a046fe5..687ea9d 100644
--- a/.env.example
+++ b/.env.example
@@ -24,6 +24,13 @@ DISABLE_WELCOME_MESSAGE= #
# Webhook Base URL (used for Telegram and payment providers)
WEBHOOK_BASE_URL=https://webhooks.yourdomain.tld
+# Payment Method Toggles
+YOOKASSA_ENABLED=True # Turn on YOOKASSA
+FREEKASSA_ENABLED=True # Turn on FreeKassa
+STARS_ENABLED=True # Turn on STARS
+TRIBUTE_ENABLED=True # Turn on TRIBUTE
+CRYPTOPAY_ENABLED=True # Turn on CRYPTOPAY
+
# YooKassa Payment Gateway Configuration
YOOKASSA_SHOP_ID=your_shop_id # Your store ID in YooKassa
YOOKASSA_SECRET_KEY=your_secret_key # Your secret key for YooKassa
@@ -33,11 +40,9 @@ YOOKASSA_VAT_CODE=1 #
YOOKASSA_AUTOPAYMENTS_ENABLED=False # Auto-renew toggle
# FreeKassa Payment Gateway Configuration
-FREEKASSA_ENABLED=True # Turn on FreeKassa
FREEKASSA_MERCHANT_ID=your_shop_id # Your shop ID in FreeKassa
FREEKASSA_API_KEY=your_api_key # API key for REST requests
FREEKASSA_SECOND_SECRET=your_second_secret # Secret word #2 (used to verify notifications)
-FREEKASSA_CURRENCY=RUB # Default currency for orders
FREEKASSA_PAYMENT_IP= # Public IP address reported to FreeKassa
# CryptoBot Payment Gateway Configuration
@@ -51,12 +56,6 @@ TRIBUTE_API_KEY= #
TRIBUTE_SKIP_NOTIFICATIONS=True # Skip renewal notifications for Tribute payments
TRIBUTE_SKIP_CANCELLATION_NOTIFICATIONS=False # Skip cancellation notifications for Tribute payments
-# Payment Method Toggles
-YOOKASSA_ENABLED=True # Turn on YOOKASSA
-STARS_ENABLED=True # Turn on STARS
-TRIBUTE_ENABLED=True # Turn on TRIBUTE
-CRYPTOPAY_ENABLED=True # Turn on CRYPTOPAY
-
# Subscription Options. Specify cost parameters or payment links here.
1_MONTH_ENABLED=True
RUB_PRICE_1_MONTH=150
diff --git a/README.md b/README.md
index 5601547..9785f83 100644
--- a/README.md
+++ b/README.md
@@ -86,7 +86,6 @@
| `FREEKASSA_API_KEY` | API-ключ для запросов к FreeKassa REST API. |
| `FREEKASSA_SECOND_SECRET` | Секретное слово №2 — используется для проверки уведомлений от FreeKassa. |
| `FREEKASSA_PAYMENT_URL` | (Опционально, legacy SCI) Базовый URL платёжной формы FreeKassa. По умолчанию `https://pay.freekassa.ru/`. |
- | `FREEKASSA_CURRENCY` | Код валюты платежа (например, `RUB`). |
| `FREEKASSA_PAYMENT_IP` | Внешний IP вашего сервера, который будет передаваться в запрос оплаты. |
| `STARS_ENABLED` | Включить/выключить Telegram Stars (`true`/`false`). |
| `TRIBUTE_ENABLED`| Включить/выключить Tribute (`true`/`false`). |
diff --git a/bot/handlers/user/payment.py b/bot/handlers/user/payment.py
index e6bc654..3c3ee9e 100644
--- a/bot/handlers/user/payment.py
+++ b/bot/handlers/user/payment.py
@@ -280,7 +280,7 @@ async def process_successful_payment(session: AsyncSession, bot: Bot,
details_message = _("payment_successful_error_details")
details_markup = get_connect_and_main_keyboard(
- user_lang, i18n, settings, config_link
+ user_lang, i18n, settings, config_link, preserve_message=True
)
try:
await bot.send_message(
diff --git a/bot/handlers/user/start.py b/bot/handlers/user/start.py
index 52f902b..e3bc368 100644
--- a/bot/handlers/user/start.py
+++ b/bot/handlers/user/start.py
@@ -427,6 +427,13 @@ async def main_action_callback_handler(
subscription_service,
session,
is_edit=True)
+ elif action == "back_to_main_keep":
+ await send_main_menu(callback,
+ settings,
+ i18n_data,
+ subscription_service,
+ session,
+ is_edit=False)
else:
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
_ = lambda key, **kwargs: i18n.gettext(
diff --git a/bot/handlers/user/subscription/payments.py b/bot/handlers/user/subscription/payments.py
index 0b89eda..ba69f91 100644
--- a/bot/handlers/user/subscription/payments.py
+++ b/bot/handlers/user/subscription/payments.py
@@ -1,6 +1,7 @@
import logging
+from datetime import datetime
from aiogram import Router, F, types
-from aiogram.utils.keyboard import InlineKeyboardBuilder
+from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncSession
@@ -239,7 +240,13 @@ async def pay_yk_callback_handler(callback: types.CallbackQuery, settings: Setti
await callback.message.edit_text(
get_text(key="payment_link_message", months=months),
- reply_markup=get_payment_url_keyboard(payment_response_yk["confirmation_url"], current_lang, i18n),
+ reply_markup=get_payment_url_keyboard(
+ payment_response_yk["confirmation_url"],
+ current_lang,
+ i18n,
+ back_callback=f"subscribe_period:{months}",
+ back_text_key="back_to_payment_methods_button",
+ ),
disable_web_page_preview=False,
)
else:
@@ -340,139 +347,24 @@ async def pay_fk_callback_handler(
pass
return
- method_keyboard = InlineKeyboardBuilder()
- method_keyboard.button(
- text=get_text("freekassa_method_qr"),
- callback_data=f"pay_fk_method:{payment_record.payment_id}:44",
- )
- method_keyboard.button(
- text=get_text("freekassa_method_card"),
- callback_data=f"pay_fk_method:{payment_record.payment_id}:36",
- )
- method_keyboard.button(
- text=get_text("freekassa_method_sberpay"),
- callback_data=f"pay_fk_method:{payment_record.payment_id}:43",
- )
- method_keyboard.button(
- text=get_text("back_to_main_menu_button"),
- callback_data="main_action:subscribe",
- )
- method_keyboard.adjust(1)
-
- try:
- await callback.message.edit_text(
- get_text("freekassa_choose_method"),
- reply_markup=method_keyboard.as_markup(),
- )
- except Exception as e_edit:
- logging.warning(f"FreeKassa: failed to show method selector ({e_edit}), sending new message.")
- try:
- await callback.message.answer(
- get_text("freekassa_choose_method"),
- reply_markup=method_keyboard.as_markup(),
- )
- except Exception:
- pass
- try:
- await callback.answer()
- except Exception:
- pass
-
-
-@router.callback_query(F.data.startswith("pay_fk_method:"))
-async def pay_fk_method_callback_handler(
- callback: types.CallbackQuery,
- settings: Settings,
- i18n_data: dict,
- freekassa_service: FreeKassaService,
- session: AsyncSession,
-):
- current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
- i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
- get_text = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) if i18n else key
-
- if not i18n or not callback.message:
- try:
- await callback.answer(get_text("error_occurred_try_again"), show_alert=True)
- except Exception:
- pass
- return
-
- if not freekassa_service or not freekassa_service.configured:
- try:
- await callback.answer(get_text("payment_service_unavailable_alert"), show_alert=True)
- except Exception:
- pass
- try:
- await callback.message.edit_text(get_text("payment_service_unavailable"))
- except Exception:
- pass
- return
-
- try:
- _, payload = callback.data.split(":", 1)
- payment_id_str, method_code = payload.split(":")
- payment_id = int(payment_id_str)
- except (ValueError, IndexError):
- logging.error(f"FreeKassa: invalid method payload {callback.data}")
- try:
- await callback.answer(get_text("error_try_again"), show_alert=True)
- except Exception:
- pass
- return
-
- try:
- payment_record = await payment_dal.get_payment_by_db_id(session, payment_id)
- except Exception as e_db:
- logging.error(f"FreeKassa: failed to load payment {payment_id}: {e_db}")
- payment_record = None
-
- if not payment_record:
- try:
- await callback.answer(get_text("error_payment_gateway"), show_alert=True)
- except Exception:
- pass
- return
-
- if payment_record.user_id != callback.from_user.id:
- logging.warning(
- f"FreeKassa: user {callback.from_user.id} attempted to access payment {payment_id} owned by {payment_record.user_id}"
- )
- try:
- await callback.answer(get_text("error_payment_gateway"), show_alert=True)
- except Exception:
- pass
- return
-
- months = payment_record.subscription_duration_months or 1
- amount = float(payment_record.amount)
-
- try:
- method_code_int = int(method_code)
- except (TypeError, ValueError):
- logging.error(f"FreeKassa: invalid method code {method_code} for payment {payment_record.payment_id}")
- try:
- await callback.answer(get_text("error_payment_gateway"), show_alert=True)
- except Exception:
- pass
- return
-
success, response_data = await freekassa_service.create_order(
payment_db_id=payment_record.payment_id,
user_id=payment_record.user_id,
months=months,
- amount=amount,
- currency=settings.DEFAULT_CURRENCY_SYMBOL or "RUB",
- method_code=method_code_int,
+ amount=price_rub,
+ currency=freekassa_service.default_currency,
+ method_code=44,
ip_address=freekassa_service.server_ip,
extra_params={
- "us_method": method_code_int,
+ "us_method": 44,
},
)
if success:
location = response_data.get("location")
- provider_identifier = response_data.get("orderHash") or response_data.get("orderId")
+ order_hash = response_data.get("orderHash")
+ order_id_api = response_data.get("orderId")
+ provider_identifier = order_hash or order_id_api
if provider_identifier:
try:
@@ -491,18 +383,36 @@ async def pay_fk_method_callback_handler(
)
if location:
+ order_identifier_display = str(order_id_api or provider_identifier or payment_record.payment_id)
+ order_info_text = get_text(
+ "free_kassa_order_info",
+ order_id=order_identifier_display,
+ date=datetime.now().strftime("%Y-%m-%d"),
+ )
try:
await callback.message.edit_text(
- get_text(key="payment_link_message", months=months),
- reply_markup=get_payment_url_keyboard(location, current_lang, i18n),
+ f"{order_info_text}\n\n" + get_text(key="payment_link_message", months=months),
+ reply_markup=get_payment_url_keyboard(
+ location,
+ current_lang,
+ i18n,
+ back_callback=f"subscribe_period:{months}",
+ back_text_key="back_to_payment_methods_button",
+ ),
disable_web_page_preview=False,
)
except Exception as e_edit:
logging.warning(f"FreeKassa: failed to display payment link ({e_edit}), sending new message.")
try:
await callback.message.answer(
- get_text(key="payment_link_message", months=months),
- reply_markup=get_payment_url_keyboard(location, current_lang, i18n),
+ f"{order_info_text}\n\n" + get_text(key="payment_link_message", months=months),
+ reply_markup=get_payment_url_keyboard(
+ location,
+ current_lang,
+ i18n,
+ back_callback=f"subscribe_period:{months}",
+ back_text_key="back_to_payment_methods_button",
+ ),
disable_web_page_preview=False,
)
except Exception:
@@ -599,14 +509,26 @@ async def pay_crypto_callback_handler(
try:
await callback.message.edit_text(
get_text(key="payment_link_message", months=months),
- reply_markup=get_payment_url_keyboard(invoice_url, current_lang, i18n),
+ reply_markup=get_payment_url_keyboard(
+ invoice_url,
+ current_lang,
+ i18n,
+ back_callback=f"subscribe_period:{months}",
+ back_text_key="back_to_payment_methods_button",
+ ),
disable_web_page_preview=False,
)
except Exception:
try:
await callback.message.answer(
get_text(key="payment_link_message", months=months),
- reply_markup=get_payment_url_keyboard(invoice_url, current_lang, i18n),
+ reply_markup=get_payment_url_keyboard(
+ invoice_url,
+ current_lang,
+ i18n,
+ back_callback=f"subscribe_period:{months}",
+ back_text_key="back_to_payment_methods_button",
+ ),
disable_web_page_preview=False,
)
except Exception:
@@ -673,6 +595,18 @@ async def pay_stars_callback_handler(
)
if payment_db_id:
+ try:
+ await callback.message.edit_text(
+ get_text("payment_invoice_sent_message", months=months),
+ reply_markup=InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(
+ text=get_text("back_to_payment_methods_button"),
+ callback_data=f"subscribe_period:{months}",
+ )]
+ ]),
+ )
+ except Exception as e_edit:
+ logging.warning(f"Stars payment: failed to show invoice info message ({e_edit})")
try:
await callback.answer()
except Exception:
diff --git a/bot/keyboards/inline/user_keyboards.py b/bot/keyboards/inline/user_keyboards.py
index dde31e3..311ddfb 100644
--- a/bot/keyboards/inline/user_keyboards.py
+++ b/bot/keyboards/inline/user_keyboards.py
@@ -118,17 +118,17 @@ def get_payment_method_keyboard(months: int, price: float,
i18n_instance, settings: Settings) -> InlineKeyboardMarkup:
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
builder = InlineKeyboardBuilder()
- if settings.STARS_ENABLED and stars_price is not None:
- builder.button(text=_("pay_with_stars_button"),
- callback_data=f"pay_stars:{months}:{stars_price}")
- if settings.TRIBUTE_ENABLED and tribute_url:
- builder.button(text=_("pay_with_tribute_button"), url=tribute_url)
+ if settings.FREEKASSA_ENABLED:
+ builder.button(text=_("pay_with_sbp_button"),
+ callback_data=f"pay_fk:{months}:{price}")
if settings.YOOKASSA_ENABLED:
builder.button(text=_("pay_with_yookassa_button"),
callback_data=f"pay_yk:{months}:{price}")
- if settings.FREEKASSA_ENABLED:
- builder.button(text=_("pay_with_freekassa_button"),
- callback_data=f"pay_fk:{months}:{price}")
+ if settings.TRIBUTE_ENABLED and tribute_url:
+ builder.button(text=_("pay_with_tribute_button"), url=tribute_url)
+ if settings.STARS_ENABLED and stars_price is not None:
+ builder.button(text=_("pay_with_stars_button"),
+ callback_data=f"pay_stars:{months}:{stars_price}")
if settings.CRYPTOPAY_ENABLED:
builder.button(text=_("pay_with_cryptopay_button"),
callback_data=f"pay_crypto:{months}:{price}")
@@ -138,13 +138,20 @@ def get_payment_method_keyboard(months: int, price: float,
return builder.as_markup()
-def get_payment_url_keyboard(payment_url: str, lang: str,
- i18n_instance) -> InlineKeyboardMarkup:
+def get_payment_url_keyboard(payment_url: str,
+ lang: str,
+ i18n_instance,
+ back_callback: Optional[str] = None,
+ back_text_key: str = "back_to_main_menu_button"
+ ) -> InlineKeyboardMarkup:
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
builder = InlineKeyboardBuilder()
builder.button(text=_(key="pay_button"), url=payment_url)
- builder.button(text=_(key="back_to_main_menu_button"),
- callback_data="main_action:back_to_main")
+ if back_callback:
+ builder.button(text=_(key=back_text_key), callback_data=back_callback)
+ else:
+ builder.button(text=_(key="back_to_main_menu_button"),
+ callback_data="main_action:back_to_main")
builder.adjust(1)
return builder.as_markup()
@@ -192,7 +199,8 @@ def get_connect_and_main_keyboard(
lang: str,
i18n_instance,
settings: Settings,
- config_link: Optional[str]) -> InlineKeyboardMarkup:
+ config_link: Optional[str],
+ preserve_message: bool = False) -> InlineKeyboardMarkup:
"""Keyboard with a connect button and a back to main menu button."""
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
builder = InlineKeyboardBuilder()
@@ -216,10 +224,11 @@ def get_connect_and_main_keyboard(
)
)
+ back_callback = "main_action:back_to_main_keep" if preserve_message else "main_action:back_to_main"
builder.row(
InlineKeyboardButton(
text=_("back_to_main_menu_button"),
- callback_data="main_action:back_to_main",
+ callback_data=back_callback,
)
)
diff --git a/bot/services/crypto_pay_service.py b/bot/services/crypto_pay_service.py
index 7cf8baf..b846438 100644
--- a/bot/services/crypto_pay_service.py
+++ b/bot/services/crypto_pay_service.py
@@ -209,7 +209,9 @@ class CryptoPayService:
end_date=final_end.strftime('%Y-%m-%d'),
config_link=config_link)
- markup = get_connect_and_main_keyboard(lang, i18n, settings, config_link)
+ markup = get_connect_and_main_keyboard(
+ lang, i18n, settings, config_link, preserve_message=True
+ )
try:
await bot.send_message(
user_id,
diff --git a/bot/services/freekassa_service.py b/bot/services/freekassa_service.py
index 91d5ece..662b379 100644
--- a/bot/services/freekassa_service.py
+++ b/bot/services/freekassa_service.py
@@ -1,4 +1,5 @@
import asyncio
+from datetime import datetime
import hashlib
import hmac
import json
@@ -42,9 +43,7 @@ class FreeKassaService:
self.shop_id: Optional[str] = settings.FREEKASSA_MERCHANT_ID
self.api_key: Optional[str] = settings.FREEKASSA_API_KEY
self.second_secret: Optional[str] = settings.FREEKASSA_SECOND_SECRET
- self.default_currency: str = (
- settings.FREEKASSA_CURRENCY or settings.DEFAULT_CURRENCY_SYMBOL or "RUB"
- ).upper()
+ self.default_currency: str = (settings.DEFAULT_CURRENCY_SYMBOL or "RUB").upper()
self.server_ip: Optional[str] = settings.FREEKASSA_PAYMENT_IP
self.api_base_url: str = "https://api.fk.life/v1"
@@ -360,8 +359,21 @@ class FreeKassaService:
end_date=end_date_str,
config_link=config_link,
)
+ if provider_payment_id:
+ order_info_text = _(
+ "free_kassa_order_full",
+ order_id=provider_payment_id,
+ date=datetime.now().strftime("%Y-%m-%d"),
+ )
+ text = f"{order_info_text}\n{text}"
- markup = get_connect_and_main_keyboard(lang, self.i18n, self.settings, config_link)
+ markup = get_connect_and_main_keyboard(
+ lang,
+ self.i18n,
+ self.settings,
+ config_link,
+ preserve_message=True,
+ )
try:
await self.bot.send_message(
payment.user_id,
diff --git a/bot/services/stars_service.py b/bot/services/stars_service.py
index 17173a2..e278796 100644
--- a/bot/services/stars_service.py
+++ b/bot/services/stars_service.py
@@ -148,7 +148,7 @@ class StarsService:
config_link=config_link,
)
markup = get_connect_and_main_keyboard(
- current_lang, i18n, self.settings, config_link
+ current_lang, i18n, self.settings, config_link, preserve_message=True
)
try:
await self.bot.send_message(
diff --git a/bot/services/tribute_service.py b/bot/services/tribute_service.py
index de12e55..0e48ce7 100644
--- a/bot/services/tribute_service.py
+++ b/bot/services/tribute_service.py
@@ -208,7 +208,11 @@ class TributeService:
config_link=config_link,
)
markup = get_connect_and_main_keyboard(
- lang, i18n, settings, config_link
+ lang,
+ i18n,
+ settings,
+ config_link,
+ preserve_message=True,
)
try:
diff --git a/config/settings.py b/config/settings.py
index b177561..198bede 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -49,7 +49,6 @@ class Settings(BaseSettings):
FREEKASSA_FIRST_SECRET: Optional[str] = None
FREEKASSA_SECOND_SECRET: Optional[str] = None
FREEKASSA_PAYMENT_URL: str = Field(default="https://pay.freekassa.ru/")
- FREEKASSA_CURRENCY: str = Field(default="RUB")
FREEKASSA_API_KEY: Optional[str] = None
FREEKASSA_PAYMENT_IP: Optional[str] = None
@@ -400,6 +399,10 @@ def get_settings() -> Settings:
logging.warning(
"WARNING: FreeKassa second secret is not set. Incoming payment notifications cannot be verified."
)
+ if not _settings_instance.subscription_options:
+ logging.warning(
+ "CRITICAL: FreeKassa is enabled but no subscription prices are configured (RUB_PRICE_*). Users will not see payment buttons."
+ )
except ValidationError as e:
logging.critical(
diff --git a/locales/en.json b/locales/en.json
index 4fe5c49..8e0ccd2 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -1,7 +1,6 @@
{
"welcome": "Welcome, {user_name}!",
"main_menu_greeting": "Hi, {user_name}! 👋\nWhat would you like to do?",
-
"menu_activate_trial_button": "🆓 Free Trial",
"menu_subscribe_inline": "🚀 Purchase",
"menu_my_subscription_inline": "🔐 My Subscription",
@@ -13,42 +12,35 @@
"menu_server_status_button": "📊 Status",
"menu_support_button": "💬 Support",
"menu_terms_button": "📄 Terms of Service",
-
"back_to_main_menu_button": "⬅️ Back",
-
"choose_language": "Choose language:",
"language_set_alert": "Language changed!",
-
"error_occurred_try_again": "An error occurred, please try again.",
"error_try_again": "Please try again.",
"error_displaying_menu": "Error displaying menu.",
"main_menu_unknown_action": "Unknown action.",
-
"select_subscription_period": "Select subscription period:",
"subscribe_for_months_button": "{months} mo. - {price} {currency_symbol}",
-
"choose_payment_method": "Choose payment method:",
"pay_button": "💳 Pay",
"pay_with_yookassa_button": "💳 YooKassa",
- "pay_with_freekassa_button": "💳 FreeKassa",
+ "pay_with_sbp_button": "📱 SBP",
+ "back_to_payment_methods_button": "⬅️ Back to payment methods",
"pay_with_cryptopay_button": "💎 CryptoBot",
"pay_with_tribute_button": "❤️ Tribute",
"pay_with_stars_button": "🌟 Telegram Stars",
- "freekassa_choose_method": "Choose how to pay with FreeKassa:",
- "freekassa_method_qr": "📱 SBP QR (i=44)",
- "freekassa_method_card": "💳 Bank Card (i=36)",
- "freekassa_method_sberpay": "🏦 SberPay (i=43)",
"connect_button": "🔗 Connect",
"cancel_button": "❌ Cancel",
"payment_description_subscription": "Subscription payment for {months} mo.",
"payment_link_message": "To pay for {months} mo. subscription, click the button below:",
+ "free_kassa_order_info": "Order #{order_id} from {date}",
+ "payment_invoice_sent_message": "Telegram has sent the invoice above. Complete the payment or pick another method below.",
"payment_successful_error_details": "✅ Payment succeeded, but an error occurred displaying details. Your subscription is active. Contact support if anything is wrong.",
"payment_successful_full": "✅ Payment successful!\nYour {months}-month subscription is active until {end_date}.\n\nConnection key:\n{config_link}\n\nTo connect, open the link and follow the instructions 👇",
"payment_successful_with_referral_bonus_full": "✅ Payment successful!\nYour {months}-month subscription (base end date: {base_end_date}) has been extended by {bonus_days} bonus days for referral from {inviter_name} and is now active until {final_end_date}.\n\nConnection key:\n{config_link}\n\nTo connect, open the link and follow the instructions 👇",
"payment_failed": "❌ Payment failed or was cancelled. Please try again or contact support.",
"config_link_not_available": "not available, contact support",
"traffic_unlimited": "Unlimited",
-
"promo_code_prompt": "Please enter your promo code:",
"promo_code_not_found": "Promo code {code} not found, expired, or already used the maximum number of times.",
"promo_code_already_used_by_user": "You have already used promo code {code}.",
@@ -70,7 +62,6 @@
"referral_bonus_inviter_notification_extended": "🎉 Congrats! Your friend {referee_name} paid for a subscription. You received {days} bonus days! Your subscription is now active until {new_end_date}.",
"referral_bonus_inviter_notification_new_sub": "🎉 Congrats! Your friend {referee_name} paid for a subscription. You received a {days}-day bonus subscription! It is active until {new_end_date}.",
"user_is_banned": "🚫 Your account is banned. Please contact support.",
-
"admin_panel_title": "Admin Panel",
"admin_stats_button": "📊 Statistics",
"admin_broadcast_button": "📢 Broadcast",
@@ -327,7 +318,6 @@
"admin_user_subscription_active_until": "⏰ Active until:",
"admin_user_subscription_error": "Loading error",
"admin_promo_management_button": "🎟 Promo Management",
-
"admin_promo_management_title": "🎟 Promo Code Management\n\nSelect a promo code for detailed view:",
"admin_promo_management_empty": "📭 No promo codes available",
"admin_promo_card_title": "🎟 Promo Code: {code}",
@@ -440,5 +430,6 @@
"admin_ads_delete_button": "🗑 Delete campaign",
"admin_ads_delete_confirm": "Are you sure you want to delete campaign #{id}? This action is irreversible.",
"admin_ads_deleted_success": "Campaign deleted.",
- "admin_ads_not_found": "Campaign not found."
-}
+ "admin_ads_not_found": "Campaign not found.",
+ "free_kassa_order_full": "Order #{order_id} from {date}\n\n"
+}
\ No newline at end of file
diff --git a/locales/ru.json b/locales/ru.json
index d667d29..7907824 100644
--- a/locales/ru.json
+++ b/locales/ru.json
@@ -1,7 +1,6 @@
{
"welcome": "Добро пожаловать, {user_name}!",
"main_menu_greeting": "Привет, {user_name}! 👋\nЧто бы вы хотели сделать?",
-
"menu_activate_trial_button": "🆓 Пробный период",
"menu_subscribe_inline": "🚀 Купить",
"menu_my_subscription_inline": "🔐 Моя подписка",
@@ -13,42 +12,35 @@
"menu_server_status_button": "📊 Статус",
"menu_support_button": "💬 Поддержка",
"menu_terms_button": "📄 Условия сервиса",
-
"back_to_main_menu_button": "⬅️ Назад",
-
"choose_language": "Выберите язык / Select language:",
"language_set_alert": "Язык изменен!",
-
"error_occurred_try_again": "Произошла ошибка, попробуйте снова.",
"error_try_again": "Попробуйте еще раз.",
"error_displaying_menu": "Ошибка отображения меню.",
"main_menu_unknown_action": "Неизвестное действие.",
-
"select_subscription_period": "Выберите срок подписки:",
"subscribe_for_months_button": "{months} мес. - {price} {currency_symbol}",
-
"choose_payment_method": "Выберите способ оплаты:",
"pay_button": "💳 Оплатить",
"pay_with_yookassa_button": "💳 ЮKassa",
- "pay_with_freekassa_button": "💳 FreeKassa",
+ "pay_with_sbp_button": "📱 СБП",
+ "back_to_payment_methods_button": "⬅️ Назад к выбору оплаты",
"pay_with_cryptopay_button": "💎 CryptoBot",
"pay_with_tribute_button": "❤️ Tribute",
"pay_with_stars_button": "🌟 Звезды Telegram",
- "freekassa_choose_method": "Выберите способ оплаты FreeKassa:",
- "freekassa_method_qr": "📱 QR по СБП (i=44)",
- "freekassa_method_card": "💳 Банковская карта РФ (i=36)",
- "freekassa_method_sberpay": "🏦 SberPay (i=43)",
"connect_button": "🔗 Подключиться",
"cancel_button": "❌ Отмена",
"payment_description_subscription": "Оплата подписки на {months} мес.",
"payment_link_message": "Для оплаты подписки на {months} мес., нажмите кнопку ниже:",
+ "free_kassa_order_info": "Заказ №{order_id} от {date}",
+ "payment_invoice_sent_message": "Счёт Telegram Stars отправлен выше. Нажмите «Оплатить» или вернитесь к выбору способа ниже.",
"payment_successful_error_details": "✅ Оплата прошла успешно, но возникла ошибка при отображении деталей. Ваша подписка активна. Свяжитесь с поддержкой, если что-то не так.",
"payment_successful_full": "✅ Оплата прошла успешно!\nВаша подписка на {months} мес. активна до {end_date}.\n\nКлюч подключения:\n{config_link}\n\nЧтобы подключиться, перейдите по ссылке и следуйте инструкции 👇",
"payment_successful_with_referral_bonus_full": "✅ Оплата прошла успешно!\nВаша подписка на {months} мес. (базовая дата окончания: {base_end_date}) продлена на {bonus_days} бонусных дней за приглашение от {inviter_name} и теперь активна до {final_end_date}.\n\nКлюч подключения:\n{config_link}\n\nЧтобы подключиться, перейдите по ссылке и следуйте инструкции 👇",
"payment_failed": "❌ Оплата не удалась или была отменена. Пожалуйста, попробуйте еще раз или свяжитесь с поддержкой.",
"config_link_not_available": "недоступна, обратитесь в поддержку",
"traffic_unlimited": "Безлимитный",
-
"promo_code_prompt": "Пожалуйста, введите ваш промокод:",
"promo_code_not_found": "Промокод {code} не найден, истек или уже использован максимальное количество раз.",
"promo_code_already_used_by_user": "Вы уже активировали промокод {code}.",
@@ -70,7 +62,6 @@
"referral_bonus_inviter_notification_extended": "🎉 Поздравляем! Ваш друг {referee_name} оплатил подписку. Вам начислено {days} бонусных дней! Ваша подписка теперь активна до {new_end_date}.",
"referral_bonus_inviter_notification_new_sub": "🎉 Поздравляем! Ваш друг {referee_name} оплатил подписку. Вам начислена бонусная подписка на {days} дней! Она активна до {new_end_date}.",
"user_is_banned": "🚫 Ваш аккаунт заблокирован. Пожалуйста, свяжитесь со службой поддержки.",
-
"admin_panel_title": "Панель администратора",
"admin_stats_button": "📊 Статистика",
"admin_broadcast_button": "📢 Рассылка",
@@ -327,7 +318,6 @@
"admin_user_subscription_active_until": "⏰ Действует до:",
"admin_user_subscription_error": "Ошибка загрузки",
"admin_promo_management_button": "🎟 Управление промокодами",
-
"admin_promo_management_title": "🎟 Управление промокодами\n\nВыберите промокод для детального просмотра:",
"admin_promo_management_empty": "📭 Промокоды отсутствуют",
"admin_promo_card_title": "🎟 Промокод: {code}",
@@ -440,5 +430,6 @@
"admin_ads_delete_button": "🗑 Удалить кампанию",
"admin_ads_delete_confirm": "Вы уверены, что хотите удалить кампанию #{id}? Это действие необратимо.",
"admin_ads_deleted_success": "Кампания удалена.",
- "admin_ads_not_found": "Кампания не найдена."
-}
+ "admin_ads_not_found": "Кампания не найдена.",
+ "free_kassa_order_full": "Заказ №{order_id} от {date}\n\n"
+}
\ No newline at end of file