From 371bce35d7fac8bbc750bf032e8ac37516fe8e92 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Wed, 1 Oct 2025 13:08:11 +0200 Subject: [PATCH] Refactor contact information handling in tournament registration --- tournaments/forms.py | 2 +- tournaments/models/player_registration.py | 3 + tournaments/models/team_registration.py | 10 +++ tournaments/services/email_service.py | 14 ++-- tournaments/services/payment_service.py | 2 + .../services/tournament_registration.py | 23 ++++-- .../templates/register_tournament.html | 8 +- tournaments/views.py | 81 ++++++++++++++++++- 8 files changed, 124 insertions(+), 19 deletions(-) diff --git a/tournaments/forms.py b/tournaments/forms.py index 3c7c7f8..e38edeb 100644 --- a/tournaments/forms.py +++ b/tournaments/forms.py @@ -171,7 +171,7 @@ class SimpleForm(forms.Form): class TournamentRegistrationForm(forms.Form): #first_name = forms.CharField(label='Prénom', max_length=50) #last_name = forms.CharField(label='Nom', max_length=50) - email = forms.EmailField(label='E-mail', widget=forms.EmailInput(attrs={'readonly': 'readonly'})) + email = forms.EmailField(label='E-mail') mobile_number = forms.CharField( label='Téléphone', max_length=15, diff --git a/tournaments/models/player_registration.py b/tournaments/models/player_registration.py index 81879bd..4dbd9ad 100644 --- a/tournaments/models/player_registration.py +++ b/tournaments/models/player_registration.py @@ -228,3 +228,6 @@ class PlayerRegistration(TournamentSubModel): return self.team_registration.tournament.entry_fee else: return 0 + + def player_contact(self): + return self.contact_email or self.email diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py index 6094e9d..09c3e66 100644 --- a/tournaments/models/team_registration.py +++ b/tournaments/models/team_registration.py @@ -550,3 +550,13 @@ class TeamRegistration(TournamentSubModel): # Check payment status for each player payment_statuses = [player.get_player_registration_fee() for player in player_registrations] return sum(payment_statuses) + + def team_contact(self): + if self.user: + return self.user.email + else: + player_registrations = self.players_sorted_by_captain + if len(player_registrations) > 0: + return player_registrations[0].player_contact() + + return None diff --git a/tournaments/services/email_service.py b/tournaments/services/email_service.py index 8b5ed54..a94a58d 100644 --- a/tournaments/services/email_service.py +++ b/tournaments/services/email_service.py @@ -531,7 +531,7 @@ class TournamentEmailService: topic = message_type.email_topic(tournament.federal_level_category, captain.time_to_confirm) email_subject = TournamentEmailService.email_subject(tournament, topic) - TournamentEmailService._send_email(captain.email, email_subject, email_body) + TournamentEmailService._send_email(captain.player_contact(), email_subject, email_body) @staticmethod def _build_email_content(message_type, recipient, tournament, tournament_details_str, other_player, request=None, waiting_list_position=None): @@ -721,11 +721,13 @@ class TournamentEmailService: tournament_prefix_that = federal_level_category.localized_prefix_that() processed_emails = set() for player in player_registrations: - if not player.email or not player.registered_online: + # Check both email and contact_email fields + player_email = player.player_contact() + if not player_email or not player.registered_online: continue - if player.email in processed_emails: + if player_email in processed_emails: continue - processed_emails.add(player.email) + processed_emails.add(player_email) tournament_details_str = tournament.build_tournament_details_str() other_player = team_registration.get_other_player(player) if len(player_registrations) > 1 else None @@ -772,7 +774,7 @@ class TournamentEmailService: email_body = "".join(body_parts) email_subject = TournamentEmailService.email_subject(tournament, "Confirmation de paiement") - TournamentEmailService._send_email(player.email, email_subject, email_body) + TournamentEmailService._send_email(player.player_contact(), email_subject, email_body) @staticmethod def send_refund_confirmation(tournament, team_registration, refund_details): @@ -856,4 +858,4 @@ class TournamentEmailService: email_body = "".join(body_parts) email_subject = TournamentEmailService.email_subject(tournament, "Confirmation de remboursement") - TournamentEmailService._send_email(player.email, email_subject, email_body) + TournamentEmailService._send_email(player.player_contact(), email_subject, email_body) diff --git a/tournaments/services/payment_service.py b/tournaments/services/payment_service.py index 0dd8430..d962dfe 100644 --- a/tournaments/services/payment_service.py +++ b/tournaments/services/payment_service.py @@ -76,6 +76,8 @@ class PaymentService: if not team_registration: print(f"[TOURNAMENT PAYMENT] Failed to create team registration") raise Exception("Erreur lors de la création de la réservation") + if not customer_email: + customer_email = team_registration.team_contact() team_registration_id = team_registration.id print(f"[TOURNAMENT PAYMENT] Created team registration: {team_registration_id}") diff --git a/tournaments/services/tournament_registration.py b/tournaments/services/tournament_registration.py index 1e8630b..3228177 100644 --- a/tournaments/services/tournament_registration.py +++ b/tournaments/services/tournament_registration.py @@ -38,7 +38,8 @@ class RegistrationCartManager: 'registration_tournament_id', 'registration_cart_players', 'registration_cart_expiry', - 'registration_mobile_number' + 'registration_mobile_number', + 'registration_email' ] for key in keys_to_delete: if key in self.session: @@ -132,9 +133,12 @@ class RegistrationCartManager: # Get user phone if authenticated user_phone = '' + user_email = '' if hasattr(self.request.user, 'phone'): user_phone = self.request.user.phone + if hasattr(self.request.user, 'email'): + user_email = self.request.user.email # Parse the expiry time from ISO format to datetime expiry_str = self.get_cart_expiry() expiry_datetime = None @@ -155,7 +159,8 @@ class RegistrationCartManager: 'is_cart_expired': self.is_cart_expired(), 'team_fee_from_cart_players': self.team_fee_from_cart_players(), 'team_fee_from_cart_players_formatted': self.team_fee_from_cart_players_formatted(), - 'mobile_number': self.session.get('registration_mobile_number', user_phone) + 'mobile_number': self.session.get('registration_mobile_number', user_phone), + 'email': self.session.get('registration_email', user_email), } # Debug: print the cart content @@ -370,11 +375,13 @@ class RegistrationCartManager: return True, "Joueur retiré." - def update_contact_info(self, mobile_number=None): + def update_contact_info(self, email=None, mobile_number=None): """Update contact info for the cart""" if self.is_cart_expired(): return False, "Votre session d'inscription a expiré, veuillez réessayer." + if email is not None: + self.session['registration_email'] = email if mobile_number is not None: self.session['registration_mobile_number'] = mobile_number @@ -394,6 +401,7 @@ class RegistrationCartManager: tournament_id = cart_data.get('tournament_id') players = cart_data.get('players') mobile_number = cart_data.get('mobile_number') + email = cart_data.get('email') # Validate cart data if not tournament_id: @@ -435,7 +443,7 @@ class RegistrationCartManager: registration_date=timezone.now(), walk_out=False, weight=weight, - user=self.request.user + user= self.request.user if self.request.user.is_authenticated else None ) for player_data in players: # Compute rank and sex using the original logic @@ -511,8 +519,8 @@ class RegistrationCartManager: rank=player_data.get('rank'), computed_rank=player_data.get('computed_rank'), licence_id=player_data.get('licence_id'), - email=matching_user.email if matching_user else player_data.get('email'), - phone_number=matching_user.phone if matching_user else player_data.get('mobile_number'), + contact_email=matching_user.email if matching_user else player_data.get('email', email), + contact_phone_number=matching_user.phone if matching_user else player_data.get('mobile_number', mobile_number), registration_status=RegistrationStatus.CONFIRMED if self.session.get('waiting_list_position', 0) < 0 else RegistrationStatus.WAITING ) @@ -533,7 +541,8 @@ class RegistrationCartManager: 'registration_tournament_id', 'registration_cart_players', 'registration_cart_expiry', - 'registration_mobile_number' + 'registration_mobile_number', + 'registration_email', ] for key in keys_to_clear: diff --git a/tournaments/templates/register_tournament.html b/tournaments/templates/register_tournament.html index d5746b3..b25cb0e 100644 --- a/tournaments/templates/register_tournament.html +++ b/tournaments/templates/register_tournament.html @@ -32,7 +32,13 @@

