from django.shortcuts import get_object_or_404 from django.conf import settings 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 import stripe from datetime import datetime, timedelta from ..models import TeamRegistration, PlayerRegistration, Tournament from ..models.player_registration import PlayerPaymentType from .email_service import TournamentEmailService from tournaments.services.currency_service import CurrencyService 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_manager=None, team_registration_id=None): """ Create a Stripe checkout session for tournament payment """ print(f"[TOURNAMENT PAYMENT] Creating checkout session") print(f"[TOURNAMENT PAYMENT] Tournament ID: {tournament_id}") print(f"[TOURNAMENT PAYMENT] Team fee: {team_fee}") print(f"[TOURNAMENT PAYMENT] Cart manager: {cart_manager}") print(f"[TOURNAMENT PAYMENT] Team registration ID: {team_registration_id}") print(f"[TOURNAMENT PAYMENT] User: {self.request.user}") session_duration_minutes = 30 expires_at = int((datetime.now() + timedelta(minutes=session_duration_minutes)).timestamp()) stripe.api_key = self.stripe_api_key tournament = get_object_or_404(Tournament, id=tournament_id) user = self.request.user if self.request.user.is_authenticated else None customer_email = user.email if user else None customer_name = f"{user.first_name} {user.last_name}".strip() if user and user.first_name else None registration_type = 'direct' if not team_registration_id: registration_type = 'cart' print(f"[TOURNAMENT PAYMENT] Tournament: {tournament.name}") print(f"[TOURNAMENT PAYMENT] Customer email: {customer_email}") print(f"[TOURNAMENT PAYMENT] Customer name: {customer_name}") print(f"[TOURNAMENT PAYMENT] Registration type: {registration_type}") currency_service = CurrencyService() # Check if payments are enabled for this tournament if not tournament.should_request_payment(): print(f"[TOURNAMENT PAYMENT] Payments not enabled for tournament") raise Exception("Les paiements ne sont pas activés pour ce tournoi.") # Get currency code and validate it currency_code = tournament.currency_code or 'EUR' if not currency_service.validate_currency_code(currency_code): print(f"[TOURNAMENT PAYMENT] Invalid currency code: {currency_code}") raise Exception(f"Devise non supportée: {currency_code}") print(f"[TOURNAMENT PAYMENT] Currency code: {currency_code}") # Convert amount to Stripe format stripe_amount = currency_service.convert_to_stripe_amount(team_fee, currency_code) print(f"[TOURNAMENT PAYMENT] Stripe amount: {stripe_amount}") # 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 not team_registration_id and cart_manager: team_registration = self._process_pre_registration_payment(cart_manager) 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}") if not team_registration_id: print(f"[TOURNAMENT PAYMENT] No team registration ID available") raise Exception("Erreur lors de la création de la réservation") # Determine the appropriate cancel URL based on the context if registration_type == 'direct': # 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 self.request.session['cancel_team_registration_id'] = str(team_registration_id) cancel_url = self.request.build_absolute_uri( reverse('register_tournament', kwargs={ 'tournament_id': tournament_id, }) ) print(f"[TOURNAMENT PAYMENT] Cancel URL: {cancel_url}") base_metadata = { 'tournament_id': str(tournament_id), 'team_registration_id': str(team_registration_id), 'customer_name': customer_name or 'Non fourni', 'customer_email': customer_email or 'Non fourni', 'user_id': str(self.request.user.id) if self.request.user.is_authenticated else None, 'payment_source': 'tournament', # Identify payment source 'registration_type': registration_type, 'source_page': 'tournament_info' if team_registration_id else 'register_tournament', 'currency_code': currency_code, # Store currency for later reference } print(f"[TOURNAMENT PAYMENT] Base metadata: {base_metadata}") if tournament.is_corporate_tournament: print(f"[TOURNAMENT PAYMENT] Corporate tournament - using direct payment") # Corporate tournament metadata metadata = { **base_metadata, 'is_corporate_tournament': 'true', 'stripe_account_type': 'direct' } else: print(f"[TOURNAMENT PAYMENT] Regular tournament - using Stripe Connect") # Regular tournament metadata metadata = { **base_metadata, 'is_corporate_tournament': 'false', 'stripe_account_type': 'connect', 'stripe_account_id': tournament.stripe_account_id } self.request.session['team_registration_id'] = str(team_registration_id) metadata.update({ 'tournament_name': tournament.broadcast_display_name(), 'tournament_date': tournament.formatted_start_date(), 'tournament_club': tournament.event.club.name, 'tournament_fee': str(team_fee) }) print(f"[TOURNAMENT PAYMENT] Final metadata: {metadata}") # Common checkout session parameters if tournament.is_corporate_tournament: # Direct charge without transfers when umpire is platform owner checkout_session_params = { 'payment_method_types': ['card'], 'expires_at': expires_at, 'line_items': [{ 'price_data': { 'currency': currency_code.lower(), # Use tournament currency 'product_data': { 'name': f'{tournament.broadcast_display_name()} du {tournament.formatted_start_date()}', 'description': f'Lieu {tournament.event.club.name}', }, 'unit_amount': stripe_amount, # Amount in proper currency format }, 'quantity': 1, }], 'mode': 'payment', 'success_url': self.request.build_absolute_uri( reverse('tournament-payment-success', kwargs={'tournament_id': tournament_id}) ), 'cancel_url': cancel_url, 'metadata': metadata } print(f"[TOURNAMENT PAYMENT] Corporate checkout params: {checkout_session_params}") else: # Get the umpire's Stripe account ID stripe_account_id = tournament.stripe_account_id if not stripe_account_id: print(f"[TOURNAMENT PAYMENT] No Stripe account ID for umpire") raise Exception("L'arbitre n'a pas configuré son compte Stripe.") print(f"[TOURNAMENT PAYMENT] Umpire Stripe account: {stripe_account_id}") # Calculate commission commission_rate = tournament.event.creator.effective_commission_rate() platform_amount = currency_service.convert_to_stripe_amount( team_fee * commission_rate, currency_code ) print(f"[TOURNAMENT PAYMENT] Commission rate: {commission_rate}") print(f"[TOURNAMENT PAYMENT] Platform amount: {platform_amount}") # Direct charge on connected account - this will appear in umpire's dashboard checkout_session_params = { 'payment_method_types': ['card'], 'expires_at': expires_at, 'line_items': [{ 'price_data': { 'currency': currency_code.lower(), 'product_data': { 'name': f'{tournament.broadcast_display_name()} du {tournament.formatted_start_date()}', 'description': f'Lieu {tournament.event.club.name}', }, 'unit_amount': stripe_amount, }, '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, # Your commission 'description': f'Inscription {tournament.broadcast_display_name()} du {tournament.formatted_start_date()}', 'metadata': metadata # All metadata will be visible to umpire }, 'metadata': metadata # Session metadata } print(f"[TOURNAMENT PAYMENT] Direct charge checkout params: {checkout_session_params}") # Add customer_email if available if customer_email: checkout_session_params['customer_email'] = customer_email print(f"[TOURNAMENT PAYMENT] Added customer email to params") # Create the checkout session try: print(f"[TOURNAMENT PAYMENT] Creating Stripe checkout session...") if tournament.is_corporate_tournament: # Create on platform account checkout_session = stripe.checkout.Session.create(**checkout_session_params) else: # Create on connected account (direct charge) stripe_account_id = tournament.stripe_account_id self.request.session['stripe_account_id'] = stripe_account_id if not tournament.is_corporate_tournament else None checkout_session = stripe.checkout.Session.create( **checkout_session_params, stripe_account=stripe_account_id # This creates the session on the connected account ) # 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 print(f"[TOURNAMENT PAYMENT] Checkout session created successfully: {checkout_session.id}") print(f"[TOURNAMENT PAYMENT] Stored session data - checkout_session_id: {checkout_session.id}") return checkout_session except stripe.error.StripeError as e: print(f"[TOURNAMENT PAYMENT] Stripe error: {str(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, checkout_session): """ Process a successful Stripe payment Returns a tuple (success, redirect_response) """ success = PaymentService.process_direct_payment(checkout_session) # Print combined payment status print(f"process_direct_payment: {success}") # Clear checkout session ID if 'stripe_checkout_session_id' in self.request.session: del self.request.session['stripe_checkout_session_id'] return success def _process_pre_registration_payment(self, cart_manager): """Process payment made during registration""" # Checkout and create registration success, result = cart_manager.checkout(confirmed=False) if not success: return None # Process payment for the new registration team_registration = result # result is team_registration object team_registration.confirm_pre_registration() # 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 team_registration # def _update_registration_payment_info(self, team_registration, payment_intent_id): # """Update player registrations with payment information""" # team_registration.confirm_registration(payment_intent_id) # return True def process_refund(self, team_registration_id, force_refund=False): """ 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() and force_refund == False: 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 from the correct account try: if tournament.is_corporate_tournament: # Corporate tournament - payment on platform account payment_intent = stripe.PaymentIntent.retrieve(payment_id) else: # Regular tournament - payment on connected account if not tournament.stripe_account_id: return False, "Compte Stripe de l'arbitre non configuré.", None #legacy retrieval try: payment_intent = stripe.PaymentIntent.retrieve(payment_id) except: payment_intent = stripe.PaymentIntent.retrieve( payment_id, stripe_account=tournament.stripe_account_id ) except stripe.error.InvalidRequestError as e: return False, f"Paiement introuvable: {str(e)}", None 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 - with different parameters based on tournament type if tournament.is_corporate_tournament: # Corporate tournament refund (simple refund) refund = stripe.Refund.create( payment_intent=payment_id ) else: # Direct charge refund (on connected account) # Check if there's an application fee to refund refund_params = { 'payment_intent': payment_id, 'stripe_account': tournament.stripe_account_id } # Only add refund_application_fee if there's actually an application fee if hasattr(payment_intent, 'application_fee_amount') and payment_intent.application_fee_amount and payment_intent.application_fee_amount > 0: refund_params['refund_application_fee'] = True print(f"[REFUND] Refunding application fee of {payment_intent.application_fee_amount}") else: print(f"[REFUND] No application fee to refund") refund = stripe.Refund.create(**refund_params) for player_reg in player_registrations: player_reg.payment_type = None player_reg.payment_id = None player_reg.save() TournamentEmailService.send_refund_confirmation(tournament, team_registration, refund) # Return success with refund object return True, "L'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 @staticmethod def process_failed_payment_intent(payment_intent): """Process a failed Payment Intent""" if hasattr(payment_intent, 'metadata'): metadata = payment_intent.metadata elif isinstance(payment_intent, dict) and 'metadata' in payment_intent: metadata = payment_intent['metadata'] else: print("[FAILED PAYMENT] No metadata found in payment intent") return False print(f"[FAILED PAYMENT] Processing failed payment intent") print(f"[FAILED PAYMENT] Metadata: {metadata}") tournament_id = metadata.get('tournament_id') team_registration_id = metadata.get('team_registration_id') registration_type = metadata.get('registration_type') print(f"[FAILED PAYMENT] Tournament ID: {tournament_id}") print(f"[FAILED PAYMENT] Team Registration ID: {team_registration_id}") print(f"[FAILED PAYMENT] Registration Type: {registration_type}") # Release reserved spot for cart registrations if registration_type == 'cart' and tournament_id: try: tournament = Tournament.objects.get(id=tournament_id) old_spots = tournament.reserved_spots tournament.reserved_spots = max(0, tournament.reserved_spots - 1) tournament.save() print(f"[FAILED PAYMENT] Released reserved spot: {old_spots} → {tournament.reserved_spots}") except Tournament.DoesNotExist: print(f"[FAILED PAYMENT] Tournament {tournament_id} not found") except Exception as e: print(f"[FAILED PAYMENT] Error updating tournament reserved spots: {str(e)}") # Clean up unpaid team registration if not team_registration_id: print("[FAILED PAYMENT] No team registration ID found in metadata") return False try: print(f"[FAILED PAYMENT] Looking for team registration with ID: {team_registration_id}") team_registration = TeamRegistration.objects.get(id=team_registration_id) if not team_registration.is_paid(): team_registration.delete() print(f"[FAILED PAYMENT] Deleted unpaid team registration {team_registration_id}") else: print(f"[FAILED PAYMENT] Team registration {team_registration_id} is already paid") return True except TeamRegistration.DoesNotExist: print(f"[FAILED PAYMENT] Team registration {team_registration_id} not found") return False except Exception as e: print(f"[FAILED PAYMENT] Error processing team registration: {str(e)}") return False @staticmethod def process_expired_checkout_session(checkout_session): """Process an expired Checkout Session""" if hasattr(checkout_session, 'metadata'): metadata = checkout_session.metadata elif isinstance(checkout_session, dict) and 'metadata' in checkout_session: metadata = checkout_session['metadata'] else: print("[EXPIRED SESSION] No metadata found in checkout session") return False print(f"[EXPIRED SESSION] Processing expired checkout session") print(f"[EXPIRED SESSION] Metadata: {metadata}") tournament_id = metadata.get('tournament_id') team_registration_id = metadata.get('team_registration_id') registration_type = metadata.get('registration_type') print(f"[EXPIRED SESSION] Tournament ID: {tournament_id}") print(f"[EXPIRED SESSION] Team Registration ID: {team_registration_id}") print(f"[EXPIRED SESSION] Registration Type: {registration_type}") # Release reserved spot for cart registrations if registration_type == 'cart' and tournament_id: try: tournament = Tournament.objects.get(id=tournament_id) old_spots = tournament.reserved_spots tournament.reserved_spots = max(0, tournament.reserved_spots - 1) tournament.save() print(f"[EXPIRED SESSION] Released reserved spot: {old_spots} → {tournament.reserved_spots}") except Tournament.DoesNotExist: print(f"[EXPIRED SESSION] Tournament {tournament_id} not found") except Exception as e: print(f"[EXPIRED SESSION] Error updating tournament reserved spots: {str(e)}") # Clean up unpaid team registration if not team_registration_id: print("[EXPIRED SESSION] No team registration ID found in metadata") return False try: print(f"[EXPIRED SESSION] Looking for team registration with ID: {team_registration_id}") team_registration = TeamRegistration.objects.get(id=team_registration_id) if not team_registration.is_paid(): team_registration.delete() print(f"[EXPIRED SESSION] Deleted unpaid team registration {team_registration_id}") else: print(f"[EXPIRED SESSION] Team registration {team_registration_id} is already paid") return True except TeamRegistration.DoesNotExist: print(f"[EXPIRED SESSION] Team registration {team_registration_id} not found") return True except Exception as e: print(f"[EXPIRED SESSION] Error processing team registration: {str(e)}") return False @staticmethod def process_failed_or_expired_session(stripe_object): """Legacy method for backward compatibility - delegates to specific handlers""" # Try to determine object type and delegate if hasattr(stripe_object, 'object'): object_type = stripe_object.object elif isinstance(stripe_object, dict) and 'object' in stripe_object: object_type = stripe_object['object'] else: print("[LEGACY HANDLER] Cannot determine stripe object type") return False print(f"[LEGACY HANDLER] Delegating {object_type} to specific handler") if object_type == 'payment_intent': return PaymentService.process_failed_payment_intent(stripe_object) elif object_type == 'checkout.session': return PaymentService.process_expired_checkout_session(stripe_object) else: print(f"[LEGACY HANDLER] Unknown object type: {object_type}") return False @staticmethod def process_direct_payment(checkout_session): """Process payment for an existing team registration""" metadata = checkout_session.metadata tournament_id = metadata.get('tournament_id') 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 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) 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}") except Exception as e: print(f"Error in _process_direct_payment: {str(e)}") return False @staticmethod @csrf_exempt @require_POST def stripe_webhook(request): payload = request.body sig_header = request.META.get('HTTP_STRIPE_SIGNATURE') # Check if this is a Connect account webhook (header method - for direct API calls) stripe_account_header = request.META.get('HTTP_STRIPE_ACCOUNT') print("=== WEBHOOK DEBUG ===") print(f"Signature: {sig_header}") print(f"Connect Account Header: {stripe_account_header}") # First, try to construct the event with any available webhook secret to inspect the payload webhook_secrets = [] if hasattr(settings, 'XLR_STRIPE_WEBHOOK_SECRET') and settings.XLR_STRIPE_WEBHOOK_SECRET: webhook_secrets.append(('XLR', settings.XLR_STRIPE_WEBHOOK_SECRET)) if hasattr(settings, 'TOURNAMENT_STRIPE_WEBHOOK_SECRET') and settings.TOURNAMENT_STRIPE_WEBHOOK_SECRET: webhook_secrets.append(('TOURNAMENT', settings.TOURNAMENT_STRIPE_WEBHOOK_SECRET)) if hasattr(settings, 'SHOP_STRIPE_WEBHOOK_SECRET') and settings.SHOP_STRIPE_WEBHOOK_SECRET: webhook_secrets.append(('SHOP', settings.SHOP_STRIPE_WEBHOOK_SECRET)) print(f"Available webhook secrets: {[name for name, _ in webhook_secrets]}") event = None used_secret = None # Try to verify with each secret to get the event payload for secret_name, secret_value in webhook_secrets: try: print(f"Trying {secret_name} webhook secret...") event = stripe.Webhook.construct_event(payload, sig_header, secret_value) used_secret = secret_name print(f"SUCCESS: Webhook verified with {secret_name} secret") break except stripe.error.SignatureVerificationError as e: print(f"Failed with {secret_name} secret: {str(e)}") continue if not event: print("ERROR: No webhook secret worked") return HttpResponse("Webhook signature verification failed", status=400) # Now check if this is a Connect webhook by looking at the payload connect_account_id = event.get('account') # This is how Connect webhooks are identified print(f"Connect Account ID from payload: {connect_account_id}") # Log webhook details print(f"Event ID: {event.get('id')}") print(f"Event Type: {event.get('type')}") print(f"Live Mode: {event.get('livemode')}") # Determine if this should have used a different webhook secret based on the account if connect_account_id: print(f"This is a Connect webhook from account: {connect_account_id}") # Check if the account matches the expected tournament account if connect_account_id == "acct_1S0jbSAs9xuFLROy": print("This matches the expected tournament Connect account") if used_secret != 'TOURNAMENT': print(f"WARNING: Used {used_secret} secret but should probably use TOURNAMENT secret") else: print(f"Unknown Connect account: {connect_account_id}") else: print("This is a platform/direct webhook (no Connect account)") if used_secret != 'XLR': print(f"WARNING: Used {used_secret} secret but should probably use XLR secret") try: # Process the webhook event stripe_object = event['data']['object'] metadata = stripe_object.get('metadata', {}) print(f"is_corporate_tournament: {metadata.get('is_corporate_tournament', 'unknown')}") print(f"payment_source: {metadata.get('payment_source', 'unknown')}") print(f"stripe_account_type: {metadata.get('stripe_account_type', 'unknown')}") print(f"stripe_account_id: {metadata.get('stripe_account_id', 'unknown')}") if event['type'] == 'checkout.session.completed': success = PaymentService.process_direct_payment(stripe_object) if success: print(f"Successfully processed completed checkout session") return HttpResponse(status=200) else: print(f"Failed to process completed checkout session") return HttpResponse(status=400) # Handle other event types if needed elif event['type'] == 'payment_intent.succeeded': print(f"Payment intent succeeded - you might want to handle this") return HttpResponse(status=200) else: print(f"Unhandled event type: {event['type']}") return HttpResponse(status=200) except Exception as e: print(f"Error processing webhook: {str(e)}") import traceback traceback.print_exc() return HttpResponse("Webhook processing failed", status=500) @staticmethod def create_payment_link(team_registration_id): """ Create a Stripe Payment Link for a team registration Returns the payment link URL or None if failed """ try: team_registration = TeamRegistration.objects.get(id=team_registration_id) tournament = team_registration.tournament if not tournament or tournament.is_free(): return None stripe.api_key = settings.STRIPE_SECRET_KEY currency_service = CurrencyService() # Calculate the team fee team_fee = team_registration.get_team_registration_fee() stripe_amount = currency_service.convert_to_stripe_amount(team_fee, tournament.currency_code) customer_email = team_registration.team_contact() currency_code = tournament.currency_code or 'EUR' print(f"[PAYMENT LINK] Tournament: {tournament.display_name()}") print(f"[PAYMENT LINK] is_corporate_tournament: {tournament.is_corporate_tournament}") # Base metadata (same as checkout session) base_metadata = { 'tournament_id': str(tournament.id), 'team_registration_id': str(team_registration.id), 'customer_email': customer_email or 'Non fourni', 'payment_source': 'payment_link', 'registration_type': 'direct', 'currency_code': currency_code, } # Create payment link params payment_link_params = { 'line_items': [{ 'price_data': { 'currency': currency_code.lower(), 'product_data': { 'name': f'{tournament.broadcast_display_name()} du {tournament.formatted_start_date()}', 'description': f'Lieu {tournament.event.club.name}', }, 'unit_amount': stripe_amount, }, 'quantity': 1, }], 'after_completion': { 'type': 'redirect', 'redirect': { 'url': f'https://padelclub.app/stripe/payment_complete/?tournament_id={tournament.id}&team_registration_id={team_registration.id}&payment=success' } }, 'automatic_tax': {'enabled': False}, 'billing_address_collection': 'auto', } # Handle corporate vs regular tournaments (same logic as checkout session) if tournament.is_corporate_tournament: print(f"[PAYMENT LINK] Corporate tournament - creating on platform account") # Corporate tournament - create on platform account (no Connect account) metadata = { **base_metadata, 'is_corporate_tournament': 'true', 'stripe_account_type': 'direct' } payment_link_params['metadata'] = metadata # Create payment link on platform account payment_link = stripe.PaymentLink.create(**payment_link_params) else: print(f"[PAYMENT LINK] Regular tournament - creating on connected account") # Regular tournament - create on connected account stripe_account_id = tournament.stripe_account_id if not stripe_account_id: print(f"[PAYMENT LINK] ERROR: No Stripe account ID for umpire") return None metadata = { **base_metadata, 'is_corporate_tournament': 'false', 'stripe_account_type': 'connect', 'stripe_account_id': stripe_account_id } payment_link_params['metadata'] = metadata print(f"[PAYMENT LINK] Creating payment link for connected account: {stripe_account_id}") # Create payment link on connected account payment_link = stripe.PaymentLink.create( **payment_link_params, stripe_account=stripe_account_id ) print(f"[PAYMENT LINK] Created payment link: {payment_link.url}") return payment_link.url except Exception as e: print(f"[PAYMENT LINK] Error creating payment link: {str(e)}") import traceback traceback.print_exc() return None @staticmethod def get_or_create_payment_link(team_registration_id): """ Get existing payment link or create a new one for a team registration This method can be used to avoid creating multiple links for the same registration """ # In a real implementation, you might want to store payment links in the database # and check if one already exists and is still valid return PaymentService.create_payment_link(team_registration_id)