Skip to content

Instantly share code, notes, and snippets.

@maddrum
Last active September 25, 2022 19:29
Show Gist options
  • Save maddrum/3c52adfc929969d9fc18b6bfc9584916 to your computer and use it in GitHub Desktop.
Save maddrum/3c52adfc929969d9fc18b6bfc9584916 to your computer and use it in GitHub Desktop.
Connect PayPal v2 checkout with Django and handle payment statuses

This GIST will help you implement PayPal v2 API with your Django app and handle payment statuses. I will use: Class-Based FormView and Django template system. To proceed you have to obtain valid PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET /have PayPal business account - production or sandbox/

Steps to complete:

  1. Get PayPal SDK - pip install paypal-checkout-serversdk

  2. Create a FormView with the payment data - amount, currency, description and cliend_id

  3. Show template to the user with a dummy hidden payment form. Eventually, PayPal connect script will "hijack" that template, will process the payment and return the order_id. We will capture that order_id and submit it back to Django.

  4. Handle PayPal order_id payment status. On success do stuff on your webapp - subscribe, confirm the order, send payment success mail, etc

  5. Notify user about successful payment.

NOTE: Valuable information like PAYPAL_CLIENT and PAYPAL_CLIENT_SECRET should be put in your settings file like any other valuable information. Also, check official https://github.com/paypal/Checkout-Python-SDK

from django import forms
class PayPalPaymentsForm(forms.Form):
""" A dummy hidden form for PayPal Checkout with just order_id field """
order_id = forms.CharField(max_length=255)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# assign HiddenInput widget
self.fields['order_id'].widget = forms.HiddenInput()
<!DOCTYPE html>
<html>
<head>
<script src="https://www.paypal.com/sdk/js?client-id={{ client_id }}&currency={{ currency }}"
xmlns="http://www.w3.org/1999/html"></script>
</head>
<body>
<!-- Some payment summary for the client -->
<h1>PayPal Checkout</h1>
<h4>Amount requested: {{ amount }} {{ currency }}</h4>
<h4>Payment description: {{ description }}</h4>
<!-- Dummy hidden form with only order_id input. It will be populated by PayPal connector/hijack/ script and send to Django backend for checks and payment handling -->
<form method="post" id='dummy_payment_form'>
{% csrf_token %}
{{ form }}
</form>
<br>
<!-- PayPal script will put Smart Payment Buttons in this div -->
<div id="paypal-button" class="text-center"></div>
<!-- PayPal connect script -->
<script>
let paymentID = $('#id_order_id')[0];
let paymentForm = $('#dummy_payment_form')[0];
paypal.Buttons({
createOrder: function (data, actions) {
// This function sets up the details of the transaction, including the amount and line item details.
return actions.order.create({
intent: "CAPTURE",
purchase_units: [{
amount: {
value: '{{ amount }}',
currency_code: '{{ currency }}',
},
description: '{{ description }}',
}],
});
},
onApprove: function (data, actions) {
// This function captures the funds from the transaction.
return actions.order.capture().then(function (details) {
// This function is called after successful payment.
// First, hide the paypay buttons
$('#paypal-button').addClass('hidden');
// then populate dummy form order_id
paymentID.value = data.orderID;
//and finally - submit the dummy form to the backend
paymentForm.submit();
});
}
}).render('#paypal-button');
//This function displays Smart Payment Buttons on your web page.
</script>
</body>
</html>
from paypalcheckoutsdk.core import LiveEnvironment, SandboxEnvironment, PayPalHttpClient
from paypalcheckoutsdk.orders import OrdersGetRequest
from django.views.generic import FormView
from .forms import PayPalPaymentsForm
from .paypal_connect import payment_execute_result
class PayPalCheckOutView(FormView):
"""Handle a PayPal payments"""
form_class = forms.PayPalPaymentsForm
template_name = 'accounts/paypal_payment.html'
success_url = '/payment-success-page/'
# add your client id here
PAYPAL_CLIENT_ID = '123456'
# add your client secret id here
PAYPAL_CLIENT_SECRET = '123456'
# Production or Sandbox PayPal environment
PAYPAL_SERVER = 'sandbox' # or production
def check_payment_status(order_id=None):
""" Checks payment status and return True if it paid or False if it not """
# handle PayPal environment - production or sandbox
if self.PAYPAL_SERVER == 'sandbox':
environment_case = SandboxEnvironment
elif self.PAYPAL_SERVER == 'production':
environment_case = LiveEnvironment
else:
return False
# connect to PayPal REST API and get the status of this order_id
environment = environment_case(client_id=self.PAYPAL_CLIENT_ID, client_secret=self.PAYPAL_CLIENT_SECRET)
client = PayPalHttpClient(environment)
request = OrdersGetRequest(order_id)
# form the response. You could put this in try block and handle IOError or other exceptions
response = client.execute(request)
#get the status of this payment
result = response.result['status'].lower()
#return True if it is completed
return result == 'completed'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# client ID for PayPal connector script
context['client_id'] = self.PAYPAL_CLIENT_ID
#prequested amount
context['amount'] = 99.98
# requested currency
context['currency'] = 'EUR'
# payment description
context['description'] = 'Some payment description to the user'
return context
def form_valid(self, form):
order_id = form.cleaned_data['order_id']
result_status = self.check_payment_status(order_id=order_id)
if result_status:
# Do stuff on success payment
print('We are rich!')
return super().form_valid(form)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment