You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
padelclub_backend/tournaments/services/payment_service.py

289 lines
12 KiB

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
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 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
if tournament.is_corporate_tournament:
# Direct charge without transfers when umpire is platform owner
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,
'metadata': {
'tournament_id': str(tournament_id),
'user_id': str(self.request.user.id) if self.request.user.is_authenticated else None,
'source_page': 'tournament_info' if team_registration_id else 'register_tournament',
'is_corporate_tournament': 'true'
}
}
else:
# 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.event.creator.effective_commission_rate()
platform_amount = int((team_fee * commission_rate) * 100) # Commission in cents
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"""
team_registration.confirm_registration(payment_intent_id)
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