Heim >Backend-Entwicklung >Python-Tutorial >Integration von Stripe in einen Ein-Produkt-Django-Python-Shop

Integration von Stripe in einen Ein-Produkt-Django-Python-Shop

DDD
DDDOriginal
2024-09-18 22:52:53535Durchsuche

Im ersten Teil dieser Serie haben wir einen Django-Onlineshop mit htmx erstellt.

In diesem zweiten Teil werden wir Bestellungen mit Stripe abwickeln.

Was wir tun werden

Wir integrieren Stripe, um Zahlungen sicher abzuwickeln. Das wollen wir erreichen:

  1. In der Kaufansicht erstellen wir zunächst eine Stripe-Checkout-Sitzung und leiten den Kunden zur entsprechenden URL weiter. Hier informieren wir Stripe über das von uns verkaufte Produkt, seine Menge und wohin der Kunde nach einem erfolgreichen Kauf weitergeleitet werden soll (die success_url).
  2. Der Kunde gibt seine Zahlungsdaten auf der Checkout-Seite von Stripe ein und schließt die Zahlung ab. Stripe stellt dann eine POST-Anfrage an einen Webhook-Endpunkt auf unserer Website, wo wir Ereignisse abhören und sie entsprechend verarbeiten. Wenn die Zahlung erfolgreich ist, speichern wir die Bestellung in unserer Datenbank und benachrichtigen den Kunden (und unsere Mitarbeiter) über den Kauf.
  3. Wenn der Webhook schließlich eine Antwort mit dem HTTP-Statuscode 200 OK zurückgibt, leitet Stripe zur im ersten Schritt erstellten success_url weiter.

Einrichten von Stripe für unseren Django Python Store

Wir müssen zuerst zu Stripe springen und Folgendes tun:

  1. Erstellen Sie ein Stripe-Konto.
  2. Erstellen Sie ein Produkt (mit einer Zahlungs-ID).
  3. Erstellen Sie einen Webhook.

1: Erstellen Sie ein Stripe-Konto

Erstellen Sie zunächst ein Stripe-Konto. Im Moment müssen Sie Ihr Konto nicht wirklich aktivieren. Sie können einfach im Testmodus arbeiten, was verhindert, dass Sie während des Tests echte Zahlungen tätigen. Gehen Sie zur Seite „API-Schlüssel“ und rufen Sie die veröffentlichbaren und geheimen Schlüssel ab. Speichern Sie sie in Ihren Projektumgebungsvariablen (STRIPE_PUBLISHABLE_KEY und STRIPE_SECRET_KEY). Wir werden diese Schlüssel verwenden, um Ihre Stripe-Anfragen zu authentifizieren.

2: Erstellen Sie Ihr Produkt

Erstellen Sie auf der Produktseite ein neues Produkt. Füllen Sie die Details aus und stellen Sie die Zahlungsart auf einmalig ein. Ihr Produkt sollte etwa so aussehen:

Integrating Stripe Into A One-Product Django Python Shop

Sobald Sie auf Produkt hinzufügen klicken, sollte Ihr Produkt in der Produktliste angezeigt werden. Wenn Sie darauf klicken und nach unten zum Abschnitt Preise scrollen, finden Sie die API-ID für den von Ihnen erstellten Preisartikel – sie sollte etwa preis_3ODP5… lauten. Speichern Sie es in einer Umgebungsvariablen (STRIPE_PRICE_ID): Sie benötigen diese beim Erstellen der Stripe-Checkout-Sitzung.

3: Erstellen Sie den Webhook

Wir müssen einen Webhook-Endpunkt erstellen, den Stripe aufrufen kann, wenn eine Zahlung abgeschlossen ist. Wählen Sie auf der Seite „Webhooks“ den Test in der lokalen Umgebung aus. Dadurch können Sie die Anfrage an eine lokale URL weiterleiten, z. B. http://127.0.0.1:8000. Laden Sie zunächst die Stripe-CLI herunter. Dann können Sie:

  1. Melden Sie sich bei Stripe an
stripe login
  1. Ereignisse an den Webhook-Endpunkt weiterleiten, den Sie erstellen werden:
stripe listen --forward-to http://127.0.0.1:8000/webhook
> Ready! Your webhook signing secret is whsec_06531a7ba22363ac038f284ac547906b89e5c939f8d55dfd03a3619f9adc590a (^C to quit)

Dadurch wird sichergestellt, dass Stripe die Webhook-Aufrufe nach dem Kauf an Ihren lokalen Endpunkt weiterleitet. Der Befehl protokolliert ein Webhook-Signaturgeheimnis, das Sie auch als Projektumgebungsvariable (STRIPE_WEBHOOK_SECRET) speichern sollten. Dies erweist sich als nützlich, um zu überprüfen, ob eine Anfrage tatsächlich von Stripe stammt und Sie den richtigen Webhook verarbeiten.

Am Ende dieses Abschnitts sollten Sie über vier Stripe-Umgebungsvariablen verfügen. Sie können sie jetzt in ecommerce_site/settings.py laden:

# 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")

Hinweis: Wir verwenden python-dotenv, um die Umgebungsvariablen zu laden.

Erweitern Sie die Ansichten

Wir müssen jetzt die Ansichten erweitern, um Stripe zu integrieren, indem wir eine Checkout-Sitzung, eine Ansicht für den erfolgreichen Kauf und eine Webhook-Ansicht erstellen.

1: Erstellen Sie eine Stripe Checkout-Sitzung

In der Kaufansicht erstellen wir eine Stripe-Checkout-Sitzung, wenn das Kaufformular gültig ist:

# 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})

Lassen Sie uns das aufschlüsseln:

  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")

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 %}

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
]

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)

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
]

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"

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

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],
    )

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)

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:

  • Tests schreiben – einige Beispiele finden Sie im GitHub-Repository.
  • Wenn Sie mehr Produkte verkaufen möchten, erstellen Sie ein Datenbankmodell dafür und verbinden Sie die LineOrder über einen ForeignKey.
  • Konfigurieren Sie die E-Mail-Einstellungen gemäß der E-Mail-Dokumentation von Django. Sie können auch Bibliotheken wie django-post-office verwenden, um Ihre E-Mail-Vorlagen und Warteschlangen zu verwalten.
  • Sobald Sie Ihre Website bereitgestellt haben, erstellen Sie einen tatsächlichen Webhook (keinen lokalen Listener).
  • Sehen Sie sich die Stripe-Dokumente an, um Alternativen zum von uns beschriebenen Checkout-Prozess zu finden, einschließlich eines eingebetteten Checkout-Formulars.

Zusammenfassung

In dieser zweiteiligen Serie haben wir erfolgreich eine Ein-Produkt-E-Commerce-Site mit Django, htmx und Stripe erstellt. Dieser Leitfaden hat Sie durch die Einrichtung Ihres Django-Projekts, die Integration von Htmx für nahtlose Benutzerinteraktionen und die Integration sicherer Zahlungen mit Stripe geführt.

Wir haben außerdem erläutert, wie Sie die Bestellabwicklung abwickeln, einschließlich der Speicherung von Bestellinformationen in Ihrer Datenbank, der Benachrichtigung der Mitarbeiter über neue Einkäufe und dem Versenden von Bestätigungs-E-Mails an Ihre Kunden. Mit diesen Grundlagen können Sie Ihre E-Commerce-Website weiter an Ihre spezifischen Bedürfnisse anpassen und erweitern.

Viel Spaß beim Codieren!

P.S. Wenn Sie Python-Beiträge lesen möchten, sobald sie veröffentlicht werden, abonnieren Sie unseren Python Wizardry-Newsletter und verpassen Sie keinen einzigen Beitrag!

Das obige ist der detaillierte Inhalt vonIntegration von Stripe in einen Ein-Produkt-Django-Python-Shop. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:PSD-StapeleditorNächster Artikel:PSD-Stapeleditor