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.
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')
]
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']
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())
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"
}
]
}
Pay attention to the link below:
{
“href”: “https://www.paypal.com/checkoutnowtoken=5O190127TN364715T",
“rel”: “approve”,
“method”: “GET”
},
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)
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'
Thanks for your help.
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.
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.
Great!
what does "requests.post" refer to?
requests.post is a function in the Python requests library that sends an HTTP POST request to the specified URL
What is the APIView you refer to? Is that something we need to create?
Okay, so I found this. It's part of the djangorestframework
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').
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?
Is the viewset class from the djangorestframework? Do I need to start a new file with this?