DEV Community

Cover image for PayPal Implementation in Django Rest Framework.
paulsaul621
paulsaul621

Posted on

PayPal Implementation in Django Rest Framework.

Introduction.

If you're a Django developer, then you know that Django Rest Framework is a great tool for building RESTful APIs. But what if you want to add PayPal functionality to your Django Rest Framework-based API?

In this blog post, we'll show you how to integrate PayPal into your Django Rest Framework-based project. We'll cover installing the necessary dependencies, setting up your PayPal account, and configuring your Django project to accept payments via PayPal.

By the end of this blog post, you'll have a Django Rest Framework-based API that supports PayPal payments. Let's get started!

Step 1: Creating a Sandbox Account

First of all, we need a PayPal Sandbox Account, to create a sandbox account visit PayPal Developer website and click Login or Signup if you do not already have an account set up. You can log in with your existing PayPal account.

After signing in to your account click dashboard and select “Create Account”, after that we can create a business and personal PayPal sandbox accounts.

Image description

Personal Sandbox account is to pay money through PayPal and the business account used for receiving money (which is a merchant account). Select a merchant account this will open a popup window. Click on the API Credentials tab. You’ll see two API keys for the Test.

Copy those API Credentials which you will have a Client_ID and a Client_Secret.

Step 2: Integrate in Rest Framework

Open up the Django Rest API Framework that you want to implement PayPal checkout.

In your urls.py add this route

from django.urls import path, include, re_path
from django_paypal import views

urlpatterns = [
    path('paypal/create/order', views.CreateOrderViewRemote.as_view(), name='ordercreate'),
    path('paypal/capture/order', views.CaptureOrderView.as_view(), name='captureorder')
]
Enter fullscreen mode Exit fullscreen mode

The code above creating a url pattern for the CreateOrderViewRemote view, which is used to create an order, and the CaptureOrderView, which is used to capture an order. The capture order API is used to capture an order that has been authorized by a buyer.

In views.py we can create a new viewset class named CreateOrderViewRemote.
To proceed with the payment we have to call 2 URLs

https://api.sandbox.paypal.com/v1/oauth2/token : This link is used to get the token from PayPal, the token which is used to create order request in PayPal.
https://api-m.sandbox.paypal.com/v2/checkout/orders : This link is used to create an order in which we want to make payment.
In the viewset class, let's create a function which we want to generate the authorization token.

import json
import base64


def PaypalToken(client_ID, client_Secret):

    url = "https://api.sandbox.paypal.com/v1/oauth2/token"
    data = {
                "client_id":client_ID,
                "client_secret":client_Secret,
                "grant_type":"client_credentials"
            }
    headers = {
                "Content-Type": "application/x-www-form-urlencoded",
                "Authorization": "Basic {0}".format(base64.b64encode((client_ID + ":" + client_Secret).encode()).decode())
            }

    token = requests.post(url, data, headers=headers)
    return token.json()['access_token']
Enter fullscreen mode Exit fullscreen mode

We are creating a function called PaypalToken() which takes two arguments: client_ID, client_Secret.

The url is the Paypal REST API which we will be calling to get the access token.

The data is the data we will be sending to Paypal. We are sending the client_id, client_secret, and the grant_type which is client_credentials.

The headers is the header of the request. We are setting the Content-Type and we are sending the Authorization header with the Base64 Encoded client_ID and client_Secret.

Finally, we are making a POST request to the url and sending the data and headers as arguments. The response will be saved to the token variable.

The token is of type dict and has a json format. Since we only need the access_token, we only select the access_token key and we return it.

#DON'T FORGET TO REPLACE THE 'XXX' BELOW WITH YOUR KEYS
clientID = 'XXX'
clientSecret = 'XXX'

