Laurent 1 month ago
commit 73c18bfbf8
  1. 1
      api/urls.py
  2. 28
      api/views.py
  3. 1
      padelclub_backend/settings_local.py.dist
  4. 2
      tournaments/forms.py
  5. 5
      tournaments/models/player_registration.py
  6. 10
      tournaments/models/team_registration.py
  7. 64
      tournaments/services/email_service.py
  8. 148
      tournaments/services/payment_service.py
  9. 23
      tournaments/services/tournament_registration.py
  10. 8
      tournaments/templates/register_tournament.html
  11. 30
      tournaments/templates/stripe/payment_complete.html
  12. 1
      tournaments/urls.py
  13. 114
      tournaments/views.py

@ -63,5 +63,6 @@ urlpatterns = [
path('dj-rest-auth/', include('dj_rest_auth.urls')),
path('stripe/create-account/', views.create_stripe_connect_account, name='create_stripe_account'),
path('stripe/create-account-link/', views.create_stripe_account_link, name='create_account_link'),
path('resend-payment-email/<str:team_registration_id>/', views.resend_payment_email, name='resend-payment-email'),
]

@ -16,6 +16,7 @@ from django.shortcuts import get_object_or_404
from .serializers import ClubSerializer, CourtSerializer, DateIntervalSerializer, DrawLogSerializer, TournamentSerializer, UserSerializer, EventSerializer, RoundSerializer, GroupStageSerializer, MatchSerializer, TeamScoreSerializer, TeamRegistrationSerializer, PlayerRegistrationSerializer, PurchaseSerializer, ShortUserSerializer, FailedApiCallSerializer, LogSerializer, DeviceTokenSerializer, CustomUserSerializer, UnregisteredTeamSerializer, UnregisteredPlayerSerializer, ImageSerializer, ActivitySerializer, ProspectSerializer, EntitySerializer, TournamentSummarySerializer
from tournaments.models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Court, DateInterval, Purchase, FailedApiCall, Log, DeviceToken, DrawLog, UnregisteredTeam, UnregisteredPlayer, Image
from tournaments.services.email_service import TournamentEmailService
from biz.models import Activity, Prospect, Entity
@ -619,6 +620,33 @@ def validate_stripe_account(request):
'needs_onboarding': True,
}, status=200)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def resend_payment_email(request, team_registration_id):
"""
Resend the registration confirmation email (which includes payment info/link)
"""
try:
team_registration = TeamRegistration.objects.get(id=team_registration_id)
tournament = team_registration.tournament
TournamentEmailService.send_registration_confirmation(
request,
tournament,
team_registration,
waiting_list_position=-1
)
return Response({
'success': True,
'message': 'Email de paiement renvoyé'
})
except TeamRegistration.DoesNotExist:
return Response({'error': 'Team not found'}, status=status.HTTP_404_NOT_FOUND)
except Exception as e:
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def is_granted_unlimited_access(request):

@ -42,6 +42,7 @@ STRIPE_PUBLISHABLE_KEY = ''
STRIPE_SECRET_KEY = ''
SHOP_STRIPE_WEBHOOK_SECRET = 'whsec_...' # Your existing webhook secret
TOURNAMENT_STRIPE_WEBHOOK_SECRET = 'whsec_...' # New webhook secret for tournaments
XLR_STRIPE_WEBHOOK_SECRET = 'whsec_...' # New webhook secret for padel club
STRIPE_FEE = 0.0075
TOURNAMENT_SETTINGS = {
'TIME_PROXIMITY_RULES': {

@ -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,

@ -228,3 +228,8 @@ class PlayerRegistration(TournamentSubModel):
return self.team_registration.tournament.entry_fee
else:
return 0
def player_contact(self):
if self.contact_email:
return self.contact_email
return self.email

@ -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

@ -516,8 +516,8 @@ class TournamentEmailService:
@staticmethod
def notify(captain, other_player, tournament, message_type: TeamEmailType):
print("TournamentEmailService.notify", captain.email, captain.registered_online, tournament, message_type)
if not captain or not captain.registered_online or not captain.email:
print("TournamentEmailService.notify", captain.player_contact(), captain.registered_online, tournament, message_type)
if not captain or not captain.registered_online or not captain.player_contact():
return
tournament_details_str = tournament.build_tournament_details_str()
@ -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):
@ -607,7 +607,7 @@ class TournamentEmailService:
print("TournamentEmailService.notify_team 2p", team)
first_player, second_player = players
TournamentEmailService.notify(first_player, second_player, tournament, message_type)
if first_player.email != second_player.email:
if first_player.player_contact() != second_player.player_contact():
TournamentEmailService.notify(second_player, first_player, tournament, message_type)
elif len(players) == 1:
print("TournamentEmailService.notify_team 1p", team)
@ -681,12 +681,38 @@ class TournamentEmailService:
# For unpaid teams, add payment instructions
formatted_fee = currency_service.format_amount(tournament.entry_fee, tournament.currency_code)
payment_info = [
"\n\n Paiement des frais d'inscription requis",
f"Les frais d'inscription de {formatted_fee} 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"
]
print("team_registration.user", team_registration.user)
# Check if team has a user account attached
if team_registration.user:
# User has account - direct to login and pay
payment_info = [
"\n\n Paiement des frais d'inscription requis",
f"Les frais d'inscription de {formatted_fee} 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"
]
else:
# No user account - create payment link
from .payment_service import PaymentService
payment_link = PaymentService.create_payment_link(team_registration.id)
if payment_link:
payment_info = [
"\n\n Paiement des frais d'inscription requis",
f"Les frais d'inscription de {formatted_fee} par joueur doivent être payés pour confirmer votre participation.",
"Vous pouvez effectuer le paiement directement via ce lien sécurisé :",
f"💳 Payer maintenant: {payment_link}",
"\nAucun compte n'est requis pour effectuer le paiement."
]
else:
# Fallback if payment link creation fails
payment_info = [
"\n\n Paiement des frais d'inscription requis",
f"Les frais d'inscription de {formatted_fee} par joueur doivent être payés pour confirmer votre participation.",
"Veuillez contacter l'organisateur du tournoi pour effectuer le paiement.",
f"Informations du tournoi: https://padelclub.app/tournament/{tournament.id}/info"
]
return "\n".join(payment_info)
@ -721,11 +747,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 +800,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):
@ -812,11 +840,11 @@ 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:
if not player.player_contact() or not player.registered_online:
continue
if player.email in processed_emails:
if player.player_contact() in processed_emails:
continue
processed_emails.add(player.email)
processed_emails.add(player.player_contact())
tournament_details_str = tournament.build_tournament_details_str()
other_player = team_registration.get_other_player(player) if len(player_registrations) > 1 else None
@ -856,4 +884,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)

@ -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}")
@ -590,20 +592,32 @@ class PaymentService:
def stripe_webhook(request):
payload = request.body
sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')
print("Received webhook call")
# Check if this is a Connect account webhook
stripe_account = request.META.get('HTTP_STRIPE_ACCOUNT')
print("=== WEBHOOK DEBUG ===")
print(f"Signature: {sig_header}")
print(f"Connect Account: {stripe_account}")
if stripe_account:
# This is a connected account (regular umpire tournament)
webhook_secret = settings.TOURNAMENT_STRIPE_WEBHOOK_SECRET
print(f"Using umpire webhook secret for connected account: {stripe_account}")
else:
# This is platform account (corporate tournament)
webhook_secret = settings.XLR_STRIPE_WEBHOOK_SECRET
print("Using XLR company webhook secret")
try:
event = stripe.Webhook.construct_event(
payload, sig_header, settings.TOURNAMENT_STRIPE_WEBHOOK_SECRET
)
event = stripe.Webhook.construct_event(payload, sig_header, webhook_secret)
print(f"Tournament webhook event type: {event['type']}")
# Debug metadata
stripe_object = event['data']['object']
# Debug: Print the object type
object_type = stripe_object.get('object', 'unknown')
print(f"Stripe object type: {object_type}")
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')}")
if event['type'] == 'checkout.session.completed':
success = PaymentService.process_direct_payment(stripe_object)
@ -637,7 +651,123 @@ class PaymentService:
return HttpResponse(status=200)
except Exception as e:
print(f"Tournament webhook error: {str(e)}")
print(f"Webhook error: {str(e)}")
import traceback
traceback.print_exc()
return HttpResponse(status=400)
@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.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)

@ -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:

@ -32,7 +32,13 @@
<p><strong>✅ Votre paiement a bien été effectué et enregistré.</strong></p>
{% endif %}
<p style="text-align: justify;">
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 %}
</p>
{% else %}
{% if not registration_successful %}

@ -0,0 +1,30 @@
{% extends 'tournaments/base.html' %}
{% block head_title %} Paiement {% endblock %}
{% block first_title %} Padel Club {% endblock %}
{% block second_title %} Paiement {% endblock %}
{% block content %}
{% load static %}
{% load tz %}
<div class="grid-x">
<div class="bubble">
<div class="cell medium-6 large-6 padding10">
{% if payment_status == 'success' %}
<label class="title">Paiement réussi !</label>
<p>Votre inscription a été confirmée. Un email de confirmation vous a été envoyé.</p>
{% if show_details and tournament %}
<p>Tournoi : <strong>{{ tournament.display_name }}</strong></p>
{% endif %}
{% elif payment_status == 'cancel' %}
<label class="title">Paiement annulé</label>
<p>Votre paiement a été annulé. Aucun montant n'a été prélevé.</p>
{% else %}
<label class="title">Paiement en cours de traitement</label>
<p>Votre paiement est en cours de traitement. Vous recevrez un email de confirmation sous peu.</p>
{% endif %}
<p>Vous pouvez maintenant fermer cette page.</p>
</div>
</div>
</div>
{% endblock %}

@ -79,6 +79,7 @@ urlpatterns = [
path('activation-success/', views.activation_success, name='activation_success'),
path('activation-failed/', views.activation_failed, name='activation_failed'),
path('tournaments/<str:tournament_id>/confirm/', views.confirm_tournament_registration, name='confirm_tournament_registration'),
path('stripe/payment_complete/', views.stripe_payment_complete, name='stripe-payment-complete'),
path('stripe-onboarding-complete/', views.stripe_onboarding_complete, name='stripe-onboarding-complete'),
path('stripe-refresh-account-link/', views.stripe_refresh_account_link, name='stripe-refresh-account-link'),
path('tournaments/<str:tournament_id>/toggle-private/', views.toggle_tournament_private, name='toggle_tournament_private'),

@ -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')
)
@ -2231,6 +2304,39 @@ def tournament_live_matches(request, tournament_id):
'live_matches': live_matches,
})
def stripe_payment_complete(request):
"""Handle payment complete page for Stripe Payment Links"""
tournament_id = request.GET.get('tournament_id')
team_registration_id = request.GET.get('team_registration_id')
payment_status = request.GET.get('payment', 'unknown')
context = {
'payment_status': payment_status,
'tournament': None,
'team_registration': None,
'players': [],
'show_details': False
}
# Try to get tournament and team registration details
if tournament_id and team_registration_id:
try:
tournament = Tournament.objects.get(id=tournament_id)
team_registration = TeamRegistration.objects.get(id=team_registration_id)
context.update({
'tournament': tournament,
'team_registration': team_registration,
'players': team_registration.players_sorted_by_captain,
'show_details': True,
'amount_paid': team_registration.get_team_registration_fee(),
'currency': tournament.currency_code
})
except (Tournament.DoesNotExist, TeamRegistration.DoesNotExist):
print(f"Tournament or team registration not found: {tournament_id}, {team_registration_id}")
return render(request, 'stripe/payment_complete.html', context)
class UserListExportView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):

Loading…
Cancel
Save