gestion améliorée du retour de paiement.
Ajout de la source du paiement stripe.
This commit is contained in:
parent
9f76969c5c
commit
290ca513ca
|
|
@ -214,10 +214,6 @@ staff_admin_site.register(Product, ProductAdmin)
|
|||
|
||||
|
||||
|
||||
staff_admin_site.register(LigneArticle, admin.ModelAdmin)
|
||||
|
||||
|
||||
|
||||
staff_admin_site.register(OptionGenerale, OptionGeneraleAdmin)
|
||||
|
||||
staff_admin_site.register(Price, admin.ModelAdmin)
|
||||
|
|
@ -230,6 +226,7 @@ class PaiementStripeAdmin(admin.ModelAdmin):
|
|||
'total',
|
||||
'order_date',
|
||||
'status',
|
||||
'source',
|
||||
)
|
||||
ordering = ('-order_date',)
|
||||
|
||||
|
|
@ -237,4 +234,16 @@ class PaiementStripeAdmin(admin.ModelAdmin):
|
|||
staff_admin_site.register(Paiement_stripe, PaiementStripeAdmin)
|
||||
|
||||
|
||||
class LigneArticleAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'datetime',
|
||||
'price',
|
||||
'qty',
|
||||
'carte',
|
||||
'status',
|
||||
'paiement_stripe',
|
||||
'status_stripe'
|
||||
)
|
||||
ordering = ('-datetime',)
|
||||
|
||||
staff_admin_site.register(LigneArticle, LigneArticleAdmin)
|
||||
|
|
|
|||
|
|
@ -241,7 +241,6 @@ class ReservationValidator(serializers.Serializer):
|
|||
lignes_article = []
|
||||
for price in self.prices_list:
|
||||
ligne_article = LigneArticle.objects.create(
|
||||
reservation=reservation,
|
||||
price=price.get('price'),
|
||||
qty=price.get('qty'),
|
||||
)
|
||||
|
|
@ -264,6 +263,8 @@ class ReservationValidator(serializers.Serializer):
|
|||
)
|
||||
|
||||
if new_paiement_stripe.is_valid():
|
||||
reservation.paiement = new_paiement_stripe.paiement_stripe_db
|
||||
reservation.save()
|
||||
print(new_paiement_stripe.checkout_session.stripe_id)
|
||||
# return new_paiement_stripe.redirect_to_stripe()
|
||||
self.checkout_session = new_paiement_stripe.checkout_session
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
# Generated by Django 2.2 on 2021-10-26 10:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('BaseBillet', '0010_auto_20211025_1002'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='lignearticle',
|
||||
name='reservation',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='configuration',
|
||||
name='name_required_for_ticket',
|
||||
field=models.BooleanField(default=False, verbose_name='Billet nominatifs'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='scan_status',
|
||||
field=models.CharField(choices=[('N', 'Non actif'), ('K', 'Non scanné'), ('S', 'scanné')], default='N', max_length=1, verbose_name='Status du scan'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 2.2 on 2021-10-26 11:07
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('PaiementStripe', '0001_initial'),
|
||||
('BaseBillet', '0011_auto_20211026_1459'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='reservation',
|
||||
name='paiement',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reservation', to='PaiementStripe.Paiement_stripe'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2 on 2021-10-26 11:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('BaseBillet', '0012_reservation_paiement'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='lignearticle',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('C', 'Annulée'), ('N', 'Non payée'), ('P', 'Payée'), ('V', 'Validée par serveur cashless')], default='N', max_length=3, verbose_name='Status de ligne article'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
import uuid
|
||||
|
||||
import requests
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
from django.db.models.signals import post_save
|
||||
from django.db.models import Q
|
||||
from django.db.models.signals import post_save, pre_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils import timezone
|
||||
from solo.models import SingletonModel
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from stdimage import StdImageField
|
||||
|
|
@ -328,11 +331,14 @@ class Reservation(models.Model):
|
|||
status = models.CharField(max_length=3, choices=TYPE_CHOICES, default=UNPAID,
|
||||
verbose_name=_("Status de la réservation"))
|
||||
|
||||
paiement = models.OneToOneField(Paiement_stripe, on_delete=models.PROTECT, blank=True, null=True, related_name='reservation')
|
||||
|
||||
options = models.ManyToManyField(OptionGenerale)
|
||||
|
||||
def user_mail(self):
|
||||
return self.user_commande.email
|
||||
#
|
||||
|
||||
|
||||
# def total_billet(self):
|
||||
# total = 0
|
||||
# for ligne in self.paiements.all():
|
||||
|
|
@ -388,7 +394,81 @@ class LigneArticle(models.Model):
|
|||
|
||||
qty = models.SmallIntegerField()
|
||||
|
||||
reservation = models.ForeignKey(Reservation, on_delete=models.CASCADE, blank=True, null=True, related_name='paiements')
|
||||
carte = models.ForeignKey(CarteCashless, on_delete=models.PROTECT, blank=True, null=True)
|
||||
|
||||
paiement_stripe = models.ForeignKey(Paiement_stripe, on_delete=models.PROTECT, blank=True, null=True)
|
||||
|
||||
CANCELED, UNPAID, PAID, VALID, = 'C', 'N', 'P', 'V'
|
||||
TYPE_CHOICES = [
|
||||
(CANCELED, _('Annulée')),
|
||||
(UNPAID, _('Non payée')),
|
||||
(PAID, _('Payée')),
|
||||
(VALID, _('Validée par serveur cashless')),
|
||||
]
|
||||
|
||||
status = models.CharField(max_length=3, choices=TYPE_CHOICES, default=UNPAID,
|
||||
verbose_name=_("Status de ligne article"))
|
||||
|
||||
def status_stripe(self):
|
||||
if self.paiement_stripe:
|
||||
return self.paiement_stripe.status
|
||||
else:
|
||||
return _('no stripe send')
|
||||
|
||||
|
||||
@receiver(post_save, sender=LigneArticle)
|
||||
def check_status_stripe(sender, instance: LigneArticle, created, **kwargs):
|
||||
if instance.paiement_stripe :
|
||||
lignes_dans_paiement_stripe = instance.paiement_stripe.lignearticle_set.all()
|
||||
if len(lignes_dans_paiement_stripe) == len(lignes_dans_paiement_stripe.filter(status=LigneArticle.VALID)):
|
||||
# toute les lignes d'article sont VALID
|
||||
# on passe le status du paiement stripe en VALID
|
||||
instance.paiement_stripe.status = Paiement_stripe.VALID
|
||||
instance.paiement_stripe.save()
|
||||
|
||||
|
||||
|
||||
|
||||
@receiver(pre_save, sender=Paiement_stripe)
|
||||
def send_to_cashless(sender, instance: Paiement_stripe, update_fields=None, **kwargs):
|
||||
# si l'instance vient d'être créé, ne rien faire :
|
||||
if instance.pk is None:
|
||||
pass
|
||||
else:
|
||||
paiementStripe = instance
|
||||
if paiementStripe.status == Paiement_stripe.PAID:
|
||||
data_pour_serveur_cashless = {'uuid_commande': paiementStripe.uuid}
|
||||
|
||||
for ligne_article in paiementStripe.lignearticle_set.all():
|
||||
if ligne_article.carte:
|
||||
data_pour_serveur_cashless['uuid'] = ligne_article.carte.uuid
|
||||
|
||||
if ligne_article.price.product.categorie_article == Product.RECHARGE_CASHLESS:
|
||||
data_pour_serveur_cashless['recharge_qty'] = ligne_article.price.prix
|
||||
|
||||
if ligne_article.price.product.categorie_article == Product.ADHESION:
|
||||
data_pour_serveur_cashless['tarif_adhesion'] = ligne_article.price.prix
|
||||
|
||||
# si il y a autre chose que uuid_commande :
|
||||
if len(data_pour_serveur_cashless) > 1:
|
||||
sess = requests.Session()
|
||||
configuration = Configuration.get_solo()
|
||||
r = sess.post(
|
||||
f'{configuration.server_cashless}/api/billetterie_endpoint',
|
||||
headers={
|
||||
'Authorization': f'Api-Key {configuration.key_cashless}'
|
||||
},
|
||||
data=data_pour_serveur_cashless,
|
||||
)
|
||||
|
||||
sess.close()
|
||||
print(
|
||||
f"{timezone.now()} demande au serveur cashless pour un rechargement. réponse : {r.status_code} ")
|
||||
|
||||
if r.status_code == 200:
|
||||
# la commande a été envoyé au serveur cashless et validé.
|
||||
for ligne_article in paiementStripe.lignearticle_set.filter(
|
||||
Q(price__product__categorie_article=Product.RECHARGE_CASHLESS) |
|
||||
Q(price__product__categorie_article=Product.ADHESION)):
|
||||
ligne_article.status = LigneArticle.VALID
|
||||
ligne_article.save()
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 2.2 on 2021-10-26 12:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('PaiementStripe', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='paiement_stripe',
|
||||
name='source',
|
||||
field=models.CharField(choices=[('Q', 'Depuis scan QR-Code'), ('B', 'Depuis billetterie')], default='N', max_length=1, verbose_name='Statut de la commande'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='paiement_stripe',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('N', 'Lien de paiement non créé'), ('O', 'Envoyée a Stripe'), ('W', 'En attente de paiement'), ('E', 'Expiré'), ('P', 'Payée'), ('V', 'Payée et validée'), ('C', 'Annulée')], default='N', max_length=1, verbose_name='Statut de la commande'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2 on 2021-10-26 12:19
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('PaiementStripe', '0002_auto_20211026_1617'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='paiement_stripe',
|
||||
name='source',
|
||||
field=models.CharField(choices=[('Q', 'Depuis scan QR-Code'), ('B', 'Depuis billetterie')], default='B', max_length=1, verbose_name='Statut de la commande'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
from django.db import models
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
import uuid
|
||||
from django.utils.translation import gettext, gettext_lazy as _
|
||||
|
||||
# Create your models here.
|
||||
from TiBillet import settings
|
||||
|
||||
|
|
@ -20,7 +22,7 @@ class Paiement_stripe(models.Model):
|
|||
|
||||
NON, OPEN, PENDING, EXPIRE, PAID, VALID, CANCELED = 'N', 'O', 'W', 'E', 'P', 'V', 'C'
|
||||
STATUT_CHOICES = (
|
||||
(NON, 'Lien de paiement non crée'),
|
||||
(NON, 'Lien de paiement non créé'),
|
||||
(OPEN, 'Envoyée a Stripe'),
|
||||
(PENDING, 'En attente de paiement'),
|
||||
(EXPIRE, 'Expiré'),
|
||||
|
|
@ -31,8 +33,14 @@ class Paiement_stripe(models.Model):
|
|||
|
||||
status = models.CharField(max_length=1, choices=STATUT_CHOICES, default=NON, verbose_name="Statut de la commande")
|
||||
|
||||
total = models.FloatField(default=0)
|
||||
QRCODE, BILLETTERIE = 'Q', 'B'
|
||||
SOURCE_CHOICES = (
|
||||
(QRCODE, _('Depuis scan QR-Code')),
|
||||
(BILLETTERIE, _('Depuis billetterie')),
|
||||
)
|
||||
source = models.CharField(max_length=1, choices=SOURCE_CHOICES, default=BILLETTERIE, verbose_name="Source de la commande")
|
||||
|
||||
total = models.FloatField(default=0)
|
||||
|
||||
def uuid_8(self):
|
||||
return f"{self.uuid}".partition('-')[0]
|
||||
|
|
@ -41,8 +49,8 @@ class Paiement_stripe(models.Model):
|
|||
return self.uuid_8()
|
||||
|
||||
def articles(self):
|
||||
return " - ".join([ f"{ligne.product.name} {ligne.qty * ligne.product.prix }€" for ligne in self.lignearticle_set.all()])
|
||||
|
||||
return " - ".join(
|
||||
[f"{ligne.product.name} {ligne.qty * ligne.product.prix}€" for ligne in self.lignearticle_set.all()])
|
||||
|
||||
|
||||
''' RECEIVER PRESAVE DANS LE VIEW QRCODECASHELESS
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ class creation_paiement_stripe():
|
|||
email_paiement: str,
|
||||
liste_ligne_article: list,
|
||||
metadata: dict,
|
||||
source: str,
|
||||
absolute_domain: str
|
||||
) -> None:
|
||||
|
||||
|
|
@ -33,6 +34,7 @@ class creation_paiement_stripe():
|
|||
self.liste_ligne_article = liste_ligne_article
|
||||
self.email_paiement = email_paiement
|
||||
self.metadata = metadata
|
||||
self.source = source
|
||||
|
||||
self.configuration = Configuration.get_solo()
|
||||
self.user = self._user_paiement()
|
||||
|
|
@ -73,9 +75,10 @@ class creation_paiement_stripe():
|
|||
user=self.user,
|
||||
total=self.total,
|
||||
metadata_stripe=self.metadata_json,
|
||||
source=self.source,
|
||||
)
|
||||
|
||||
for ligne_article in self.liste_ligne_article :
|
||||
for ligne_article in self.liste_ligne_article:
|
||||
ligne_article: LigneArticle
|
||||
ligne_article.paiement_stripe = paiementStripeDb
|
||||
ligne_article.save()
|
||||
|
|
@ -123,7 +126,7 @@ class creation_paiement_stripe():
|
|||
|
||||
def is_valid(self):
|
||||
if self.checkout_session.id and \
|
||||
self.checkout_session.url :
|
||||
self.checkout_session.url:
|
||||
return True
|
||||
|
||||
else:
|
||||
|
|
@ -133,7 +136,6 @@ class creation_paiement_stripe():
|
|||
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
|
||||
|
|
@ -154,6 +156,7 @@ def metatadata_valid(paiement_stripe_db: Paiement_stripe, checkout_session):
|
|||
f"metadata ne correspondent pas : {metadata_stripe} {metadata_db}")
|
||||
return False
|
||||
|
||||
|
||||
class retour_stripe(View):
|
||||
|
||||
def get(self, request, uuid_stripe):
|
||||
|
|
@ -170,8 +173,8 @@ class retour_stripe(View):
|
|||
|
||||
checkout_session = stripe.checkout.Session.retrieve(paiement_stripe.id_stripe)
|
||||
|
||||
# on vérfie que les metatada soient cohérente. #NTUI !
|
||||
if metatadata_valid(paiement_stripe , checkout_session):
|
||||
# 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
|
||||
|
|
@ -181,7 +184,14 @@ class retour_stripe(View):
|
|||
|
||||
elif checkout_session.payment_status == "paid":
|
||||
paiement_stripe.status = Paiement_stripe.PAID
|
||||
# le .save() lance le process pre_save dans le view QrcodeCashless qui peut modifier son status
|
||||
|
||||
# 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
|
||||
|
||||
paiement_stripe.save()
|
||||
|
||||
else:
|
||||
|
|
@ -192,19 +202,28 @@ class retour_stripe(View):
|
|||
|
||||
# on vérifie que le status n'ai pas changé
|
||||
paiement_stripe.refresh_from_db()
|
||||
if paiement_stripe.status == Paiement_stripe.VALID:
|
||||
|
||||
# si c'est depuis le qrcode, on renvoie vers la vue mobile :
|
||||
if paiement_stripe.source == Paiement_stripe.QRCODE :
|
||||
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 :
|
||||
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é. Merci de vérifier votre moyen de paiement ou contactez un responsable.")
|
||||
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 ou contactez un responsable.")
|
||||
return HttpResponseRedirect(f"/qr/{ligne_article.carte.uuid}#error")
|
||||
|
||||
return HttpResponse('Un problème de validation de paiement a été detecté. Merci de vérifier votre moyen de paiement ou contactez un responsable.')
|
||||
else:
|
||||
return HttpResponse(
|
||||
'Un problème de validation de paiement a été detecté. Merci de vérifier votre moyen de paiement ou contactez un responsable.')
|
||||
# return HttpResponseRedirect("/")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
<li><a href="#recharger">Recharger</a></li>
|
||||
|
||||
{% if history %}
|
||||
<li><a href="#historique">Historique</a></li>
|
||||
<li><a href="#historique">Solde</a></li>
|
||||
{% endif %}
|
||||
|
||||
{% if carte_resto %}
|
||||
|
|
@ -130,7 +130,7 @@
|
|||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<h2 class="major">Historique</h2>
|
||||
<h2 class="major">Historique 24h</h2>
|
||||
|
||||
|
||||
<div class="table-wrapper">
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from datetime import datetime
|
|||
import requests, json
|
||||
from django.contrib import messages
|
||||
from django.db import connection
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponseRedirect, HttpResponse, Http404
|
||||
from django.shortcuts import render
|
||||
from django.utils import timezone
|
||||
|
|
@ -201,6 +202,7 @@ class index_scan(View):
|
|||
email_paiement=data.get('email'),
|
||||
liste_ligne_article=ligne_articles,
|
||||
metadata=metadata,
|
||||
source=Paiement_stripe.QRCODE,
|
||||
absolute_domain=request.build_absolute_uri().partition('/qr')[0],
|
||||
)
|
||||
|
||||
|
|
@ -256,44 +258,3 @@ class index_scan(View):
|
|||
else:
|
||||
messages.error(request, f'Erreur {r.status_code} {r.text}')
|
||||
return HttpResponseRedirect(f'#erreur')
|
||||
|
||||
|
||||
@receiver(pre_save, sender=Paiement_stripe)
|
||||
def changement_paid_to_valid(sender, instance: Paiement_stripe, update_fields=None, **kwargs):
|
||||
# si l'instance vient d'être créé, ne rien faire :
|
||||
if instance.pk is None:
|
||||
pass
|
||||
else:
|
||||
paiementStripe = instance
|
||||
if paiementStripe.status == Paiement_stripe.PAID:
|
||||
data_pour_serveur_cashless = {'uuid_commande': paiementStripe.uuid}
|
||||
|
||||
for ligne_article in paiementStripe.lignearticle_set.all():
|
||||
if ligne_article.carte:
|
||||
data_pour_serveur_cashless['uuid'] = ligne_article.carte.uuid
|
||||
|
||||
if ligne_article.price.product.categorie_article == Product.RECHARGE_CASHLESS:
|
||||
data_pour_serveur_cashless['recharge_qty'] = ligne_article.price.prix
|
||||
|
||||
if ligne_article.price.product.categorie_article == Product.ADHESION:
|
||||
data_pour_serveur_cashless['tarif_adhesion'] = ligne_article.price.prix
|
||||
|
||||
# si il y a autre chose que uuid_commande :
|
||||
if len(data_pour_serveur_cashless) > 1:
|
||||
sess = requests.Session()
|
||||
configuration = Configuration.get_solo()
|
||||
r = sess.post(
|
||||
f'{configuration.server_cashless}/api/billetterie_endpoint',
|
||||
headers={
|
||||
'Authorization': f'Api-Key {configuration.key_cashless}'
|
||||
},
|
||||
data=data_pour_serveur_cashless,
|
||||
)
|
||||
|
||||
sess.close()
|
||||
print(
|
||||
f"{timezone.now()} demande au serveur cashless pour un rechargement. réponse : {r.status_code} ")
|
||||
|
||||
if r.status_code == 200:
|
||||
# la commande a été envoyé au serveur cashless et validé.
|
||||
paiementStripe.status = Paiement_stripe.VALID
|
||||
|
|
|
|||
Loading…
Reference in New Issue