From 63cc967dfcd24b8c255f04114909b1b64684e5ce Mon Sep 17 00:00:00 2001 From: Boris Kovalskii <36034823+JustYay@users.noreply.github.com> Date: Thu, 24 Jul 2025 19:10:14 +1000 Subject: [PATCH 1/2] Fix paymentid and success and fail urls --- handlers/payments/kassai.py | 9 ++++++--- web/kassai_payment.py | 10 +++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/handlers/payments/kassai.py b/handlers/payments/kassai.py index 8231ad0a..c09f5259 100644 --- a/handlers/payments/kassai.py +++ b/handlers/payments/kassai.py @@ -12,7 +12,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from config import ( KASSAI_ENABLE, KASSAI_API_KEY, KASSAI_SECRET_KEY, KASSAI_DOMAIN, KASSAI_SHOP_ID, - REDIRECT_LINK, FAIL_REDIRECT_LINK, WEBHOOK_HOST, KASSAI_IP + REDIRECT_LINK, FAIL_REDIRECT_LINK, WEBHOOK_HOST, KASSAI_IP, KASSAI_SUCCESS_URL, KASSAI_FAILURE_URL ) from handlers.buttons import BACK, PAY_2, KASSAI_CARDS, KASSAI_SBP @@ -287,7 +287,7 @@ async def generate_kassai_payment_link(amount: int, tg_id: int, method: dict) -> """ nonce = int(time.time()) unique_payment_id = f"{nonce}_{tg_id}" - url = f"https://api.fk.life/v1/orders/create?paymentId={unique_payment_id}" + url = "https://api.fk.life/v1/orders/create" headers = { "Content-Type": "application/json", @@ -303,7 +303,10 @@ async def generate_kassai_payment_link(amount: int, tg_id: int, method: dict) -> "email": client_email, "ip": client_ip, "amount": int(amount), - "currency": "RUB" + "currency": "RUB", + "success_url": KASSAI_SUCCESS_URL, + "failure_url": KASSAI_FAILURE_URL, + "paymentId": unique_payment_id } sorted_keys = sorted(data_for_signature.keys()) diff --git a/web/kassai_payment.py b/web/kassai_payment.py index d21169f2..c8a4c425 100644 --- a/web/kassai_payment.py +++ b/web/kassai_payment.py @@ -37,6 +37,8 @@ async def kassai_payment_webhook(request: web.Request): p_email = data_dict.get("P_EMAIL") intid = data_dict.get("intid") + logger.info(f"KassaAI payment: intid={intid}, MERCHANT_ORDER_ID={merchant_order_id}, amount={amount}") + if not amount or not intid: logger.error(f"KassaAI: Missing AMOUNT or intid") return web.Response(status=400, text="Missing required fields") @@ -45,6 +47,10 @@ async def kassai_payment_webhook(request: web.Request): logger.warning(f"KassaAI: Duplicate payment intid={intid}") return web.Response(status=200, text="YES") + if merchant_order_id and merchant_order_id in processed_payments: + logger.warning(f"KassaAI: Duplicate payment MERCHANT_ORDER_ID={merchant_order_id}") + return web.Response(status=200, text="YES") + try: if p_email and "@" in p_email: tg_id = int(p_email.split("@")[0]) @@ -80,8 +86,10 @@ async def kassai_payment_webhook(request: web.Request): logger.warning(f"Error handling payment message deletion: {e}") processed_payments.add(intid) + if merchant_order_id: + processed_payments.add(merchant_order_id) - logger.info(f"✅ KassaAI: Payment processed for user {tg_id}, amount {amount_float}, intid={intid}") + logger.info(f"✅ KassaAI: Payment processed for user {tg_id}, amount {amount_float}, intid={intid}, MERCHANT_ORDER_ID={merchant_order_id}") return web.Response(status=200, text="YES") except Exception as e: From 2bf2d98c8873987078ec0b85922453f86b5c82ba Mon Sep 17 00:00:00 2001 From: Boris Kovalskii <36034823+JustYay@users.noreply.github.com> Date: Thu, 24 Jul 2025 20:01:55 +1000 Subject: [PATCH 2/2] Fix for old users --- handlers/payments/kassai.py | 118 ++++++++++++++++++------------------ web/kassai_payment.py | 20 ------ 2 files changed, 59 insertions(+), 79 deletions(-) diff --git a/handlers/payments/kassai.py b/handlers/payments/kassai.py index c09f5259..a09ec810 100644 --- a/handlers/payments/kassai.py +++ b/handlers/payments/kassai.py @@ -41,65 +41,73 @@ KASSAI_PAYMENT_METHODS = [ @router.callback_query(F.data == "pay_kassai") async def process_callback_pay_kassai(callback_query: types.CallbackQuery, state: FSMContext, session: AsyncSession, method_name: str = None): - tg_id = callback_query.message.chat.id - logger.info(f"User {tg_id} initiated KassaAI payment.") - - if method_name: - method = next((m for m in KASSAI_PAYMENT_METHODS if m["name"] == method_name and m["enable"]), None) - if not method: + try: + tg_id = callback_query.message.chat.id + logger.info(f"User {tg_id} initiated KassaAI payment.") + + await state.clear() + + if method_name: + method = next((m for m in KASSAI_PAYMENT_METHODS if m["name"] == method_name and m["enable"]), None) + if not method: + await edit_or_send_message( + target_message=callback_query.message, + text="Ошибка: выбранный способ оплаты недоступен.", + reply_markup=types.InlineKeyboardMarkup(), + force_text=True, + ) + return + + builder = InlineKeyboardBuilder() + for i in range(0, len(PAYMENT_OPTIONS), 2): + if i + 1 < len(PAYMENT_OPTIONS): + builder.row( + InlineKeyboardButton( + text=PAYMENT_OPTIONS[i]["text"], + callback_data=f'kassai_amount|{method_name}|{PAYMENT_OPTIONS[i]["callback_data"].split("|")[1]}', + ), + InlineKeyboardButton( + text=PAYMENT_OPTIONS[i + 1]["text"], + callback_data=f'kassai_amount|{method_name}|{PAYMENT_OPTIONS[i + 1]["callback_data"].split("|")[1]}', + ), + ) + else: + builder.row( + InlineKeyboardButton( + text=PAYMENT_OPTIONS[i]["text"], + callback_data=f'kassai_amount|{method_name}|{PAYMENT_OPTIONS[i]["callback_data"].split("|")[1]}', + ) + ) + builder.row(InlineKeyboardButton(text="Ввести сумму", callback_data=f"kassai_custom_amount|{method_name}")) + builder.row(InlineKeyboardButton(text=BACK, callback_data="balance")) + await edit_or_send_message( target_message=callback_query.message, - text="Ошибка: выбранный способ оплаты недоступен.", - reply_markup=types.InlineKeyboardMarkup(), + text=method["desc"], + reply_markup=builder.as_markup(), force_text=True, ) + await state.update_data(kassai_method=method_name) + await state.set_state(ReplenishBalanceKassaiState.choosing_amount) return builder = InlineKeyboardBuilder() - for i in range(0, len(PAYMENT_OPTIONS), 2): - if i + 1 < len(PAYMENT_OPTIONS): - builder.row( - InlineKeyboardButton( - text=PAYMENT_OPTIONS[i]["text"], - callback_data=f'kassai_amount|{method_name}|{PAYMENT_OPTIONS[i]["callback_data"].split("|")[1]}', - ), - InlineKeyboardButton( - text=PAYMENT_OPTIONS[i + 1]["text"], - callback_data=f'kassai_amount|{method_name}|{PAYMENT_OPTIONS[i + 1]["callback_data"].split("|")[1]}', - ), - ) - else: - builder.row( - InlineKeyboardButton( - text=PAYMENT_OPTIONS[i]["text"], - callback_data=f'kassai_amount|{method_name}|{PAYMENT_OPTIONS[i]["callback_data"].split("|")[1]}', - ) - ) - builder.row(InlineKeyboardButton(text="Ввести сумму", callback_data=f"kassai_custom_amount|{method_name}")) + for method in KASSAI_PAYMENT_METHODS: + if method["enable"]: + builder.row(InlineKeyboardButton(text=method["button"], callback_data=f'kassai_method|{method["name"]}')) builder.row(InlineKeyboardButton(text=BACK, callback_data="balance")) - await callback_query.message.delete() - new_message = await callback_query.message.answer( - text=method["desc"], + await edit_or_send_message( + target_message=callback_query.message, + text="Выберите способ оплаты через KassaAI:", reply_markup=builder.as_markup(), ) - await state.update_data(message_id=new_message.message_id, chat_id=new_message.chat.id, kassai_method=method_name) - await state.set_state(ReplenishBalanceKassaiState.choosing_amount) - return - - builder = InlineKeyboardBuilder() - for method in KASSAI_PAYMENT_METHODS: - if method["enable"]: - builder.row(InlineKeyboardButton(text=method["button"], callback_data=f'kassai_method|{method["name"]}')) - builder.row(InlineKeyboardButton(text=BACK, callback_data="balance")) - - await callback_query.message.delete() - new_message = await callback_query.message.answer( - text="Выберите способ оплаты через KassaAI:", - reply_markup=builder.as_markup(), - ) - await state.update_data(message_id=new_message.message_id, chat_id=new_message.chat.id) - await state.set_state(ReplenishBalanceKassaiState.choosing_method) + await state.update_data(message_id=callback_query.message.message_id, chat_id=callback_query.message.chat.id) + await state.set_state(ReplenishBalanceKassaiState.choosing_method) + + except Exception as e: + logger.error(f"Error in process_callback_pay_kassai for user {callback_query.message.chat.id}: {e}") + await callback_query.answer("Произошла ошибка при инициализации платежа. Попробуйте позже.", show_alert=True) @router.callback_query(F.data.startswith("kassai_method|")) @@ -141,12 +149,12 @@ async def process_method_selection(callback_query: types.CallbackQuery, state: F builder.row(InlineKeyboardButton(text="Ввести сумму", callback_data=f"kassai_custom_amount|{method_name}")) builder.row(InlineKeyboardButton(text=BACK, callback_data="pay_kassai")) - await callback_query.message.delete() - new_message = await callback_query.message.answer( + await edit_or_send_message( + target_message=callback_query.message, text=method["desc"], reply_markup=builder.as_markup(), ) - await state.update_data(message_id=new_message.message_id, chat_id=new_message.chat.id) + await state.update_data(message_id=callback_query.message.message_id, chat_id=callback_query.message.chat.id) await state.set_state(ReplenishBalanceKassaiState.choosing_amount) @@ -220,10 +228,6 @@ async def handle_custom_amount_input(message: types.Message, state: FSMContext): force_text=True, ) - await state.update_data( - payment_message_id=payment_message.message_id if hasattr(payment_message, 'message_id') else message.message_id, - payment_chat_id=message.chat.id - ) await state.set_state(ReplenishBalanceKassaiState.waiting_for_payment_confirmation) @@ -274,10 +278,6 @@ async def process_amount_selection(callback_query: types.CallbackQuery, state: F force_text=True, ) - await state.update_data( - payment_message_id=payment_message.message_id if hasattr(payment_message, 'message_id') else callback_query.message.message_id, - payment_chat_id=callback_query.message.chat.id - ) await state.set_state(ReplenishBalanceKassaiState.waiting_for_payment_confirmation) diff --git a/web/kassai_payment.py b/web/kassai_payment.py index c8a4c425..664b6e1f 100644 --- a/web/kassai_payment.py +++ b/web/kassai_payment.py @@ -1,12 +1,9 @@ import hashlib import json -import bot from aiohttp import web from database import add_payment, async_session_maker, update_balance from handlers.payments.utils import send_payment_success_notification from handlers.payments.kassai import verify_kassai_signature -from aiogram.fsm.context import FSMContext -from aiogram.fsm.storage.base import StorageKey from config import KASSAI_SECRET_KEY, KASSAI_SHOP_ID from logger import logger @@ -68,23 +65,6 @@ async def kassai_payment_webhook(request: web.Request): await send_payment_success_notification(tg_id, amount_float, session) await add_payment(session, tg_id, amount_float, "kassai") - try: - storage_key = StorageKey(bot_id=bot.bot.id, chat_id=tg_id, user_id=tg_id) - state_data = await bot.dp.storage.get_data(storage_key) - - if state_data and 'payment_message_id' in state_data: - payment_message_id = state_data['payment_message_id'] - try: - await bot.bot.delete_message(chat_id=tg_id, message_id=payment_message_id) - logger.info(f"Payment message deleted for user {tg_id}") - except Exception as e: - logger.warning(f"Could not delete payment message for user {tg_id}: {e}") - - await bot.dp.storage.set_state(storage_key, None) - await bot.dp.storage.set_data(storage_key, {}) - except Exception as e: - logger.warning(f"Error handling payment message deletion: {e}") - processed_payments.add(intid) if merchant_order_id: processed_payments.add(merchant_order_id)