How to Integrate Stripe Payment Gateway in Python Django ?

Published On: 13/07/2022 | Category: Django Python


Hi Dev,

This tutorial shows you how to integrate stripe payment gateway in python django. you can see stripe payment gateway integration with python django. I explained simply step by step stripe payment gateway integration with django app. you will learn stripe payment gateway integration with python django developer.

Here i explained simply step by step let's see bellow example I explained simply step by step stripe payment gateway integration with django app.

Step 1 : Create a Project

In this step, we’ll create a new django project using the django-admin. Head back to your command-line interface and run the following command:

django-admin startproject example
Step 2 : Create a App
python3 manage.py startapp payment
Step 3 : Update setting.py

In this step we require to do two things in our settings.py file, One is to change the path of template look up directory. Second one is to configure our media folder. Add the below lines to your settings.py file:

Next, you need to add it in the settings.py file as follows:

....
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'payment',
]
Step 4 : Database Setup

Next step, we will modify the settings.py file and update the database settings to configure the mydb database:

settings.py
DATABASES = {  
    'default': {  
        'ENGINE': 'django.db.backends.mysql',  
        'NAME': 'example',  
        'USER':'root',  
        'PASSWORD':'root',  
        'HOST':'localhost',  
        'PORT':'3306'  
    }  
}  
Step 5: Create Stripe Account

First you need to create account on stripe. then you can easily get account stripe publishable api key and a secret key.

Create Account from here: Stripe Dashboard.

Next you can get account key id and secret and add on settings.py file as like bellow:

setting.py
if DEBUG:
    STRIPE_PUBLISHABLE_KEY = 'test_publishable_key'
    STRIPE_SECRET_KEY = 'test_secret_key'
# Uncomment these lines if you have a live keys
# else:
#     STRIPE_PUBLISHABLE_KEY = 'production_publishable_key'
#     STRIPE_SECRET_KEY = 'production_secret_key'
Step 6: Install Stripe Library

In this step, we need to install stripe package to use stripe api. so let's run bellow command:

pip install stripe
Step 7: Create a Model

In this step we will require the database model for storing contacts.Open the payment/models.py file and add the following code:

payment/models.py
from django.db import models
from django.core import validators

# Create your models here.
class Product(models.Model):
    id = models.BigAutoField(
        primary_key=True
    )

    name = models.CharField(
        max_length=70,
        verbose_name='Product Name'
    )

    description = models.TextField(
        max_length=800,
        verbose_name='Description'
    )

    price = models.FloatField(
        verbose_name='Price',
        validators=[
            validators.MinValueValidator(50),
            validators.MaxValueValidator(100000)
        ]
    )

class OrderDetail(models.Model):

    id = models.BigAutoField(
        primary_key=True
    )

    # You can change as a Foreign Key to the user model
    customer_email = models.EmailField(
        verbose_name='Customer Email'
    )

    product = models.ForeignKey(
        to=Product,
        verbose_name='Product',
        on_delete=models.PROTECT
    )

    amount = models.IntegerField(
        verbose_name='Amount'
    )

    stripe_payment_intent = models.CharField(
        max_length=200
    )

    # This field can be changed as status
    has_paid = models.BooleanField(
        default=False,
        verbose_name='Payment Status'
    )

    created_on = models.DateTimeField(
        auto_now_add=True
    )

    updated_on = models.DateTimeField(
        auto_now_add=True
    )

After creating these model, you need to create migrations using the following command:

Step 8 : Create a Migrations
python manage.py makemigrations

After successfully run the above command go to the payment/migrations/0001_initial.py

payment/migrations/0001_initial.py
import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Product',
            fields=[
                ('id', models.BigAutoField(primary_key=True, serialize=False)),
                ('name', models.CharField(max_length=70, verbose_name='Product Name')),
                ('description', models.TextField(max_length=800, verbose_name='Description')),
                ('price', models.FloatField(validators=[django.core.validators.MinValueValidator(50), django.core.validators.MaxValueValidator(100000)], verbose_name='Price')),
            ],
        ),
    ]

payment/migrations/0002_orderdetail.py
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        ('payments', '0001_initial'),
    ]

    operations = [
        migrations.CreateModel(
            name='OrderDetail',
            fields=[
                ('id', models.BigAutoField(primary_key=True, serialize=False)),
                ('customer_email', models.EmailField(max_length=254, verbose_name='Customer Email')),
                ('amount', models.IntegerField(verbose_name='Amount')),
                ('stripe_payment_intent', models.CharField(max_length=200)),
                ('has_paid', models.BooleanField(default=False, verbose_name='Payment Status')),
                ('created_on', models.DateTimeField(auto_now_add=True)),
                ('updated_on', models.DateTimeField(auto_now_add=True)),
                ('product', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='payments.product', verbose_name='Product')),
            ],
        ),
    ]

Next, you need to migrate your database using the following command:

python manage.py migrate
Step 9 : Creating the Views

In this step, we need to create the views for performing fetch record to the database.Open the payment/views.py file and add:

payment/views.py
from django.http.response import HttpResponseNotFound, JsonResponse
from django.shortcuts import get_object_or_404, render
from django.urls import reverse, reverse_lazy
from .models import *
from django.views.generic import ListView, CreateView, DetailView, TemplateView
import stripe
from .forms import ProductForm
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
import json

# Create your views here.

class ProductListView(ListView):
    model = Product
    template_name = "payments/product_list.html"
    context_object_name = 'product_list'


class ProductCreateView(CreateView):
    model = Product
    form_class = ProductForm
    # fields = '__all__'
    template_name = "payments/product_create.html"
    success_url = reverse_lazy("home")


class ProductDetailView(DetailView):
    model = Product
    template_name = "payments/product_detail.html"
    pk_url_kwarg = 'id'

    def get_context_data(self, **kwargs):
        context = super(ProductDetailView, self).get_context_data(**kwargs)
        context['stripe_publishable_key'] = settings.STRIPE_PUBLISHABLE_KEY
        return context  

@csrf_exempt
def create_checkout_session(request, id):

    request_data = json.loads(request.body)
    product = get_object_or_404(Product, pk=id)

    stripe.api_key = settings.STRIPE_SECRET_KEY
    checkout_session = stripe.checkout.Session.create(
        # Customer Email is optional,
        # It is not safe to accept email directly from the client side
        customer_email = request_data['email'],
        payment_method_types=['card'],
        line_items=[
            {
                'price_data': {
                    'currency': 'inr',
                    'product_data': {
                    'name': product.name,
                    },
                    'unit_amount': int(product.price * 100),
                },
                'quantity': 1,
            }
        ],
        mode='payment',
        success_url=request.build_absolute_uri(
            reverse('success')
        ) + "?session_id={CHECKOUT_SESSION_ID}",
        cancel_url=request.build_absolute_uri(reverse('failed')),
    )

    order = OrderDetail()
    order.customer_email = request_data['email']
    order.product = product
    order.stripe_payment_intent = checkout_session['payment_intent']
    order.amount = int(product.price * 100)
    order.save()

    # return JsonResponse({'data': checkout_session})
    return JsonResponse({'sessionId': checkout_session.id})


class PaymentSuccessView(TemplateView):
    template_name = "payments/payment_success.html"

    def get(self, request, *args, **kwargs):
        session_id = request.GET.get('session_id')
        if session_id is None:
            return HttpResponseNotFound()
        
        stripe.api_key = settings.STRIPE_SECRET_KEY
        session = stripe.checkout.Session.retrieve(session_id)

        order = get_object_or_404(OrderDetail, stripe_payment_intent=session.payment_intent)
        order.has_paid = True
        order.save()
        return render(request, self.template_name)

class PaymentFailedView(TemplateView):
    template_name = "payments/payment_failed.html"
Step 10 : Creating the Templates

Here, in this step we need to create a new folder named payments in the templates folder of payments.

  • base.html
  • product_create.html
  • product_detail.html
  • payment_success.html
  • payment_failed.html
  • product_list.html

Here's the folder structure of payments app.

├── admin.py
├── apps.py
├── forms.py
├── __init__.py
├── migrations
│   ├── 0001_initial.py
│   ├── 0002_orderdetail.py
│   ├── __init__.py
│   └── __pycache__
│       ├── 0001_initial.cpython-38.pyc
│       ├── 0002_orderdetail.cpython-38.pyc
│       └── __init__.cpython-38.pyc
├── models.py
├── __pycache__
│   ├── admin.cpython-38.pyc
│   ├── apps.cpython-38.pyc
│   ├── forms.cpython-38.pyc
│   ├── __init__.cpython-38.pyc
│   ├── models.cpython-38.pyc
│   ├── urls.cpython-38.pyc
│   └── views.cpython-38.pyc
├── templates
│   └── payments
│       ├── base.html
│       ├── payment_failed.html
│       ├── payment_success.html
│       ├── product_create.html
│       ├── product_detail.html
│       └── product_list.html
├── tests.py
├── urls.py
└── views.py

5 directories, 27 files

Next, open the payment/templates/base.html file and the add:

payment/templates/base.html
<!doctype html>
<html lang="en">

<head>
    <title>Django Payments App</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
    <style type="text/css">
        .success-animation {
          margin: 50px auto;
        }
        .checkmark {
          width: 100px;
          height: 100px;
          border-radius: 50%;
          display: block;
          stroke-width: 2;
          stroke: #4bb71b;
          stroke-miterlimit: 10;
          box-shadow: inset 0px 0px 0px #4bb71b;
          animation: fill 0.4s ease-in-out 0.4s forwards,
            scale 0.3s ease-in-out 0.9s both;
          position: relative;
          top: 5px;
          right: 5px;
          margin: 0 auto;
        }
        .checkmark__circle {
          stroke-dasharray: 166;
          stroke-dashoffset: 166;
          stroke-width: 2;
          stroke-miterlimit: 10;
          stroke: #4bb71b;
          fill: #fff;
          animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
        }
        .checkmark__check {
          transform-origin: 50% 50%;
          stroke-dasharray: 48;
          stroke-dashoffset: 48;
          animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
        }
        @keyframes stroke {
          100% {
            stroke-dashoffset: 0;
          }
        }
        @keyframes scale {
          0%,
          100% {
            transform: none;
          }
          50% {
            transform: scale3d(1.1, 1.1, 1);
          }
        }
        @keyframes fill {
          100% {
            box-shadow: inset 0px 0px 0px 30px #4bb71b;
          }
        }
    </style>
</head>

<body>

    {% block content %}

    {% endblock content %}

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
</body>

</html>

Next, open the payment/templates/product_create.html file and the add:

payment/templates/product_create.html
{% extends 'payments/base.html' %}

{% block content %}
<div class="container mt-3">
    <h1 class="text-center">Create Product</h1>
    <div class="row">
        <div class="col-sm-6 offset-sm-3">
            <form method="post" novalidate>
                {% csrf_token %}
                {{ form.as_p }}
                <div class="form-group">
                    <button type="submit" class="btn btn-primary">Save</button>
                    <a href="{% url 'home' %}" class="btn btn-secondary">Back</a>
                </div>
            </form>
        </div>
    </div>
</div>
<script>
    document.querySelectorAll('input, textarea').forEach(e => {
        e.classList.add('form-control');
    })
</script>
{% endblock content %}

Next, open the payment/templates/product_list.html file and the add:

payment/templates/product_list.html
{% extends 'payments/base.html' %}

{% block content %}
<div class="container mt-5">
    <div class="row">
        <div class="col-md-7 text-right">
            <h1>Product List</h1>
        </div>
        <div class="col-md-5 text-right">
            <a href="{% url 'create' %}" class="btn btn-success">Create Product</a>
        </div>
    </div>
    {% if product_list %}
    <div class="row">


        {% for p in product_list %}
        <div class="col-sm-4">
            <div class="card">
                <img class="card-img-top" src="https://dummyimage.com/140x100.jpg?text={{ p.name }}" alt="">
                <div class="card-body">
                    <h4 class="card-title">{{ p.name }}</h4>
                    <p class="card-text">{{ p.description }}</p>
                </div>
                <div class="card-footer d-flex">
                    <a href="{% url 'detail' id=p.id %}" class="btn btn-success ml-auto">Buy Now</a>
                </div>
            </div>
        </div>
        {% endfor %}

    </div>
    {% else %}
    <div class="alert alert-info text-center mt-5">
        The product list is empty. Please add some products first.
    </div>
    {% endif %}
</div>
{% endblock content %}

Next, open the payment/templates/product_detail.html file and the add:

payment/templates/product_detail.html
{% extends 'payments/base.html' %}

