Intégration de Stripe dans une boutique Django Python mono-produit

DDD
Libérer: 2024-09-18 22:52:53
original
341 Les gens l'ont consulté

Dans la première partie de cette série, nous avons créé une boutique en ligne Django avec htmx.

Dans cette deuxième partie, nous traiterons les commandes avec Stripe.

Ce que nous ferons

Nous intégrerons Stripe pour gérer les paiements en toute sécurité. C'est ce que nous voulons réaliser :

  1. Dans la vue achat, nous commençons par créer une session de paiement Stripe et redirigeons le client vers l'URL correspondante. C'est ici que nous informons Stripe du produit que nous vendons, de sa quantité et vers lequel le client doit être redirigé après un achat réussi (le success_url).
  2. Le client remplit ses informations de paiement sur la page de paiement Stripe et finalise le paiement. Stripe envoie ensuite une requête POST à ​​un point de terminaison webhook sur notre site Web, où nous écoutons les événements et les traitons en conséquence. Si le paiement réussit, nous enregistrons la commande dans notre base de données et informons le client (et nos utilisateurs) de l'achat.
  3. Enfin, si le webhook renvoie une réponse avec un code d'état HTTP 200 OK, Stripe redirige vers le success_url créé lors de la première étape.

Configuration de Stripe pour notre boutique Django Python

Nous devons d'abord passer à Stripe et procéder comme suit :

  1. Créez un compte Stripe.
  2. Créez un produit (avec un identifiant de paiement).
  3. Créez un webhook.

1 : Créer un compte Stripe

Commencez par créer un compte Stripe. Pour l’instant, vous n’avez pas vraiment besoin d’activer votre compte. Vous pouvez simplement travailler en mode test, ce qui vous empêchera d'effectuer de vrais paiements pendant les tests. Accédez à la page des clés API et récupérez les clés publiables et secrètes. Enregistrez-les dans les variables d'environnement de votre projet (STRIPE_PUBLISHABLE_KEY et STRIPE_SECRET_KEY). Nous utiliserons ces clés pour authentifier vos demandes Stripe.

2 : Créez votre produit

Créez un nouveau produit sur la page produits. Remplissez les détails et définissez le type de paiement sur unique. Votre produit devrait ressembler à ceci :

Integrating Stripe Into A One-Product Django Python Shop

Une fois que vous avez appuyé sur Ajouter un produit, vous devriez pouvoir voir votre produit dans la liste des produits. Si vous cliquez dessus et faites défiler jusqu'à la section Prix, vous pouvez trouver l'ID API pour l'élément de prix que vous avez créé — il devrait être quelque chose comme price_3ODP5…. Enregistrez-le dans une variable d'environnement (STRIPE_PRICE_ID) : vous en aurez besoin lors de la création de la session de paiement Stripe.

3 : Créer le Webhook

Nous devons créer un point de terminaison webhook que Stripe pourra appeler lorsqu'un paiement est terminé. Dans la page des webhooks, choisissez de tester dans l'environnement local. Cela vous permettra de transférer la demande vers une URL locale, comme http://127.0.0.1:8000. Commencez par télécharger la Stripe CLI. Ensuite, vous pouvez :

  1. Connectez-vous à Stripe
stripe login
Copier après la connexion
  1. Transférer les événements vers le point de terminaison du webhook que vous allez créer :
stripe listen --forward-to http://127.0.0.1:8000/webhook
> Ready! Your webhook signing secret is whsec_06531a7ba22363ac038f284ac547906b89e5c939f8d55dfd03a3619f9adc590a (^C to quit)
Copier après la connexion

Cela garantit qu'une fois l'achat effectué, Stripe transmet les appels du webhook à votre point de terminaison local. La commande enregistrera un secret de signature de webhook, que vous devez également enregistrer en tant que variable d'environnement de projet (STRIPE_WEBHOOK_SECRET). Cela s'avérera utile pour vérifier qu'une requête provient bien de Stripe et que vous manipulez le bon webhook.

À la fin de cette section, vous devriez avoir quatre variables d'environnement Stripe. Vous pouvez maintenant les charger dans ecommerce_site/settings.py :

# ecommerce_site/settings.py

import os
from dotenv import load_dotenv
load_dotenv()

STRIPE_PUBLISHABLE_KEY = os.environ.get("STRIPE_PUBLISHABLE_KEY")
STRIPE_SECRET_KEY = os.environ.get("STRIPE_SECRET_KEY")
STRIPE_PRICE_ID = os.environ.get("STRIPE_PRICE_ID")
STRIPE_WEBHOOK_SECRET = os.environ.get("STRIPE_WEBHOOK_SECRET")
Copier après la connexion

Remarque : Nous utilisons python-dotenv pour charger les variables d'environnement.

Étendre les vues

Nous devons maintenant étendre les vues pour intégrer Stripe en créant une session de paiement, une vue d'achat réussi et une vue webhook.

