Integrate QuickBooks Payments with Django Rest Framework: A Comprehensive Guide

Integrate QuickBooks Payments with Django Rest Framework: A Comprehensive Guide

Photo by rupixen.com on Unsplash

In this blog post, I will delve into the process of seamlessly integrating QuickBooks Payment within the Django Rest Framework. There is no concrete documentation available about QuickBooks Integration in online searches, so this article aims to provide you with all the essential insights necessary to initiate the integration of QuickBooks into your Django Application.

Step 1: Clone the GitHub Repository

The first step is to clone the GitHub repository provided below. This will provide you with starter files that will help you to start and set up the process.

GitHub Repository Link: https://github.com/karthiksbh/QuickBooks-Django-Start

git clone https://github.com/karthiksbh/QuickBooks-Django-Start

The GitHub Repository contains the predefined code of a Django Application "payment" which can be used to get started with the QuickBooks Payment Integration. Before starting with the process, make sure you install all the packages given in the requirements.txt file into your virtual environment.

The code in the repository contains a basic Django Application with some additional files which are explained as follows:

  1. client.py - This file defines a Python class, AuthClient, which handles OAuth 2.0 and OpenID Connect flows for QuickBooks Online integration, provides methods for token management and user information retrieval.

  2. config.py - This file defines static URLs and information for user-agent configuration used in the QuickBooks Online integration, including migration URLs, discovery URLs, and user-agent data.

  3. enums.py - This file defines an enumeration class called Scopes, which contains a list of scopes supported by Intuit for OAuth and OpenID flows, enabling access to specific user data and functionality in QuickBooks Online.

  4. exceptions.py - This file defines an AuthClientError class that serves as an exception object used in cases where the API response status code is not equal to 200, allowing for detailed error handling and reporting.

  5. migration.py - This file assists in migrating OAuth 1.0a tokens to OAuth 2.0, providing a migrate function that takes OAuth 1.0a credentials and migrates them to OAuth 2.0 tokens, as specified by Intuit's migration process.

  6. tokens.py - This file defines configuration settings and authentication tokens for integrating with QuickBooks Online, including client credentials, authentication assets, a refresh token, and access token information.

  7. utils.py - This file module provides utility functions for authentication and communication with QuickBooks Online APIs in a Django application.

These are all the files that are present in the GitHub repository other than the default files of the Django Application like models, views, etc.

Step 2: Creating a SandBox Account

After the previous step, the next step is to create a Sandbox Account. Visit the "Intuit Developer" website and sign in to create a new account. You can log in if you have one already.

After signing in to your account click dashboard and select โ€œCreate an app" to create a new application for your project.

Give a name to your application and then click on the OAuth 2.0 Playground to generate the API Credentials. Select the application (Sandbox or Production), and use the client ID and client secret to generate the Authorization Code, Realm ID, Access and Refresh Tokens.

Now, copy these credentials and add them respectively to the tokens.py file in the Django Application "app" that you have cloned. You have successfully created the credentials to use QuickBooks in your Django Application.

Step 3: Integrating it into the Rest Framework

Note: Assuming that you have already created a Django Project with the requirements and proceeding with integrating this "payment" app into the project.

The first step will be to add the "payment" app into the INSTALLED_APPS section in the settings.py file of the Django Project.

INSTALLED_APPS = [
    ...
    "rest_framework",
    "payment",
]

In your models.py file, add the "Transaction" model given below to store the details of the payment including the amount, transaction ID, order ID etc.,

Note: The model given below is just a basic model for understanding. You can modify the given model based on your requirements.

class Transaction(models.Model):
    trans_id=models.CharField(max_length=255,primary_key=True,default=uuid4)
    amount=models.FloatField()
    order_id=models.CharField(max_length=255,null=True,blank=True)
    payment_status=models.CharField(max_length=255,choices=PAYMENT_STATUS_CHOICES,default="pending")
    created_at=models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return str(self.trans_id)

In your urls.py file, add the routes given below,

urlpatterns = [
    path("create/", views.QuickbooksCreatePaymentView.as_view(), name='payment'),
    path("verify/", views.QuickbooksVerifyPaymentView.as_view(), name='verify'),
]

The first route will call the CreatePaymentView which will create the payment and the second route will call the VerifyPaymentView which will verify the given payment.

Add the below imports and the below functions that define a function for refreshing OAuth tokens using the AuthClient class, and is associated with a specific set of client secrets and tokens.

import requests
from rest_framework.views import APIView
from rest_framework.response import Response
from .client import AuthClient
from . import tokens as tokens
import uuid
from .models import Transaction

# Create your views here.
auth_client = AuthClient(**tokens.client_secrets)

def refresh_token():
    response = auth_client.refresh(refresh_token=tokens.refreshToken)
    return response

Now, the next step is to create an APIView for QuickbooksCreatePaymentView, which handles the creation of payments in QuickBooks Online using the OAuth 2.0 authentication tokens. The post method of this view processes incoming payment data, communicates with QuickBooks Online's payment API, and creates payment transactions, returning appropriate responses based on the success or failure of the operation.

class QuickbooksCreatePaymentView(APIView):
    def post(self, request):
        try:
            response = refresh_token()
            accessToken = response["access_token"]
            base_url = 'https://sandbox.api.intuit.com/quickbooks/v4/payments/charges/'
            auth_header = 'Bearer {0}'.format(accessToken)
            headers = {
                'Authorization': auth_header,
                'Request-Id': str(uuid.uuid4()),
                'Content-Type': 'application/json',
                'User-Agent': 'Mozilla/5.0',
                'Accept-Encoding': 'gzip, deflate, br'
            }
            data = request.data
            amount = data.get('amount')
            payment_method = data.get('payment_method')

            if payment_method == 'card':    
                payment_data = {
                    'amount': float(amount),
                    'currency': data.get('currency'),
                    'card': {
                        'name': data.get('card_name'),
                        'address':data.get('address'),
                        'expYear': data.get('exp_year'),
                        'expMonth': data.get('exp_month'),
                        'number': data.get('number'),
                        'cvc': data.get('cvc')
                    },
                    'context': {
                        'mobile': False,
                        'isEcommerce': True
                    }
                }
            elif payment_method == 'digital_wallet':
                payment_data = {
                    'amount': float(amount),
                    'currency': data.get('currency'),
                    'digitalWallet': {
                        'type': data.get('digital_wallet_type'),
                        'id': data.get('digital_wallet_id')
                    },
                    'context': {
                        'mobile': False,
                        'isEcommerce': True
                    }
                }
            elif payment_method == 'internet_banking':
                payment_data = {
                    'amount': float(amount),
                    'currency': data.get('currency'),
                    'bankTransfer': {
                        'account': data.get('bank_account_number'),
                        'routingNumber': data.get('bank_routing_number')
                    },
                    'context': {
                        'mobile': False,
                        'isEcommerce': True
                    }
                }
            else:
                return Response({'error': 'Invalid payment method'})

            response = requests.post(base_url, headers=headers, json=payment_data)
            if response.status_code == 201:
                response_data = response.json()
                charge_id = response_data.get('id')
                Transaction.objects.create(order_id=charge_id,amount=amount)
                return Response({'message':'Payment Success','charge_id': charge_id,'success':True})
            else:
                return Response({'error': 'Failed to create a payment','success':False})
        except Exception as e:
            return Response({'error': str(e)})

This view takes an input where the client sends a JSON payload containing the information required to create a payment. The payload includes details such as the payment amount, payment method, and related information, depending on the selected payment method. For example:

{
    "payment_method":"card",
    "amount":"10.55",
    "currency":"USD",
    "card_name":"karthik",
    "address": {
          "streetAddress": "123 Main St",
          "city": "Anytown",
          "region": "CA",
          "country": "US",
          "postalCode": "12345"
    },
    "exp_year": "2024",
    "exp_month": "07",
    "number": "4111111111111111",
    "cvc": "223"
}

This view returns an HTTP response to the client based on the outcome of the payment creation process:

  • Success Response (HTTP Status 201): If the payment creation is successful, the view returns a JSON response with a success message, the payment charge ID, and a success flag set to True. For example:

      {
          "message": "Payment Success",
          "charge_id": "some_charge_id",
          "success": true
      }
    
  • Failure Response (HTTP Status other than 201): If the payment creation fails, the view returns a JSON response with an error message and a success flag set to False. For example:

      {
          "error": "Failed to create a payment",
          "success": false
      }
    
  • Exception Response: In the event of an unexpected error or exception during the payment creation process, the view catches the exception and returns a response containing the error message. For example:

      {
          "error": "An error message",
      }
    

Now, the next step is to create another APIView to verify the payment that has been created in the previous step. In the previous step, we get the response which is a charge ID. We have to pass this charge ID to this APIView to verify if it is a valid payment or not. Based on the output, the status of the payment in the "Transaction" model is updated. The code for the APIView is as follows:

class QuickbooksVerifyPaymentView(APIView):
    def post(self, request):
        try:
            response = refresh_token()
            accessToken = response["access_token"]
            data = request.data
            charge_id = data.get('charge_id')
            headers = {'Authorization': f'Bearer {accessToken}'}
            response = requests.get(f'https://sandbox.api.intuit.com/quickbooks/v4/payments/charges/{charge_id}', headers=headers)

            if response.status_code == 200:
                response_data = response.json()
                status = response_data.get('status')
                transaction = Transaction.objects.get(order_id=charge_id)
                transaction.payment_status = 'success'
                transaction.save()
                return Response({'status': status,'message':'Payment Successfully verified','success':True})
            else:
                return Response({'error': 'Failed to verify payment','success':False})
        except Exception as e:
            return Response({'error': str(e)})

This view takes an input where the client sends a JSON payload. The payload includes the charge_id, which is used to identify the payment to be verified.For example:

{
    "charge_id":"E6N5XXXXXXXX"
}

This view returns an HTTP response to the client based on the outcome of the payment verification process:

  • Success Response (HTTP Status 200): If the payment verification is successful, the view returns a JSON response with the payment status, a success message, and a success flag set to True. For example:

      {
          "status": "payment_status",
          "message": "Payment Successfully verified",
          "success": true
      }
    
  • Failure Response (HTTP Status other than 200): If the payment verification fails, the view returns a JSON response with an error message and a success flag set to False. For example:

      {
          "error": "Failed to verify payment",
          "success": false
      }
    
  • Exception Response: In the event of an unexpected error or exception during the payment verification process, the view catches the exception and returns a response containing the error message. For example:

      {
          "error": "An error message",
      }
    

Therefore, these are all the steps on how to integrate QuickBooks into your Django Rest Framework Project. This is just the basic project which will help you understand on how to start with the integration. You can customize this based on your project requirement.

In conclusion, this comprehensive guide demonstrates the seamless integration of QuickBooks Payments with Django Rest Framework. By following the outlined steps and customizing the provided examples, you can enhance your project's payment processing capabilities, making it more efficient and user-friendly.

Check this GitHub Repository Link to access the entire code: https://github.com/karthiksbh/QuickBooks-Django

ย