Django Stripe Payment Integration: Simplify Online Transactions

django stripe integration

Introduction

Django is a well known, high-level Python Framework, which is designed to facilitate the rapid creation of web applications with clean and precise design. Thanks to its well-integrated components, developers are able to generate rich and efficient web applications that are easy to grow and tend.

Stripe is a popular comprehensive platform which provides all kinds of online payment processing services for any company to accept payments. This system has a set of APIs which allow web developers to perform the integration of payment operations into their pieces of software without a hitch.

Today, in this tutorial, we are going to see how we can integrate Stripe payment to our Django web apps. So, fasten your belt (I mean your keyboard 😀) and let’s proceed.

Benefits of Integrating Stripe in Django Applications

Embedding Stripe’s payment processor within your Django application has a few key advantages:

  • Seamless Payment Processing: With the help of Stripe’s advanced APIs, you are able to offer the customers the least irritating way of purchasing goods almost as effortlessly as it is for a person making purchases with the telephone.
  • Comprehensive Security: Stripe can be rated high in the provision of credit card processing services considering the importance in safe practices such as PCI compliance, tokenization, safe information storage among others.
  • Support for Multiple Payment Methods: Users can pay through credit cards, debit cards, Apple pay or Google pay and even other types of payments for which Stripe has facilities making it easier.
  • Recurring Billing and Subscriptions: When a subscription billing feature is needed within your application, it also provides the necessary processes and systems for maintaining such billing systems.
  • Detailed Analytics and Reporting: Using the Stripe as the majority of users of the transact processes facilitates the clear and thorough reporting of the undertaken business transactions.
  • Easy Integration and Customization: The process of integrating the stripe payment’s processor within the application’s code is easy with its documentation.
  • Test Mode for Development: Stripe offers a test mode that allows you to simulate transactions without processing real payments. This feature enables you to thoroughly test your payment integration before going live.

By leveraging Django and Stripe together, you can create a powerful, secure, and user-friendly payment processing system that enhances your application’s functionality and user experience.

Prerequisites

Before you set out on the journey of adding Stripe to your Django application, it is necessary that you get prepared with the right tools and libraries. In this case, you need Python installed on your system first.

creating a Virtual Environment is essential for isolating your project’s dependencies. To create one, navigate to your project directory and run.

Bash
$ python -m venv env

Activate the virtual environment:

  • On Windows:
Bash
$ myenv\Scripts\activate
  • On macOS/Linux:
Bash
$ source myenv/bin/activate

Once you have activated the virtual environment, you will be requiring Django. The following command can be used to install Django.

Bash
$ pip install django

Additionally, to interact with Stripe’s API, you need the official Stripe Python Library, which can be installed via.

Bash
$ pip install stripe

Now that we have both Django and the Stripe package installed, it is time to create a new Django project. to create a django project use the following command.

Bash
$ django-admin startproject django_stripe

Within your project, create a Django app for managing Stripe integration by running.

Bash
$ python manage.py startapp payment

after creating the Django app. Don’t forget to add it to the settings.py installed apps.

# settings.py
INSTALLED_APPS = [
    # Other installed apps
    'payments',  # Your payments app
]

After creating our Django project and Django app, our project structure will look like below.

Configuring Settings

After setting up your Django environment and installing the necessary libraries, it’s time to configure your settings to integrate Stripe effectively into your Django application. Start by updating your settings.py file to include Stripe’s configuration.

# settings.py
# Set the Stripe API keys
STRIPE_PUBLISHABLE_KEY = "YOUR_STRIPE_PUBLISHABLE_KEY"
STRIPE_SECRET_KEY = "YOUR_STRIPE_SECRET_KEY"
STRIPE_WEBHOOK_SECRET = "YOUR_STRIPE_WEBHOOK_SECRET"

To get the Stripe API secret key and publishable key, navigate to the Stripe developer dashboard and copy the keys easily.

django stripe integration

Building Essential Models

In this section, we will create two models. One is called ‘tutorials‘ which is to save tutorials with their basic information like title, description, image, and price.

The other model we are going to create is “Purchaser,” which is responsible for storing tutorials purchased.

# views.py

from django.db import models
from django.contrib.auth.models import User

# Create your models here.
class tutorials(models.Model):
    title = models.CharField(max_length=255)
    description = models.CharField(max_length=255)
    image = models.URLField()
    price = models.DecimalField(max_digits=10, decimal_places=2)

    def __str__(self):
        return self.title
    
class Purchaser(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='purchasers')
    purchased_courses = models.ManyToManyField(tutorials, related_name='purchasers', blank=True)
    date_created = models.DateTimeField(auto_now_add=True)  # Automatically set to now when the object is created

    def __str__(self):
        return self.user.username

Creating Views for Payment Processing

In this scope of the article, we shall focus on how to embed Stripe in a Django application, which will help in purchasing tutorials. The initial procedure includes importing necessary libraries, logging, stripe, and the required modules for the Django application. 

By setting up the logging system, different errors and significant events can be monitored.

import logging
import stripe
from django.shortcuts import get_object_or_404
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect, reverse
from django.conf import settings
from django.contrib.auth.models import User
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_exempt
from .models import tutorials, Purchaser

logger = logging.getLogger(__name__)
stripe.api_key = settings.STRIPE_SECRET_KEY


@login_required
def tutorial_list(request):
    tutorials_list = tutorials.objects.all()
    context = {"tutorials": tutorials_list}
    return render(request, "tutorial_detail.html", context)


@login_required
def stripe_checkout(request, tutorial_id):
    try:
        get_tutorial = get_object_or_404(tutorials, pk=tutorial_id)

        if get_tutorial.price <= 0:
            logger.error(f"Invalid price for tutorial {tutorial_id}.")
            return redirect('tutorial_list')  # Redirect or handle the error accordingly

        tutorial_price_in_cent = int(get_tutorial.price * 100)

        stripe_session = stripe.checkout.Session.create(
            payment_method_types=["card"],
            line_items=[{
                "price_data": {
                    "currency": "usd",
                    "unit_amount": tutorial_price_in_cent,
                    "product_data": {"name": get_tutorial.title},
                },
                "quantity": 1,
            }],
            mode="payment",
            success_url=request.build_absolute_uri(reverse("payment-success")),
            cancel_url=request.build_absolute_uri(reverse("payment-cancel")),
            metadata={"tutorial_id": get_tutorial.id, "buyer_id": request.user.id},
        )

        return redirect(stripe_session.url)

    except stripe.error.StripeError as e:
        logger.error(f"Stripe error: {str(e)}")
        return redirect('tutorial_list')  # Redirect or handle the error appropriately


@csrf_exempt
@require_POST
def webhook_manager(request):
    stripe_payload = request.body.decode('utf-8')
    signature_header = request.META.get("HTTP_STRIPE_SIGNATURE", None)
    if not signature_header:
        return JsonResponse({"error": "Missing signature"}, status=400)

    try:
        stripe_event = stripe.Webhook.construct_event(
            stripe_payload, signature_header, settings.STRIPE_WEBHOOK_SECRET
        )
    except ValueError:
        return JsonResponse({"error": "Invalid payload"}, status=400)
    except stripe.error.SignatureVerificationError:
        return JsonResponse({"error": "Invalid signatures"}, status=400)

    if stripe_event["type"] == "checkout.session.completed":
        stripe_session = stripe_event["data"]["object"]
        logger.info("Checkout Session Completed!")
        manage_checkout_session(stripe_session)

    return JsonResponse({"status": "success"})


def manage_checkout_session(stripe_session):
    tutorial_id = stripe_session["metadata"]["tutorial_id"]
    user_id = stripe_session["metadata"]["buyer_id"]

    try:
        user = User.objects.get(id=user_id)
    except User.DoesNotExist:
        logger.error(f"User with ID {user_id} not found.")
        return

    tutorial = get_object_or_404(tutorials, pk=tutorial_id)
    purchaser = Purchaser.objects.create(user=user)
    purchaser.purchased_courses.set([tutorial])


@login_required
def payment_success(request):

    return render(request, "payment_success.html")


@login_required
def payment_cancel(request):
    
    return render(request, "payment_cancel.html")

The tutorial_list() view returns a list of all tutorials available in the system and sends to a user who has logged in.

The stripe_checkout() view creates a stripe checkout session when a user clicks on the button to buy a tutorial.

django stripe integration

The webhook_manager() handles the actual events and is also the function that will handle all the incoming events from the stripe for webhooks. It receives a stripe signature and extends that to an event object. It refers to complete payment with regard to the bin and if so to call the manage_checkout_session. This function updates the Purchaser model of the given user and tutorial objects with the information of purchase.

