From 9d636355026ad1e50d045e78ffa21e76cfef0774 Mon Sep 17 00:00:00 2001 From: Fringg Date: Thu, 2 Apr 2026 06:47:40 +0300 Subject: [PATCH] feat: add SberPay as KassaAI sub-method (payment_system_id=43) Adds SberPay alongside existing SBP (44) and Card (36) sub-methods. Changes across all 8 required files: - config.py: KASSA_AI_SBERPAY_ENABLED, display name, helper methods - kassa_ai_service.py: payment_system_id=43 in KASSA_AI_SUB_METHODS - kassa_ai.py handler: method config + start_kassa_ai_sberpay_topup - main.py: route tuple, import, handler registration - payment_service.py: guest purchase flow method check - cabinet/balance.py: KASSA_AI_OPTION_MAP sberpay=43 - payment_method_config_service.py: sub_option for admin panel - inline.py: keyboard button + fallback condition guard Env vars: KASSA_AI_SBERPAY_ENABLED, KASSA_AI_SBERPAY_DISPLAY_NAME --- app/cabinet/routes/balance.py | 2 +- app/config.py | 12 ++++++++++++ app/handlers/balance/kassa_ai.py | 16 ++++++++++++++++ app/handlers/balance/main.py | 4 +++- app/keyboards/inline.py | 13 +++++++++++++ app/services/kassa_ai_service.py | 1 + app/services/payment_method_config_service.py | 1 + app/services/payment_service.py | 2 +- 8 files changed, 48 insertions(+), 3 deletions(-) diff --git a/app/cabinet/routes/balance.py b/app/cabinet/routes/balance.py index 7d37babd..cdb9f848 100644 --- a/app/cabinet/routes/balance.py +++ b/app/cabinet/routes/balance.py @@ -699,7 +699,7 @@ async def create_topup( ) # Use payment_option to select sbp or card - KASSA_AI_OPTION_MAP = {'sbp': 44, 'card': 36} + KASSA_AI_OPTION_MAP = {'sbp': 44, 'card': 36, 'sberpay': 43} option = (request.payment_option or '').strip().lower() ps_id = KASSA_AI_OPTION_MAP.get(option) # None = use env default diff --git a/app/config.py b/app/config.py index 4c861abd..5159377a 100644 --- a/app/config.py +++ b/app/config.py @@ -558,6 +558,8 @@ class Settings(BaseSettings): KASSA_AI_SBP_DISPLAY_NAME: str = 'СБП (KassaAI)' KASSA_AI_CARD_ENABLED: bool = False # Карты РФ — payment_system_id=36 KASSA_AI_CARD_DISPLAY_NAME: str = 'Карта (KassaAI)' + KASSA_AI_SBERPAY_ENABLED: bool = False # SberPay — payment_system_id=43 + KASSA_AI_SBERPAY_DISPLAY_NAME: str = 'SberPay (KassaAI)' # RioPay (api.riopay.online) v2.0.1 RIOPAY_ENABLED: bool = False @@ -1980,6 +1982,16 @@ class Settings(BaseSettings): def get_kassa_ai_card_display_name_html(self) -> str: return html.escape(self.get_kassa_ai_card_display_name()) + def is_kassa_ai_sberpay_enabled(self) -> bool: + return self.KASSA_AI_SBERPAY_ENABLED and self.is_kassa_ai_enabled() + + def get_kassa_ai_sberpay_display_name(self) -> str: + name = (self.KASSA_AI_SBERPAY_DISPLAY_NAME or '').strip() + return name if name else 'SberPay (KassaAI)' + + def get_kassa_ai_sberpay_display_name_html(self) -> str: + return html.escape(self.get_kassa_ai_sberpay_display_name()) + def is_payment_verification_auto_check_enabled(self) -> bool: return self.PAYMENT_VERIFICATION_AUTO_CHECK_ENABLED diff --git a/app/handlers/balance/kassa_ai.py b/app/handlers/balance/kassa_ai.py index 281f03aa..30d8d404 100644 --- a/app/handlers/balance/kassa_ai.py +++ b/app/handlers/balance/kassa_ai.py @@ -39,6 +39,11 @@ _KASSA_AI_METHOD_CONFIG = { 'display_name': settings.get_kassa_ai_card_display_name, 'unavailable_text': 'KassaAI Карта временно недоступна', }, + 'kassa_ai_sberpay': { + 'is_enabled': settings.is_kassa_ai_sberpay_enabled, + 'display_name': settings.get_kassa_ai_sberpay_display_name, + 'unavailable_text': 'KassaAI SberPay временно недоступен', + }, } @@ -350,3 +355,14 @@ async def start_kassa_ai_card_topup( ): """Start KassaAI Card top-up process.""" await _start_kassa_ai_sub_topup(callback, db_user, db, state, 'kassa_ai_card') + + +@error_handler +async def start_kassa_ai_sberpay_topup( + callback: types.CallbackQuery, + db_user: User, + db: AsyncSession, + state: FSMContext, +): + """Start KassaAI SberPay top-up process.""" + await _start_kassa_ai_sub_topup(callback, db_user, db, state, 'kassa_ai_sberpay') diff --git a/app/handlers/balance/main.py b/app/handlers/balance/main.py index 57483bdb..1c947074 100644 --- a/app/handlers/balance/main.py +++ b/app/handlers/balance/main.py @@ -133,7 +133,7 @@ async def route_payment_by_method( ) return True - if payment_method in ('kassa_ai', 'kassa_ai_sbp', 'kassa_ai_card'): + if payment_method in ('kassa_ai', 'kassa_ai_sbp', 'kassa_ai_card', 'kassa_ai_sberpay'): from .kassa_ai import process_kassa_ai_payment_amount async with AsyncSessionLocal() as db: @@ -701,6 +701,7 @@ def register_balance_handlers(dp: Dispatcher): from .kassa_ai import ( start_kassa_ai_card_topup, + start_kassa_ai_sberpay_topup, start_kassa_ai_sbp_topup, start_kassa_ai_topup, ) @@ -708,6 +709,7 @@ def register_balance_handlers(dp: Dispatcher): dp.callback_query.register(start_kassa_ai_topup, F.data == 'topup_kassa_ai') dp.callback_query.register(start_kassa_ai_sbp_topup, F.data == 'topup_kassa_ai_sbp') dp.callback_query.register(start_kassa_ai_card_topup, F.data == 'topup_kassa_ai_card') + dp.callback_query.register(start_kassa_ai_sberpay_topup, F.data == 'topup_kassa_ai_sberpay') from .riopay import start_riopay_topup diff --git a/app/keyboards/inline.py b/app/keyboards/inline.py index eb2f8211..e824d312 100644 --- a/app/keyboards/inline.py +++ b/app/keyboards/inline.py @@ -1755,10 +1755,23 @@ def get_payment_methods_keyboard(amount_kopeks: int, language: str = DEFAULT_LAN ) has_direct_payment_methods = True + if settings.is_kassa_ai_sberpay_enabled(): + sberpay_name = settings.get_kassa_ai_sberpay_display_name() + keyboard.append( + [ + InlineKeyboardButton( + text=texts.t('PAYMENT_KASSA_AI_SBERPAY', f'💳 {sberpay_name}'), + callback_data=_build_callback('kassa_ai_sberpay'), + ) + ] + ) + has_direct_payment_methods = True + if ( settings.is_kassa_ai_enabled() and not settings.is_kassa_ai_sbp_enabled() and not settings.is_kassa_ai_card_enabled() + and not settings.is_kassa_ai_sberpay_enabled() ): kassa_ai_name = settings.get_kassa_ai_display_name() keyboard.append( diff --git a/app/services/kassa_ai_service.py b/app/services/kassa_ai_service.py index c29bfff1..b86d2600 100644 --- a/app/services/kassa_ai_service.py +++ b/app/services/kassa_ai_service.py @@ -18,6 +18,7 @@ logger = structlog.get_logger(__name__) KASSA_AI_SUB_METHODS = { 'kassa_ai_sbp': {'payment_system_id': 44}, 'kassa_ai_card': {'payment_system_id': 36}, + 'kassa_ai_sberpay': {'payment_system_id': 43}, } # Кэш для публичного IP diff --git a/app/services/payment_method_config_service.py b/app/services/payment_method_config_service.py index e65b3b57..ebb8d552 100644 --- a/app/services/payment_method_config_service.py +++ b/app/services/payment_method_config_service.py @@ -129,6 +129,7 @@ def _get_method_defaults() -> dict: 'available_sub_options': [ {'id': 'sbp', 'name': 'СБП'}, {'id': 'card', 'name': 'Карта'}, + {'id': 'sberpay', 'name': 'SberPay'}, ], }, 'riopay': { diff --git a/app/services/payment_service.py b/app/services/payment_service.py index 18446ca3..0fa4d73e 100644 --- a/app/services/payment_service.py +++ b/app/services/payment_service.py @@ -703,7 +703,7 @@ class PaymentService( return None # --- KassaAI ---------------------------------------------------------- - if payment_method in ('kassa_ai', 'kassa_ai_sbp', 'kassa_ai_card'): + if payment_method in ('kassa_ai', 'kassa_ai_sbp', 'kassa_ai_card', 'kassa_ai_sberpay'): if not settings.is_kassa_ai_enabled(): logger.warning('KassaAI is not enabled, cannot create guest payment') return None