Django Stripe Tutorial

wsvincent

Will Vincent

Posted on January 6, 2024

Django Stripe Tutorial

In this tutorial, we will create a Stripe-powered Django website to handle purchases. The final site will have a homepage for orders, a success page when an order goes through, and a cancel page if the order fails.

Initial Setup

To start, open up your terminal and create a new Python virtual environment.

# Windows
$ python -m venv .venv
$ .venv\Scripts\Activate.ps1
(.venv) $

# macOS
$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $
Enter fullscreen mode Exit fullscreen mode

Then install Django, create a new project called django_project, and run migrate to initialize the new SQLite local database. Execute the runserver command to start the local web server and confirm everything works properly.

(.venv) $ python -m pip install django~=5.0.0
(.venv) $ django-admin startproject django_project .
(.venv) $ python manage.py migrate
(.venv) $ python manage.py runserver 
Enter fullscreen mode Exit fullscreen mode

In your web browser, navigate to 127.0.0.1:8000 to see the familiar Django welcome page.

Django welcome page

Django Configuration

We will create a dedicated products app for our logic now using the startapp command.

(.venv) $ python manage.py startapp products
Enter fullscreen mode Exit fullscreen mode

Don't forget to register the app immediately in our settings.py file so that Django knows to look for it.

# django_project/settings.py
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "products",  # new
]
Enter fullscreen mode Exit fullscreen mode

Let's start by creating the URL paths for the three pages: home, success, and cancel. We could do this in an app-level file like products/urls.py, but for simplicity, we'll place them in the project-level django_project/urls.py file.

# django_project/urls.py
from django.contrib import admin
from django.urls import path

from products import views


urlpatterns = [
    path("admin/", admin.site.urls),
    path("", views.home, name="home"),
    path("success/", views.success, name="success"),
    path("cancel/", views.cancel, name="cancel"),
]
Enter fullscreen mode Exit fullscreen mode

Next up are our three views that display a template file.

# products/views.py
from django.shortcuts import render


def home(request):
    return render(request, "home.html")

def success(request):
    return render(request, "success.html")

def cancel(request):
    return render(request, "cancel.html")
Enter fullscreen mode Exit fullscreen mode

And lastly, the template files. Create a project-level directory called templates.

(.venv) $ mkdir templates
Enter fullscreen mode Exit fullscreen mode

Then, update django_project/settings.py so that Django's template loader knows to look for it.

# django_project/settings.py
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],  # new
Enter fullscreen mode Exit fullscreen mode

Create three new template files--home.html, success.html, and cancel.html--within the templates folder.

<!-- templates/home.html -->
<h1>Homepage</h1>

<!-- templates/success.html -->
<h1>Success page</h1>

<!-- templates/cancel.html -->
<h1>Cancel page</h1>
Enter fullscreen mode Exit fullscreen mode

That's it! Make sure runserver is running, and then confirm that all three webpages exist.

Home page

Success page

Cancel page

Configure Stripe

Log in to your existing Stripe account or register for a free one. If you only want to use test mode, where we can mock transactions, there is no need to complete the application with bank details. Once registered, head over to the dashboard.

Stripe Dashboard Page

Click on the "Developers" nav link in the upper right-hand corner.

Stripe Developers page

Make sure to activate "Test mode" and then click on the nav link for API keys.

Stripe API Keys page

Stripe provides a "Publishable key" and a "Secret key" that work together to process payment transactions. The publishable (or public) key is used to initiate a transaction and is visible to the client; the secret key is used on our server and should be kept confidential.

We will hardcode both for this tutorial into the bottom of our django_project/settings.py file. The live API keys should be stored in environment variables in production for proper security.

# django_project/settings.py
STRIPE_PUBLIC_KEY = "<your_test_public_api_key>"
STRIPE_SECRET_KEY = "<your_test_secret_api_key>"
Enter fullscreen mode Exit fullscreen mode

Now we need a product to sell. On the lefthand sidebar, click the link for "More +" and then "Product catalog," which takes us to the Products page.

Stripe Products Navbar Link

Click the "+ Add product" button in the upper right-hand corner to create a product to sell.

Stripe Products Page

The required fields are name, one-off or recurring, and the amount. We are processing a one-off payment here. Click the "Add Product" button.

Stripe Add a Product Page

We are redirected to the main "Product catalog" page, where our new product is now visible.

Stripe Product Catalog Page

Click on the new product to open up its page. Under the Pricing section, note the "API ID" which we will use shortly.

Stripe Product Page

Now that we have a product to sell, we will focus on the Django app.

Stripe Hosted Page