{% block content %}
<div class="container mt-5 pt-5">

    <div class="card">
        <div class="card-header">
            <h2>Product Detail</h2>
        </div>
        <div class="card-body">
            <div class="container row">
                <div class="col-md-2">
                    <img src="https://dummyimage.com/150x220.gif?text={{ object.name }}" alt="">
                </div>
                <div class="col-md-10">
                    <h1>Name: {{ object.name }}</h1>
                    <p>Description: {{ object.description }}</p>
                    <p>Price: {{ object.price }}</p>

                    <div class="form-group">
                        <label for="email">Email: </label>
                        <input type="email" name="email" id="email" class="form-control" placeholder="Email">
                        <small>Please enter your email address</small>
                    </div>
                </div>
            </div>
        </div>
        <div class="card-footer d-flex">
            <a href="{% url 'home' %}" class="btn btn-secondary">Back to Home</a>
            <button class="btn btn-success ml-auto" id="checkout-button">Checkout</button>
        </div>
    </div>
</div>

<script src="https://js.stripe.com/v3/"></script>
<script type="text/javascript">
    // Create an instance of the Stripe object with your publishable API key
    var stripe = Stripe('{{ stripe_publishable_key }}');
    var checkoutButton = document.getElementById('checkout-button');

    checkoutButton.addEventListener('click', function () {

        var email = document.getElementById('email').value;
        if (email.length == 0) {
            alert("Please enter your email address.");
            return;
        }

        // Create a new Checkout Session using the server-side endpoint you
        // created in step 3.
        fetch("{% url 'api_checkout_session' id=object.id %}", {
            method: 'POST',
            body: JSON.stringify(
                { email: email }
            )
        })
            .then(function (response) {
                return response.json();
            })
            .then(function (session) {
                return stripe.redirectToCheckout({ sessionId: session.sessionId });
            })
            .then(function (result) {
                // If `redirectToCheckout` fails due to a browser or network
                // error, you should display the localized error message to your
                // customer using `error.message`.
                if (result.error) {
                    alert(result.error.message);
                }
            })
            .catch(function (error) {
                console.error('Error:', error);
            });
    });
</script>
{% endblock content %}

Next, open the payment/templates/payment_success.html file and the add:

payment/templates/payment_success.html
{% extends 'payments/base.html' %}

{% block content %}
<div class="container mt-5">
    <div class="jumbotron text-center mt-5">
        <div class="success-animation">
          <svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
            <circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none" />
            <path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" />
          </svg>
          <h1 class="text-success mt-3">Payment Successful</h1>
        </div>
        <p>Please wait while your payment is successfully confirmed and page is automatically redirect to your home page after few seconds..</p>
    </div>
</div>
<script type="text/javascript">
window.setTimeout( function() {
  window.location.reload();
  window.location.href = "{% url 'home' %}";
}, 5000);
</script>
{% endblock content %}

Next, open the payment/templates/payment_failed.html file and the add:

payment/templates/payment_failed.html
{% extends 'payments/base.html' %}

{% block content %}
<div class="container mt-5">
    <div class="jumbotron text-center mt-5">
        <h1 class="text-danger">Payment Failed</h1>
        <p>Please wait while your payment failure page is automatically redirect to your home page after few seconds..</p>
    </div>
</div>
<script type="text/javascript">
window.setTimeout( function() {
  window.location.reload();
  window.location.href = "{% url 'home' %}";
}, 5000);
</script>
{% endblock content %}
Step 11 : Creating URLs

In this section, we’ll create the urls to access our views.Go to the urls.py payment/urls.py file and update it as follows:

payment/urls.py
from django.urls import path
from .views import *

urlpatterns = [
    path('', ProductListView.as_view(), name='home'),
    path('create/', ProductCreateView.as_view(), name='create'),
    path('detail/<id>/', ProductDetailView.as_view(), name='detail'),
    path('success/', PaymentSuccessView.as_view(), name='success'),
    path('failed/', PaymentFailedView.as_view(), name='failed'),

    path('api/checkout-session/<id>/', create_checkout_session, name='api_checkout_session'),
]

Next, we will require the modify the urls.py your root preoject folder lets update the file.

example/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('payment.urls')),
]
Run the Server

In this step, we’ll run the local development server for playing with our app without deploying it to the web.

python manage.py runserver

Next, go to the http://localhost:8000/ address with a web browser.

Product Checkout Page:



Payment Success Page:



We can check all these payment in Stripe dashboard



I Hope It will help you....