How To Integrate Paypal In Django

People are currently reading this guide.

Integrating PayPal into Your Django Application: A Comprehensive Guide

Welcome, fellow Django developers! Are you looking to seamlessly integrate PayPal into your web application to accept payments from users worldwide? You've come to the right place! This comprehensive guide will walk you through the entire process, step by step, ensuring a smooth 1 and successful integration. Let's dive in!  

Step 1: Setting Up Your PayPal Developer Account

Before we even touch our Django code, we need to establish a sandbox environment on PayPal. This allows us to test our integration without using real money.

1.1: Navigating to the PayPal Developer Portal

Open your web browser and head over to the PayPal Developer website (developer.paypal.com). If you don't have an account, you'll need to sign up. It's a straightforward process.

1.2: Creating Sandbox Accounts

Once logged in, navigate to the "Sandbox" section. Here, you'll need to create two types of accounts:

  • A Business Sandbox Account: This will act as your merchant account, the one receiving payments.
  • Personal Sandbox Accounts (Optional but Recommended): Create a few personal accounts to simulate different customer payment scenarios.

Pay close attention to the email addresses and passwords you create for these sandbox accounts, as you'll need them later for testing.

1.3: Obtaining API Credentials

For your Django application to communicate with PayPal, you'll need API credentials. Within your Business Sandbox account settings, look for the "API credentials" section. PayPal offers different types of API credentials. For most Django integrations, the "REST API credentials" are the recommended choice. Generate these credentials (Client ID and Secret). Keep these credentials secure and do not share them publicly.

Step 2: Installing the Necessary Django Library

Now that our PayPal sandbox environment is ready, let's move to our Django project. We'll need a library that simplifies the interaction with the PayPal API.

2.1: Activating Your Virtual Environment

It's always best practice to work within a virtual environment in Django. If you haven't already, activate your project's virtual environment using the appropriate command for your operating system (e.g., source venv/bin/activate on Linux/macOS or venv\Scripts\activate on Windows).

2.2: Installing django-paypal

The django-paypal library is a popular and well-maintained package that handles much of the heavy lifting for PayPal integration in Django. To install it, use pip:

Bash
pip install django-paypal
  

Wait for the installation to complete successfully.

Step 3: Configuring Your Django Settings

With the library installed, we need to tell our Django project about it.

3.1: Adding paypal.standard.ipn to INSTALLED_APPS

Open your project's settings.py file and add 'paypal.standard.ipn' to the INSTALLED_APPS list:

Python
INSTALLED_APPS = [
      # ... other apps
          'paypal.standard.ipn',
              # ...
              ]
              

3.2: Setting Up PayPal Settings

In the same settings.py file, add a PAYPAL dictionary to configure your PayPal connection. You'll need to include your sandbox API credentials here:

Python
PAYPAL = {
                  'business': 'YOUR_BUSINESS_SANDBOX_EMAIL',  # Replace with your sandbox business email
                      'sandbox': True,  # Set to True for sandbox environment, False for live
                          'api_credentials': {
                                  'client_id': 'YOUR_SANDBOX_CLIENT_ID',  # Replace with your sandbox Client ID
                                          'client_secret': 'YOUR_SANDBOX_SECRET',  # Replace with your sandbox Secret
                                              },
                                                  'notify_url': '/paypal/',  # The URL where PayPal will send IPN notifications
                                                      'return_url': '/payment/success/',  # URL to redirect after successful payment
                                                          'cancel_url': '/payment/cancel/',  # URL to redirect if payment is cancelled
                                                          }
                                                          

Important Notes:

  • Replace the placeholder values with your actual sandbox credentials and desired URLs.
  • The notify_url is crucial for receiving Instant Payment Notifications (IPN) from PayPal, which inform your application about the payment status.
  • Make sure these URLs are properly defined in your urls.py file later.
  • When you go live, remember to set 'sandbox': False and use your live PayPal business email and API credentials.

Step 4: Defining Your Payment Forms and Views

Now, let's create the necessary forms and views in your Django application to handle the payment process.