Finally, the payment_success() and payment_cancel() views display confirmation or cancellation messages after the transaction process. This integration not only streamlines tutorial purchases but also enhances user experience through secure payment processing.

Creating HTML Templates

Good, until now we have done a lot of great tasks. Now it’s time to create HTML template files. The first one we are going to create is “tutorial_list.html” which is responsible for displaying a list of tutorials recovered from the database.

<!-- 1 -->

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{{ course.title }}</title>
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" />
</head>

<body>

    <div class="container mt-5">
        <div class="row justify-content-center">

            {% for tutorial in tutorials %}
            <div class="col-md-3">
                <div class="card custom-card">
                    <div class="card-body text-center">
                        <img src="{{ tutorial.image }}" class="img-fluid mb-3" alt="{{ course.title }}"
                            style="max-width: 200px; height: 200px;">
                        <p class="card-text">{{ tutorial.title }}</p>
                        <h5 class="text-dark">Price: ${{ tutorial.price }}</h5>
                        <a href="{% url 'tutorial-checkout' tutorial.id %}" class="btn btn-success btn-sm">Buy
                            Tutorial</a>
                    </div>
                </div>
            </div>
            {% endfor %}

        </div>
    </div>

</body>

</html>

This template will display a list of tutorials recovered from the database as shown below.

Next, when ever a successful transaction is made with Stripe, the user must be redirected to a success page, and let’s now create an HTML template named “payment_success.html” for that.

<!-- payment_success.html -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tutorial Purchase Successful</title>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-5">
        <div class="text-center">
            <h1 class="display-4 text-success">Payment Successful!</h1>
            <p class="lead">Thank you for purchasing the tutorial!</p>
            <hr class="my-4">
         </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.0.7/dist/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

This template will be displayed whenever a successful Stripe transaction is made.

And in the final round of creating our project template, we will create an HTML template for cancelled transactions named ‘payment_cancel.html‘.

<!-- payment_cancel.html -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Payment Canceled</title>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-5">
        <div class="text-center">
            <h1 class="display-4 text-danger">Payment Canceled</h1>
            <p class="lead">We're sorry to inform you that your payment was not completed.</p>
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.0.7/dist/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

URL Mapping

Since we have created our django view function previously, let’s map these views to their corresponding urls. to do this open urls.py inside our main project directory and modify with this code snippet.

# django_stripe/urls.py

from django.contrib import admin
from django.urls import path
from payment import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path("tutorial-list", views.tutorial_list, name="tutorial-list"),
    path("checkout/<int:tutorial_id>", views.stripe_checkout, name="tutorial-checkout"),
    path("stripe-webhook", views.webhook_manager, name = "webhook-manager"),
    path("payment-success/", views.payment_success, name = "payment-success"),
    path("payment-cancel/", views.payment_cancel, name = "payment-cancel")
]

Stripe Webhooking with CLI

Webhooks are important features that allow Stripe to push data to your application when certain events occur, such as a payment being completed, a refund being made, or a subscription update. Testing Stripe webhooks with Stripe CLI is a great option to imagine and implement these webhooks functionality comprehensively without the fear of the production environment. Here’s how you do it effectively.

First things first, you have to install Stripe’s CLI in your computer. The CLI lets you listen for webhooks so that you can do testing without the complete server being deployed.

If you haven’t already done so, please go ahead and install it following the instructions provided in the Stripe documentation pertaining to your OS.

After that type stripe login to your terminal and press enter. This command will log you in to Stripe and.

After making the respective configuration of the CLI, it is time to move on to the next section where the webhook events have to be listened to locally. This is achieved through the command stripe listen --forward-to localhost:8000/stripe-webhook.

This command is will provide you with stripe webhook secret code and save the webhook secret in settings.py STRIPE_WEBHOOK_SECRET and will start listening for incoming events from stripe.

Conclusion

For those who want to build secure, effective and scalable e-commerce applications, integrating Stripe into a Django application is one skill that needs to be on the developers list.

In this article, we have covered the entire process of integration of stripe in a Django application in detail step by step, addressing the required configurations, models setup, and payment workflow incorporation. Following these directives, developers will be able to transact, accept payments from their clients or users, and better their interface with little resistance.

This integration not only optimizes payment processing but also adds room in terms of future growth and enhancement.

🧷Explore the complete source code on GitHub.

🧷Related Article: Django PayPal Integration Tutorial: Accept Payments Easily


Light
×