skip to Main Content

How to accept Paypal payments on your Django application

In this tutorial you’ll learn how to integrate Django and Paypal to accept payments in your Django web application.

Paypal is a popular solution for online payments. Even if nowadays there are many valid alternatives, Paypal is still a big player in the market with a solid reputation and it’s trusted by millions of users.

Here is a simple step-by-step guide on how to integrate the django-paypal third party app on your website.

1. Install the django-paypal app

pip install django-paypal

2. Insert the app in your Django settings

INSTALLED_APPS = (
  ...
  'paypal.standard.ipn',
)

3. Insert a Paypal form on a view

Assuming you are using Django class-based views, you can use a FormView like this:

from django.views.generic import FormView
from django.urls import reverse
from paypal.standard.forms import PayPalPaymentsForm

class PaypalFormView(FormView):
    template_name = 'paypal_form.html'
    form_class = PayPalPaymentsForm

    def get_initial(self):
        return {
            "business": '[email protected]',
            "amount": 20,
            "currency_code": "EUR",
            "item_name": 'Example item,
            "invoice": 1234,
            "notify_url": self.request.build_absolute_uri(reverse('paypal-ipn')),
            "return_url": self.request.build_absolute_uri(reverse('paypal-return')),
            "cancel_return": self.request.build_absolute_uri(reverse('paypal-cancel')),
            "lc": 'EN',
            "no_shipping": '1',
        }

This is a regular FormView and the template paypal_form.html is a standard Django template like this:

<html>
  <body>
    {{ form.render }}
  </body>
</html>

Notice how we are settings some initial values for the Paypal form. Let us see all the parameters used here:

  • business: this is your Paypal account email. Payments will be sent to this account, so choose it carefully.
  • amount: the payment amount.
  • currency_code: the currency used in the previous amount parameter. Here is a list of accepted currency codes.
  • item_name: the name of the item that the customer is paying. Choose it carefully, because Paypal will show this name to the user in the payment confirmation page.
  • invoice: this is an “invoice code” that you can use to match the payment with an object on your database. I suggest to use some sort of primary key for your transaction here.
  • notify_url: a complete URI where Paypal will send an IPN (Instant Payment Notification). This is a Django endpoint you’ll have to provide and that we’ll see in a moment. Here you’ll receive an HTTP request by the Paypal servers, when the payment will be correctly registered by Paypal.
  • return_url: a complete URI where the customer will be redirected upon a successful payment.
  • cancel_return: a complete URI where the customer will be redirected when the payment is cancelled.
  • lc: The locale code to use in the checkout page. Here is a list of supported locale codes.
  • no_shipping: Can be set to 1 if you are selling digital goods, that doesn’t require shipping.

Some of this data will likely be dynamic, because it depends on the object you are selling.

4. Provide an URL for Paypal IPN

In your Django urls.py file, add an urlconf like this:

path('paypal/', include('paypal.standard.ipn.urls')),

5. Create views for success and failure of Paypal checkout

You can add two TemplateView to show your user a message upon success or failure of the Paypal checkout.

from django.views.generic import TemplateView

class PaypalReturnView(TemplateView):
    template_name = 'paypal_success.html'

class PaypalCancelView(TemplateView):
    template_name = 'paypal_cancel.html'

Then you can mount these views in your urls.py:

from . import views
urlpatterns = [
    path('/paypal-return/', views.PaypalReturnView.as_view(), name='paypal-return'),
    path('/paypal-cancel/', views.PaypalCancelView.as_view(), name='paypal-cancel'),
    ...
]

The names of the urls should match with those defined in the form initial data, as shown at step 3.

6. Setup a listener to detect successful Paypal payments

When a successful payment if performed on Paypal, you’ll receive a so-called IPN: “Instant Payment Notification” from Paypal.

django-paypal uses the Django signal dispatcher to let you hook your code when a successful payment arrives. You can define a listener like this:

from paypal.standard.models import ST_PP_COMPLETED
from paypal.standard.ipn.signals import valid_ipn_received

@receiver(valid_ipn_received)
def paypal_payment_received(sender, **kwargs):
    ipn_obj = sender
    if ipn_obj.payment_status == ST_PP_COMPLETED:
        # WARNING !
        # Check that the receiver email is the same we previously
        # set on the `business` field. (The user could tamper with
        # that fields on the payment form before it goes to PayPal)
        if ipn_obj.receiver_email != '[email protected]':
            # Not a valid payment
            return

        # ALSO: for the same reason, you need to check the amount
        # received, `custom` etc. are all what you expect or what
        # is allowed.
        try:
            my_pk = ipn_obj.invoice
            mytransaction = MyTransaction.objects.get(pk=my_pk)
            assert ipn_obj.mc_gross == mytransaction.amount and ipn_obj.mc_currency == 'EUR'
        except Exception:
            logger.exception('Paypal ipn_obj data not valid!')
        else:
            mytransaction.paid = True
            mytransaction.save()
    else:
        logger.debug('Paypal payment status not completed: %s' % ipn_obj.payment_status)

Here we assumed that you had a model MyTransaction on your Django application, and that you want to set the transaction as paid when the Paypal payment is confirmed.

That’s it! Following these steps you should have a working Paypal integration in your Django project. Please let me know if you have questions or comments.

augusto

Freelance developer and sysadmin

This Post Has 4 Comments
  1. I am using a JavaScript Ajax request to pass dynamic variables to the to the PayPalForm view. This is the first class based view, and I don’t know how to get it to render. I’ve set everything up as your instructions say. I’m not getting any errors on the server, nor in JavaScript. I click my pay button (witht he javascript request function that should direct to me the class based view), and my page just seems to reload. It’s not rendering the form. Do you have any suggestions?

  2. Can you please elaborate on what did the MyTransaction model contains ? And share the source code for the same.

  3. Thank you so much for this presentation Augusto. For me am trying to create a system where I charge per post created, how do i implement this code in my application. Would you also please help me with the model file. Am quite new to Django.

    Thank you again

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top