4.1: Creating a Payment Form

In one of your app's forms.py file (or create one if it doesn't exist), define a form that inherits from paypal.standard.forms.PayPalPaymentsForm. This form will automatically generate the necessary hidden fields for PayPal.

Python
from django import forms
  from paypal.standard.forms import PayPalPaymentsForm
  
  class PurchaseForm(forms.Form):
      item_name = forms.CharField(widget=forms.HiddenInput())
          amount = forms.DecimalField(widget=forms.HiddenInput())
              invoice = forms.CharField(widget=forms.HiddenInput())  # Optional, but good for tracking
                  currency_code = forms.CharField(initial='USD', widget=forms.HiddenInput())  # Or your preferred currency
                  
                      class Meta:
                              widgets = {
                                          'business': forms.HiddenInput(),
                                                      'notify_url': forms.HiddenInput(),
                                                                  'return_url': forms.HiddenInput(),
                                                                              'cancel_url': forms.HiddenInput(),
                                                                                          'item_name': forms.HiddenInput(),
                                                                                                      'amount': forms.HiddenInput(),
                                                                                                                  'invoice': forms.HiddenInput(),
                                                                                                                              'currency_code': forms.HiddenInput(),
                                                                                                                                      }
                                                                                                                                      
                                                                                                                                          def __init__(self, *args, **kwargs):
                                                                                                                                                  super().__init__(*args, **kwargs)
                                                                                                                                                          paypal_settings = kwargs.pop('paypal_settings', None)
                                                                                                                                                                  if paypal_settings:
                                                                                                                                                                              self.fields['business'].initial = paypal_settings.get('business')
                                                                                                                                                                                          self.fields['notify_url'].initial = paypal_settings.get('notify_url')
                                                                                                                                                                                                      self.fields['return_url'].initial = paypal_settings.get('return_url')
                                                                                                                                                                                                                  self.fields['cancel_url'].initial = paypal_settings.get('cancel_url')
                                                                                                                                                                                                                  

4.2: Creating a View to Initiate Payment

In your app's views.py file, create a view that handles the creation of the PayPal payment form and redirects the user to PayPal.

Python
from django.shortcuts import render, redirect
                                                                                                                                                                                                                  from django.urls import reverse
                                                                                                                                                                                                                  from .forms import PurchaseForm
                                                                                                                                                                                                                  from django.conf import settings
                                                                                                                                                                                                                  import uuid
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  def initiate_payment(request):
                                                                                                                                                                                                                      item_name = "Your Product Name"  # Replace with the actual item name
                                                                                                                                                                                                                          amount = 10.00  # Replace with the actual amount
                                                                                                                                                                                                                              invoice_id = uuid.uuid4()  # Generate a unique invoice ID
                                                                                                                                                                                                                              
                                                                                                                                                                                                                                  paypal_dict = {
                                                                                                                                                                                                                                          'business': settings.PAYPAL['business'],
                                                                                                                                                                                                                                                  'amount': amount,
                                                                                                                                                                                                                                                          'item_name': item_name,
                                                                                                                                                                                                                                                                  'invoice': invoice_id,
                                                                                                                                                                                                                                                                          'currency_code': 'USD',
                                                                                                                                                                                                                                                                                  'notify_url': request.build_absolute_uri(reverse('paypal-ipn')),
                                                                                                                                                                                                                                                                                          'return_url': request.build_absolute_uri(reverse('payment_success')),
                                                                                                                                                                                                                                                                                                  'cancel_url': request.build_absolute_uri(reverse('payment_cancel')),
                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                                          form = PurchaseForm(initial=paypal_dict, paypal_settings=settings.PAYPAL)
                                                                                                                                                                                                                                                                                                              return render(request, 'payment/process.html', {'form': form})
                                                                                                                                                                                                                                                                                                              

4.3: Creating Success and Cancel Views

You'll also need views to handle successful and cancelled payments.

Python
from django.shortcuts import render
                                                                                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                                                                              def payment_success(request):
                                                                                                                                                                                                                                                                                                                  return render(request, 'payment/success.html')
                                                                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                                                                  def payment_cancel(request):
                                                                                                                                                                                                                                                                                                                      return render(request, 'payment/cancel.html')
                                                                                                                                                                                                                                                                                                                      

Step 5: Defining Your URLs

Now, let's connect these views to URLs in your project's or app's urls.py file.

Python
from django.urls import path
                                                                                                                                                                                                                                                                                                                      from . import views
                                                                                                                                                                                                                                                                                                                      from paypal.standard.ipn import urls as paypal_urls
                                                                                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                                                      urlpatterns = [
                                                                                                                                                                                                                                                                                                                          path('process/', views.initiate_payment, name='initiate_payment'),
                                                                                                                                                                                                                                                                                                                              path('success/', views.payment_success, name='payment_success'),
                                                                                                                                                                                                                                                                                                                                  path('cancel/', views.payment_cancel, name='payment_cancel'),
                                                                                                                                                                                                                                                                                                                                      path('paypal/', paypal_urls),  # This line includes the URLs for django-paypal IPN
                                                                                                                                                                                                                                                                                                                                      ]
                                                                                                                                                                                                                                                                                                                                      

Crucially, include paypal_urls under the /paypal/ path. This is where PayPal will send the IPN notifications.

Step 6: Creating Your Templates

You'll need simple templates to display the payment form, success message, and cancellation message.

6.1: payment/process.html

HTML
<!DOCTYPE html>
                                                                                                                                                                                                                                                                                                                                      <html>
                                                                                                                                                                                                                                                                                                                                      <head>
                                                                                                                                                                                                                                                                                                                                          <title>Process Payment</title>
                                                                                                                                                                                                                                                                                                                                          </head>
                                                                                                                                                                                                                                                                                                                                          <body>
                                                                                                                                                                                                                                                                                                                                              <h1>Review Your Order</h1>
                                                                                                                                                                                                                                                                                                                                                  <p>Item: {{ form.item_name.value }}</p>
                                                                                                                                                                                                                                                                                                                                                      <p>Amount: ${{ form.amount.value }}</p>
                                                                                                                                                                                                                                                                                                                                                          <form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post">
                                                                                                                                                                                                                                                                                                                                                                  {% csrf_token %}
                                                                                                                                                                                                                                                                                                                                                                          {{ form.as_p }}
                                                                                                                                                                                                                                                                                                                                                                                  <input type="hidden" name="cmd" value="_xclick">
                                                                                                                                                                                                                                                                                                                                                                                          <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_xpressCheckout.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
                                                                                                                                                                                                                                                                                                                                                                                                  <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
                                                                                                                                                                                                                                                                                                                                                                                                      </form>
                                                                                                                                                                                                                                                                                                                                                                                                          <p><a href="{% url 'payment_cancel' %}">Cancel Payment</a></p>
                                                                                                                                                                                                                                                                                                                                                                                                          </body>
                                                                                                                                                                                                                                                                                                                                                                                                          </html>
                                                                                                                                                                                                                                                                                                                                                                                                          

Important: Note the action attribute in the form. It points to https://www.sandbox.paypal.com/cgi-bin/webscr for the sandbox environment. For live transactions, this should be https://www.paypal.com/cgi-bin/webscr. The {{ form.as_p }} renders the hidden fields generated by django-paypal.

6.2: payment/success.html

HTML
<!DOCTYPE html>
                                                                                                                                                                                                                                                                                                                                                                                                          <html>
                                                                                                                                                                                                                                                                                                                                                                                                          <head>
                                                                                                                                                                                                                                                                                                                                                                                                              <title>Payment Successful</title>
                                                                                                                                                                                                                                                                                                                                                                                                              </head>
                                                                                                                                                                                                                                                                                                                                                                                                              <body>
                                                                                                                                                                                                                                                                                                                                                                                                                  <h1>Thank you for your payment!</h1>
                                                                                                                                                                                                                                                                                                                                                                                                                      <p>Your transaction was successful.</p>
                                                                                                                                                                                                                                                                                                                                                                                                                          <p><a href="/">Back to Home</a></p>
                                                                                                                                                                                                                                                                                                                                                                                                                          </body>
                                                                                                                                                                                                                                                                                                                                                                                                                          </html>
                                                                                                                                                                                                                                                                                                                                                                                                                          

6.3: payment/cancel.html

HTML
<!DOCTYPE html>
                                                                                                                                                                                                                                                                                                                                                                                                                          <html>
                                                                                                                                                                                                                                                                                                                                                                                                                          <head>
                                                                                                                                                                                                                                                                                                                                                                                                                              <title>Payment Cancelled</title>
                                                                                                                                                                                                                                                                                                                                                                                                                              </head>
                                                                                                                                                                                                                                                                                                                                                                                                                              <body>
                                                                                                                                                                                                                                                                                                                                                                                                                                  <h1>Payment Cancelled</h1>
                                                                                                                                                                                                                                                                                                                                                                                                                                      <p>Your payment was cancelled.</p>
                                                                                                                                                                                                                                                                                                                                                                                                                                          <p><a href="/">Back to Home</a></p>
                                                                                                                                                                                                                                                                                                                                                                                                                                          </body>
                                                                                                                                                                                                                                                                                                                                                                                                                                          </html>
                                                                                                                                                                                                                                                                                                                                                                                                                                          

Step 7: Handling Instant Payment Notifications (IPN)

The IPN is a crucial part of the integration. It's how PayPal tells your application about the status of a transaction (success, failure, pending, etc.). The django-paypal library handles the verification of these messages.

7.1: Ensuring IPN URL is Accessible

Make sure the notify_url you defined in your settings.py and the corresponding URL pattern (/paypal/) are accessible by PayPal's servers. If your development server is behind a firewall or on localhost, you might need to use a tool like ngrok to expose it temporarily for testing IPN.

7.2: Processing IPN Signals

django-paypal provides signals that you can connect to in your Django code to handle different IPN statuses. In your app's signals.py file (create one if it doesn't exist), you can define functions to be executed when specific IPN signals are received.

