”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 将 Stripe 集成到单一产品 Django Python 商店中

将 Stripe 集成到单一产品 Django Python 商店中

发布于2024-11-05
浏览:115

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:

  1. 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).
  2. 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.
  3. 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:

  1. Create a Stripe account.
  2. Create a product (with a payment id).
  3. 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:

Integrating Stripe Into A One-Product Django Python Shop

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:

  1. Log into Stripe
stripe login
  1. 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:

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

Thank you for your purchase

Your purchase was successful. You will receive an email with the details of your purchase soon.

{% 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
        """,
        "[email protected]",
        [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
            """,
        "[email protected]",
        [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!

版本声明 本文转载于:https://dev.to/appsignal/integrating-stripe-into-a-one-product-django-python-shop-41ep?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何在gRPC中实现服务器到客户端的广播?
    如何在gRPC中实现服务器到客户端的广播?
    gRPC 中的广播:服务器到客户端通信建立 gRPC 连接时,通常需要将事件或更新从服务器广播到客户端连接的客户端。为了实现这一点,可以采用各种方法。Stream Observables一种常见的方法是利用服务器端流。每个连接的客户端都与服务器建立自己的流。然而,直接订阅其他服务器客户端流是不可行的...
    编程 发布于2024-11-05
  • 为什么填充在 Safari 和 IE 选择列表中不起作用?
    为什么填充在 Safari 和 IE 选择列表中不起作用?
    在 Safari 和 IE 的选择列表中不显示填充尽管 W3 规范中没有限制,但 WebKit 浏览器不支持选择框中的填充,包括Safari 和 Chrome。因此,这些浏览器中不应用填充。要解决此问题,请考虑使用 text-indent 而不是 padding-left。通过相应增加选择框的宽度来...
    编程 发布于2024-11-05
  • 在 Spring Boot 中创建自定义注释的终极指南
    在 Spring Boot 中创建自定义注释的终极指南
    Such annotations fill the entire project in Spring Boot. But do you know what problems these annotations solve? Why were custom annotations introduce...
    编程 发布于2024-11-05
  • 为什么 Elixir 在异步处理方面比 Node.js 更好?
    为什么 Elixir 在异步处理方面比 Node.js 更好?
    简单回答:Node.js 是单线程的,并拆分该单线程来模拟并发,而 Elixir 利用了 Erlang 虚拟机 BEAM 原生的并发和并行性,同时执行进程。 下面,我们将更深入地了解这种差异,探索两个关键概念:Node.js 事件循环和 Elixir 的 BEAM VM 和 OTP。这些元素对于理解...
    编程 发布于2024-11-05
  • AngularJS $watch 如何替代动态导航高度调整中的计时器?
    AngularJS $watch 如何替代动态导航高度调整中的计时器?
    避免 AngularJS 的高度监视计时器当导航高度是动态时,AngularJS 程序员经常面临响应式导航的挑战。这就导致需要调整内容的 margin-top 值以响应导航高度的变化。以前,使用计时器来检测导航高度的变化,但这种方法有缺点:使用计时器和调整内容的 margin-top 出现延迟。幸运...
    编程 发布于2024-11-05
  • 从零到 Web 开发人员:掌握 PHP 基础知识
    从零到 Web 开发人员:掌握 PHP 基础知识
    掌握PHP基础知识至关重要:安装PHP创建PHP文件运行代码理解变量和数据类型使用表达式和运算符创建实际项目以提高技能PHP开发入门:掌握PHP基础PHP是一种用途广泛、功能强大的脚本语言,用于创建动态且交互式Web应用程序。对于初学者来说,掌握PHP的基本知识至关重要。一、安装PHP在本地开发机器...
    编程 发布于2024-11-05
  • 缓冲区:Node.js
    缓冲区:Node.js
    Node.js 中缓冲区的简单指南 Node.js 中的 Buffer 用于处理原始二进制数据,这在处理流、文件或网络数据时非常有用。 如何创建缓冲区 来自字符串: const buf = Buffer.from('Hello'); 分配特定大小的Buffer...
    编程 发布于2024-11-05
  • 掌握 Node.js 中的版本管理
    掌握 Node.js 中的版本管理
    作为开发者,我们经常遇到需要不同 Node.js 版本的项目。对于可能不经常参与 Node.js 项目的新手和经验丰富的开发人员来说,这种情况都是一个陷阱:确保每个项目使用正确的 Node.js 版本。 在安装依赖项并运行项目之前,验证您的 Node.js 版本是否匹配或至少兼容项目的要求至关重要。...
    编程 发布于2024-11-05
  • 如何在 Go 二进制文件中嵌入 Git 修订信息以进行故障排除?
    如何在 Go 二进制文件中嵌入 Git 修订信息以进行故障排除?
    确定 Go 二进制文件中的 Git 修订版部署代码时,将二进制文件与构建它们的 git 修订版关联起来会很有帮助排除故障的目的。然而,直接使用修订号更新源代码是不可行的,因为它会改变源代码。解决方案:利用构建标志解决此挑战的方法包括利用构建标志。通过使用构建标志在主包中设置当前 git 修订版的版本...
    编程 发布于2024-11-05
  • 常见 HTML 标签:视角
    常见 HTML 标签:视角
    HTML(超文本标记语言)构成了 Web 开发的基础,是互联网上每个网页的结构。通过了解最常见的 HTML 标签及其高级用途,到 2024 年,开发人员可以创建更高效​​、更易于访问且更具视觉吸引力的网页。在这篇文章中,我们将探讨这些 HTML 标签及其最高级的用例,以帮助您提高 Web 开发技能。...
    编程 发布于2024-11-05
  • CSS 媒体查询
    CSS 媒体查询
    确保网站在各种设备上无缝运行比以往任何时候都更加重要。随着用户通过台式机、笔记本电脑、平板电脑和智能手机访问网站,响应式设计已成为必要。响应式设计的核心在于媒体查询,这是一项强大的 CSS 功能,允许开发人员根据用户设备的特征应用不同的样式。在本文中,我们将探讨什么是媒体查询、它们如何工作以及实现它...
    编程 发布于2024-11-05
  • 了解 JavaScript 中的提升:综合指南
    了解 JavaScript 中的提升:综合指南
    JavaScript 中的提升 提升是一种行为,其中变量和函数声明在之前被移动(或“提升”)到其包含范围(全局范围或函数范围)的顶部代码被执行。这意味着您可以在代码中实际声明变量和函数之前使用它们。 变量提升 变量 用 var 声明的变量被提升到其作...
    编程 发布于2024-11-05
  • 将 Stripe 集成到单一产品 Django Python 商店中
    将 Stripe 集成到单一产品 Django Python 商店中
    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...
    编程 发布于2024-11-05
  • 在 Laravel 中测试排队作业的技巧
    在 Laravel 中测试排队作业的技巧
    使用 Laravel 应用程序时,经常会遇到命令需要执行昂贵任务的情况。为了避免阻塞主进程,您可能决定将任务卸载到可以由队列处理的作业。 让我们看一个例子。想象一下命令 app:import-users 需要读取一个大的 CSV 文件并为每个条目创建一个用户。该命令可能如下所示: /* Import...
    编程 发布于2024-11-05
  • 如何创建人类水平的自然语言理解 (NLU) 系统
    如何创建人类水平的自然语言理解 (NLU) 系统
    Scope: Creating an NLU system that fully understands and processes human languages in a wide range of contexts, from conversations to literature. ...
    编程 发布于2024-11-05

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3