1 : Créer une session de paiement Stripe

Dans la vue d'achat, nous créerons une session de paiement Stripe si le formulaire d'achat est valide :

# ecommerce/views.py
from django_htmx import HttpResponseClientRedirect
from django.conf import settings
import stripe

@require_POST
def purchase(request):
    form = OrderForm(request.POST)
    if form.is_valid():
        quantity = form.cleaned_data["quantity"]

        # replace time.sleep(2) with the following code ⬇️

        # 1 - set stripe api key
        stripe.api_key = settings.STRIPE_SECRET_KEY

        # 2 - create success url
        success_url = (
            request.build_absolute_uri(
                reverse("purchase_success")
            )
            + "?session_id={CHECKOUT_SESSION_ID}"
        )

        # 3 - create cancel url
        cancel_url = request.build_absolute_uri(reverse("home"))

        # 4 - create checkout session
        checkout_session = stripe.checkout.Session.create(
            line_items=[
                {
                    "price": settings.STRIPE_PRICE_ID,
                    "quantity": quantity,
                }
            ],
            mode="payment",
            success_url=success_url,
            cancel_url=cancel_url
        )

        # 5 - redirect to checkout session url
        return HttpResponseClientRedirect(checkout_session.url)
    return render(request, "product.html", {"form": form})
Copier après la connexion

Décomposons cela :

  1. We first set the Stripe API key.
  2. We then create a successful purchase URL pointing to the purchase_success view (which we'll create in the next step). Stripe should automatically populate the CHECKOUT_SESSION_ID.
  3. We create a URL for when a purchase is canceled — for example, when the customer changes their mind. In this case, it’s just the home view.
  4. We create a Stripe checkout session with our price ID (the product identifier) and the quantity the customer wants to purchase.
  5. Stripe returns a session object from which we can extract the URL and redirect the customer. Since this request is coming from htmx, we can’t really use the standard Django redirect function. Instead, we use the django-htmx package, which provides this HttpResponseClientRedirect class.

2: Create the Successful Purchase View

After completing the purchase, Stripe will redirect the customer to our specified success_url. Here, we can handle the post-purchase logic:

from django.shortcuts import redirect

def purchase_success(request):
    session_id = request.GET.get("session_id")
    if session_id is None:
          return redirect("home")

    stripe.api_key = settings.STRIPE_SECRET_KEY
    try:
        stripe.checkout.Session.retrieve(session_id)
    except stripe.error.InvalidRequestError:
        messages.error(request, "There was a problem while buying your product. Please try again.")
        return redirect("home")
    return render(request, "purchase_success.html")
Copier après la connexion

In this view, we first check if the session_id query parameter is present. If it is, we retrieve the corresponding session from Stripe using the secret key and the session_id. We then render the successful purchase template, which looks like this:

# ecommerce/templates/purchase_success.html {% extends "base.html" %} {% block
content %}
<section>
  <header>
    <h2>Thank you for your purchase</h2>
    <p>
      Your purchase was successful. You will receive an email with the details
      of your purchase soon.
    </p>
  </header>
</section>
{% endblock %}
Copier après la connexion

You should also add it to the urlpatterns:

# ecommerce_site/urls.py

# ... same imports as before

urlpatterns = [
    # ... same urls as before
    path("purchase_success", views.purchase_success, name="purchase_success"),  # ⬅️ new
]
Copier après la connexion

3: Create the Webhook View

While the customer is in the purchase process, and before they are redirected to the success view, Stripe will call our webhook endpoint (remember to have the webhook listener running, as explained in the earlier 'Create the Webhook' section of this post):

from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse

@csrf_exempt
def webhook(request):
    stripe.api_key = settings.STRIPE_SECRET_KEY
    sig_header = request.headers.get('stripe-signature')
    payload = request.body
    event = None
    try:
            event = stripe.Webhook.construct_event(
                payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
            )
    except stripe.error.SignatureVerificationError:
        # Invalid signature
        return HttpResponse(status=400)

    # Handle the checkout.session.completed event
    if event.type == "checkout.session.completed":
        # TODO: create line orders
        return HttpResponse(status=200)
    return HttpResponse(status=400)
Copier après la connexion

Let’s break this down:

  • We try to construct a Stripe event from the payload, the signature header, and the webhook secret: the first is used to build the actual event, and the last two variables are relevant to validate the authenticity of the request.
  • If the signature verification fails, we return a 400 HTTP response. Remember that Stripe is actually calling this endpoint, not our customer, so Stripe will know what to do in this scenario.
  • We check if the event type is checkout.session.completed, i.e., if a customer successfully paid for our product. For now, we don’t do much else here, but we will process the order in the next step.

Note: A Stripe event can have multiple types but we will only handle completed sessions in this post. However, you can (and should) extend a webhook by following the docs.

You should also add this view to urlpatterns:

# ecommerce_site/urls.py

# ... same imports as before

urlpatterns = [
    # ... same urls as before
    path("webhook", views.webhook, name="webhook"),  # ⬅️ new
]
Copier après la connexion

If everything works well, once you click “buy”, you should be redirected to a Stripe payment page. Since we are in test mode, we can fill in the payment details with dummy data, like a 4242 4242 4242 4242 card:

Integrating Stripe Into A One-Product Django Python Shop

Once you press Pay, Stripe should call the webhook view and redirect you to the purchase_success view. Congratulations, you have successfully processed a payment with Stripe!

Create the Orders and Notify Users

Once a purchase is completed, we need to do a few things in the webhook view:

  • Save the order information in our database.
  • Notify staff users about the recent purchase.
  • Send a confirmation email to the customer.

Let’s create a LineOrder database model in ecommerce/models.py to store some of the order information:

# ecommerce/models.py

from django.db import models

class LineOrder(models.Model):
    quantity = models.IntegerField()
    name = models.CharField(max_length=255, null=True, blank=True)
    email = models.EmailField(null=True, blank=True)
    shipping_details = models.TextField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Order {self.id} - {self.quantity} units"
Copier après la connexion

Remember to create and run the migrations:

python manage.py makemigrations # ⬅️ creates the migration files
python manage.py migrate # ⬅️ applies the migrations in the database
Copier après la connexion

We can now create a function to process the orders and call it from the webhook view:

# ecommerce/views.py

@csrf_exempt
def webhook(request):
    # ...same code as before
        if event.type == "checkout.session.completed":
            create_line_orders(event.data.object) # ⬅️ new
            return HttpResponse(status=200)
        return HttpResponse(status=400)

# new ⬇️
def create_line_orders(session: stripe.checkout.Session):
    line_items = stripe.checkout.Session.list_line_items(session.id)
    for line_item in line_items.data:
        LineOrder.objects.create(
            name=session.customer_details.name,
            email=session.customer_details.email,
            shipping_details=session.shipping_details,
            quantity=line_item.quantity,
        )
    mail.send_mail(
        "Your order has been placed",
        f"""
        Hi {session.customer_details.name},
        Your order has been placed. Thank you for shopping with us!
        You will receive an email with tracking information shortly.

        Best,
        The one product e-commerce Team
        """,
        "from@example.com",
        [session.customer_details.email],
    )

    staff_users = User.objects.filter(is_staff=True)
    mail.send_mail(
        "You have a new order!",
        """
            Hi team!
            You have a new order in your shop! go to the admin page to see it.

            Best,
            The one product e-commerce Team
            """,
        "from@example.com",
        [user.email for user in staff_users],
    )
Copier après la connexion

Let’s break this down:

  • We first create line order instances from the Stripe session and send a confirmation email to the customer about their purchase.
  • We then send an email to all staff users telling them to check the admin panel.

You can now register the LineOrder model in the admin panel, so it’s accessible to staff users:

# ecommerce/admin.py
from django.contrib import admin
from ecommerce.models import LineOrder

# Register your models here.
admin.site.register(LineOrder)
Copier après la connexion

When staff users log in to the admin page, they will now be able to check new orders and process them accordingly — in this case, pack and ship mugs to the customer!

Some Tips to Optimize Your Django Store

Here are some tips to further improve on the store you've built:

  • Écrire des tests - vous pouvez voir quelques exemples dans le référentiel GitHub.
  • Si vous avez plus de produits à vendre, créez un modèle de base de données pour eux et connectez le LineOrder via une ForeignKey.
  • Configurez les paramètres de messagerie selon la documentation de messagerie de Django. Vous pouvez également utiliser des bibliothèques telles que Django-post-office pour gérer vos modèles de courrier électronique et vos files d'attente.
  • Une fois que vous avez déployé votre site Web, créez un véritable webhook (pas un écouteur local).
  • Jetez un œil à la documentation Stripe pour découvrir des alternatives au processus de paiement que nous avons décrit, y compris un formulaire de paiement intégré.

Conclusion

Dans cette série en deux parties, nous avons créé avec succès un site de commerce électronique mono-produit en utilisant Django, htmx et Stripe. Ce guide vous a accompagné dans la configuration de votre projet Django, l'intégration de HTML pour des interactions utilisateur transparentes et l'intégration de paiements sécurisés avec Stripe.

Nous avons également expliqué comment gérer le traitement des commandes, notamment en enregistrant les informations de commande dans votre base de données, en informant les utilisateurs du personnel des nouveaux achats et en envoyant des e-mails de confirmation à vos clients. Grâce à ces bases, vous pouvez personnaliser et développer davantage votre site de commerce électronique en fonction de vos besoins spécifiques.

Bon codage !

P.S. Si vous souhaitez lire les articles Python dès leur sortie de presse, abonnez-vous à notre newsletter Python Wizardry et ne manquez jamais un seul article !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!