From 850f135bf8c14895e4d7f066ef219dcf66e98a96 Mon Sep 17 00:00:00 2001 From: machka-pasla Date: Thu, 4 Sep 2025 17:52:21 +0300 Subject: [PATCH] Implement multi-card deletion for payment methods with enhanced error handling - Added functionality to delete specific payment methods based on their ID, supporting multi-card management. - Improved error handling during deletion processes, including rollback mechanisms for database transactions. - Updated user notifications to reflect the success or failure of deletion attempts, ensuring a consistent user experience. - Refactored the response messages to include updated lists of remaining payment methods after deletion. --- bot/handlers/user/subscription.py | 53 ++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/bot/handlers/user/subscription.py b/bot/handlers/user/subscription.py index 0e33037..5889620 100644 --- a/bot/handlers/user/subscription.py +++ b/bot/handlers/user/subscription.py @@ -667,11 +667,56 @@ async def payment_method_delete(callback: types.CallbackQuery, settings: Setting current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE) i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance") _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) if i18n else key - # Single-card storage: ignore pm_id for now but retain for future multi-card - deleted = await user_billing_dal.delete_yk_payment_method(session, callback.from_user.id) - await session.commit() + # Try to parse specific method id for multi-card deletion + pm_id_raw = callback.data.split(":", 1)[-1] if ":" in callback.data else "" + deleted = False + # Attempt multi-card deletion first + try: + if pm_id_raw and pm_id_raw.isdigit(): + from db.dal.user_billing_dal import delete_user_payment_method, list_user_payment_methods + deleted = await delete_user_payment_method(session, callback.from_user.id, int(pm_id_raw)) + await session.commit() + # Build updated list + methods = await list_user_payment_methods(session, callback.from_user.id) + text = _("payment_methods_title") + cards = [] + for m in methods: + title = _("payment_method_card_title", network=m.card_network or "Card", last4=m.card_last4 or "????") + cards.append((str(m.method_id), title if not m.is_default else f"⭐ {title}")) + if not cards: + text += "\n\n" + _("payment_method_none") + msg = _("payment_method_deleted_success") if deleted else _("error_try_again") + # Prepend status message to title + await callback.message.edit_text(f"{msg}\n\n{text}", reply_markup=get_payment_methods_list_keyboard(cards, 0, current_lang, i18n)) + try: + await callback.answer() + except Exception: + pass + return + except Exception: + await session.rollback() + deleted = False + + # Fallback: legacy single-card storage deletion + try: + deleted = await user_billing_dal.delete_yk_payment_method(session, callback.from_user.id) + await session.commit() + except Exception: + await session.rollback() + deleted = False + msg = _("payment_method_deleted_success") if deleted else _("error_try_again") - await callback.message.edit_text(msg, reply_markup=get_payment_methods_manage_keyboard(current_lang, i18n, has_card=False)) + # After legacy deletion, route user to list (which will be empty) for consistency + from db.dal.user_billing_dal import list_user_payment_methods + methods = await list_user_payment_methods(session, callback.from_user.id) + cards = [] + for m in methods: + title = _("payment_method_card_title", network=m.card_network or "Card", last4=m.card_last4 or "????") + cards.append((str(m.method_id), title if not m.is_default else f"⭐ {title}")) + text = _("payment_methods_title") + if not cards: + text += "\n\n" + _("payment_method_none") + await callback.message.edit_text(f"{msg}\n\n{text}", reply_markup=get_payment_methods_list_keyboard(cards, 0, current_lang, i18n)) try: await callback.answer() except Exception: