Django PayPal Integration Tutorial: Accept Payments Easily

Django PayPal Integration

Introduction

Having PayPal integrated into the Django framework may seem like an easy method of introducing a payment feature onto our website. PayPal avoids any payment processing problems as all transactions are done online. It is fully integrated as it is a widely accepted service and as well as a widely used framework.

Why Use PayPal for Payments?

  • Trust and Security: The fact that this is a payment system that we enhance our app each time we integrate PayPal into it. The users feel instead of making payments on unknown websites, they would rather pay through something they know.
  • Global Reach: Another major advantage is the global presence of PayPal services. PayPal supports several languages, currencies, and countries therefore. Which means it is possible to receive payments from almost any end of the earth without additional effort.
  • Easy to Integrate: The integration is not troublesome. Though and alongside using the django-paypal package, we are able to go fast and easy.
  • Extra Protection: And let’s not forget about the built-in protection. PayPal provides dispute resolution and chargeback handling, keeping both us and our users safe from fraud.

Today, we’re going to embark on an adventurous journey into the world of web development, where we’ll learn how to sprinkle some PayPal magic into our Django applications!

So, buckle up, grab your favorite snack (because coding is always better with snacks 😊), and let’s get started on transforming our web apps into money-making machines!

Prerequisites & Creating a New Django Project

In this section, we are going to create our Django project in the most basic way possible. First, we are going to create a virtual environment so that we can have our project dependencies separated and easy to maintain when it comes to managing them.

It is a good practice and will help us in avoiding the problem of conflicting dependencies in different projects. We can create it by executing the following command in our terminal.

Bash
$ python -m venv env

After that, we will activate our virtual environment. The command differs slightly depending on your operating system. For windows, we use.

Bash
$ .\venv\Scripts\activate

Whereas for macOS and Linux, the command is.

Bash
$ source venv/bin/activate

Once our virtual environment is active, we shall move ahead and install Django. We can do this with the following command.

Bash
$ pip install django

Then we will proceed to install the PayPal Django package, which will assist us with the integration of PayPal. You can install it using.

Bash
$ pip install django-paypal

Now that we have both Django and the django-paypal package installed, it is time to create a new Django project that will be the primary structure for the PayPal integration. This project can be named appropriately, django_paypal for example, by running.

Bash
$ django-admin startproject django_paypal

Next, we will go to our project directory.

Bash
$ cd django_paypal

In this project solicitation, we will develop a new application that will take care of our payment processing functionalities. This app will be referred to as payments app to remain organized. We can create the app using.

Bash
$ python manage.py startapp payment

With our project and app set up, we are now ready to start integrating PayPal into our Django application!

Paypal Developer Configuration

The first thing to do when looking to integrate PayPal into a Django app is to sign up for a PayPal Developer account. Such an account gives us a chance to access the developer tools offered by PayPal such as REST API credentials that help in securely capturing payments within our application.

In order to create PayPal developer account, the very first step is to go to the PayPal Developer site and either create an account or use an already existing Paypal account to sign in.

Django PayPal Integration
Paypal Developer SignIn

After signing in, access to paypal developer dashboard will be provided where sandbox accounts for testing purposes can be created.

Django PayPal Integration
Paypal Developer Dashboard

Once we’ve created our PayPal Developer Account, the next step would be to create REST API credentials (Client Id and Secret Key).

Django PayPal Integration
Paypal RESTAPI Credentials

These authentication credentials are critical for the operation of this payment system as the Django app communicates to the Paypal payment system.

Configuring Settings

Moving on with the integration of PayPal into the Django project, we will turn the attention to the settings of Django, add PayPal to the INSTALLED_APPS.

First of all, we are going to go to your settings.py to the variable named INSTALLED_APPS and index the PayPal application there.

# settings.py
INSTALLED_APPS = [
    # other installed apps
    'paypal.standard.ipn',
]

Next, configure the PayPal settings in the same file by adding your Client ID and Secret Key from PayPal Developer Dashboard.

# PayPal Settings
PAYPAL_CLIENT_ID = 'your-client-id'
PAYPAL_SECRET_KEY = 'your-secret-key'

PAYPAL_MODE = 'sandbox'  # Use 'live' for production

This ensures that Django is set up to use PayPal’s sandbox environment during development. When you’re ready to go live, switch PAYPAL_MODE to 'live'.

Defining the Course Model

Now, we will create the model that will depict the courses users can acquire using PayPal. This model will specify the main characteristics of a course including the title, description, image and its cost.

from django.db import models

