Django Stripe Tutorial
Will Vincent
Posted on January 6, 2024
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) $
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
In your web browser, navigate to 127.0.0.1:8000
to see the familiar 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
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
]
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"),
]
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")
And lastly, the template files. Create a project-level directory called templates
.
(.venv) $ mkdir templates
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
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>
That's it! Make sure runserver
is running, and then confirm that all three webpages exist.
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.
Click on the "Developers" nav link in the upper right-hand corner.
Make sure to activate "Test mode" and then click on the nav link for API keys.
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>"
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.
Click the "+ Add product" button in the upper right-hand corner to create a product to sell.
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.
We are redirected to the main "Product catalog" page, where our new product is now visible.
Click on the new product to open up its page. Under the Pricing section, note the "API ID" which we will use shortly.
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
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>
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")
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.
You'll be redirected to a unique Stripe Checkout session for the product.
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.
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.
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.
Posted on January 6, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.