Résolue

Problèmes de panier dans un site e-commerce Django

# Stripe # Django

Bonjour, je suis occupé sur le site e-commerce Django et je rencontre 2 problèmes:

  1. mon panier n'est pas marqué comme ordered et supprimé après le paiement

  2. peut importe les items que j'ajoute dans le panier quand je me dirige vers la page de checkout avec stripe se ne sont pas les items qui s'affichent

vieuw :

from http.client import HTTPS_PORT
from importlib.metadata import metadata
from pprint import pprint

import stripe
from django.http import JsonResponse, HttpResponse
from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt

from accounts.models import Shopper
from shop import settings
from store.models import Product, Cart, Order  # import product
import json

YOUR_DOMAIN = 'http://localhost:8000'

stripe.api_key = settings.STRIPE_API_KEY

def index(request):
    products = Product.objects.all()

    return render(request, 'store/index.html', context={"products": products})

def product_detail(request, slug):
    product = get_object_or_404(Product, slug=slug)
    if request.method == "POST":
        comment = request.POST.get("comment")
        if comment:
            product.user_comment = comment  # Sauvegarde du commentaire
            product.save()
            return redirect("product", slug=product.slug)  # Rafraîchit la page
    return render(request, 'store/product-detail.html', context={"product": product})

def add_to_cart(request, slug):
    user = request.user # get user
    product = get_object_or_404(Product, slug=slug)
    cart, _ = Cart.objects.get_or_create(user=user)  # Get or create cart
    order, created = Order.objects.get_or_create(user=user, ordered=False,product=product) # Get or create order

    if created:
        cart.orders.add(order)
        cart.save()
    else:
        order.quantity += 1
        order.save()

    return redirect(reverse("product", kwargs={"slug": slug}))

def delete_cart(request):
    cart = getattr(request.user, "cart", None)  

    if cart:
        cart.delete()

    return redirect("index")


def cart(request):
    cart = get_object_or_404(Cart, user=request.user, ordered=False)
    orders = cart.orders.filter(ordered=False)  # Seules les commandes non terminées
    return render(request, 'store/cart.html', context={"cart": cart, "orders": orders})


def create_checkout_session(request):
    cart = request.user.cart
    line_items = [{"price": order.product.stripe_id,
                   "quantity": order.quantity} for order in cart.orders.all()]

    try:
        # Create a checkout session
        checkout_session = stripe.checkout.Session.create(
            locale="fr",
            payment_method_types=['card'],  # Only card payments
            line_items=line_items,
            mode='payment',
            success_url = YOUR_DOMAIN + reverse('checkout-success'),
            cancel_url=YOUR_DOMAIN + '/',
            metadata={
                "user_email": request.user.email
            }
        )

        # Redirect to Stripe checkout page
        return redirect(checkout_session.url, code=303)

    except Exception as e:
        return JsonResponse({"error": str(e)}, status=500)  # Return a JSON response on error

endpoint_secret = 'whsec_517e4430b4ef6cd82c3dd1bc5875745dc3eed84b823491b73760bb05adf8288e'

def checkout_success(request):
    return render(request, 'store/success.html')

@csrf_exempt
def stripe_webhook(request):
    payload = request.body
    sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')
    event = None

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, endpoint_secret
        )
    except ValueError as e:
        print(f"Payload invalide : {str(e)}")
        return HttpResponse(status=400)
    except stripe.error.SignatureVerificationError as e:
        print(f"Signature invalide : {str(e)}")
        return HttpResponse(status=400)

    # Logging pour vérifier les données reçues
    print(f"Événement reçu : {event.type}")
    pprint(event)

    # Gérer l'événement checkout.session.completed
    if event.type == 'checkout.session.completed':
        data = event['data']['object']
        return complete_order(data)

    return HttpResponse(status=200)


def complete_order(data):
    try:
        user_email = data['metadata'].get('user_email')
        if not user_email:
            raise KeyError("Email manquant dans les métadonnées")
    except KeyError as e:
        print(f"Erreur: {str(e)}")
        return HttpResponse("Invalid user email", status=404)

    user = get_object_or_404(Shopper, email=user_email)

    if not hasattr(user, 'cart'):
        print("Erreur: Aucun panier associé à cet utilisateur")
        return HttpResponse("Cart not found for user", status=404)

    # Marquer le panier comme commandé
    cart = user.cart
    cart.ordered = True
    cart.ordered_date = timezone.now()
    cart.save()

    # Marquer toutes les commandes associées comme commandées
    for order in cart.orders.all():
        order.ordered = True
        order.save()

    return HttpResponse(status=200)

