From 01a2c8880ba77304d9731b7fde141dea80bc8ac7 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 14 Apr 2025 18:39:19 +0200 Subject: [PATCH] fix payment errors --- api/views.py | 3 +- tournaments/services/email_service.py | 77 ++++++++++++++++++- tournaments/services/payment_service.py | 2 + .../services/tournament_registration.py | 14 +++- tournaments/views.py | 13 ++++ 5 files changed, 104 insertions(+), 5 deletions(-) diff --git a/api/views.py b/api/views.py index 0f8173a..d13ee9f 100644 --- a/api/views.py +++ b/api/views.py @@ -313,11 +313,12 @@ def process_refund(request, team_registration_id): }, status=403) payment_service = PaymentService(request) + players_serializer = PlayerRegistrationSerializer(team_registration.players_sorted_by_rank, many=True) success, message, refund = payment_service.process_refund(team_registration_id) return Response({ 'success': success, 'message': message, - 'players': team_registration.players_sorted_by_rank + 'players': players_serializer.data }) except Exception as e: return Response({ diff --git a/tournaments/services/email_service.py b/tournaments/services/email_service.py index 0514c3c..1c736c5 100644 --- a/tournaments/services/email_service.py +++ b/tournaments/services/email_service.py @@ -2,6 +2,7 @@ from django.core.mail import EmailMessage from enum import Enum from ..models.enums import RegistrationStatus from ..models.tournament import TeamSortingType +from django.utils import timezone class TeamEmailType(Enum): REGISTERED = "registered" @@ -515,13 +516,85 @@ class TournamentEmailService: # For unpaid teams, add payment instructions payment_info = [ "\n\n⚠️ Paiement des frais d'inscription requis", - f"Les frais d'inscription de {tournament.entry_fee}€ par joueur doivent être payés pour confirmer votre participation.", + f"Les frais d'inscription de {tournament.entry_fee:.2f}€ par joueur doivent être payés pour confirmer votre participation.", "Vous pouvez effectuer le paiement en vous connectant à votre compte Padel Club.", f"Lien pour payer: https://padelclub.app/tournament/{tournament.id}/info" ] return "\n".join(payment_info) + @staticmethod + def send_payment_confirmation(team_registration, payment): + """ + Send a payment confirmation email to team members + + Args: + team_registration: The team registration + payment: The payment details from Stripe + """ + tournament = team_registration.tournament + player_registrations = team_registration.players_sorted_by_rank + + # Calculate payment amount + payment_amount = None + if payment and 'amount' in payment: + # Convert cents to euros + payment_amount = payment['amount'] / 100 + + if payment_amount is None: + payment_amount = tournament.team_fee() + + for player in player_registrations: + if not player.email or not player.registered_online: + continue + + tournament_details_str = tournament.build_tournament_details_str() + other_player = team_registration.get_other_player(player) if len(player_registrations) > 1 else None + + body_parts = [ + "Bonjour,\n\n", + f"Votre paiement pour le tournoi {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} a été reçu avec succès." + ] + + # Add information about the other player if available + if other_player: + body_parts.append( + f"\n\nVous êtes inscrit avec {other_player.name()}, n'oubliez pas de prévenir votre partenaire de la confirmation du paiement." + ) + + # Add payment details + body_parts.append( + f"\n\nMontant payé : {payment_amount:.2f}€" + ) + + payment_date = timezone.now().strftime("%d/%m/%Y") + body_parts.append( + f"\nDate du paiement : {payment_date}" + ) + + absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info" + link_text = "informations sur le tournoi" + absolute_url = f'{link_text}' + + body_parts.append(f"\n\nVoir les {absolute_url}") + + if tournament.team_sorting == TeamSortingType.RANK: + cloture_date = tournament.local_registration_federal_limit().strftime("%d/%m/%Y à %H:%M") + loc = "" + if cloture_date is not None: + loc = f", prévu le {cloture_date}" + body_parts.append(f"\n\nAttention, la sélection définitive se fera par poids d'équipe à la clôture des inscriptions{loc}.") + + body_parts.extend([ + f"\n\n{TournamentEmailService._format_umpire_contact(tournament)}", + "\n\nCeci est un e-mail automatique, veuillez ne pas y répondre." + ]) + + email_body = "".join(body_parts) + + email_subject = TournamentEmailService.email_subject(tournament, "Confirmation de paiement") + TournamentEmailService._send_email(player.email, email_subject, email_body) + @staticmethod def send_refund_confirmation(tournament, team_registration, refund_details): """ @@ -562,7 +635,7 @@ class TournamentEmailService: # Add refund details body_parts.append( - f"\n\nMontant remboursé : {refund_amount}€ par joueur" + f"\n\nMontant remboursé : {refund_amount:.2f}€" ) refund_date = timezone.now().strftime("%d/%m/%Y") diff --git a/tournaments/services/payment_service.py b/tournaments/services/payment_service.py index e043f66..78729d9 100644 --- a/tournaments/services/payment_service.py +++ b/tournaments/services/payment_service.py @@ -233,6 +233,8 @@ class PaymentService: if 'team_registration_id' in self.request.session: del self.request.session['team_registration_id'] + if success: + TournamentEmailService.send_payment_confirmation(team_registration, checkout_session.payment_intent) return success except TeamRegistration.DoesNotExist: print(f"Team registration not found with ID: {team_registration_id}") diff --git a/tournaments/services/tournament_registration.py b/tournaments/services/tournament_registration.py index a237b31..b64b09b 100644 --- a/tournaments/services/tournament_registration.py +++ b/tournaments/services/tournament_registration.py @@ -6,6 +6,7 @@ from ..utils.licence_validator import LicenseValidator from ..utils.player_search import get_player_name_from_csv from ..models.enums import FederalCategory, RegistrationStatus from ..models.player_enums import PlayerSexType, PlayerDataSource +from django.contrib.auth import get_user_model class RegistrationCartManager: """ @@ -351,6 +352,15 @@ class RegistrationCartManager: if player_data.get('found_in_french_federation', False) == True: data_source = PlayerDataSource.FRENCH_FEDERATION # Now using the enum value + User = get_user_model() + matching_user = None + if player_licence_id: + try: + # Using icontains for case-insensitive match + matching_user = User.objects.get(licence_id__icontains=player_licence_id) + except User.DoesNotExist: + pass + # Create player registration with all the original fields PlayerRegistration.objects.create( team_registration=team_registration, @@ -369,8 +379,8 @@ class RegistrationCartManager: rank=rank, computed_rank=computed_rank, licence_id=player_data.get('licence_id'), - email=player_data.get('email'), - phone_number=player_data.get('mobile_number', mobile_number), + 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'), registration_status=RegistrationStatus.CONFIRMED if self.session.get('waiting_list_position', 0) < 0 else RegistrationStatus.WAITING ) diff --git a/tournaments/views.py b/tournaments/views.py index e7fbead..36018b9 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -1306,6 +1306,14 @@ def handle_add_player_request(request, tournament, cart_manager, context): """Handle the 'add_player' POST action""" add_player_form = AddPlayerForm(request.POST) if add_player_form.is_valid(): + # Get the current form data for phone number + team_form = TournamentRegistrationForm(request.POST) + if team_form.is_valid(): + # Update cart with mobile number before adding player + cart_manager.update_contact_info( + mobile_number=team_form.cleaned_data.get('mobile_number') + ) + success, message = cart_manager.add_player(add_player_form.cleaned_data) if success: messages.success(request, message) @@ -1313,6 +1321,11 @@ def handle_add_player_request(request, tournament, cart_manager, context): cart_data = cart_manager.get_cart_data() context['current_players'] = cart_data['players'] + context['team_form'] = TournamentRegistrationForm(initial={ + 'email': request.user.email if request.user.is_authenticated else '', + 'mobile_number': cart_data.get('mobile_number', '') + }) + # Prepare a fresh form for the next player if needed if len(cart_data['players']) < tournament.minimum_player_per_team: context['add_player_form'] = AddPlayerForm()