The "Basics" of adding PayPal payments to Django, server and client-side
Tags: django, python, tutorial, webdev
Published: 2020-07-11 22:28:27

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](https://github.com/abderrahmaneMustapha/my-django-refrences/tree/master/django%20and%20paypal/basics/paypallpay/learn), because I want to focus on Paypal payments setup in this post

## installing the requirements
go to your command line and run 
```shell
pip install Django==3.0.7
pip install Pillow==7.1.2
pip install paypal-checkout-serversdk
pip install django-environ==0.4.5
```

## create a sandbox account
first of all, [click here](https://www.paypal.com/signin?intent=developer&returnUri=https%3A%2F%2Fdeveloper.paypal.com%2Fdeveloper%2Fapplications)  and create a sandbox account 
after creating this account successfully you will be redirected to your dashboard and click on  [My Apps & Cardntials](https://developer.paypal.com/developer/applications)

after this click on  default application or you can create a new one 
![sandbox default](//live.staticflickr.com/65535/50116990552_40d342e8b2_h.jpg)



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 ](//live.staticflickr.com/65535/50116990437_84f9fd10ae_h.jpg)



## update settings
now you can copy these two keys and past them, in line 26, and 27 in the settings.py file
```python
#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"
```

## 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
```python
#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 })
```

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

```html
{% 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 %}
```
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](https://developer.paypal.com/demo/checkout/#/pattern/server)
 you will see something like this
![button container ](//live.staticflickr.com/65535/50118769491_1f7fcb1986_h.jpg)

copy 
```html
<!-- Set up a container element for the button -->
    <div id="paypal-button-container"></div>
```
got to learn/tempaltes/pay.html and replace pay button with ""   <div id="paypal-button-container"></div>""

```html
<!-- 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>
```


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

```javascript
  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');
```
on the top of main.js add this lines of code 
```javascript
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');
```
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

```python 
#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")
]
```

we need to update our  capture and create views to match our urls, go to learn/views.py  
```python
#learn/views.py
#line 18
def capture(request,order_id,id):
......
```

#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

```python
from paypalcheckoutsdk.orders import OrdersCreateRequest
from paypalcheckoutsdk.orders import OrdersCaptureRequest
from paypalcheckoutsdk.core import SandboxEnvironment
```
in our views , lets go to the create view, and setup the sandbox environment
```python 
#learn/views.py
def create(request):
    environment = SandboxEnvironment(client_id=settings.PAYPAL_CLIENT_ID, client_secret=settings.PAYPAL_SECRET_ID)
    client = PayPalHttpClient(environment)
.......
```
its time to create an order add an item and get the response  from  the Paypal api

```python
 #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']
```
now we can return a json response to our client-side in the main js 
our create view will look like this now 
```python
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"})
```

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 

```html
#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>
```

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

```javascript
#learn/static/learn/js/main.js
const course_id = document.getElementById('course-id').value
```

# 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

```python
#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"})
```

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](//live.staticflickr.com/65535/50119392513_93d397dab2_k.jpg)



💀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](https://github.com/abderrahmaneMustapha/my-django-refrences/tree/final/django%20and%20paypal/basics/paypallpay)
you just need to replace
```python 
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
```

#for further explorations
- check the Paypal API [here](https://developer.paypal.com/docs/api/overview/)
- and the Paypal checkout repository it helped me a lot to understand how things work [here](https://github.com/paypal/Checkout-Python-SDK)