✅ Votre paiement a bien été effectué et enregistré.

{% endif %}

- Un email de confirmation a été envoyé à l'adresse associée à votre compte Padel Club ({{ user.email }}). Pensez à vérifier vos spams si vous ne recevez pas l'email. En cas de problème, contactez le juge-arbitre. + {% if user.email %} + Un email de confirmation a été envoyé à l'adresse associée à votre compte Padel Club ({{ user.email }}). Pensez à vérifier vos spams si vous ne recevez pas l'email. En cas de problème, contactez le juge-arbitre. + {% elif registered_team.team_contact %} + Un email de confirmation a été envoyé à l'adresse associée à votre compte Padel Club ({{ registered_team.team_contact }}). Pensez à vérifier vos spams si vous ne recevez pas l'email. En cas de problème, contactez le juge-arbitre. + {% else %} + Aucun email de confirmation n'a été envoyé car vous n'avez pas fourni d'adresse email. Contactez le juge-arbitre. + {% endif %}

{% else %} {% if not registration_successful %} diff --git a/tournaments/views.py b/tournaments/views.py index 41226ba..b64e6f8 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -1583,9 +1583,14 @@ def proceed_to_payment(request, tournament_id): messages.error(request, f"Erreur lors de la création de la session de paiement: {str(e)}") return redirect('tournament-info', tournament_id=tournament_id) -@login_required def tournament_payment_success(request, tournament_id): """Handle successful Stripe payment for tournament registration""" + + # For unauthenticated users, process payment and redirect directly to registration page + if not request.user.is_authenticated: + return _handle_unauthenticated_payment_success(request, tournament_id) + + # Original logic for authenticated users # Get checkout session ID from session checkout_session_id = request.session.get('stripe_checkout_session_id') if not checkout_session_id: @@ -1641,6 +1646,68 @@ def tournament_payment_success(request, tournament_id): # For direct payments, go to tournament info return redirect('tournament-info', tournament_id=tournament_id) + +def _handle_unauthenticated_payment_success(request, tournament_id): + """Handle payment success for unauthenticated users""" + print(f"[PAYMENT SUCCESS] Handling unauthenticated user payment for tournament {tournament_id}") + + # Get checkout session ID from session + checkout_session_id = request.session.get('stripe_checkout_session_id') + if not checkout_session_id: + print(f"[PAYMENT SUCCESS] No checkout session ID found") + messages.error(request, "Session de paiement introuvable.") + return redirect('register_tournament', tournament_id=tournament_id) + + try: + # Verify payment status with Stripe + stripe.api_key = settings.STRIPE_SECRET_KEY + print(f"[PAYMENT SUCCESS] Retrieving checkout session: {checkout_session_id}") + + stripe_account_id = request.session.get('stripe_account_id') + if not stripe_account_id: + checkout_session = stripe.checkout.Session.retrieve(checkout_session_id) + else: + checkout_session = stripe.checkout.Session.retrieve(checkout_session_id, stripe_account=stripe_account_id) + + print(f"[PAYMENT SUCCESS] Payment status: {checkout_session.payment_status}") + + if checkout_session.payment_status == 'paid': + # Process the payment success + payment_service = PaymentService(request) + success = payment_service.process_successful_payment(checkout_session) + + print(f"[PAYMENT SUCCESS] Payment processing success: {success}") + + if success: + # Always set success flags for unauthenticated users since they come from registration + request.session['registration_successful'] = True + request.session['registration_paid'] = True + + # Clear payment-related session data + for key in ['stripe_checkout_session_id', 'team_registration_id', 'payment_source_page', 'stripe_account_id']: + if key in request.session: + del request.session[key] + + print(f"[PAYMENT SUCCESS] Redirecting to registration page with success flags") + # Redirect directly to registration page with success context + return redirect('register_tournament', tournament_id=tournament_id) + else: + messages.error(request, "Erreur lors du traitement du paiement.") + else: + messages.error(request, "Le paiement n'a pas été complété.") + + except Exception as e: + print(f"[PAYMENT SUCCESS] Payment processing error: {str(e)}") + messages.error(request, f"Erreur lors de la vérification du paiement: {str(e)}") + + # Clean up session variables even if there was an error + for key in ['stripe_checkout_session_id', 'team_registration_id', 'payment_source_page', 'stripe_account_id']: + if key in request.session: + del request.session[key] + + # Always redirect to registration page for unauthenticated users + return redirect('register_tournament', tournament_id=tournament_id) + @csrf_protect def register_tournament(request, tournament_id): tournament = get_object_or_404(Tournament, id=tournament_id) @@ -1657,7 +1724,7 @@ def register_tournament(request, tournament_id): # Check for registration_successful flag registration_successful = request.session.pop('registration_successful', False) registration_paid = request.session.pop('registration_paid', False) - + registered_team = None # Handle payment cancellation - check for cancelled team registration cancel_team_registration_id = request.session.pop('cancel_team_registration_id', None) if cancel_team_registration_id: @@ -1676,7 +1743,8 @@ def register_tournament(request, tournament_id): if not team_registration.is_paid(): team_registration.delete() print(f"[PAYMENT CANCEL] Deleted unpaid team registration {cancel_team_registration_id}") - + else: + registered_team = team_registration except TeamRegistration.DoesNotExist: print(f"[PAYMENT CANCEL] Team registration {cancel_team_registration_id} not found") except Exception as e: @@ -1702,6 +1770,7 @@ def register_tournament(request, tournament_id): 'tournament': tournament, 'registration_successful': True, 'registration_paid': registration_paid, + 'registered_team': registered_team, 'current_players': [], 'cart_data': {'players': []} } @@ -1823,6 +1892,7 @@ def handle_add_player_request(request, tournament, cart_manager, context): if team_form.is_valid(): # Update cart with mobile number before adding player cart_manager.update_contact_info( + email=team_form.cleaned_data.get('email'), mobile_number=team_form.cleaned_data.get('mobile_number') ) @@ -1834,7 +1904,7 @@ def handle_add_player_request(request, tournament, cart_manager, context): context['current_players'] = cart_data['players'] context['cart_data'] = cart_data context['team_form'] = TournamentRegistrationForm(initial={ - 'email': request.user.email if request.user.is_authenticated else '', + 'email': request.user.email if request.user.is_authenticated else cart_data.get('email', ''), 'mobile_number': cart_data.get('mobile_number', '') }) @@ -1898,6 +1968,7 @@ def handle_register_team_request(request, tournament, cart_manager, context): # Update cart with contact info cart_manager.update_contact_info( + email=team_form.cleaned_data.get('email'), mobile_number=team_form.cleaned_data.get('mobile_number') ) @@ -1922,6 +1993,7 @@ def handle_register_team_request(request, tournament, cart_manager, context): ) context['registration_successful'] = True + context['registered_team'] = result context['registration_paid'] = False context['current_players'] = [] context['add_player_form'] = None # No more adding players after success @@ -1939,6 +2011,7 @@ def handle_payment_request(request, cart_manager, context, tournament_id): if team_form.is_valid(): # Update cart with contact info cart_manager.update_contact_info( + email=team_form.cleaned_data.get('email'), mobile_number=team_form.cleaned_data.get('mobile_number') )