model :

from ckeditor.fields import RichTextField
from django.urls import reverse
from django.utils import timezone
from django.utils.text import slugify
from django.db import models
from shop.settings import AUTH_USER_MODEL


class Variant(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

class Product(models.Model):
    name = models.CharField(max_length=200)
    slug = models.SlugField(max_length=100, unique=True, blank=True)  # Slug généré automatiquement si vide
    price = models.FloatField(default=0.0)
    available = models.BooleanField(default=True)
    description = RichTextField(blank=True)  # RichText pour mise en forme
    thumbnail = models.ImageField(upload_to="products", blank=True, null=True)  # Image principale
    variants = models.ManyToManyField("self", blank=True, symmetrical=False)
    user_comment = models.TextField(blank=True, null=True)
    stripe_id = models.CharField(max_length=90, blank=True)

    def save(self, *args, **kwargs):
        if not self.slug:  # Générer le slug automatiquement
            self.slug = slugify(self.name)
        super().save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse("product", kwargs={"slug": self.slug})

    def __str__(self):
        return self.name

class ProductImage(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="images")
    image = models.ImageField(upload_to="product_images", blank=True, null=True)

    def __str__(self):
        return f"Image de {self.product.name}"

# (Order)
class Order(models.Model):
    user = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE) #user relie a plusieurs articles grace a foreignKey() - on_delete-> si user deleted from db if will erase his stored products
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.IntegerField(default=1) # quantity of one item in the cart
    ordered = models.BooleanField(default=False)


    def __str__(self):
        return f"{self.product.name} ({self.quantity})"

# (Cart)
class Cart(models.Model):
    user = models.OneToOneField(AUTH_USER_MODEL, on_delete=models.CASCADE)
    orders = models.ManyToManyField(Order)
    ordered = models.BooleanField(default=False)
    ordered_date = models.DateTimeField(blank=True, null=True)


    def __str__(self):
        return self.user.username

    def delete(self, *args, **kwargs):
        # Mark all orders in the cart as ordered and set the ordered date
        for order in self.orders.all():
            order.ordered = True
            order.ordered_date = timezone.now()
            order.save()
        # Clear the orders from the cart
        self.orders.clear()
        # Call the superclass delete method to delete the cart
        super().delete(*args, **kwargs)

    # def process_payment(self):
    #     # Mark all orders in the cart as ordered
    #     for order in self.orders.all():
    #         order.ordered = True
    #         order.save()
    #     # Mark the cart as ordered and set the ordered date
    #     self.ordered = True
    #     self.ordered_date = timezone.now()
    #     self.save()
    #     # Delete the cart
    #     super().delete()

Gabriel Trouvé

Mentor

Salut,

Dans ton webhook tu fais ça via complete_order :

    cart.ordered = True
    cart.ordered_date = timezone.now()
    cart.save()

    # Marquer toutes les commandes associées comme commandées
    for order in cart.orders.all():
        order.ordered = True
        order.save()

Voici quelques pistes :

  • Le cart ne devrait pas avoir d'attribut ordered et de ordered_date. Mais bien un attrbut orders. Il y a un petit problème de conception au niveau du modèle.

  • Dans ta fonction complete_order tu devrais appeler la méthode delete de Cart qui va faire le boulot de passer les commandes à ordered et supprimer le panier.

  • Aussi, tu ne devrais pas retourner complete_order mais une HttpResponse.

Si tu veux, regarde ce que j'avais fait à l'époque : https://github.com/gabigab117/DocShop.git

Et tu as quoi qui est affiché au checkout ?

N'hésites pas si jamais tu as d'autres difficultés :)

Merci pour ta réponse.

Gabriel Trouvé

Mentor

Ah, je ne vois plus ton message par rapport à la supression du panier après le paiement. C'est bon du coup ?

En effet, le problème venait du fait que mon stripe cli n'etait pas lancé..je n'avais pas compris que le webhook était nécessaire pour écouter les actions

Gabriel Trouvé

Mentor

Super alors si ça fonctionne :)

Inscris-toi

(c'est gratuit !)

Inscris-toi

Tu dois créer un compte pour participer aux discussions.

Créer un compte

Rechercher sur le site

Formulaire de contact

Inscris-toi à Docstring

Pour commencer ton apprentissage.

Tu as déjà un compte ? Connecte-toi.