Python
from django.dispatch import receiver
                                                                                                                                                                                                                                                                                                                                                                                                                                          from paypal.signals import payment_was_successful, payment_was_flagged
                                                                                                                                                                                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                                                                                                                                                          @receiver(payment_was_successful)
                                                                                                                                                                                                                                                                                                                                                                                                                                          def payment_successful_handler(sender, **kwargs):
                                                                                                                                                                                                                                                                                                                                                                                                                                              ipn_obj = sender
                                                                                                                                                                                                                                                                                                                                                                                                                                                  # Here you can access ipn_obj attributes like invoice, amount, custom, etc.
                                                                                                                                                                                                                                                                                                                                                                                                                                                      # Update your database, send confirmation emails, etc.
                                                                                                                                                                                                                                                                                                                                                                                                                                                          print(f"Payment successful for invoice: {ipn_obj.invoice}")
                                                                                                                                                                                                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                                                                                                                                                                          @receiver(payment_was_flagged)
                                                                                                                                                                                                                                                                                                                                                                                                                                                          def payment_flagged_handler(sender, **kwargs):
                                                                                                                                                                                                                                                                                                                                                                                                                                                              ipn_obj = sender
                                                                                                                                                                                                                                                                                                                                                                                                                                                                  # Handle flagged payments (e.g., due to potential fraud)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                      print(f"Payment flagged for invoice: {ipn_obj.invoice}")
                                                                                                                                                                                                                                                                                                                                                                                                                                                                      

Remember to import this signals.py file in your app's __init__.py file to ensure the signals are registered:

Python
# your_app/__init__.py
                                                                                                                                                                                                                                                                                                                                                                                                                                                                      default_app_config = 'your_app.apps.YourAppConfig'
                                                                                                                                                                                                                                                                                                                                                                                                                                                                      

And in your app's apps.py file:

