import json from datetime import datetime from django.contrib import messages from django.contrib.auth import get_user_model from django.db import connection from django.http import HttpResponse, Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404 import stripe # Create your views here. from django.utils import timezone from django.views import View from rest_framework import serializers from AuthBillet.models import HumanUser from BaseBillet.models import Configuration, LigneArticle, Paiement_stripe, Reservation from django.utils.translation import gettext, gettext_lazy as _ import logging logger = logging.getLogger(__name__) class creation_paiement_stripe(): def __init__(self, email_paiement: str, liste_ligne_article: list, metadata: dict, reservation: (Reservation, None), source: str, absolute_domain: str ) -> None: self.absolute_domain = absolute_domain self.liste_ligne_article = liste_ligne_article self.email_paiement = email_paiement self.metadata = metadata self.reservation = reservation self.source = source self.configuration = Configuration.get_solo() self.user = self._user_paiement() self.total = self._total() self.metadata_json = json.dumps(self.metadata) self.paiement_stripe_db = self._paiement_stripe_db() self.stripe_api_key = self._stripe_api_key() self.line_items = self._line_items() self.checkout_session = self._checkout_session() def _user_paiement(self): User = get_user_model() user_paiement, created = User.objects.get_or_create( email=self.email_paiement, username=self.email_paiement, ) # On ne lie pas tout de suite la carte a l'user, # on attendra une réponse positive du serveur cashless. if created: user_paiement: HumanUser user_paiement.client_source = connection.tenant user_paiement.client_achat.add(connection.tenant) user_paiement.is_active = False else: user_paiement.client_achat.add(connection.tenant) user_paiement.save() return user_paiement def _total(self): total = 0 for ligne in self.liste_ligne_article: total += float(ligne.qty) * float(ligne.price.prix) return total def _paiement_stripe_db(self): paiementStripeDb = Paiement_stripe.objects.create( user=self.user, total=self.total, metadata_stripe=self.metadata_json, reservation=self.reservation, source=self.source, ) for ligne_article in self.liste_ligne_article: ligne_article: LigneArticle ligne_article.paiement_stripe = paiementStripeDb ligne_article.save() return paiementStripeDb def _stripe_api_key(self): if self.configuration.stripe_mode_test: stripe.api_key = self.configuration.stripe_test_api_key else: stripe.api_key = self.configuration.stripe_api_key if stripe.api_key : return stripe.api_key else : raise serializers.ValidationError(_(f"No Stripe Api Key in configuration")) def _line_items(self): line_items = [] for ligne in self.liste_ligne_article: ligne: LigneArticle line_items.append( { "price": f"{ligne.price.get_id_price_stripe()}", "quantity": int(ligne.qty), } ) return line_items def _checkout_session(self): checkout_session = stripe.checkout.Session.create( success_url=f'{self.absolute_domain}/stripe/return/{self.paiement_stripe_db.uuid}', cancel_url=f'{self.absolute_domain}/stripe/return/{self.paiement_stripe_db.uuid}', payment_method_types=["card"], customer_email=f'{self.user.email}', line_items=self.line_items, mode='payment', metadata=self.metadata, client_reference_id=f"{self.user.uuid}", ) print(checkout_session.id) self.paiement_stripe_db.id_stripe = checkout_session.id self.paiement_stripe_db.status = Paiement_stripe.PENDING self.paiement_stripe_db.save() return checkout_session def is_valid(self): if self.checkout_session.id and \ self.checkout_session.url: return True else: return False def redirect_to_stripe(self): return HttpResponseRedirect(self.checkout_session.url) # On vérifie que les métatada soient les meme dans la DB et chez Stripe. def metatadata_valid(paiement_stripe_db: Paiement_stripe, checkout_session): metadata_stripe_json = checkout_session.metadata metadata_stripe = json.loads(str(metadata_stripe_json)) metadata_db_json = paiement_stripe_db.metadata_stripe metadata_db = json.loads(metadata_db_json) try: assert metadata_stripe == metadata_db assert set(metadata_db.keys()) == set(metadata_stripe.keys()) for key in set(metadata_stripe.keys()): assert metadata_db[key] == metadata_stripe[key] return True except: logger.error(f"{timezone.now()} " f"retour_stripe {paiement_stripe_db.uuid} : " f"metadata ne correspondent pas : {metadata_stripe} {metadata_db}") return False class retour_stripe(View): def get(self, request, uuid_stripe): paiement_stripe = get_object_or_404(Paiement_stripe, uuid=uuid_stripe) if paiement_stripe.traitement_en_cours: return HttpResponse( 'traitement_en_cours') if paiement_stripe.reservation: if paiement_stripe.reservation.status == Reservation.PAID_ERROR: return HttpResponse( "Erreur dans l'envoie du mail. Merci de vérifier l'adresse") if paiement_stripe.status == Paiement_stripe.VALID or paiement_stripe.reservation.status == Reservation.VALID: return HttpResponse( 'Paiement déja validé !') configuration = Configuration.get_solo() if configuration.stripe_mode_test: stripe.api_key = configuration.stripe_test_api_key else: stripe.api_key = configuration.stripe_api_key print(paiement_stripe.status) if paiement_stripe.status != Paiement_stripe.VALID: checkout_session = stripe.checkout.Session.retrieve(paiement_stripe.id_stripe) # on vérfie que les metatada soient cohérentes. #NTUI ! if metatadata_valid(paiement_stripe, checkout_session): if checkout_session.payment_status == "unpaid": paiement_stripe.status = Paiement_stripe.PENDING if datetime.now().timestamp() > checkout_session.expires_at: paiement_stripe.status = Paiement_stripe.EXPIRE paiement_stripe.save() return HttpResponse( f'stripe : {checkout_session.payment_status} - paiement : {paiement_stripe.status}') elif checkout_session.payment_status == "paid": # le .save() lance le process pre_save BaseBillet.models.send_to_cashless # qui modifie le status de chaque ligne # et envoie les informations au serveur cashless. # si validé par le serveur cashless, alors la ligne sera VALID. # Si toute les lignes sont VALID, le paiement_stripe sera aussi VALID # grace au post_save BaseBillet.models.check_status_stripe logger.info(f"retour_stripe - checkout_session.payment_status : {checkout_session.payment_status}") paiement_stripe.status = Paiement_stripe.PAID paiement_stripe.last_action = timezone.now() paiement_stripe.traitement_en_cours = True paiement_stripe.save() logger.info(f"retour_stripe - paiement_stripe.save() {paiement_stripe.status}") else: paiement_stripe.status = Paiement_stripe.CANCELED paiement_stripe.save() else: raise Http404 # on vérifie que le status n'ai pas changé paiement_stripe.refresh_from_db() # import ipdb; ipdb.set_trace() # si c'est depuis le qrcode, on renvoie vers la vue mobile : if paiement_stripe.source == Paiement_stripe.QRCODE: # SI le paiement est valide, c'est que les presave et postsave # ont validé la réponse du serveur cashless pour les recharges if paiement_stripe.status == Paiement_stripe.VALID: # on boucle ici pour récuperer l'uuid de la carte. for ligne_article in paiement_stripe.lignearticle_set.all(): if ligne_article.carte: messages.success(request, f"Paiement validé. Merci !") return HttpResponseRedirect(f"/qr/{ligne_article.carte.uuid}#success") else: # on boucle ici pour récuperer l'uuid de la carte. for ligne_article in paiement_stripe.lignearticle_set.all(): if ligne_article.carte: messages.error(request, f"Un problème de validation de paiement a été detecté. " f"Merci de vérifier votre moyen de paiement et/ou contactez un responsable.") return HttpResponseRedirect(f"/qr/{ligne_article.carte.uuid}#erreurpaiement") elif paiement_stripe.source == Paiement_stripe.API_BILLETTERIE: if paiement_stripe.reservation: if paiement_stripe.reservation.status == Reservation.VALID: return HttpResponse( 'Paiement déja validé !') if paiement_stripe.status == Paiement_stripe.VALID: return HttpResponse( 'Paiement déja validé !') elif paiement_stripe.status == Paiement_stripe.PAID: logger.info(f"Paiement_stripe.API_BILLETTERIE : {paiement_stripe.status}") return HttpResponse( 'Paiement okay, on lance les process de validation.') raise Http404(f'{paiement_stripe.status}') ''' class webhook_stripe(View): def get(self, request): print(f"webhook_stripe GET") return HttpResponse(f'ok') def post(self, request): endpoint_secret = 'whsec_1Urn98yUMsgwdXA7vhN5dwDTRQLD2vmD' event = None payload = request.data sig_header = request.headers['STRIPE_SIGNATURE'] try: event = stripe.Webhook.construct_event( payload, sig_header, endpoint_secret ) except ValueError as e: # Invalid payload raise e except stripe.error.SignatureVerificationError as e: # Invalid signature raise e # Handle the event print('Unhandled event type {}'.format(event['type'])) print(f"webhook_stripe POST {event}") return HttpResponse(f'ok {event}') '''