If you look at the Stripe docs, the Python example provided is for a Flask Stripe-hosted page. However, we can adapt this for our Django app. At a high level, we need to add an HTML form to the home.html template and then update our views.py file so that when a POST request is sent to Stripe, our server-side Stripe SDK and Stripe Private key are used to generate a new Checkout session. The user is redirected to this unique session and then, if payment is successful, redirected to our success.html page. Otherwise, they will be sent to the cancel.html page.

Let's start by installing the Python library for Stripe, which is available on Github.

(.venv) $ python -m pip install stripe==7.10.0
Enter fullscreen mode Exit fullscreen mode

In our home.html file, add a basic form using the POST method and Django's CSRF protection.

<!-- templates/home.html -->
<h1>Homepage</h1>
<form method="post">
  {% csrf_token %}
  <button type="submit">Purchase!</button>
</form>
Enter fullscreen mode Exit fullscreen mode

Most of the magic happens in our views.py file. The updated code is below, and we'll work through the changes line-by-line. But before getting into specifics, it's essential to understand that at a high level, we are loading in the Stripe Private Key from our settings.py file and using it to create a new Checkout session. The user will be redirected to a custom URL for the Stripe-hosted checkout page and then, after submitting payment, either sent to a success or cancel page.

# products/views.py
from django.shortcuts import render, redirect  # new
from django.conf import settings  # new
from django.urls import reverse  # new

import stripe  # new


def home(request):  # new
    stripe.api_key = settings.STRIPE_SECRET_KEY
    if request.method == "POST":
        checkout_session = stripe.checkout.Session.create(
            line_items=[
                {
                    "price": "<price_API_ID_HERE>",  # enter yours here!!!
                    "quantity": 1,
                }
            ],
            mode="payment",
            success_url=request.build_absolute_uri(reverse("success")),
            cancel_url=request.build_absolute_uri(reverse("cancel")),
        )
        return redirect(checkout_session.url, code=303)
    return render(request, "home.html")


def success(request):
    return render(request, "success.html")


def cancel(request):
    return render(request, "cancel.html")
Enter fullscreen mode Exit fullscreen mode

At the top, we've imported redirect, settings, reverse, and stripe. The first step in our home function-based view is to set the Stripe api_key to the Stripe Private Key for our account; it is stored in our settings.py file for security and not visible to the user client-side. Next, if a POST request is made a new checkout_session is created and requires, at a minimum, the following attributes: line_items, mode, success_url, and cancel_url. Other attributes are available but are not necessary at this stage.

We define a product to sell with the line_items attribute, using the exact Price ID for our product set on the Stripe website. Later on we might want to have the option of storing multiple IDs in our database to load in, but hardcoding it is fine for example purposes. We also specify the quantity of 1.

Next, we set a mode to "payment" for one-time purchases, but we could also do "subscription" for subscriptions or "setup" to collect customer payment details to reuse later.

After the user places an order, they are sent to either a success_url or a cancel_url. Django's reverse() function is used alongside the URL name of each. We also take advantage of Django's [build_absolute_uri] method to send an absolute URL to Stripe.

The final bit is redirecting to Checkout. After our POST request is sent to Stripe, authenticated, and a new Checkout session is created, Stripe's API will send us a unique checkout_session.url that the user is redirected to.

The full Stripe Checkout flow can take a while to sink in, but that's all the code we need!

Ensure the server is running, and go to the homepage at 127.0.0.1:8000 and click on the "Purchase" button.

Purchase Button Page

You'll be redirected to a unique Stripe Checkout session for the product.

Stripe Checkout Session Page

Fill in the form. For the credit card information, use the number 4242 4242 4242 4242, any date in the future for expiration, and any three digits for the CVC. Then click the "Pay" button at the bottom.

Stripe Checkout Session Page Filled Out

After successful payment, you will be redirected to the success page at http://127.0.0.1:8000/success/. You can view the confirmation of the payment on your Stripe dashboard. The Payments section, available from the left sidebar, provides further payment details.

Stripe Payments Page

If you want to customize the Checkout page, you can pass in additional attributes to the Checkout session.

Next Steps

For more free tutorials and premium courses on Django, check out LearnDjango.com.

💖 💪 🙅 🚩
wsvincent
Will Vincent

Posted on January 6, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

What was your win this week?
weeklyretro What was your win this week?

November 29, 2024

Where GitOps Meets ClickOps
devops Where GitOps Meets ClickOps

November 29, 2024

How to Use KitOps with MLflow
beginners How to Use KitOps with MLflow

November 29, 2024

Modern C++ for LeetCode 🧑‍💻🚀
leetcode Modern C++ for LeetCode 🧑‍💻🚀

November 29, 2024