Python
# your_app/apps.py
                                                                                                                                                                                                                                                                                                                                                                                                                                                                      from django.apps import AppConfig
                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                                                                                                                                                                                                      class YourAppConfig(AppConfig):
                                                                                                                                                                                                                                                                                                                                                                                                                                                                          default_auto_field = 'django.db.models.BigAutoField'
                                                                                                                                                                                                                                                                                                                                                                                                                                                                              name = 'your_app'
                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  def ready(self):
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          import your_app.signals
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          

Step 8: Testing Your Integration (Sandbox Mode)

Now it's time to test your PayPal integration in the sandbox environment.

  1. Run your Django development server (python manage.py runserver).
  2. Navigate to the URL that triggers the initiate_payment view.
  3. You should see the payment form with the PayPal button.
  4. Click the PayPal button. You will be redirected to the PayPal sandbox login page.
  5. Log in using one of your personal sandbox account credentials.
  6. Complete the payment process.
  7. You should be redirected back to your return_url (the success page).
  8. Check your Django development server logs. You should see output from your IPN signal handlers indicating that the payment was successful.
  9. You can also simulate cancelled payments by clicking the "Cancel and return to merchant" button on the PayPal sandbox page. You should be redirected to your cancel_url.
  10. Examine your PayPal sandbox business account to see the simulated transaction details.

Step 9: Going Live

Once you've thoroughly tested your integration in the sandbox and are confident it's working correctly, you can prepare to go live.

  1. Update your settings.py:
    • Set 'sandbox': False.
    • Replace 'YOUR_BUSINESS_SANDBOX_EMAIL', 'YOUR_SANDBOX_CLIENT_ID', and 'YOUR_SANDBOX_SECRET' with your actual live PayPal business email and API credentials.
  2. Update your template: In your payment/process.html template, change the form action URL to https://www.paypal.com/cgi-bin/webscr.
  3. Ensure your notify_url is publicly accessible: Your live server must be able to receive POST requests from PayPal on the notify_url.
  4. Thoroughly test with small live transactions: Before fully launching, perform a few small live transactions to ensure everything works as expected in the production environment.

Step 10: Security Considerations

Integrating payments requires careful attention to security.

  • Always use HTTPS: Ensure your website is served over HTTPS to encrypt communication between your users and your server.
  • Keep your API credentials secure: Never hardcode your API credentials directly in your views or templates. Store them securely in your Django settings.
  • Validate IPN messages: The django-paypal library helps with this, but it's crucial to understand the importance of verifying the authenticity of IPN messages to prevent fraudulent activities.
  • Follow PayPal's security best practices: Familiarize yourself with PayPal's developer documentation and security guidelines.

Frequently Asked Questions (How to...)

How to get PayPal API credentials?

Navigate to the PayPal Developer website (developer.paypal.com), log in, go to the "Sandbox" or "Live" section, and find the "API credentials" area under your account settings. Generate the REST API credentials (Client ID and Secret).

How to install the django-paypal library?

Open your terminal or command prompt, activate your Django project's virtual environment, and run the command: pip install django-paypal.

How to configure PayPal settings in Django?

In your project's settings.py file, add a PAYPAL dictionary with your business email, sandbox/live mode, API credentials, and URLs for notify, return, and cancel.

How to create a payment form for PayPal?

Create a Django form that inherits from paypal.standard.forms.PayPalPaymentsForm and includes necessary fields like item_name, amount, invoice, etc., often as hidden input fields.

How to handle successful PayPal payments?

Connect a receiver function to the payment_was_successful signal provided by django-paypal. This function will be executed when a successful IPN is received, allowing you to update your database, send emails, etc.

How to handle cancelled PayPal payments?

Create a Django view that is linked to the cancel_url defined in your PayPal settings. This view will be displayed to the user if they cancel the payment on PayPal.

How to test PayPal integration in Django?

Use the PayPal sandbox environment with your sandbox accounts and API credentials. Ensure your sandbox setting in settings.py is set to True and the form action points

6293240809095454510

You have our undying gratitude for your visit!