refactor: split backend domains and add API behavior coverage

This commit is contained in:
3252a8
2026-05-13 18:38:10 +03:00
parent a96007d48a
commit 7401649841
103 changed files with 10558 additions and 9832 deletions
+18 -18
View File
@@ -71,7 +71,7 @@ async def process_successful_payment(
or (not payment_db_id_str and not auto_renew_subscription_id_str)
):
logging.error(
f"Missing crucial metadata for payment: {payment_info_from_webhook.get('id')}, metadata: {metadata}"
f"Missing crucial metadata for payment: {payment_info_from_webhook.get('id')}, metadata: {metadata}" # noqa: E501
)
return
@@ -103,7 +103,7 @@ async def process_successful_payment(
try:
if not yk_payment_id_from_hook:
logging.error(
"Auto-renew webhook missing YooKassa payment id; cannot ensure payment record."
"Auto-renew webhook missing YooKassa payment id; cannot ensure payment record." # noqa: E501
)
return
from db.dal import payment_dal as _payment_dal
@@ -126,7 +126,7 @@ async def process_successful_payment(
payment_db_id = payment_record.payment_id
except Exception as e_ensure:
logging.error(
f"Failed to ensure payment record for auto-renew webhook (YK {payment_info_from_webhook.get('id')}): {e_ensure}",
f"Failed to ensure payment record for auto-renew webhook (YK {payment_info_from_webhook.get('id')}): {e_ensure}", # noqa: E501
exc_info=True,
)
return
@@ -140,14 +140,14 @@ async def process_successful_payment(
if payment_record and payment_record.status == "succeeded":
logging.info(
f"Skipping duplicate YooKassa webhook for payment {payment_db_id} (YK: {yk_payment_id_from_hook})."
f"Skipping duplicate YooKassa webhook for payment {payment_db_id} (YK: {yk_payment_id_from_hook})." # noqa: E501
)
return
db_user = await user_dal.get_user_by_id(session, user_id)
if not db_user:
logging.error(
f"User {user_id} not found in DB during successful payment processing for YK ID {payment_info_from_webhook.get('id')}. Payment record {payment_db_id}."
f"User {user_id} not found in DB during successful payment processing for YK ID {payment_info_from_webhook.get('id')}. Payment record {payment_db_id}." # noqa: E501
)
await payment_dal.update_payment_status_by_db_id(
@@ -260,7 +260,7 @@ async def process_successful_payment(
if not activation_details or not activation_details.get("end_date"):
logging.error(
f"Failed to activate subscription for user {user_id} after payment {yk_payment_id_from_hook}"
f"Failed to activate subscription for user {user_id} after payment {yk_payment_id_from_hook}" # noqa: E501
)
raise Exception(f"Subscription Error: Failed to activate for user {user_id}")
@@ -272,7 +272,7 @@ async def process_successful_payment(
)
if not updated_payment_record:
logging.error(
f"Failed to update payment record {payment_db_id} for yk_id {yk_payment_id_from_hook}"
f"Failed to update payment record {payment_db_id} for yk_id {yk_payment_id_from_hook}" # noqa: E501
)
raise Exception(f"DB Error: Could not update payment record {payment_db_id}")
@@ -403,7 +403,7 @@ async def process_successful_payment(
)
else:
logging.error(
f"Critical error: final_end_date_for_user is None for user {user_id} after successful payment logic."
f"Critical error: final_end_date_for_user is None for user {user_id} after successful payment logic." # noqa: E501
)
details_message = _("payment_successful_error_details")
@@ -455,7 +455,7 @@ async def process_successful_payment(
except Exception as e_process:
logging.error(
f"Error during process_successful_payment main try block for user {user_id}: {e_process}",
f"Error during process_successful_payment main try block for user {user_id}: {e_process}", # noqa: E501
exc_info=True,
)
@@ -496,11 +496,11 @@ async def process_cancelled_payment(
if updated_payment:
logging.info(
f"Payment {payment_db_id} (YK: {payment_info_from_webhook.get('id')}) status updated to cancelled for user {user_id}."
f"Payment {payment_db_id} (YK: {payment_info_from_webhook.get('id')}) status updated to cancelled for user {user_id}." # noqa: E501
)
else:
logging.warning(
f"Could not find payment record {payment_db_id} to update status to cancelled for user {user_id}."
f"Could not find payment record {payment_db_id} to update status to cancelled for user {user_id}." # noqa: E501
)
db_user = await user_dal.get_user_by_id(session, user_id)
@@ -513,7 +513,7 @@ async def process_cancelled_payment(
except Exception as e_process_cancel:
logging.error(
f"Error processing cancelled payment for user {user_id}, payment_db_id {payment_db_id}: {e_process_cancel}",
f"Error processing cancelled payment for user {user_id}, payment_db_id {payment_db_id}: {e_process_cancel}", # noqa: E501
exc_info=True,
)
raise
@@ -547,7 +547,7 @@ async def yookassa_webhook_route(request: web.Request):
logging.info(
f"YooKassa Webhook Parsed: Event='{notification_object.event}', "
f"PaymentId='{payment_data_from_notification.id}', Status='{payment_data_from_notification.status}'"
f"PaymentId='{payment_data_from_notification.id}', Status='{payment_data_from_notification.status}'" # noqa: E501
)
if (
@@ -556,7 +556,7 @@ async def yookassa_webhook_route(request: web.Request):
or payment_data_from_notification.metadata is None
):
logging.error(
f"YooKassa webhook payment {payment_data_from_notification.id} lacks metadata. Cannot process."
f"YooKassa webhook payment {payment_data_from_notification.id} lacks metadata. Cannot process." # noqa: E501
)
return web.Response(status=200, text="ok_error_no_metadata")
@@ -633,8 +633,8 @@ async def yookassa_webhook_route(request: web.Request):
await session.commit()
else:
logging.warning(
f"Payment Succeeded event for {payment_dict_for_processing.get('id')} "
f"but data not as expected: status='{payment_dict_for_processing.get('status')}', "
f"Payment Succeeded event for {payment_dict_for_processing.get('id')} " # noqa: E501
f"but data not as expected: status='{payment_dict_for_processing.get('status')}', " # noqa: E501
f"paid='{payment_dict_for_processing.get('paid')}'"
)
elif notification_object.event == YOOKASSA_EVENT_PAYMENT_CANCELED:
@@ -682,7 +682,7 @@ async def yookassa_webhook_route(request: web.Request):
"yoo-money",
"wallet",
}:
# Normalize wallet display name to avoid leaking full account from title
# Normalize wallet display name to avoid leaking full account from title # noqa: E501
display_network = "YooMoney"
if (
isinstance(account_number, str)
@@ -767,7 +767,7 @@ async def yookassa_webhook_route(request: web.Request):
except Exception:
await session.rollback()
logging.exception(
"Error processing YooKassa webhook event '%s' for YK Payment ID %s in DB transaction.",
"Error processing YooKassa webhook event '%s' for YK Payment ID %s in DB transaction.", # noqa: E501
notification_object.event,
payment_dict_for_processing.get("id"),
)
+4 -4
View File
@@ -95,7 +95,7 @@ async def process_promo_code_input(
session: AsyncSession,
):
logging.info(
f"Processing promo code input from user {message.from_user.id} in state {await state.get_state()}: '{message.text}'"
f"Processing promo code input from user {message.from_user.id} in state {await state.get_state()}: '{message.text}'" # noqa: E501
)
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
@@ -122,7 +122,7 @@ async def process_promo_code_input(
):
is_suspicious = True
logging.warning(
f"Suspicious input for promo code by user {user.id} (len: {len(code_input)}): '{code_input}'"
f"Suspicious input for promo code by user {user.id} (len: {len(code_input)}): '{code_input}'" # noqa: E501
)
response_to_user_text = ""
@@ -182,7 +182,7 @@ async def process_promo_code_input(
)
await state.clear()
logging.info(
f"Promo code input '{code_input}' processing finished for user {message.from_user.id}. State cleared."
f"Promo code input '{code_input}' processing finished for user {message.from_user.id}. State cleared." # noqa: E501
)
@@ -203,7 +203,7 @@ async def cancel_promo_input_via_button(
return
logging.info(
f"User {callback.from_user.id} cancelled promo code input via button from state {await state.get_state()}. Clearing state."
f"User {callback.from_user.id} cancelled promo code input via button from state {await state.get_state()}. Clearing state." # noqa: E501
)
await state.clear()
+1 -1
View File
@@ -29,7 +29,7 @@ async def referral_command_handler(
target_message_obj = event.message if isinstance(event, types.CallbackQuery) else event
if not target_message_obj:
logging.error(
"Target message is None in referral_command_handler (possibly from callback without message)."
"Target message is None in referral_command_handler (possibly from callback without message)." # noqa: E501
)
if isinstance(event, types.CallbackQuery):
await event.answer("Error displaying referral info.", show_alert=True)
+5 -6
View File
@@ -115,7 +115,7 @@ async def send_main_menu(
await safe_answer_callback(target_event)
except Exception as e_send_edit:
logging.warning(
f"Failed to send/edit main menu (user: {user_id}, is_edit: {is_edit}): {type(e_send_edit).__name__} - {e_send_edit}."
f"Failed to send/edit main menu (user: {user_id}, is_edit: {is_edit}): {type(e_send_edit).__name__} - {e_send_edit}." # noqa: E501
)
if is_edit and target_message_obj:
try:
@@ -530,7 +530,7 @@ async def start_command_handler(
return
logging.info(
f"New user {user_id} added to session. Referred by: {referred_by_user_id or 'N/A'}."
f"New user {user_id} added to session. Referred by: {referred_by_user_id or 'N/A'}." # noqa: E501
)
# Auto-grant referral welcome bonus to newly registered referred users.
@@ -550,7 +550,7 @@ async def start_command_handler(
if referral_bonus_end_date:
await session.commit()
logging.info(
"Referral welcome bonus applied: user %s got %s days, new end date %s.",
"Referral welcome bonus applied: user %s got %s days, new end date %s.", # noqa: E501
user_id,
referral_welcome_days,
referral_bonus_end_date.isoformat(),
@@ -566,7 +566,7 @@ async def start_command_handler(
else:
await session.rollback()
logging.warning(
"Referral welcome bonus was not applied for user %s (referred by %s).",
"Referral welcome bonus was not applied for user %s (referred by %s).", # noqa: E501
user_id,
referred_by_user_id,
)
@@ -708,7 +708,7 @@ async def start_command_handler(
else:
await session.commit()
logging.warning(
f"Failed to auto-apply promo code '{promo_code_to_apply}' for user {user_id}: {result}"
f"Failed to auto-apply promo code '{promo_code_to_apply}' for user {user_id}: {result}" # noqa: E501
)
await message.answer(str(result), parse_mode="HTML")
# Continue to show main menu if promo failed
@@ -916,7 +916,6 @@ async def main_action_callback_handler(
session: AsyncSession,
):
action = callback.data.split(":")[1]
user_id = callback.from_user.id
if action in {"back_to_main", "back_to_main_keep", "bot_interface"}:
await state.clear()
+6 -6
View File
@@ -329,7 +329,7 @@ async def tariff_topup_list_callback(
carryover_lines = []
if rub_packages or premium_packages:
carryover_lines.append(
"Докупленный трафик не сгорает: сначала расходуется месячный лимит, затем докупленный остаток."
"Докупленный трафик не сгорает: сначала расходуется месячный лимит, затем докупленный остаток." # noqa: E501
)
if int(active.get("premium_limit_bytes") or 0) > 0:
premium_left = max(
@@ -345,7 +345,7 @@ async def tariff_topup_list_callback(
if len(labels) > len(visible):
premium_lines.append(f"• ... еще {len(labels) - len(visible)}")
premium_lines.append(
f"Premium использовано: {active.get('premium_used')} из {active.get('premium_limit')}. Осталось: {premium_left / 2**30:.2f} GB."
f"Premium использовано: {active.get('premium_used')} из {active.get('premium_limit')}. Осталось: {premium_left / 2**30:.2f} GB." # noqa: E501
)
text = get_text("choose_payment_method_traffic")
if carryover_lines:
@@ -647,7 +647,7 @@ async def tariff_change_confirm_apply_callback(
],
]
await callback.message.edit_text(
f"Подтвердите смену тарифа\n\nНовый тариф: {target.name(current_lang)}\nИзменение: {action_text}",
f"Подтвердите смену тарифа\n\nНовый тариф: {target.name(current_lang)}\nИзменение: {action_text}", # noqa: E501
reply_markup=InlineKeyboardMarkup(inline_keyboard=rows),
)
await callback.answer()
@@ -680,7 +680,7 @@ async def tariff_change_confirm_pay_callback(
],
]
await callback.message.edit_text(
f"Подтвердите смену тарифа\n\nНовый тариф: {target.name(current_lang)}\nБудет создана оплата на {amount_raw} RUB.",
f"Подтвердите смену тарифа\n\nНовый тариф: {target.name(current_lang)}\nБудет создана оплата на {amount_raw} RUB.", # noqa: E501
reply_markup=InlineKeyboardMarkup(inline_keyboard=rows),
)
await callback.answer()
@@ -905,7 +905,7 @@ async def my_subscription_command_handler(
f"Докупленный остаток: <b>{premium_balance / 2**30:.2f} GB</b>\n"
"Отдельный лимит действует на:\n"
f"{label_block}\n\n"
"Premium-докупка не сгорает: сначала расходуется месячный лимит premium-серверов, затем докупленный premium-трафик."
"Premium-докупка не сгорает: сначала расходуется месячный лимит premium-серверов, затем докупленный premium-трафик." # noqa: E501
)
base_markup = get_back_to_main_menu_markup(
@@ -1028,7 +1028,7 @@ async def my_subscription_command_handler(
[
InlineKeyboardButton(
text=toggle_text,
callback_data=f"toggle_autorenew:{local_sub.subscription_id}:{1 if not local_sub.auto_renew_enabled else 0}",
callback_data=f"toggle_autorenew:{local_sub.subscription_id}:{1 if not local_sub.auto_renew_enabled else 0}", # noqa: E501
)
]
)
@@ -310,7 +310,7 @@ async def payment_method_view(
last_tx = lp.created_at.strftime("%Y-%m-%d")
except Exception:
pass
details = f"{title}\n{_('payment_method_added_at', date=added_at)}\n{_('payment_method_last_tx', date=last_tx)}"
details = f"{title}\n{_('payment_method_added_at', date=added_at)}\n{_('payment_method_last_tx', date=last_tx)}" # noqa: E501
await callback.message.edit_text(
details,
reply_markup=get_payment_method_details_keyboard(
@@ -366,7 +366,7 @@ async def payment_method_view(
return _("payment_method_generic_title", network=network_name)
title = _format_pm_title(billing.card_network, billing.card_last4)
details = f"{title}\n{_('payment_method_added_at', date=added_at)}\n{_('payment_method_last_tx', date=last_tx)}"
details = f"{title}\n{_('payment_method_added_at', date=added_at)}\n{_('payment_method_last_tx', date=last_tx)}" # noqa: E501
await callback.message.edit_text(
details,
reply_markup=get_payment_method_details_keyboard(
@@ -145,7 +145,7 @@ async def pay_fk_callback_handler(
except Exception as e_status:
await session.rollback()
logging.error(
f"FreeKassa: failed to store provider order id for payment {payment_record.payment_id}: {e_status}",
f"FreeKassa: failed to store provider order id for payment {payment_record.payment_id}: {e_status}", # noqa: E501
exc_info=True,
)
@@ -209,7 +209,7 @@ async def pay_fk_callback_handler(
return
logging.error(
"FreeKassa: create_order succeeded but no payment link returned for payment %s. Response: %s",
"FreeKassa: create_order succeeded but no payment link returned for payment %s. Response: %s", # noqa: E501
payment_record.payment_id,
response_data,
)
@@ -230,7 +230,7 @@ async def pay_fk_callback_handler(
except Exception as e_status:
await session.rollback()
logging.error(
f"FreeKassa: failed to mark payment {payment_record.payment_id} as failed_creation: {e_status}",
f"FreeKassa: failed to mark payment {payment_record.payment_id} as failed_creation: {e_status}", # noqa: E501
exc_info=True,
)
@@ -179,7 +179,7 @@ async def pay_platega_callback_handler(
except Exception as e_status:
await session.rollback()
logging.error(
f"Platega: failed to store transaction id for payment {payment_record.payment_id}: {e_status}",
f"Platega: failed to store transaction id for payment {payment_record.payment_id}: {e_status}", # noqa: E501
exc_info=True,
)
@@ -232,7 +232,7 @@ async def pay_platega_callback_handler(
return
logging.error(
"Platega: transaction created but missing transaction id or payment link for payment %s. Response: %s",
"Platega: transaction created but missing transaction id or payment link for payment %s. Response: %s", # noqa: E501
payment_record.payment_id,
response_data,
)
@@ -247,7 +247,7 @@ async def pay_platega_callback_handler(
except Exception as e_status:
await session.rollback()
logging.error(
f"Platega: failed to mark payment {payment_record.payment_id} as failed_creation: {e_status}",
f"Platega: failed to mark payment {payment_record.payment_id} as failed_creation: {e_status}", # noqa: E501
exc_info=True,
)
@@ -138,7 +138,7 @@ async def pay_severpay_callback_handler(
except Exception as e_status:
await session.rollback()
logging.error(
f"SeverPay: failed to store provider payment id for payment {payment_record.payment_id}: {e_status}",
f"SeverPay: failed to store provider payment id for payment {payment_record.payment_id}: {e_status}", # noqa: E501
exc_info=True,
)
@@ -207,7 +207,7 @@ async def pay_severpay_callback_handler(
except Exception as e_status:
await session.rollback()
logging.error(
f"SeverPay: failed to mark payment {payment_record.payment_id} as failed_creation: {e_status}",
f"SeverPay: failed to mark payment {payment_record.payment_id} as failed_creation: {e_status}", # noqa: E501
exc_info=True,
)
@@ -64,7 +64,7 @@ async def select_subscription_period_callback_handler(
)
if currency_methods_enabled:
logging.error(
"Currency price missing for traffic option %s while fiat providers are enabled.",
"Currency price missing for traffic option %s while fiat providers are enabled.", # noqa: E501
months,
)
try:
@@ -76,7 +76,7 @@ async def select_subscription_period_callback_handler(
currency_symbol_val = ""
else:
logging.error(
f"Price not found for option {months} using {'traffic_packages' if traffic_mode else 'subscription_options'}."
f"Price not found for option {months} using {'traffic_packages' if traffic_mode else 'subscription_options'}." # noqa: E501
)
try:
await callback.answer(get_text("error_try_again"), show_alert=True)
@@ -115,7 +115,7 @@ async def _initiate_yk_payment(
db_payment_record = await payment_dal.create_payment_record(session, payment_record_data)
await session.commit()
logging.info(
f"Payment record {db_payment_record.payment_id} created for user {user_id} with status 'pending_yookassa'."
f"Payment record {db_payment_record.payment_id} created for user {user_id} with status 'pending_yookassa'." # noqa: E501
)
except Exception as e_db_payment:
await session.rollback()
@@ -227,7 +227,7 @@ async def _initiate_yk_payment(
except Exception as e_db_update_ykid:
await session.rollback()
logging.error(
f"Failed to update payment record {db_payment_record.payment_id} with YK ID: {e_db_update_ykid}",
f"Failed to update payment record {db_payment_record.payment_id} with YK ID: {e_db_update_ykid}", # noqa: E501
exc_info=True,
)
try:
@@ -300,7 +300,7 @@ async def _initiate_yk_payment(
except Exception as e_db_update_saved:
await session.rollback()
logging.error(
f"Failed to update saved-card payment record {db_payment_record.payment_id}: {e_db_update_saved}",
f"Failed to update saved-card payment record {db_payment_record.payment_id}: {e_db_update_saved}", # noqa: E501
exc_info=True,
)
try:
@@ -334,11 +334,11 @@ async def _initiate_yk_payment(
except Exception as e_db_fail_create:
await session.rollback()
logging.error(
f"Additionally failed to update payment record to 'failed_creation': {e_db_fail_create}",
f"Additionally failed to update payment record to 'failed_creation': {e_db_fail_create}", # noqa: E501
exc_info=True,
)
logging.error(
f"Failed to create payment in YooKassa for user {user_id}, payment_db_id {db_payment_record.payment_id}. Response: {payment_response_yk}"
f"Failed to create payment in YooKassa for user {user_id}, payment_db_id {db_payment_record.payment_id}. Response: {payment_response_yk}" # noqa: E501
)
try:
await callback.message.edit_text(get_text("error_payment_gateway"))
+1 -2
View File
@@ -40,10 +40,9 @@ async def request_trial_confirmation_handler(
pass
return
show_trial_btn_in_menu_if_fail = False
if settings.TRIAL_ENABLED:
if not await subscription_service.has_had_any_subscription(session, user_id):
show_trial_btn_in_menu_if_fail = True
pass
if not settings.TRIAL_ENABLED:
await callback.message.edit_text(