在本系列的第一部分中,我們使用 htmx 建立了一個 Django 線上商店。
在第二部分中,我們將使用 Stripe 處理訂單。
我們將整合 Stripe 以安全地處理付款。這就是我們想要實現的目標:
我們首先需要跳到 Stripe 並執行以下操作:
先建立一個 Stripe 帳戶。目前,您實際上不需要啟動您的帳戶。您可以只在測試模式下工作,這將阻止您在測試時進行真正的付款。前往 API 金鑰頁面並檢索可發布金鑰和秘密金鑰。將它們保存在專案環境變數中(STRIPE_PUBLISHABLE_KEY 和 STRIPE_SECRET_KEY)。我們將使用這些金鑰來驗證您的 Stripe 請求。
在產品頁面上建立新產品。填寫詳細資料並將付款類型設定為一次性。您的產品應如下所示:
按下新增產品後,您應該能夠在產品清單中看到您的產品。如果您點擊它並向下捲動到定價部分,您可以找到您建立的價格項目的API ID - 它應該類似於price_3ODP5…。將其保存在環境變數 (STRIPE_PRICE_ID) 中:建立 Stripe 結帳會話時將需要它。
我們需要建立一個 Webhook 端點,以便 Stripe 在付款完成時呼叫。在webhooks頁面,選擇在本地環境進行測試。這將允許您將請求轉發到本機 URL,例如 http://127.0.0.1:8000。首先下載 Stripe CLI。然後,您可以:
stripe login
stripe listen --forward-to http://127.0.0.1:8000/webhook > Ready! Your webhook signing secret is whsec_06531a7ba22363ac038f284ac547906b89e5c939f8d55dfd03a3619f9adc590a (^C to quit)
這可確保一旦進行購買,Stripe 會將 Webhook 呼叫轉發到您的本地端點。該命令將記錄一個 Webhook 簽章金鑰,您也應該將其儲存為專案環境變數 (STRIPE_WEBHOOK_SECRET)。這對於驗證請求確實來自 Stripe 並且您正在處理正確的 Webhook 非常有用。
在本節結束時,您應該有四個 Stripe 環境變數。現在您可以將它們載入到 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")
注意:我們使用 python-dotenv 來載入環境變數。
我們現在需要透過建立結帳會話、成功購買視圖和 Webhook 視圖來擴展視圖以整合 Stripe。
在購買檢視中,如果購買表單有效,我們將建立一個 Stripe 結帳會話:
# 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})
讓我們分解一下:
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 ]
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:
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:
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!
Once a purchase is completed, we need to do a few things in the webhook view:
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:
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!
Here are some tips to further improve on the store you've built:
在這個由兩部分組成的系列中,我們使用 Django、htmx 和 Stripe 成功建立了一個單一產品的電子商務網站。本指南引導您完成 Django 專案的設定、整合 htmx 以實現無縫用戶交互,以及將安全支付與 Stripe 結合。
我們也介紹如何處理訂單處理,包括將訂單資訊儲存到您的資料庫、通知員工使用者新購買的商品以及向您的客戶發送確認電子郵件。有了這些基礎,您可以進一步客製化和擴展您的電子商務網站以滿足您的特定需求。
編碼愉快!
P.S.如果您想在 Python 文章發布後立即閱讀,請訂閱我們的 Python Wizardry 時事通訊,不錯過任何一篇文章!
以上是將 Stripe 整合到單一產品 Django Python 商店中的詳細內容。更多資訊請關注PHP中文網其他相關文章!