/ subscription_routes.py
subscription_routes.py
  1  from fastapi import APIRouter, Depends, HTTPException, status, Request, BackgroundTasks
  2  from fastapi.responses import JSONResponse
  3  from typing import Dict, Any, Optional
  4  import stripe
  5  import json
  6  import logging
  7  from datetime import datetime
  8  import os
  9  
 10  from auth import get_current_active_user
 11  from auth_models import User, ApiKeyLevel
 12  from database import update_user, record_subscription_event
 13  
 14  # Configuration de Stripe
 15  stripe.api_key = os.environ.get("STRIPE_SECRET_KEY", "sk_test_51XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
 16  webhook_secret = os.environ.get("STRIPE_WEBHOOK_SECRET", "whsec_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
 17  
 18  # Configuration du logging
 19  logger = logging.getLogger("subscription_routes")
 20  
 21  # Configuration des produits et plans Stripe
 22  STRIPE_PLANS = {
 23      "basic": {
 24          "price_id": "price_1XXXXXXXXXXXXXXXXXXXbasic",
 25          "api_level": ApiKeyLevel.BASIC,
 26          "name": "Basic Plan"
 27      },
 28      "premium": {
 29          "price_id": "price_1XXXXXXXXXXXXXXXXXXXpremium",
 30          "api_level": ApiKeyLevel.PREMIUM,
 31          "name": "Premium Plan"
 32      },
 33      "enterprise": {
 34          "price_id": "price_1XXXXXXXXXXXXXXXXXXXenterprise",
 35          "api_level": ApiKeyLevel.ENTERPRISE,
 36          "name": "Enterprise Plan"
 37      }
 38  }
 39  
 40  # Mapper les price_id aux plans
 41  PRICE_ID_TO_PLAN = {plan["price_id"]: {"id": plan_id, **plan} for plan_id, plan in STRIPE_PLANS.items()}
 42  
 43  router = APIRouter(
 44      prefix="/api/subscriptions",
 45      tags=["subscriptions"],
 46      responses={401: {"description": "Non autorisé"}},
 47  )
 48  
 49  @router.get("/current")
 50  async def get_current_subscription(current_user: User = Depends(get_current_active_user)):
 51      """
 52      Récupère l'abonnement actuel de l'utilisateur.
 53      """
 54      try:
 55          # Si l'utilisateur a un ID client Stripe, récupérer les informations d'abonnement
 56          if hasattr(current_user, "stripe_customer_id") and current_user.stripe_customer_id:
 57              # Récupérer les abonnements Stripe
 58              subscriptions = stripe.Subscription.list(
 59                  customer=current_user.stripe_customer_id,
 60                  status="active",
 61                  limit=1
 62              )
 63              
 64              if subscriptions and subscriptions.data:
 65                  subscription = subscriptions.data[0]
 66                  
 67                  # Récupérer les détails du plan
 68                  price_id = subscription.items.data[0].price.id
 69                  plan_details = PRICE_ID_TO_PLAN.get(price_id, {})
 70                  
 71                  return {
 72                      "subscription": {
 73                          "id": subscription.id,
 74                          "status": subscription.status,
 75                          "current_period_end": datetime.fromtimestamp(subscription.current_period_end).isoformat(),
 76                          "cancel_at_period_end": subscription.cancel_at_period_end,
 77                          "planId": plan_details.get("id", "unknown"),
 78                          "planName": plan_details.get("name", "Unknown Plan")
 79                      }
 80                  }
 81              
 82          # Si aucun abonnement actif n'est trouvé
 83          return {
 84              "subscription": None
 85          }
 86          
 87      except stripe.error.StripeError as e:
 88          logger.error(f"Erreur Stripe lors de la récupération de l'abonnement: {e}")
 89          raise HTTPException(
 90              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
 91              detail=f"Erreur lors de la récupération de l'abonnement: {str(e)}"
 92          )
 93      except Exception as e:
 94          logger.error(f"Erreur lors de la récupération de l'abonnement: {e}")
 95          raise HTTPException(
 96              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
 97              detail="Erreur interne du serveur"
 98          )
 99  
100  @router.post("/create-subscription")
101  async def create_subscription(
102      data: Dict[str, Any],
103      current_user: User = Depends(get_current_active_user)
104  ):
105      """
106      Crée un nouvel abonnement Stripe pour l'utilisateur.
107      """
108      try:
109          price_id = data.get("priceId")
110          if not price_id:
111              raise HTTPException(
112                  status_code=status.HTTP_400_BAD_REQUEST,
113                  detail="ID de prix manquant"
114              )
115          
116          # Vérifier si l'utilisateur a déjà un ID client Stripe
117          customer_id = None
118          if hasattr(current_user, "stripe_customer_id") and current_user.stripe_customer_id:
119              customer_id = current_user.stripe_customer_id
120          
121          # Si l'utilisateur n'a pas d'ID client, en créer un
122          if not customer_id:
123              customer = stripe.Customer.create(
124                  email=current_user.email,
125                  name=current_user.full_name or current_user.username,
126                  metadata={
127                      "user_id": current_user.id
128                  }
129              )
130              customer_id = customer.id
131              
132              # Mettre à jour l'utilisateur avec l'ID client Stripe
133              current_user.stripe_customer_id = customer_id
134              await update_user(current_user)
135          
136          # Vérifier les abonnements existants
137          existing_subscriptions = stripe.Subscription.list(
138              customer=customer_id,
139              status="active"
140          )
141          
142          # Si l'utilisateur a déjà un abonnement actif, le mettre à jour
143          if existing_subscriptions and existing_subscriptions.data:
144              existing_sub = existing_subscriptions.data[0]
145              
146              # Mettre à jour l'abonnement existant
147              updated_subscription = stripe.Subscription.modify(
148                  existing_sub.id,
149                  items=[{
150                      'id': existing_sub['items']['data'][0].id,
151                      'price': price_id,
152                  }],
153                  payment_behavior='allow_incomplete',
154                  proration_behavior='create_prorations'
155              )
156              
157              return {
158                  "clientSecret": updated_subscription.latest_invoice.payment_intent.client_secret,
159                  "subscription": {
160                      "id": updated_subscription.id,
161                      "status": updated_subscription.status
162                  }
163              }
164          
165          # Créer un nouvel abonnement
166          subscription = stripe.Subscription.create(
167              customer=customer_id,
168              items=[
169                  {
170                      "price": price_id
171                  }
172              ],
173              payment_behavior='default_incomplete',
174              expand=["latest_invoice.payment_intent"]
175          )
176          
177          return {
178              "clientSecret": subscription.latest_invoice.payment_intent.client_secret,
179              "subscription": {
180                  "id": subscription.id,
181                  "status": subscription.status
182              }
183          }
184          
185      except stripe.error.StripeError as e:
186          logger.error(f"Erreur Stripe lors de la création de l'abonnement: {e}")
187          raise HTTPException(
188              status_code=status.HTTP_400_BAD_REQUEST,
189              detail=f"Erreur Stripe: {str(e)}"
190          )
191      except Exception as e:
192          logger.error(f"Erreur lors de la création de l'abonnement: {e}")
193          raise HTTPException(
194              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
195              detail="Erreur interne du serveur"
196          )
197  
198  @router.post("/cancel")
199  async def cancel_subscription(
200      data: Dict[str, Any],
201      current_user: User = Depends(get_current_active_user)
202  ):
203      """
204      Annule l'abonnement de l'utilisateur à la fin de la période actuelle.
205      """
206      try:
207          subscription_id = data.get("subscriptionId")
208          if not subscription_id:
209              raise HTTPException(
210                  status_code=status.HTTP_400_BAD_REQUEST,
211                  detail="ID d'abonnement manquant"
212              )
213          
214          # Récupérer l'abonnement
215          subscription = stripe.Subscription.retrieve(subscription_id)
216          
217          # Vérifier que l'utilisateur est bien le propriétaire de l'abonnement
218          if not hasattr(current_user, "stripe_customer_id") or subscription.customer != current_user.stripe_customer_id:
219              raise HTTPException(
220                  status_code=status.HTTP_403_FORBIDDEN,
221                  detail="Vous n'êtes pas autorisé à annuler cet abonnement"
222              )
223          
224          # Annuler l'abonnement à la fin de la période
225          canceled_subscription = stripe.Subscription.modify(
226              subscription_id,
227              cancel_at_period_end=True
228          )
229          
230          return {
231              "subscription": {
232                  "id": canceled_subscription.id,
233                  "status": canceled_subscription.status,
234                  "cancel_at_period_end": canceled_subscription.cancel_at_period_end,
235                  "current_period_end": datetime.fromtimestamp(canceled_subscription.current_period_end).isoformat()
236              }
237          }
238          
239      except stripe.error.StripeError as e:
240          logger.error(f"Erreur Stripe lors de l'annulation de l'abonnement: {e}")
241          raise HTTPException(
242              status_code=status.HTTP_400_BAD_REQUEST,
243              detail=f"Erreur Stripe: {str(e)}"
244          )
245      except Exception as e:
246          logger.error(f"Erreur lors de l'annulation de l'abonnement: {e}")
247          raise HTTPException(
248              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
249              detail="Erreur interne du serveur"
250          )
251  
252  @router.post("/reactivate")
253  async def reactivate_subscription(
254      data: Dict[str, Any],
255      current_user: User = Depends(get_current_active_user)
256  ):
257      """
258      Réactive un abonnement annulé.
259      """
260      try:
261          subscription_id = data.get("subscriptionId")
262          if not subscription_id:
263              raise HTTPException(
264                  status_code=status.HTTP_400_BAD_REQUEST,
265                  detail="ID d'abonnement manquant"
266              )
267          
268          # Récupérer l'abonnement
269          subscription = stripe.Subscription.retrieve(subscription_id)
270          
271          # Vérifier que l'utilisateur est bien le propriétaire de l'abonnement
272          if not hasattr(current_user, "stripe_customer_id") or subscription.customer != current_user.stripe_customer_id:
273              raise HTTPException(
274                  status_code=status.HTTP_403_FORBIDDEN,
275                  detail="Vous n'êtes pas autorisé à réactiver cet abonnement"
276              )
277          
278          # Réactiver l'abonnement
279          reactivated_subscription = stripe.Subscription.modify(
280              subscription_id,
281              cancel_at_period_end=False
282          )
283          
284          return {
285              "subscription": {
286                  "id": reactivated_subscription.id,
287                  "status": reactivated_subscription.status,
288                  "cancel_at_period_end": reactivated_subscription.cancel_at_period_end,
289                  "current_period_end": datetime.fromtimestamp(reactivated_subscription.current_period_end).isoformat()
290              }
291          }
292          
293      except stripe.error.StripeError as e:
294          logger.error(f"Erreur Stripe lors de la réactivation de l'abonnement: {e}")
295          raise HTTPException(
296              status_code=status.HTTP_400_BAD_REQUEST,
297              detail=f"Erreur Stripe: {str(e)}"
298          )
299      except Exception as e:
300          logger.error(f"Erreur lors de la réactivation de l'abonnement: {e}")
301          raise HTTPException(
302              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
303              detail="Erreur interne du serveur"
304          )
305  
306  @router.post("/webhook")
307  async def stripe_webhook(
308      request: Request,
309      background_tasks: BackgroundTasks
310  ):
311      """
312      Webhook pour recevoir les événements Stripe.
313      """
314      # Obtenir la charge utile de la requête
315      payload = await request.body()
316      sig_header = request.headers.get("Stripe-Signature")
317      
318      try:
319          # Vérifier la signature Stripe
320          event = stripe.Webhook.construct_event(
321              payload, sig_header, webhook_secret
322          )
323      except ValueError as e:
324          logger.error(f"Erreur de décodage de la charge utile: {e}")
325          raise HTTPException(status_code=400, detail="Charge utile non valide")
326      except stripe.error.SignatureVerificationError as e:
327          logger.error(f"Erreur de vérification de signature: {e}")
328          raise HTTPException(status_code=400, detail="Signature non valide")
329      
330      # Traiter l'événement
331      background_tasks.add_task(handle_stripe_event, event)
332      
333      return {"status": "success"}
334  
335  async def handle_stripe_event(event):
336      """
337      Traite un événement Stripe en arrière-plan.
338      """
339      try:
340          # Log de l'événement
341          logger.info(f"Événement Stripe reçu: {event.type}")
342          
343          # Traiter différents types d'événements
344          if event.type == "invoice.payment_succeeded":
345              await handle_payment_succeeded(event.data.object)
346          elif event.type == "invoice.payment_failed":
347              await handle_payment_failed(event.data.object)
348          elif event.type == "customer.subscription.created":
349              await handle_subscription_created(event.data.object)
350          elif event.type == "customer.subscription.updated":
351              await handle_subscription_updated(event.data.object)
352          elif event.type == "customer.subscription.deleted":
353              await handle_subscription_deleted(event.data.object)
354      except Exception as e:
355          logger.error(f"Erreur lors du traitement de l'événement Stripe: {e}")
356  
357  async def handle_payment_succeeded(invoice):
358      """
359      Traite un paiement réussi.
360      """
361      try:
362          # Récupérer les informations client et abonnement
363          subscription_id = invoice.subscription
364          customer_id = invoice.customer
365          
366          # Récupérer l'abonnement
367          subscription = stripe.Subscription.retrieve(subscription_id)
368          
369          # Récupérer les détails du plan
370          price_id = subscription.items.data[0].price.id
371          plan_details = PRICE_ID_TO_PLAN.get(price_id, {})
372          
373          # Récupérer l'utilisateur à partir des métadonnées du client
374          from database import get_user_by_stripe_id
375          user = await get_user_by_stripe_id(customer_id)
376          
377          if user:
378              # Mettre à jour le niveau d'API de l'utilisateur si l'abonnement est actif
379              if subscription.status == "active":
380                  api_level = plan_details.get("api_level", ApiKeyLevel.FREE)
381                  user.subscription = api_level
382                  await update_user(user)
383              
384              # Enregistrer l'événement d'abonnement
385              await record_subscription_event(
386                  user_id=user.id,
387                  event_type="subscription_created",
388                  subscription_id=subscription.id,
389                  plan_id=plan_details.get("id", "unknown"),
390                  plan_name=plan_details.get("name", "Unknown Plan"),
391                  status=subscription.status
392              )
393              
394              logger.info(f"Abonnement créé pour l'utilisateur {user.id}: {subscription.id}")
395      except Exception as e:
396          logger.error(f"Erreur lors du traitement de la création d'abonnement: {e}")
397  
398  async def handle_subscription_updated(subscription):
399      """
400      Traite la mise à jour d'un abonnement.
401      """
402      try:
403          # Récupérer les informations client
404          customer_id = subscription.customer
405          
406          # Récupérer les détails du plan
407          price_id = subscription.items.data[0].price.id
408          plan_details = PRICE_ID_TO_PLAN.get(price_id, {})
409          
410          # Récupérer l'utilisateur à partir des métadonnées du client
411          from database import get_user_by_stripe_id
412          user = await get_user_by_stripe_id(customer_id)
413          
414          if user:
415              # Mettre à jour le niveau d'API de l'utilisateur en fonction du statut de l'abonnement
416              if subscription.status == "active":
417                  api_level = plan_details.get("api_level", ApiKeyLevel.FREE)
418                  user.subscription = api_level
419              elif subscription.status in ["past_due", "unpaid", "canceled", "incomplete_expired"]:
420                  # Rétrograder au niveau gratuit si l'abonnement est inactif
421                  user.subscription = ApiKeyLevel.FREE
422              
423              await update_user(user)
424              
425              # Enregistrer l'événement d'abonnement
426              await record_subscription_event(
427                  user_id=user.id,
428                  event_type="subscription_updated",
429                  subscription_id=subscription.id,
430                  plan_id=plan_details.get("id", "unknown"),
431                  plan_name=plan_details.get("name", "Unknown Plan"),
432                  status=subscription.status,
433                  cancel_at_period_end=subscription.cancel_at_period_end
434              )
435              
436              logger.info(f"Abonnement mis à jour pour l'utilisateur {user.id}: {subscription.id}, statut: {subscription.status}")
437      except Exception as e:
438          logger.error(f"Erreur lors du traitement de la mise à jour d'abonnement: {e}")
439  
440  async def handle_subscription_deleted(subscription):
441      """
442      Traite la suppression d'un abonnement.
443      """
444      try:
445          # Récupérer les informations client
446          customer_id = subscription.customer
447          
448          # Récupérer l'utilisateur à partir des métadonnées du client
449          from database import get_user_by_stripe_id
450          user = await get_user_by_stripe_id(customer_id)
451          
452          if user:
453              # Rétrograder au niveau gratuit
454              user.subscription = ApiKeyLevel.FREE
455              await update_user(user)
456              
457              # Enregistrer l'événement d'abonnement
458              await record_subscription_event(
459                  user_id=user.id,
460                  event_type="subscription_deleted",
461                  subscription_id=subscription.id,
462                  status="canceled"
463              )
464              
465              logger.info(f"Abonnement supprimé pour l'utilisateur {user.id}: {subscription.id}")
466      except Exception as e:
467          logger.error(f"Erreur lors du traitement de la suppression d'abonnement: {e}")
468  
469  @router.get("/plans")
470  async def get_plans():
471      """
472      Récupère la liste des plans disponibles.
473      """
474      try:
475          # Formatage des plans pour l'interface utilisateur
476          formatted_plans = []
477          
478          for plan_id, plan_details in STRIPE_PLANS.items():
479              # Récupérer les détails du prix Stripe
480              price = stripe.Price.retrieve(plan_details["price_id"], expand=["product"])
481              
482              formatted_plans.append({
483                  "id": plan_id,
484                  "name": plan_details["name"],
485                  "priceId": plan_details["price_id"],
486                  "price": price.unit_amount / 100,  # Convertir les centimes en dollars/euros
487                  "currency": price.currency,
488                  "interval": price.recurring.interval,
489                  "description": price.product.description if hasattr(price.product, "description") else None,
490                  "features": price.product.metadata.get("features", "").split(",") if hasattr(price.product, "metadata") else []
491              })
492          
493          return {
494              "plans": formatted_plans
495          }
496          
497      except stripe.error.StripeError as e:
498          logger.error(f"Erreur Stripe lors de la récupération des plans: {e}")
499          raise HTTPException(
500              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
501              detail=f"Erreur Stripe: {str(e)}"
502          )
503      except Exception as e:
504          logger.error(f"Erreur lors de la récupération des plans: {e}")
505          raise HTTPException(
506              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
507              detail="Erreur interne du serveur"
508          )
509  
510  @router.get("/invoices")
511  async def get_invoices(current_user: User = Depends(get_current_active_user)):
512      """
513      Récupère les factures de l'utilisateur.
514      """
515      try:
516          # Vérifier si l'utilisateur a un ID client Stripe
517          if not hasattr(current_user, "stripe_customer_id") or not current_user.stripe_customer_id:
518              return {
519                  "invoices": []
520              }
521          
522          # Récupérer les factures
523          invoices = stripe.Invoice.list(
524              customer=current_user.stripe_customer_id,
525              limit=20
526          )
527          
528          # Formatage des factures pour l'interface utilisateur
529          formatted_invoices = []
530          
531          for invoice in invoices.data:
532              formatted_invoice = {
533                  "id": invoice.id,
534                  "number": invoice.number,
535                  "amount": invoice.total / 100,  # Convertir les centimes en dollars/euros
536                  "currency": invoice.currency,
537                  "status": invoice.status,
538                  "date": datetime.fromtimestamp(invoice.created).isoformat(),
539                  "due_date": datetime.fromtimestamp(invoice.due_date).isoformat() if invoice.due_date else None,
540                  "pdf": invoice.invoice_pdf
541              }
542              
543              formatted_invoices.append(formatted_invoice)
544          
545          return {
546              "invoices": formatted_invoices
547          }
548          
549      except stripe.error.StripeError as e:
550          logger.error(f"Erreur Stripe lors de la récupération des factures: {e}")
551          raise HTTPException(
552              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
553              detail=f"Erreur Stripe: {str(e)}"
554          )
555      except Exception as e:
556          logger.error(f"Erreur lors de la récupération des factures: {e}")
557          raise HTTPException(
558              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
559              detail="Erreur interne du serveur"
560          )
561  
562  @router.post("/update-payment-method")
563  async def update_payment_method(
564      data: Dict[str, Any],
565      current_user: User = Depends(get_current_active_user)
566  ):
567      """
568      Met à jour la méthode de paiement de l'utilisateur.
569      """
570      try:
571          # Vérifier si l'utilisateur a un ID client Stripe
572          if not hasattr(current_user, "stripe_customer_id") or not current_user.stripe_customer_id:
573              raise HTTPException(
574                  status_code=status.HTTP_400_BAD_REQUEST,
575                  detail="Aucun client Stripe associé à cet utilisateur"
576              )
577          
578          # Récupérer le payment_method_id
579          payment_method_id = data.get("paymentMethodId")
580          if not payment_method_id:
581              raise HTTPException(
582                  status_code=status.HTTP_400_BAD_REQUEST,
583                  detail="ID de méthode de paiement manquant"
584              )
585          
586          # Attacher la méthode de paiement au client
587          payment_method = stripe.PaymentMethod.attach(
588              payment_method_id,
589              customer=current_user.stripe_customer_id
590          )
591          
592          # Définir comme méthode de paiement par défaut
593          stripe.Customer.modify(
594              current_user.stripe_customer_id,
595              invoice_settings={
596                  "default_payment_method": payment_method_id
597              }
598          )
599          
600          return {
601              "success": True,
602              "payment_method": {
603                  "id": payment_method.id,
604                  "type": payment_method.type,
605                  "last4": payment_method.card.last4 if payment_method.type == "card" else None,
606                  "brand": payment_method.card.brand if payment_method.type == "card" else None,
607                  "exp_month": payment_method.card.exp_month if payment_method.type == "card" else None,
608                  "exp_year": payment_method.card.exp_year if payment_method.type == "card" else None
609              }
610          }
611          
612      except stripe.error.StripeError as e:
613          logger.error(f"Erreur Stripe lors de la mise à jour de la méthode de paiement: {e}")
614          raise HTTPException(
615              status_code=status.HTTP_400_BAD_REQUEST,
616              detail=f"Erreur Stripe: {str(e)}"
617          )
618      except Exception as e:
619          logger.error(f"Erreur lors de la mise à jour de la méthode de paiement: {e}")
620          raise HTTPException(
621              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
622              detail="Erreur interne du serveur"
623          )
624  
625  @router.get("/payment-methods")
626  async def get_payment_methods(current_user: User = Depends(get_current_active_user)):
627      """
628      Récupère les méthodes de paiement de l'utilisateur.
629      """
630      try:
631          # Vérifier si l'utilisateur a un ID client Stripe
632          if not hasattr(current_user, "stripe_customer_id") or not current_user.stripe_customer_id:
633              return {
634                  "payment_methods": []
635              }
636          
637          # Récupérer les méthodes de paiement
638          payment_methods = stripe.PaymentMethod.list(
639              customer=current_user.stripe_customer_id,
640              type="card"
641          )
642          
643          # Récupérer la méthode de paiement par défaut
644          customer = stripe.Customer.retrieve(current_user.stripe_customer_id)
645          default_payment_method_id = customer.invoice_settings.default_payment_method
646          
647          # Formatage des méthodes de paiement pour l'interface utilisateur
648          formatted_payment_methods = []
649          
650          for pm in payment_methods.data:
651              formatted_pm = {
652                  "id": pm.id,
653                  "type": pm.type,
654                  "last4": pm.card.last4 if pm.type == "card" else None,
655                  "brand": pm.card.brand if pm.type == "card" else None,
656                  "exp_month": pm.card.exp_month if pm.type == "card" else None,
657                  "exp_year": pm.card.exp_year if pm.type == "card" else None,
658                  "is_default": pm.id == default_payment_method_id
659              }
660              
661              formatted_payment_methods.append(formatted_pm)
662          
663          return {
664              "payment_methods": formatted_payment_methods
665          }
666          
667      except stripe.error.StripeError as e:
668          logger.error(f"Erreur Stripe lors de la récupération des méthodes de paiement: {e}")
669          raise HTTPException(
670              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
671              detail=f"Erreur Stripe: {str(e)}"
672          )
673      except Exception as e:
674          logger.error(f"Erreur lors de la récupération des méthodes de paiement: {e}")
675          raise HTTPException(
676              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
677              detail="Erreur interne du serveur"
678          )
679  
680  @router.delete("/payment-methods/{payment_method_id}")
681  async def delete_payment_method(
682      payment_method_id: str,
683      current_user: User = Depends(get_current_active_user)
684  ):
685      """
686      Supprime une méthode de paiement.
687      """
688      try:
689          # Vérifier si l'utilisateur a un ID client Stripe
690          if not hasattr(current_user, "stripe_customer_id") or not current_user.stripe_customer_id:
691              raise HTTPException(
692                  status_code=status.HTTP_400_BAD_REQUEST,
693                  detail="Aucun client Stripe associé à cet utilisateur"
694              )
695          
696          # Vérifier que la méthode de paiement appartient à l'utilisateur
697          payment_methods = stripe.PaymentMethod.list(
698              customer=current_user.stripe_customer_id,
699              type="card"
700          )
701          
702          payment_method_belongs_to_user = False
703          for pm in payment_methods.data:
704              if pm.id == payment_method_id:
705                  payment_method_belongs_to_user = True
706                  break
707          
708          if not payment_method_belongs_to_user:
709              raise HTTPException(
710                  status_code=status.HTTP_403_FORBIDDEN,
711                  detail="Cette méthode de paiement n'appartient pas à cet utilisateur"
712              )
713          
714          # Vérifier si c'est la méthode de paiement par défaut
715          customer = stripe.Customer.retrieve(current_user.stripe_customer_id)
716          is_default = customer.invoice_settings.default_payment_method == payment_method_id
717          
718          # Si c'est la méthode par défaut et qu'il y a d'autres méthodes, en définir une autre comme défaut
719          if is_default and len(payment_methods.data) > 1:
720              new_default = next(pm for pm in payment_methods.data if pm.id != payment_method_id)
721              stripe.Customer.modify(
722                  current_user.stripe_customer_id,
723                  invoice_settings={
724                      "default_payment_method": new_default.id
725                  }
726              )
727          
728          # Détacher la méthode de paiement
729          stripe.PaymentMethod.detach(payment_method_id)
730          
731          return {"success": True}
732          
733      except stripe.error.StripeError as e:
734          logger.error(f"Erreur Stripe lors de la suppression de la méthode de paiement: {e}")
735          raise HTTPException(
736              status_code=status.HTTP_400_BAD_REQUEST,
737              detail=f"Erreur Stripe: {str(e)}"
738          )
739      except Exception as e:
740          logger.error(f"Erreur lors de la suppression de la méthode de paiement: {e}")
741          raise HTTPException(
742              status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
743              detail="Erreur interne du serveur"
744          )_id)
745          
746          if user:
747              # Mettre à jour le niveau d'API de l'utilisateur
748              api_level = plan_details.get("api_level", ApiKeyLevel.FREE)
749              user.subscription = api_level
750              await update_user(user)
751              
752              # Enregistrer l'événement d'abonnement
753              await record_subscription_event(
754                  user_id=user.id,
755                  event_type="payment_succeeded",
756                  subscription_id=subscription_id,
757                  invoice_id=invoice.id,
758                  amount=invoice.amount_paid / 100,  # Convertir les centimes en dollars/euros
759                  plan_id=plan_details.get("id", "unknown"),
760                  plan_name=plan_details.get("name", "Unknown Plan")
761              )
762              
763              logger.info(f"Niveau d'API mis à jour pour l'utilisateur {user.id}: {api_level}")
764      except Exception as e:
765          logger.error(f"Erreur lors du traitement du paiement réussi: {e}")
766  
767  async def handle_payment_failed(invoice):
768      """
769      Traite un paiement échoué.
770      """
771      try:
772          # Récupérer les informations client et abonnement
773          subscription_id = invoice.subscription
774          customer_id = invoice.customer
775          
776          # Récupérer l'utilisateur à partir des métadonnées du client
777          from database import get_user_by_stripe_id
778          user = await get_user_by_stripe_id(customer_id)
779          
780          if user:
781              # Enregistrer l'événement d'abonnement
782              await record_subscription_event(
783                  user_id=user.id,
784                  event_type="payment_failed",
785                  subscription_id=subscription_id,
786                  invoice_id=invoice.id,
787                  amount=invoice.amount_due / 100,  # Convertir les centimes en dollars/euros
788                  error_message=invoice.last_payment_error.message if invoice.last_payment_error else "Unknown error"
789              )
790              
791              logger.warning(f"Paiement échoué pour l'utilisateur {user.id}, abonnement {subscription_id}")
792      except Exception as e:
793          logger.error(f"Erreur lors du traitement du paiement échoué: {e}")
794  
795  async def handle_subscription_created(subscription):
796      """
797      Traite la création d'un abonnement.
798      """
799      try:
800          # Récupérer les informations client
801          customer_id = subscription.customer
802          
803          # Récupérer les détails du plan
804          price_id = subscription.items.data[0].price.id
805          plan_details = PRICE_ID_TO_PLAN.get(price_id, {})
806          
807          # Récupérer l'utilisateur à partir des métadonnées du client
808          from database import get_user_by_stripe_id
809          user = await get_user_by_stripe_id(customer_id)
810          if user:
811              # Mettre à jour le niveau d'API de l'utilisateur
812              api_level = plan_details.get("api_level", ApiKeyLevel.FREE)
813              user.subscription = api_level
814              await update_user(user)
815              
816              # Enregistrer l'événement d'abonnement
817              await record_subscription_event(
818                  user_id=user.id,
819                  event_type="subscription_created",
820                  subscription_id=subscription.id,
821                  plan_id=plan_details.get("id", "unknown"),
822                  plan_name=plan_details.get("name", "Unknown Plan"),
823                  status=subscription.status
824              )
825              
826              logger.info(f"Abonnement créé pour l'utilisateur {user.id}: {subscription.id}")
827      except Exception as e:
828          logger.error(f"Erreur lors du traitement de la création d'abonnement: {e}")
829  
830  async def handle_subscription_updated(subscription):
831      """
832      Traite la mise à jour d'un abonnement.
833      """
834      try:
835          # Récupérer les informations client
836          customer_id = subscription.customer
837          
838          # Récupérer les détails du plan
839          price_id = subscription.items.data[0].price.id
840          plan_details = PRICE_ID_TO_PLAN.get(price_id, {})
841          
842          # Récupérer l'utilisateur à partir des métadonnées du client
843          from database import get_user_by_stripe_id
844          user = await get_user_by_stripe_id(customer_id)
845          if user:
846              # Mettre à jour le niveau d'API de l'utilisateur
847              api_level = plan_details.get("api_level", ApiKeyLevel.FREE)
848              user.subscription = api_level
849              await update_user(user)
850              
851              # Enregistrer l'événement d'abonnement
852              await record_subscription_event(
853                  user_id=user.id,
854                  event_type="subscription_updated",
855                  subscription_id=subscription.id,
856                  plan_id=plan_details.get("id", "unknown"),
857                  plan_name=plan_details.get("name", "Unknown Plan"),
858                  status=subscription.status
859              )
860              
861              logger.info(f"Abonnement mis à jour pour l'utilisateur {user.id}: {subscription.id}")
862      except Exception as e:
863          logger.error(f"Erreur lors du traitement de la mise à jour d'abonnement: {e}")
864  async def handle_subscription_deleted(subscription):
865      """
866      Traite la suppression d'un abonnement.
867      """
868      try:
869          # Récupérer les informations client
870          customer_id = subscription.customer
871          
872          # Récupérer l'utilisateur à partir des métadonnées du client
873          from database import get_user_by_stripe_id
874          user = await get_user_by_stripe_id(customer_id)
875          
876          if user:
877              # Mettre à jour le niveau d'API de l'utilisateur
878              user.subscription = ApiKeyLevel.FREE
879              await update_user(user)
880              
881              # Enregistrer l'événement d'abonnement
882              await record_subscription_event(
883                  user_id=user.id,
884                  event_type="subscription_deleted",
885                  subscription_id=subscription.id,
886                  status=subscription.status
887              )
888              
889              logger.info(f"Abonnement supprimé pour l'utilisateur {user.id}: {subscription.id}")
890      except Exception as e:
891          logger.error(f"Erreur lors du traitement de la suppression d'abonnement: {e}")