Integrating Stripe Into A One-Product Django Python Shop
In the first part of this series, we created a Django online shop with htmx.
In this second part, we'll handle orders using Stripe.
What We'll Do
We'll integrate Stripe to handle payments securely. This is what we want to achieve:
- In the purchase view, we start by creating a Stripe checkout session and redirect the customer to the corresponding URL. This is where we tell Stripe about the product we are selling, its quantity, and where the customer should be redirected to after a successful purchase (the success_url).
- The customer fills in their payment details on the Stripe checkout page and completes the payment. Stripe then makes a POST request to a webhook endpoint on our website, where we listen to events and process them accordingly. If the payment is successful, we save the order in our database and notify the customer (and our staff users) about the purchase.
- Finally, if the webhook returns a response with a 200 OK HTTP status code, Stripe redirects to the success_url created in the first step.
Setting Up Stripe for Our Django Python Store
We first need to jump over to Stripe and do the following:
- Create a Stripe account.
- Create a product (with a payment id).
- Create a webhook.
1: Create a Stripe Account
Start by creating a Stripe account. For now, you don’t really need to activate your account. You can just work in test mode, which will prevent you from making real payments while testing. Go to the API keys page and retrieve the publishable and secret keys. Save them in your project environment variables (STRIPE_PUBLISHABLE_KEY and STRIPE_SECRET_KEY). We will use these keys to authenticate your Stripe requests.
2: Create Your Product
Create a new product on the products page. Fill out the details and set the payment type to one-off. Your product should look something like this:
Once you press Add product, you should be able to see your product on the product list. If you click on it and scroll down to the Pricing section, you can find the API ID for the price item you created — it should be something like price_3ODP5…. Save it in an environment variable (STRIPE_PRICE_ID): you will need this when creating the Stripe checkout session.
3: Create the Webhook
We need to create a webhook endpoint for Stripe to call when a payment completes. In the webhooks page, choose to test in the local environment. This will allow you to forward the request to a local URL, like http://127.0.0.1:8000. Start by downloading the Stripe CLI. Then, you can:
- Log into Stripe
stripe login
- Forward events to the webhook endpoint that you will create:
stripe listen --forward-to http://127.0.0.1:8000/webhook > Ready! Your webhook signing secret is whsec_06531a7ba22363ac038f284ac547906b89e5c939f8d55dfd03a3619f9adc590a (^C to quit)
This ensures that once a purchase is made, Stripe forwards the webhook calls to your local endpoint. The command will log a webhook signing secret, which you should also save as a project environment variable (STRIPE_WEBHOOK_SECRET). This will prove useful for verifying that a request does indeed come from Stripe and that you are handling the right webhook.
By the end of this section, you should have four Stripe environment variables. You can now load them in 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")
Note: We are using python-dotenv to load the environment variables.
Extend the Views
We now need to extend the views to integrate Stripe by creating a checkout session, a successful purchase view, and a webhook view.
1: Create a Stripe Checkout Session
In the purchase view, we'll create a Stripe checkout session if the purchase form is valid:
# 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})
Let’s break this down:
- We first set the Stripe API key.
- 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.
- 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.
- We create a Stripe checkout session with our price ID (the product identifier) and the quantity the customer wants to purchase.
- 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:
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:
- Write tests - you can see some examples in the GitHub repository.
- If you have more products to sell, create a database model for them, and connect the LineOrder through a ForeignKey.
- Configure email settings according to Django's email documentation. You can also use libraries such as django-post-office to manage your email templates and queues.
- Once you deploy your website, create an actual webhook (not a local listener).
- Take a look at the Stripe docs for alternatives to the checkout process we've outlined, including an embedded checkout form.
Wrapping Up
In this two-part series, we successfully built a one-product e-commerce site using Django, htmx, and Stripe. This guide has walked you through setting up your Django project, integrating htmx for seamless user interactions, and incorporating secure payments with Stripe.
We also covered how to handle order processing, including saving order information to your database, notifying staff users of new purchases, and sending confirmation emails to your customers. With these foundations, you can further customize and expand your e-commerce site to suit your specific needs.
Happy coding!
P.S. If you'd like to read Python posts as soon as they get off the press, subscribe to our Python Wizardry newsletter and never miss a single post!
以上是Integrating Stripe Into A One-Product Django Python Shop的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Python更易学且易用,C 则更强大但复杂。1.Python语法简洁,适合初学者,动态类型和自动内存管理使其易用,但可能导致运行时错误。2.C 提供低级控制和高级特性,适合高性能应用,但学习门槛高,需手动管理内存和类型安全。

要在有限的时间内最大化学习Python的效率,可以使用Python的datetime、time和schedule模块。1.datetime模块用于记录和规划学习时间。2.time模块帮助设置学习和休息时间。3.schedule模块自动化安排每周学习任务。

Python在开发效率上优于C ,但C 在执行性能上更高。1.Python的简洁语法和丰富库提高开发效率。2.C 的编译型特性和硬件控制提升执行性能。选择时需根据项目需求权衡开发速度与执行效率。

每天学习Python两个小时是否足够?这取决于你的目标和学习方法。1)制定清晰的学习计划,2)选择合适的学习资源和方法,3)动手实践和复习巩固,可以在这段时间内逐步掌握Python的基本知识和高级功能。

Python和C 各有优势,选择应基于项目需求。1)Python适合快速开发和数据处理,因其简洁语法和动态类型。2)C 适用于高性能和系统编程,因其静态类型和手动内存管理。

pythonlistsarepartofthestAndArdLibrary,herilearRaysarenot.listsarebuilt-In,多功能,和Rused ForStoringCollections,而EasaraySaraySaraySaraysaraySaraySaraysaraySaraysarrayModuleandleandleandlesscommonlyusedDduetolimitedFunctionalityFunctionalityFunctionality。

Python在自动化、脚本编写和任务管理中表现出色。1)自动化:通过标准库如os、shutil实现文件备份。2)脚本编写:使用psutil库监控系统资源。3)任务管理:利用schedule库调度任务。Python的易用性和丰富库支持使其在这些领域中成为首选工具。

Python在Web开发中的关键应用包括使用Django和Flask框架、API开发、数据分析与可视化、机器学习与AI、以及性能优化。1.Django和Flask框架:Django适合快速开发复杂应用,Flask适用于小型或高度自定义项目。2.API开发:使用Flask或DjangoRESTFramework构建RESTfulAPI。3.数据分析与可视化:利用Python处理数据并通过Web界面展示。4.机器学习与AI:Python用于构建智能Web应用。5.性能优化:通过异步编程、缓存和代码优
