From 65b854279195d986f94216dfa790acfdb180dde2 Mon Sep 17 00:00:00 2001 From: Joan Date: Tue, 16 Sep 2025 23:00:28 +0200 Subject: [PATCH] Did some other modifications --- app/database.py | 17 ++++++++++++ app/handlers.py | 7 +++-- app/helpers.py | 2 +- app/paypal_processor.py | 61 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 80 insertions(+), 7 deletions(-) diff --git a/app/database.py b/app/database.py index 4f086c3..eabc6f7 100644 --- a/app/database.py +++ b/app/database.py @@ -641,5 +641,22 @@ def get_all_invoice_ids(raffle_id): except Exception as e: logger.error(f"Error getting invoice IDs for raffle {raffle_id}: {e}") return [] + finally: + conn.close() + +def delete_reservation_timestamp(invoice_id): + """Clears the reservation_timestamp for a participant by invoice_id.""" + conn = connect_db() + cur = conn.cursor() + try: + cur.execute( + "UPDATE participants SET reservation_timestamp=NULL WHERE invoice_id=? AND step='waiting_for_payment'", + (invoice_id,) + ) + conn.commit() + logger.info(f"Cleared reservation_timestamp for invoice {invoice_id}") + except Exception as e: + logger.error(f"Error clearing reservation_timestamp for invoice {invoice_id}: {e}") + conn.rollback() finally: conn.close() \ No newline at end of file diff --git a/app/handlers.py b/app/handlers.py index 0973446..65ff72e 100644 --- a/app/handlers.py +++ b/app/handlers.py @@ -34,7 +34,7 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): madrid_tz = pytz.timezone("Europe/Madrid") current_time = datetime.now(madrid_tz) if current_time.time() >= dtime(20, 55) and current_time.time() <= dtime(22, 0): - await update.message.reply_text("No puedes unirte al sorteo en este momento.") + await update.message.reply_text("Lo siento, no puedes unirte a sorteos entre las 20:55 y las 22:00 (hora de España) para evitar conflictos con el sorteo en directo. Inténtalo de nuevo más tarde.") return if len(get_reserved_numbers(user.id, raffle_id)) > 0: await update.message.reply_text("Ya tienes participaciones reservadas para este sorteo. Por favor, completa la donación o espera a que caduquen antes de unirte de nuevo.") @@ -433,6 +433,7 @@ async def number_callback(update: Update, context: CallbackContext): participant_user_id = participant_data['user_id'] participant_step = participant_data['step'] participant_db_id = participant_data['id'] # The ID from the participants table + user_name = participant_data['user_name'] or participant_data['user_id'] if participant_user_id == user_id: # User clicked a number they already interact with @@ -440,7 +441,7 @@ async def number_callback(update: Update, context: CallbackContext): # User clicked a number they have reserved -> Deselect it remove_reserved_number(participant_db_id, number_string) await query.answer(f"Has quitado la reserva de la participación {number_string}.") - logger.info(f"User {user_id} deselected reserved number {number_string} for raffle {raffle_id}") + logger.info(f"User {user_id} (Name: {user_name}) deselected reserved number {number_string} for raffle {raffle_id}") # Refresh keyboard keyboard = generate_numbers_keyboard(raffle_id, user_id, page=page) await query.edit_message_reply_markup(reply_markup=keyboard) @@ -463,7 +464,7 @@ async def number_callback(update: Update, context: CallbackContext): # Number is free -> Reserve it for the user reserve_number(user_id, username, raffle_id, number_string) await query.answer(f"Participación {number_string} reservada para ti. Confirma tu selección cuando termines.") - logger.info(f"User {user_id} reserved number {number_string} for raffle {raffle_id}") + logger.info(f"User {user_id} (Name: {username}) reserved number {number_string} for raffle {raffle_id}") # Refresh keyboard to show the lock icon keyboard = generate_numbers_keyboard(raffle_id, user_id, page=page) await query.edit_message_reply_markup(reply_markup=keyboard) diff --git a/app/helpers.py b/app/helpers.py index 63d3e74..d7b0829 100644 --- a/app/helpers.py +++ b/app/helpers.py @@ -348,7 +348,7 @@ def generate_table_image(raffle_id): legend_items = [ ("Libre", free_bg_color), ("Reservado", reserved_bg_color), - ("Pagado", taken_bg_color) + ("Asignado", taken_bg_color) ] spacing = 280 # more spacing between legend items diff --git a/app/paypal_processor.py b/app/paypal_processor.py index cada401..88ba9d3 100644 --- a/app/paypal_processor.py +++ b/app/paypal_processor.py @@ -16,9 +16,10 @@ from database import ( get_remaining_numbers_amount, get_main_message_id, store_update_message_id, get_update_message_id, - get_last_n_other_participants, get_remaining_numbers + get_last_n_other_participants, get_remaining_numbers, + delete_reservation_timestamp, cancel_reservation_by_id ) -from config import BOT_TOKEN, BOT_NAME, WEBHOOK_ID, TYC_DOCUMENT_URL, REVERSE_CHANNELS, NEWRELIC_API_KEY, PAYPAL_URL +from config import BOT_TOKEN, BOT_NAME, WEBHOOK_ID, TYC_DOCUMENT_URL, REVERSE_CHANNELS, NEWRELIC_API_KEY, PAYPAL_URL, ADMIN_IDS from newrelic_telemetry_sdk import Log, LogClient app = Flask(__name__) @@ -411,7 +412,12 @@ def paypal_webhook(): # Capture payment resource = event["resource"] invoice_id = resource.get("id") # capture ID - capture_order(invoice_id, access_token) + delete_reservation_timestamp(invoice_id) + response = capture_order(invoice_id, access_token) + if response.get("status") == "COMPLETED": + logger.info(f"Payment for order {invoice_id} captured successfully.") + else: + logger.error(f"Failed to capture payment for order {invoice_id}. Response: {response}") elif event_type == "PAYMENT.CAPTURE.COMPLETED": logger.info(f"✅ Payment completed: {event['resource']['id']}") resource = event["resource"] @@ -426,6 +432,55 @@ def paypal_webhook(): # Process the valid payment receive_paypal_payment(invoice_id, payment_status, payment_amount) + elif event_type == "PAYMENT.CAPTURE.PENDING": + logger.info(f"⏳ Payment pending: {event['resource']['id']}. Awaiting completion.") + # Optionally notify user about pending status + resource = event["resource"] + invoice_id = resource["supplementary_data"]["related_ids"]["order_id"] + payment_status = resource.get("status") + payment_amount = resource["amount"]["value"] + status_details = resource["status_details"]["reason"] + + delete_reservation_timestamp(invoice_id) + + if status_details == "PENDING_REVIEW": + logger.info(f"Payment for invoice {invoice_id} is pending review. Notifying admins.") + for admin_id in ADMIN_IDS: + send_telegram_message( + admin_id, + f"⚠️ El pago para la factura {invoice_id} está pendiente. " + f"Estado actual: '{payment_status}'. " + f"Detalles: '{status_details}'. " + f"Por favor, revisa manualmente si es necesario." + ) + elif status_details == "REFUNDED": + logger.info(f"Payment for invoice {invoice_id} has been refunded. Notifying admins.") + for admin_id in ADMIN_IDS: + send_telegram_message( + admin_id, + f"⚠️ El pago para la factura {invoice_id} ha sido reembolsado. " + f"Estado actual: '{payment_status}'. " + f"Detalles: '{status_details}'. " + f"Por favor, revisa manualmente si es necesario." + ) + elif event_type == "PAYMENT.CAPTURE.DENIED": + logger.info(f"❌ Payment denied: {event['resource']['id']}. Notifying user.") + resource = event["resource"] + invoice_id = resource["supplementary_data"]["related_ids"]["order_id"] + payment_status = resource.get("status") + payment_amount = resource["amount"]["value"] + + participant_data = get_user_by_invoice_id(invoice_id) + if participant_data: + user_id = participant_data['user_id'] + cancel_reservation_by_id(participant_data['id']) + send_telegram_message( + user_id, + f"❌ Tu pago para la factura {invoice_id} ha sido denegado. " + f"Por favor, inténtalo de nuevo o contacta con un administrador si crees que es un error." + ) + else: + logger.warning(f"Could not find participant data for denied payment invoice {invoice_id}.") else: logger.info(f"ℹ️ Received event: {event_type}") else: