diff --git a/api/urls.py b/api/urls.py index 2a73de4..4f86836 100644 --- a/api/urls.py +++ b/api/urls.py @@ -51,5 +51,7 @@ urlpatterns = [ # forgotten password 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'), ] diff --git a/api/views.py b/api/views.py index 7f14fd2..25d12b6 100644 --- a/api/views.py +++ b/api/views.py @@ -357,46 +357,6 @@ def process_refund(request, team_registration_id): 'message': str(e) }, status=400) -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def validate_stripe_account(request): - stripe.api_key = settings.STRIPE_SECRET_KEY - # Parse the request body - data = json.loads(request.body) - account_id = data.get('account_id') - - if not account_id: - return Response({ - 'valid': False, - 'error': 'Account ID is required' - }, status=400) - - # Try to retrieve the account from Stripe - try: - # Basic account verification - account = stripe.Account.retrieve(account_id) - - # Only check if the account can receive payments - is_valid = account.id is not None - return Response({ - 'valid': is_valid, - 'account': { - 'id': account.id - } - }) - - except stripe.error.PermissionError: - return Response({ - 'valid': False, - 'error': 'No permission to access this account' - }, status=403) - - except stripe.error.InvalidRequestError: - return Response({ - 'valid': False, - 'error': 'Invalid account ID' - }, status=400) - @api_view(['POST']) @permission_classes([IsAuthenticated]) def xls_to_csv(request): @@ -464,3 +424,144 @@ def get_payment_config(request): return Response({ 'stripe_fee': getattr(settings, 'STRIPE_FEE', 0) }) + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +def create_stripe_connect_account(request): + stripe.api_key = settings.STRIPE_SECRET_KEY + user = request.user + + try: + # Create a new Standard account + account = stripe.Account.create( + type='standard', + metadata={ + 'padelclub_email': user.email, + 'platform': 'padelclub' + } + ) + + return Response({ + 'success': True, + 'account_id': account.id, + }) + + except Exception as e: + return Response({ + 'success': False, + 'error': str(e) + }, status=400) + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +def create_stripe_account_link(request): + """ + Create an account link for a Stripe account. + Uses HTTPS URLs only - no custom URL schemes. + """ + stripe.api_key = settings.STRIPE_SECRET_KEY + + # Parse request data + data = json.loads(request.body) + account_id = data.get('account_id') + + if not account_id: + return Response({ + 'success': False, + 'error': 'No Stripe account ID found' + }, status=400) + + try: + base_path = f"{request.scheme}://{request.get_host()}" + + refresh_url = f"{base_path}/stripe-refresh-account-link/" + return_url = f"{base_path}/stripe-onboarding-complete/" + + # Generate the account link URL + account_link = stripe.AccountLink.create( + account=account_id, + refresh_url=refresh_url, + return_url=return_url, + type='account_onboarding', + ) + + return Response({ + 'success': True, + 'url': account_link.url, + 'account_id': account_id + }) + except Exception as e: + return Response({ + 'success': False, + 'error': str(e) + }, status=400) + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +def validate_stripe_account(request): + """ + Validate a Stripe account for a tournament. + Returns validation status and onboarding URL if needed. + """ + stripe.api_key = settings.STRIPE_SECRET_KEY + + # Parse the request body + data = json.loads(request.body) + account_id = data.get('account_id') + + if not account_id: + return Response({ + 'valid': False, + 'error': 'No account ID found to validate', + 'needs_onboarding': True + }, status=200) + + try: + # Validate the account with Stripe + account = stripe.Account.retrieve(account_id) + + # Check account capabilities + charges_enabled = account.get('charges_enabled', False) + payouts_enabled = account.get('payouts_enabled', False) + details_submitted = account.get('details_submitted', False) + + # Determine if the account is valid and ready + is_valid = account.id is not None + can_process_payments = charges_enabled and payouts_enabled + onboarding_complete = details_submitted + needs_onboarding = not (can_process_payments and onboarding_complete) + + return Response({ + 'valid': is_valid, + 'can_process_payments': can_process_payments, + 'onboarding_complete': onboarding_complete, + 'needs_onboarding': needs_onboarding, + 'account': { + 'id': account.id, + 'charges_enabled': charges_enabled, + 'payouts_enabled': payouts_enabled, + 'details_submitted': details_submitted + } + }) + + except stripe.error.PermissionError: + # Account doesn't exist or isn't connected to your platform + return Response({ + 'valid': False, + 'error': 'This Stripe account is not connected to your platform or does not exist.', + 'needs_onboarding': True, + }, status=200) + + except stripe.error.InvalidRequestError: + return Response({ + 'valid': False, + 'error': 'Invalid account ID format', + 'needs_onboarding': True, + }, status=200) + + except Exception as e: + return Response({ + 'valid': False, + 'error': f'Unexpected error: {str(e)}', + 'needs_onboarding': True, + }, status=200) diff --git a/tournaments/templates/stripe/onboarding_complete.html b/tournaments/templates/stripe/onboarding_complete.html new file mode 100644 index 0000000..ffa7efe --- /dev/null +++ b/tournaments/templates/stripe/onboarding_complete.html @@ -0,0 +1,17 @@ +{% extends 'tournaments/base.html' %} +{% block head_title %} Intégration Stripe {% endblock %} +{% block first_title %} Padel Club {% endblock %} +{% block second_title %} Intégration Stripe {% endblock %} + +{% block content %} +{% load static %} +{% load tz %} + +
+
+
+ +

Veuillez retourner dans l'application pour terminer et valider la configuration.

+
+
+{% endblock %} diff --git a/tournaments/templates/stripe/refresh_account_link.html b/tournaments/templates/stripe/refresh_account_link.html new file mode 100644 index 0000000..6f940da --- /dev/null +++ b/tournaments/templates/stripe/refresh_account_link.html @@ -0,0 +1,19 @@ +{% extends 'tournaments/base.html' %} +{% block head_title %} Configuration Stripe {% endblock %} +{% block first_title %} Padel Club {% endblock %} +{% block second_title %} Configuration Stripe {% endblock %} + +{% block content %} +{% load static %} +{% load tz %} + +
+
+
+ +

Votre lien d'intégration Stripe a expiré. Un nouveau lien doit être généré.

+

Veuillez retourner dans l'application et ré-essayer.

+
+
+
+{% endblock %} diff --git a/tournaments/urls.py b/tournaments/urls.py index bd79d64..6867a7c 100644 --- a/tournaments/urls.py +++ b/tournaments/urls.py @@ -78,4 +78,6 @@ urlpatterns = [ path('activation-success/', views.activation_success, name='activation_success'), path('activation-failed/', views.activation_failed, name='activation_failed'), path('tournaments//confirm/', views.confirm_tournament_registration, name='confirm_tournament_registration'), + 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'), ] diff --git a/tournaments/views.py b/tournaments/views.py index d871c70..c6f75c7 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -1616,6 +1616,13 @@ def get_user_tournaments(user, tournaments): return user_tournaments +def stripe_onboarding_complete(request): + return render(request, 'stripe/onboarding_complete.html') + + +def stripe_refresh_account_link(request): + return render(request, 'stripe/refresh_account_link.html') + class UserListExportView(LoginRequiredMixin, View): def get(self, request, *args, **kwargs):