class CreateOrderViewRemote(APIView):

    def get(self, request):
        token = PaypalToken(clientID, clientSecret)
        headers = {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer '+token,
        }
        json_data = {
             "intent": "CAPTURE",
             "application_context": {
                 "notify_url": "https://pesapedia.co.ke",
                 "return_url": "https://pesapedia.co.ke",#change to your doma$
                 "cancel_url": "https://pesapedia.co.ke", #change to your domain
                 "brand_name": "PESAPEDIA SANDBOX",
                 "landing_page": "BILLING",
                 "shipping_preference": "NO_SHIPPING",
                 "user_action": "CONTINUE"
             },
             "purchase_units": [
                 {
                     "reference_id": "294375635",
                     "description": "African Art and Collectibles",

                     "custom_id": "CUST-AfricanFashion",
                     "soft_descriptor": "AfricanFashions",
                     "amount": {
                         "currency_code": "USD",
                         "value": "200" #amount,
                     },
                 }
             ]
         }
         response = requests.post('https://api-m.sandbox.paypal.com/v2/checkout/orders', headers=headers, json=json_data)
         order_id = response.json()['id']
         linkForPayment = response.json()['links'][1]['href']
         return linkForPayment

class CaptureOrderView(APIView):
    #capture order aims to check whether the user has authorized payments.
    def get(self, request):
        token = request.data.get('token')#the access token we used above for creating an order, or call the function for generating the token
        captureurl = request.data.get('url')#captureurl = 'https://api.sandbox.paypal.com/v2/checkout/orders/6KF61042TG097104C/capture'#see transaction status
        headers = {"Content-Type": "application/json", "Authorization": "Bearer "+token}
        response = requests.post(captureurl, headers=headers)
        return Response(response.json())
Enter fullscreen mode Exit fullscreen mode

Note as a result, we will get a response to a set of URLs. The output would look like as follows:

{
  "id": "5O190127TN364715T",
  "status": "CREATED",
  "links": [
    {
      "href": "https://api.paypal.com/v2/checkout/orders/5O190127TN364715T",
      "rel": "self",
      "method": "GET"
    },
    {
      "href": "https://www.paypal.com/checkoutnow?token=5O190127TN364715T",
      "rel": "approve",
      "method": "GET"
    },
    {
      "href": "https://api.paypal.com/v2/checkout/orders/5O190127TN364715T/capture",
      "rel": "capture",
      "method": "POST"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Pay attention to the link below:

{ 
  “href”: “https://www.paypal.com/checkoutnowtoken=5O190127TN364715T", 
“rel”: “approve”, 
“method”: “GET” 
},
Enter fullscreen mode Exit fullscreen mode

This is the link that initiates a payment request in PayPal. When visiting this link it would be redirected to your PayPal account and request you to log in. (Remember: Login using personal sandbox account to pay money). After logging in, the amount which we created for the product would be shown and we can proceed with the payment. If the payment is completed it would be redirected to return_url, if the payment canceled it would redirected to cancel_url.

Conclusion.

PayPal is one of the most popular payment processors in the world, and implementing it in Django Rest Framework is a breeze. With just a few simple steps, you can have PayPal up and running in your Django project.

Hope you enjoyed!

Top comments (11)

Collapse
 
tmcmanamey profile image
Tim • Edited

Okay, I got a little further. I'm trying to get links back from paypal for my payment. I've got my token from paypal and requesting the links. I don't know how to get more info other than a 400 error. I can't find on the paypal site the format for the message. I am running this from localhost. I also noticed in the json "landing_page": "BILLING" is this important or just a name?

This is the error
order_id = response.json()['id'] …
Local vars
Variable Value
headers
{'Authorization': 'Bearer '
'this is the auth token',
'Content-Type': 'application/json'}
json_data

{'application_context': {'brand_name': 'Nebraska Youth Camp',
'cancel_url': '/registration/cancel_url',
'landing_page': 'BILLING',
'notify_url': '/registration/notify_url',
'return_url': '/registration/return_url',
'shipping_preference': 'NO_SHIPPING',
'user_action': 'CONTINUE'},
'intent': 'CAPTURE',
'purchase_units': [{'amount': {'currency_code': 'USD', 'value': '200'},
'custom_id': 'Camp Session',
'description': 'Summer Camp',
'reference_id': '294375635',
'soft_descriptor': 'Camp Session'}]}
request

response


self


token

'my token was here'

def PaypalToken(client_ID, client_Secret):

    url = "https://api-m.sandbox.paypal.com/v1/oauth2/token"
    data = {
                "client_id":client_ID,
                "client_secret":client_Secret,
                "grant_type":"client_credentials"
            }
    headers = {
                "Content-Type": "application/x-www-form-urlencoded",
                "Authorization": "Basic {0}".format(base64.b64encode((client_ID + ":" + client_Secret).encode()).decode())
            }

    token = requests.post(url, data, headers=headers)
    return token.json()['access_token']

class CreateOrderViewRemote(APIView):
    def get(self, request):
        token = PaypalToken(clientID, clientSecret)
        headers = {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer '+token,
        }
        json_data = {
             "intent":"CAPTURE",
             "application_context": {
                 "notify_url": reverse('paypal-notify'),
                 "return_url": reverse('paypal-return'),#change to your doma$
                 "cancel_url": reverse('paypal-cancel'), #change to your domain
                 "brand_name": "Nebraska Youth Camp",
                 "landing_page": "BILLING",
                 "shipping_preference": "NO_SHIPPING",
                 "user_action": "CONTINUE"
             },
             "purchase_units": [
                 {
                     "reference_id": "294375635",
                     "description": "Summer Camp",

                     "custom_id": "Camp Session",
                     "soft_descriptor": "Camp Session",
                     "amount": {
                         "currency_code": "USD",
                         "value": "200" #amount,
                     },
                 }
             ]
         }
        response = requests.post('https://api-m.sandbox.paypal.com/v2/checkout/orders', headers=headers, json=json_data)
        order_id = response.json()['id'] # error here because return error 400, no id
        linkForPayment = response.json()['links'][1]['href']
        return linkForPayment
Enter fullscreen mode Exit fullscreen mode

Thanks for your help.

Collapse
 
paulwababu profile image
paulsaul621

Please print("response.text") so that I cam see the actual error. Landing page is where the user lands after being redirected to paypal for payments. In our case is billing. You can check PayPal documentation for the other landing pages.

Collapse
 
tmcmanamey profile image
Tim

I was able to get it to work. I used json.dump() but someone else said that if I name the variable json or at lease put json=json_data in the args that it should retain the json format. After trying this I see he's correct. I know you have that in your code but I had combined with other code I saw. I also ran through a json validator and found some commas that were not strict json. Thanks again.

Thread Thread
 
paulwababu profile image
paulsaul621

Great!

Collapse
 
tmcmanamey profile image
Tim

what does "requests.post" refer to?

Collapse
 
paulwababu profile image
paulsaul621

requests.post is a function in the Python requests library that sends an HTTP POST request to the specified URL

Collapse
 
tmcmanamey profile image
Tim

What is the APIView you refer to? Is that something we need to create?

Collapse
 
tmcmanamey profile image
Tim

Okay, so I found this. It's part of the djangorestframework

Collapse
 
tmcmanamey profile image
Tim

How do we use the Capture Order function? Do we need to make a request of Paypal then have it return to the capture order? When I have it redirect after payment the only thing in the url is the token and the payerId. Does the request.data.get('token') just get the token from the url? I get a "missing schema" on the request.data.get('url').

Collapse
 
tmcmanamey profile image
Tim

I have another question. I'm not really familiar with the rest-framework api. I get a page with the link to pay, see below, but how do I get that on my own html? Will it work if I just return to a page that I've created?

Image description

Collapse
 
tmcmanamey profile image
Tim

Is the viewset class from the djangorestframework? Do I need to start a new file with this?