From 11d691380722a358e213690814ee5f2247dacc33 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Mon, 20 Oct 2025 14:02:45 +0200 Subject: [PATCH] Improve Payment Service: Add Transaction Safety and Error Handling --- tournaments/services/payment_service.py | 94 +++++++++++++++---------- 1 file changed, 58 insertions(+), 36 deletions(-) diff --git a/tournaments/services/payment_service.py b/tournaments/services/payment_service.py index 3a76ba3..099c29b 100644 --- a/tournaments/services/payment_service.py +++ b/tournaments/services/payment_service.py @@ -4,8 +4,10 @@ from django.urls import reverse from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST +from django.db import transaction import stripe from datetime import datetime, timedelta +import traceback from ..models import TeamRegistration, PlayerRegistration, Tournament from ..models.player_registration import PlayerPaymentType @@ -544,47 +546,67 @@ class PaymentService: team_registration_id = metadata.get('team_registration_id') registration_type = metadata.get('registration_type') - if tournament_id and registration_type == 'cart': - try: - tournament = Tournament.objects.get(id=tournament_id) - tournament.reserved_spots = max(0, tournament.reserved_spots - 1) - tournament.save() - print(f"Decreased reserved spots for tournament {tournament_id} after payment failure") - except Tournament.DoesNotExist: - print(f"Tournament not found with ID: {tournament_id}") - except Exception as e: - print(f"Error saving tournament for team registration: {str(e)}") - - if not team_registration_id: - print("No team registration ID found in session") - return False - + # Wrap all database operations in an atomic transaction + # This ensures either all changes are saved or none are try: - print(f"Looking for team registration with ID: {team_registration_id}") - team_registration = TeamRegistration.objects.get(id=team_registration_id) - if tournament_id and registration_type == 'cart' and team_registration.tournament is None: - try: - tournament = Tournament.objects.get(id=tournament_id) - team_registration.tournament = tournament - team_registration.save() - print(f"Saved tournament for team registration {team_registration.id}") - except Tournament.DoesNotExist: - print(f"Tournament not found with ID: {tournament_id}") - except Exception as e: - print(f"Error saving tournament for team registration: {str(e)}") - - if team_registration.is_paid(): - return True - - team_registration.confirm_registration(checkout_session.payment_intent) + with transaction.atomic(): + if tournament_id and registration_type == 'cart': + try: + tournament = Tournament.objects.get(id=tournament_id) + tournament.reserved_spots = max(0, tournament.reserved_spots - 1) + tournament.save() + print(f"Decreased reserved spots for tournament {tournament_id}") + except Tournament.DoesNotExist: + print(f"Tournament not found with ID: {tournament_id}") + except Exception as e: + print(f"Error saving tournament for team registration: {str(e)}") + + if not team_registration_id: + print("No team registration ID found in session") + return False + + print(f"Looking for team registration with ID: {team_registration_id}") + team_registration = TeamRegistration.objects.get(id=team_registration_id) + + if tournament_id and registration_type == 'cart' and team_registration.tournament is None: + try: + tournament = Tournament.objects.get(id=tournament_id) + team_registration.tournament = tournament + team_registration.save() + print(f"Saved tournament for team registration {team_registration.id}") + except Tournament.DoesNotExist: + print(f"Tournament not found with ID: {tournament_id}") + except Exception as e: + print(f"Error saving tournament for team registration: {str(e)}") + + if team_registration.is_paid(): + print(f"Team registration {team_registration.id} is already paid") + return True + + # Update player registration with payment info + team_registration.confirm_registration(checkout_session.payment_intent) + print(f"✅ Registration confirmed and committed to database") - TournamentEmailService.send_payment_confirmation(team_registration, checkout_session.payment_intent) - return True except TeamRegistration.DoesNotExist: print(f"Team registration not found with ID: {team_registration_id}") + return False except Exception as e: - print(f"Error in _process_direct_payment: {str(e)}") - return False + print(f"❌ Error in process_direct_payment database operations: {str(e)}") + traceback.print_exc() + return False + + # After successful database commit, send confirmation email + # Email failures won't affect the payment confirmation + try: + print(f"Sending payment confirmation email...") + TournamentEmailService.send_payment_confirmation(team_registration, checkout_session.payment_intent) + print(f"✅ Email sent successfully") + except Exception as email_error: + print(f"⚠️ Warning: Email sending failed but payment was confirmed: {str(email_error)}") + traceback.print_exc() + # Don't return False - payment is still confirmed + + return True @staticmethod @csrf_exempt