class Courses(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

Once this model is set up, the next step is to create and apply the database migrations.

Bash
$ python manage.py makemigrations
$ python manage.py migrate

Now your course model is ready to be used in your Django project, and you can start adding courses through the admin panel or directly via views.

Creating Views

Now it’s time to implement the specific views of the application intended to work with PayPal payments in the course-based app which will include making a payment, a success message and cancellation message.

import paypalrestsdk
from django.shortcuts import render, redirect, get_object_or_404
from .models import Courses
from django.conf import settings
from django.http import JsonResponse

paypalrestsdk.configure({
    "mode": settings.PAYPAL_MODE,
    "client_id": settings.PAYPAL_CLIENT_ID,
    "client_secret": settings.PAYPAL_SECRET_KEY,
})

def course_detail(request, course_id):
    course = get_object_or_404(Courses, id=course_id)
    return render(request, 'course_detail.html', {'course': course, 'CLIENT_ID': settings.PAYPAL_CLIENT_ID})

def create_order(request, course_id):
    """View to handle PayPal order creation"""
    course = get_object_or_404(Courses, id=course_id)
    
    # Create the PayPal payment
    payment = paypalrestsdk.Payment({
        "intent": "sale",
        "payer": {
            "payment_method": "paypal"
        },
        "redirect_urls": {
            "return_url": request.build_absolute_uri(f'/courses/{course_id}/payment-success'),
            "cancel_url": request.build_absolute_uri(f'/courses/{course_id}/payment-cancel')
        },
        "transactions": [{
            "item_list": {
                "items": [{
                    "name": course.title,
                    "sku": course.id,
                    "price": str(course.price),
                    "currency": "USD",
                    "quantity": 1
                }]
            },
            "amount": {
                "total": str(course.price),
                "currency": "USD"
            },
            "description": f"Purchase of {course.title}."
        }]
    })
    
    if payment.create():
        # Redirect user to PayPal for payment approval
        for link in payment.links:
            if link.rel == "approval_url":
                return JsonResponse({'approval_url': link.href})  # Return the approval URL as JSON
    else:
        # Handle payment creation failure
        return JsonResponse({'error': payment.error}, status=400)

def payment_success(request, course_id):
    """View to handle successful PayPal payments"""
    payment_id = request.GET.get('paymentId')
    payer_id = request.GET.get('PayerID')
    
    payment = paypalrestsdk.Payment.find(payment_id)
    
    if payment.execute({"payer_id": payer_id}):
        # Payment was successful
        return render(request, 'payment_success.html', {'payment': payment})
    else:
        # Handle payment execution failure
        return JsonResponse({'error': payment.error}, status=400)

def payment_cancel(request, course_id):
    """View to handle payment cancellation"""
    return render(request, 'payment_cancel.html')

The course_detail() View gets specific details of the course and displays them in course_detail.html where prospective buyers can view the course before payment is made.

In the create_order() view, we start the PayPal payment process by creating a Payment object. This object holds transaction details like the course name, price, and currency. After the payment object is created, it returns a JSON response with the PayPal approval URL.

The payment_success() View concerns itself with what happens once the payment has been approved by PayPal and the user is redirected back to the website. It uses the payer_id and payment_id from PayPal’s response to execute the payment fully.

The payment_cancel() View covers scenarios when the users abandon the payment. If a payment has been started but some changes or deletion of the payment have occurred, the user is taken to an appropriate page.

As soon as these views are implemented, let’s pass those views into django_paypal/urls.py.

# django_paypal/urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('courses/<int:course_id>/', views.course_detail, name='course_detail'),
    path('courses/<int:course_id>/create-order/', views.create_order, name='create_order'),
    path('courses/<int:course_id>/payment-success/', views.payment_success, name='payment_success'),
    path('courses/<int:course_id>/payment-cancel/', views.payment_cancel, name='payment_cancel'),
]

Creating Templates for PayPal Integration

With proper PayPal integration, one of the components is a set of templates which will help to navigate the user to the specifics of the payment. We will prepare three templates: course_detail.html, payment_success.html, and payment_cancel.html. Each of these templates serves a specific purpose in handling the payment process.

course_detail.html

This template is where a user can see course descriptions and pay for the particular course via PayPal account. The course's title, course's description, and course's price are presented along with a button that initiates PayPal payment for the selected course.

<!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" />
    <style>
        /* Custom styles for the card */
        .custom-card {
            border: 2px solid #007bff; /* Card border color */
            border-radius: 15px; /* Rounded corners */
            overflow: hidden; /* Hide overflow for rounded corners */
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); /* Shadow effect */
            transition: transform 0.3s; /* Animation for hover effect */
        }

        .custom-card:hover {
            transform: scale(1.05); /* Scale effect on hover */
        }

        .card-header {
            background-color: #007bff; /* Header background color */
            color: white; /* Header text color */
        }

        .card-title {
            font-size: 1.5rem; /* Custom title size */
            font-weight: bold; /* Bold title */
        }

        .card-body {
            padding: 20px; /* Custom body padding */
        }

        .btn-custom {
            background-color: #28a745; /* Custom button color */
            color: white; /* Button text color */
            border-radius: 20px; /* Rounded button */
        }

        .btn-custom:hover {
            background-color: #218838; /* Button hover color */
        }
    </style>
</head>
<body>

<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card custom-card">
                <div class="card-header">
                    <h5 class="card-title">{{ course.title }}</h5>
                </div>
                <div class="card-body text-center">
                    <img src="{{ course.image }}" class="img-fluid mb-3" alt="{{ course.title }}" style="max-width: 200px; height: auto;">
                    <p class="card-text">{{ course.description }}</p>
                    <h2 class="text-primary">Price: ${{ course.price }}</h2>

                    <!-- PayPal button will be rendered here -->
                    <div id="paypal-button-container" class="mt-4"></div>
                    <p id="result-message" class="mt-2"></p>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- Include PayPal SDK -->
<script src="https://www.paypal.com/sdk/js?client-id={{CLIENT_ID}}&currency=USD&components=buttons&enable-funding=venmo"></script>

<script>
    // Render the PayPal button
    window.paypal.Buttons({
        style: {
            shape: "rect",
            layout: "vertical",
            color: "blue",
            label: "paypal",
        },
        createOrder: async function() {
            try {
                // Create the order by sending a POST request to your Django backend
                const response = await fetch(`/courses/{{ course.id }}/create-order/`, {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json",
                        "X-CSRFToken": '{{ csrf_token }}',
                    },
                    body: JSON.stringify({
                        cart: [
                            {
                                id: "{{ course.id }}",  // Sending the course ID to the backend
                                quantity: 1,  // You can modify this for multiple quantities
                            },
                        ],
                    }),
                });

                if (!response.ok) {
                    // Handle errors if the order could not be created
                    const errorData = await response.json();
                    throw new Error(errorData.error || "Could not create order.");
                }

                const orderData = await response.json();
                // Redirect the user to PayPal to approve the payment
                window.location.href = orderData.approval_url;
            } catch (error) {
                // Display error messages
                console.error(error);
                document.getElementById("result-message").innerHTML = `Could not initiate PayPal Checkout...<br><br>${error}`; // Use backticks for template literal
            }
        },

        onApprove: async function(data, actions) {
            try {
                // Once the user approves the payment, capture the order
                const response = await fetch(`/api/orders/${data.orderID}/capture/`, {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json",
                    },
                });

                const orderData = await response.json();

                if (orderData.error) {
                    throw new Error(orderData.error);
                }

                // Display success message
                document.getElementById("result-message").innerHTML = `Transaction successful: ${orderData.id}`; // Use backticks for template literal
            } catch (error) {
                // Handle capture errors
                console.error(error);
                document.getElementById("result-message").innerHTML = `Could not capture PayPal transaction...<br><br>${error}`; // Use backticks for template literal
            }
        }
    }).render("#paypal-button-container");
</script>

</body>
</html>
  • This template fetches the course details such as title, description, and price.
  • The PayPal button is rendered using the PayPal SDK, with functionality to initiate and approve payments.
  • When a user clicks the PayPal button, the createOrder function sends the order details to your backend to generate an order, and the user is redirected to PayPal for payment approval.
course_detail.html

when the user clicks on the PayPal payment buttons. It will be redirected to PayPal to proceed to payment.

We have created a template where a user can purchase a specific course with PayPal. Next, we will create payment success and cancellation templates.

payment_success.html

This template contents informs the user that the payment has been successfully made. Such a template contains a “Thank you….” notice and the PayPal receipt.

<!DOCTYPE html>
<html>
<head>
    <title>Payment Success</title>
</head>
<body>
    <h1>Payment Completed Successfully</h1>
    <p>Thank you for purchasing the course.</p>
    <p>Transaction ID: {{ payment.id }}</p>
</body>
</html>
  • After the payment is successfully completed, PayPal redirects the user back to your site, where this template is displayed.
  • The transaction ID is fetched from the payment object to give the user a reference for their completed payment.

payment_cancel.html

This template takes care of when the user aborts the payment process. It only tells the user that the payment is cancelled.

<!DOCTYPE html>
<html>
<head>
    <title>Payment Canceled</title>
</head>
<body>
    <h1>Payment Canceled</h1>
    <p>Your payment was canceled.</p>
</body>
</html>

Cool! We have now finished setting up our template files. It’s time to run our Django server and test the application and enjoy the power of Paypal payments in our Django web application.

Conclusion

To sum up, this basic setup for integrating PayPal payments into your Django web application should help you process payments with confidence. However, the real fun part starts when you mod the payment system.

You can give a deeper look at other types of payment methods and additional features. Also, do not forget to take a look at the PayPal Developer Documentation, where there is a lot of advanced. Have a good day!

🧷Explore the complete source code on GitHub.

🧷Related Article: Django Performance: 5 Practices to Optimize Your Web App


Light
×