from django.shortcuts import get_object_or_404 from django.conf import settings from django.urls import reverse import stripe from ..models import TeamRegistration, PlayerRegistration, Tournament from ..models.player_registration import PlayerPaymentType, RegistrationStatus from .email_service import TournamentEmailService from .tournament_registration import RegistrationCartManager from ..utils.extensions import is_not_sqlite_backend class PaymentService: """ Service for handling payment processing for tournament registrations """ def __init__(self, request): self.request = request self.stripe_api_key = settings.STRIPE_SECRET_KEY def create_checkout_session(self, tournament_id, team_fee, cart_data=None, team_registration_id=None): """ Create a Stripe checkout session for tournament payment """ stripe.api_key = self.stripe_api_key tournament = get_object_or_404(Tournament, id=tournament_id) # Check if payments are enabled for this tournament if not tournament.should_request_payment(): raise Exception("Les paiements ne sont pas activés pour ce tournoi.") # Get the umpire's Stripe account ID stripe_account_id = tournament.stripe_account_id if not stripe_account_id: raise Exception("L'arbitre n'a pas configuré son compte Stripe.") # Calculate commission commission_rate = tournament.effective_commission_rate() / 100 platform_amount = int((team_fee * commission_rate) * 100) # Commission in cents # Get user email if authenticated customer_email = self.request.user.email if self.request.user.is_authenticated else None # Determine the appropriate cancel URL based on the context if team_registration_id: # If we're paying for an existing registration, go back to tournament info cancel_url = self.request.build_absolute_uri( reverse('tournament-info', kwargs={'tournament_id': tournament_id}) ) else: # If we're in the registration process, go back to registration form cancel_url = self.request.build_absolute_uri( reverse('register_tournament', kwargs={'tournament_id': tournament_id}) ) # Common checkout session parameters checkout_session_params = { 'payment_method_types': ['card'], 'line_items': [{ 'price_data': { 'currency': 'eur', 'product_data': { 'name': f'{tournament.broadcast_display_name()} du {tournament.formatted_start_date()}', 'description': f'Lieu {tournament.event.club.name}', }, 'unit_amount': int(team_fee * 100), # Amount in cents }, 'quantity': 1, }], 'mode': 'payment', 'success_url': self.request.build_absolute_uri( reverse('tournament-payment-success', kwargs={'tournament_id': tournament_id}) ), 'cancel_url': cancel_url, 'payment_intent_data': { 'application_fee_amount': platform_amount, 'transfer_data': { 'destination': stripe_account_id, }, }, 'metadata': { 'tournament_id': str(tournament_id), # Convert UUID to string 'user_id': str(self.request.user.id) if self.request.user.is_authenticated else None, # Convert UUID to string 'source_page': 'tournament_info' if team_registration_id else 'register_tournament', } } # Add cart or team data to metadata based on payment context if cart_data: checkout_session_params['metadata']['registration_cart_id'] = str(cart_data['cart_id']) # Convert to string elif team_registration_id: checkout_session_params['metadata']['team_registration_id'] = str(team_registration_id) # Convert to string self.request.session['team_registration_id'] = str(team_registration_id) # Convert to string # Add customer_email if available if customer_email: checkout_session_params['customer_email'] = customer_email # Create the checkout session try: checkout_session = stripe.checkout.Session.create(**checkout_session_params) # Store checkout session ID and source page in session self.request.session['stripe_checkout_session_id'] = checkout_session.id self.request.session['payment_source_page'] = 'tournament_info' if team_registration_id else 'register_tournament' self.request.session.modified = True return checkout_session except stripe.error.StripeError as e: # Handle specific Stripe errors more gracefully if 'destination' in str(e): raise Exception("Erreur avec le compte Stripe de l'arbitre. Contactez l'administrateur.") else: raise Exception(f"Erreur Stripe: {str(e)}") def process_successful_payment(self, tournament_id, checkout_session): """ Process a successful Stripe payment Returns a tuple (success, redirect_response) """ print(f"Processing payment for tournament {tournament_id}") tournament = get_object_or_404(Tournament, id=tournament_id) # Check if this is a payment for an existing team registration team_registration_id = self.request.session.get('team_registration_id') print(f"Team registration ID from session: {team_registration_id}") # Track payment statuses for debugging payment_statuses = [] if team_registration_id: success = self._process_direct_payment(checkout_session) payment_statuses.append(success) print(f"Direct payment processing result: {success}") else: # This is a payment during registration process success = self._process_registration_payment(tournament, checkout_session) payment_statuses.append(success) print(f"Registration payment processing result: {success}") # Print combined payment status print(f"Payment statuses: {payment_statuses}") print(any(payment_statuses)) # Clear checkout session ID if 'stripe_checkout_session_id' in self.request.session: del self.request.session['stripe_checkout_session_id'] return any(payment_statuses) def _process_direct_payment(self, checkout_session): """Process payment for an existing team registration""" team_registration_id = self.request.session.get('team_registration_id') if not team_registration_id: print("No team registration ID found in session") return False try: print(f"Looking for team registration with ID: {team_registration_id}") team_registration = TeamRegistration.objects.get(id=team_registration_id) success = self._update_registration_payment_info( team_registration, checkout_session.payment_intent ) # Clean up session if 'team_registration_id' in self.request.session: del self.request.session['team_registration_id'] return success 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 def _process_registration_payment(self, tournament, checkout_session): """Process payment made during registration""" cart_manager = RegistrationCartManager(self.request) cart_data = cart_manager.get_cart_data() # Checkout and create registration success, result = cart_manager.checkout() if not success: return False # Process payment for the new registration team_registration = result # result is team_registration object self._update_registration_payment_info( team_registration, checkout_session.payment_intent ) # Send confirmation email if appropriate waiting_list_position = cart_data.get('waiting_list_position', -1) if is_not_sqlite_backend(): email_service = TournamentEmailService() email_service.send_registration_confirmation( self.request, tournament, team_registration, waiting_list_position ) return True def _update_registration_payment_info(self, team_registration, payment_intent_id): """Update player registrations with payment information""" player_registrations = PlayerRegistration.objects.filter(team_registration=team_registration) for player_reg in player_registrations: player_reg.payment_type = PlayerPaymentType.CREDIT_CARD player_reg.registration_status = RegistrationStatus.CONFIRMED player_reg.payment_id = payment_intent_id player_reg.save() return True def process_refund(self, team_registration_id): """ Process a refund for a tournament registration as part of unregistration Returns a tuple (success, message) """ stripe.api_key = self.stripe_api_key try: # Get the team registration team_registration = get_object_or_404(TeamRegistration, id=team_registration_id) tournament = team_registration.tournament # Check if refund is possible for this tournament if not tournament.is_refund_possible(): return False, "Les remboursements ne sont plus possibles pour ce tournoi.", None # Get payment ID from player registrations player_registrations = PlayerRegistration.objects.filter(team_registration=team_registration) payment_id = None for player_reg in player_registrations: # Find the first valid payment ID if player_reg.payment_id and player_reg.payment_type == PlayerPaymentType.CREDIT_CARD: payment_id = player_reg.payment_id break if not payment_id: return False, "Aucun paiement trouvé pour cette équipe.", None # Get the Stripe payment intent payment_intent = stripe.PaymentIntent.retrieve(payment_id) if payment_intent.status != 'succeeded': return False, "Le paiement n'a pas été complété, il ne peut pas être remboursé.", None # Process the refund refund = stripe.Refund.create( payment_intent=payment_id, refund_application_fee=True, reverse_transfer=True ) # Return success with refund object return True, "Votre inscription a été remboursée automatiquement.", refund except stripe.error.StripeError as e: return False, f"Erreur de remboursement Stripe: {str(e)}", None except Exception as e: return False, f"Erreur lors du remboursement: {str(e)}", None