DEV Community

Cover image for The "Basics" of adding PayPal payments to Django, server and client-side
Abderrahmane Mustapha
Abderrahmane Mustapha

Posted on • Updated on

The "Basics" of adding PayPal payments to Django, server and client-side

in this article will show a basic way on how to implement the Paypal payments using the checkout API on your Django project, for this I will create an e-learning website where the user can purchase courses

first clone this codebase from GitHub click here, because I want to focus on Paypal payments setup in this post

installing the requirements

go to your command line and run

pip install Django==3.0.7
pip install Pillow==7.1.2
pip install paypal-checkout-serversdk
pip install django-environ==0.4.5
Enter fullscreen mode Exit fullscreen mode

create a sandbox account

first of all, click here and create a sandbox account
after creating this account successfully you will be redirected to your dashboard and click on My Apps & Cardntials

after this click on default application or you can create a new one
sandbox default

this will redirect you to this page where you will see a public key, the client key is public so you know anyone can see it

but don't let anyone see the private/secret key
here is the page of the client and secret key
client and secret key

update settings

now you can copy these two keys and past them, in line 26, and 27 in the settings.py file

#paypallpay/settings.py

#replace  this 
# 26 PAYPAL_CLIENT_ID  = env('PAYPAL_CLIENT_ID')
# 27 PAYPAL_SECRET_ID  =  env('PAYPAL_SECRET_ID')
#with this
PAYPAL_CLIENT_ID  = "client id here"
PAYPAL_SECRET_ID  =  "secret id here"
Enter fullscreen mode Exit fullscreen mode

get client id from server-side

now let's add Paypal js sdk to our pay.html template, but the first thing that we need to do is to add a client_add variable to our pay in the views.py and assign the Paypal client id to it

#learn/views.py

def pay(request, pk):
    #line 10
    client_id = settings.PAYPAL_CLIENT_ID
    course = get_object_or_404(Course, pk=pk)
    return render(request, 'pay.html', {'course':course, 'client_id':client_id })
Enter fullscreen mode Exit fullscreen mode

go to the learn/templates/pay.html, and scroll to the bottom and replace SB_CLIENT_ID with

{{client_id}}

, here is how you do it

{% block extrascript %}
#line 23
    <script src="https://www.paypal.com/sdk/js?client-id={{client_id}}&disable-funding=credit,card" > </script> 
    <script src="{% static  'learn/js/main.js' %}" ></script>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

I added "&disable-funding=credit,card" because I just wan work with Paypal button , so I need to disable credit card button

client side setup

go to paypal server side smart buttons
you will see something like this
button container

copy

<!-- Set up a container element for the button -->
    <div id="paypal-button-container"></div>
Enter fullscreen mode Exit fullscreen mode

got to learn/tempaltes/pay.html and replace pay button with ""

""
<!-- delete this  -->
 <a href="{% url 'pay' course.pk %}" class="btn btn-warning 
font-weight-bold btn-lg col-12">Pay</a>

<!-- and replace it with this -->
    <div id="paypal-button-container"></div>
Enter fullscreen mode Exit fullscreen mode

now go to the main.js file write or copy/paste ✨ this code

  paypal.Buttons({

            // Call your server to set up the transaction
            createOrder: function(data, actions) {
                return fetch('/paypal/create/'+course_id+'/', {
                    method: 'post',
                    headers: {"X-CSRFToken": csrftoken}
                }).then(function(res) {
                    return res.json();
                }).then(function(orderData) {
                    return orderData.id;
                });
            },

            // Call your server to finalize the transaction
            onApprove: function(data, actions) {
                return fetch('/paypal/' + data.orderID + '/capture/'+course_id+'/', {
                    method: 'post',
                    headers: {"X-CSRFToken": csrftoken}
                }).then(function(res) {
                    return res.json();
                }).then(function(orderData) {
                    // Three cases to handle:
                    //   (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
                    //   (2) Other non-recoverable errors -> Show a failure message
                    //   (3) Successful transaction -> Show a success / thank you message

                    // Your server defines the structure of 'orderData', which may differ
                    var errorDetail = Array.isArray(orderData.details) && orderData.details[0];

                    if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
                        // Recoverable state, see: "Handle Funding Failures"
                        // https://developer.paypal.com/docs/checkout/integration-features/funding-failure/
                        return actions.restart();
                    }

                    if (errorDetail) {
                        var msg = 'Sorry, your transaction could not be processed.';
                        if (errorDetail.description) msg += '\n\n' + errorDetail.description;
                        if (orderData.debug_id) msg += ' (' + orderData.debug_id + ')';
                        // Show a failure message
                        return alert(msg);
                    }

                    // Show a success message to the buyer
                    alert('Transaction completed by ' + orderData.payer.name.given_name);
                });
            }


        }).render('#paypal-button-container');
Enter fullscreen mode Exit fullscreen mode

on the top of main.js add this lines of code

function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
const csrftoken = getCookie('csrftoken');
Enter fullscreen mode Exit fullscreen mode

why we add this because csrftokens is important to secure our requests against csrf attacks

but before we execute this we need to edit the learn/urls.py, here is how you do it replace the old URLs patterns with this one

#learn/urls.py
urlpatterns = [
    path('', index, name="index"),
    path('paypal/pay/<pk>/', pay, name="pay"),
    path('paypal/create/<id>/', create, name="paypal-create"),
    path('paypal/<order_id>/capture/<id>/', capture, name="paypal-capture"), #changed #
     path('paypal/client-id/', getClientId , name="client-id")
]
Enter fullscreen mode Exit fullscreen mode

we need to update our capture and create views to match our urls, go to learn/views.py

#learn/views.py
#line 18
def capture(request,order_id,id):
......
Enter fullscreen mode Exit fullscreen mode

server side create an order

we start by importing create and capture request from python Paypal checkout SDK, add these lines on the top of learn/views.py

from paypalcheckoutsdk.orders import OrdersCreateRequest
from paypalcheckoutsdk.orders import OrdersCaptureRequest
from paypalcheckoutsdk.core import SandboxEnvironment
Enter fullscreen mode Exit fullscreen mode

in our views , lets go to the create view, and setup the sandbox environment

#learn/views.py
def create(request):
    environment = SandboxEnvironment(client_id=settings.PAYPAL_CLIENT_ID, client_secret=settings.PAYPAL_SECRET_ID)
    client = PayPalHttpClient(environment)
.......
Enter fullscreen mode Exit fullscreen mode

its time to create an order add an item and get the response from the Paypal api

 #order            
    create_order.request_body (
        {
            "intent": "CAPTURE",
            "purchase_units": [
                {
                    "amount": {
                        "currency_code": "USD",
                        "value": course.price,
                        "breakdown": {
                            "item_total": {
                                "currency_code": "USD",
                                "value": course.price
                            }
                            },
                        },                               


                }
            ]
        }
    )


#get the response
    response = client.execute(create_order)

#get the data dictionary  from the response  
    data = response.result.__dict__['_dict']
Enter fullscreen mode Exit fullscreen mode

now we can return a json response to our client-side in the main js
our create view will look like this now

def create(request,id):
    if request.method =="POST":
        environment = SandboxEnvironment(client_id=settings.PAYPAL_CLIENT_ID, client_secret=settings.PAYPAL_SECRET_ID)
        client = PayPalHttpClient(environment)

        course = Course.objects.get(pk=id)
        create_order = OrdersCreateRequest()

        #order            
        create_order.request_body (
            {
                "intent": "CAPTURE",
                "purchase_units": [
                    {
                        "amount": {
                            "currency_code": "USD",
                            "value": course.price,
                            "breakdown": {
                                "item_total": {
                                    "currency_code": "USD",
                                    "value": course.price
                                }
                                },
                            },                               


                    }
                ]
            }
        )

        response = client.execute(create_order)
        data = response.result.__dict__['_dict']
        return JsonResponse(data)
    else:
        return JsonResponse({'details': "invalide request"})
Enter fullscreen mode Exit fullscreen mode

before we can run this we need to add a course id so go to the pay.html and create a hidden input and add course id to it

#learn/templates/pay.html
<div class="card-footer bg-white"> 
   <input id="course-id"  value={{course.id}} hidden/>         
   <div id="paypal-button-container"></div>
</div>
Enter fullscreen mode Exit fullscreen mode

and in the top of main.js let's add a client_id const , which contain the value of the course id

#learn/static/learn/js/main.js
const course_id = document.getElementById('course-id').value
Enter fullscreen mode Exit fullscreen mode

Server-side capture the order id and finish the payment

it's simple we just need to send a request containing the order id to the paypal API to check if this order exists and then we get a response and send it to the client-side when everything is working fine the javascript code will send an alert to our end-user that the payment completed successfully, our capture view will look like this now

#learn/views
def capture(request,order_id,id):
    if request.method =="POST":
        capture_order = OrdersCaptureRequest(order_id)
        environment = SandboxEnvironment(client_id=settings.PAYPAL_CLIENT_ID, client_secret=settings.PAYPAL_SECRET_ID)
        client = PayPalHttpClient(environment)

        response = client.execute(capture_order)
        data = response.result.__dict__['_dict']

        return JsonResponse(data)
    else:
        return JsonResponse({'details': "invalide request"})
Enter fullscreen mode Exit fullscreen mode

everything is ready now go to you sandbox dashboard and go to accounts u will see two email addresses the first one is personal and the other one is for business, personal for payer and business email for the receiver
sandbox account emails

💀Note: this two email addresses and keys is for testing if you want real keys and emails you need to upgrade your account upgrade

if you want to run this app in your localhost check this GitHub repository click here
you just need to replace

SECRET_KEY = 'SECRET_KEY' #your secret key here
PAYPAL_CLIENT_ID = 'PAYPAL_CLIENT_ID' #paypal client id here
PAYPAL_SECRET_ID = 'PAYPAL_SECRET_ID' #paypal secret id here
Enter fullscreen mode Exit fullscreen mode

for further explorations

  • check the Paypal API here
  • and the Paypal checkout repository it helped me a lot to understand how things work here

Top comments (9)

Collapse
 
antonjohn profile image
AntonJohn

It is helpful ☺️
Thanks 👍

Collapse
 
abderahmane profile image
Abderrahmane Mustapha • Edited

thats great :) !!!!

Collapse
 
kutaiba_momani profile image
Kutaiba Momani

thanks 💜 .
i want ask please , when user buy a course how to save course to his profile for life time .. so he can access and watch the course how to do this?

Collapse
 
abderahmane profile image
Abderrahmane Mustapha

can you contact me on my email so I can show you how to do it
abdmusttoumi@gmail.com

Collapse
 
kutaiba_momani profile image
Kutaiba Momani

Thank you, I have sent a message

Collapse
 
kutaiba_momani profile image
Kutaiba Momani

done 💜

Collapse
 
jiraiyasennin profile image
Dostow**->

Amazing!! It really helped me!! I've done a tutorial of almost 2 hours in youtube and in the end it didnt' work xD.
I feel lucky to find your article! ᕙ(`▿´)ᕗ
Cheers

Collapse
 
shivamrohilla profile image
Shivam Rohilla

can you write a blog about paypal subscription

Collapse
 
jiraiyasennin profile image
Dostow**->

I have a question, using this server side code means a user can not forge the payment ammount?
Thanks again!