Paytm Payment Gateway Integration in Django 
Tags: django, paytm, integration, payment
Published: 2020-02-02 15:27:06

# Paytm Payment Gateway Integration in Django 

**Synopsis**: This is a great way to learn how to integrate payments in your Django application. The official docs from Paytm are [here](https://developer.paytm.com/docs/v1/payment-gateway). ​

> Note: You would require a Paytm Merchant Account to be able to use the Payment Gateway

The process of sending the request to the gateway is mentioned in the following document:

![](https://developer.paytm.com/assets/img-flow-paytm-checkout.png)

<p style="text-align: center;">Image Source: <a href="https://developer.paytm.com/docs/v1/payment-gateway">Paytm</a> </p>

We will create an Django App from scratch to achieve our goal.

## Step 0: Setting up the Project

First we'll create a virtual environment for out project.$ 

```bash
# Install Django & virtualenv if not already
$ pip install Django virtualenv
# create a new Django project
$ django-admin startproject pay2me
$ cd pay2me
# Initialize a new virtualenv with Py3 since Python 2 is dead
$ virtualenv env --python=python3
# Activate the virtual environment and intall Django
$ source ./env/bin/activate
(env)$ pip install Django
```

You can use the command `$ source ./env/bin/activate` to activate the virtual environment.

We would use the default Django User models for the authentication system to save time but you can use custom User model if you wish.

Now let's add login view to our project. open `pay2me/urls.py` and add entry to login/logout views.

```python
from django.contrib.auth.views import LoginView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', LoginView.as_view(), name='login'),
]
```

Now to make login and logout pages we first have to migrate our changes and create a superuser. Run the following commands in the terminal.  

```bash
(env)$ python manage.py makemigrations
(env)$ python manage.py migrate
(env)$ python manage.py createsuperuser
```

Now we can run the server by executing `python manage.py runserver` and visit [http://localhost:8000/login](http://localhost:8000/login) to see the login page. 

The page will not load since the login page template is missing so we will create a template for `LoginView`. Create a directory `templates` in the project's root directory and another directory `registration` in `templates`. Now add a file `login.html ` in the `registration` directory and add the following to it.

```html
<!DOCTYPE html>
<head>
    <title>Login</title>
</head>
<body>
    <h2>Login</h2>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Login</button>
    </form>
</body>
</html>
```

The login page contains contain a ***form*** and a ***csrf_token*** which will be provided by the `LoginView` so we don't have to worry about it.

We would also have to modify `pay2me/settings.py`. Open the file, Navigate to the `TEMPLATES` setting and add `'templates'` in the `DIRS` list, it should look like this:

```python
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
```

This would help Django locate our `registration/login.html` template.

Since the template is all set, now restart the server and go to [http://localhost:8000/login](http://localhost:8000/login) and the login form should look like this:

![Login Page](https://dev-to-uploads.s3.amazonaws.com/i/86klukfeilihjtillay7.png)

Let's continue to the next step.

## Step 1: Making the UI

Since we are keeping this concise we will only have two pages: 

* **Payment page** :  Where we will enter the amount we have to pay
* **Callback Page** : The response received from Paytm carrying payment status  

Lets start by making a new app to organize these features.

```bash
(env)$ python manage.py startapp payments
```

First of all let's add the ***payments*** app to the project. To do that we will add `'payments'` in the `INSTALLED_APPS` list in `pay2me/settings.py` .

```py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'payments',
]
```

Now lets add the template files `pay.html` and `callback.html` in `templates/payments` in the ***payments*** app directory.

##### pay.html

```html
<!DOCTYPE html>

<head>
    <title>Payment Home</title>
</head>

<body>
    <h2>Payment Home</h2>
    {% if error %}
    <h3>{{ error }}</h3>
    {% endif %}
    <form method="post">
        {% csrf_token %}
        <label for="username">Username: </label>
        <input type="text" name="username" required>
        <label for="password">Password: </label>
        <input type="text" name="password" required>
        <label for="amount">Amount: </label>
        <input type="number" name="amount" value="100">
        <input type="submit" name="submit" value="Submit" required>
    </form>
</body>

</html>

```

##### callback.html

```html
<!DOCTYPE html>
<head>
    <title>Callback</title>
</head>
<body>
    <h2>Callback Messsage: </h2>                    <br>
    <h3> Checksum Verification: {{ message }} </h3> <br>
    MID: {{ MID }}                                  <br>
    TXNID: {{ TXNID }}                              <br>
    ORDERID: {{ ORDERID }}                          <br>
    BANKTXNID: {{ BANKTXNID }}                      <br>
    TXNAMOUNT: {{ TXNAMOUNT }}                      <br>
    CURRENCY: {{ CURRENCY }}                        <br>
    <h3> STATUS: {{ STATUS }} </h3>                 <br>
    RESPCODE: {{ RESPCODE }}                        <br>
    RESPMSG: {{ RESPMSG }}                          <br>
    TXNDATE: {{ TXNDATE }}                          <br>
    GATEWAYNAME: {{ GATEWAYNAME }}                  <br>
    BANKNAME: {{ BANKNAME }}                        <br>
    BIN_NAME: {{ BIN_NAME }}                        <br>
    PAYMENTMODE: {{ PAYMENTMODE }}                  <br>
    CHECKSUMHASH: {{ CHECKSUMHASH }}
</body>
</html>

```

The ***Payment page*** asks for user login information and payment amount, whereas ***Callback page*** shows the values of a lot of parameters which would be provided by Paytm upon completion of payment.

## Step 2: Make Transaction model

Since any payment related application would require transactions we would create a Transaction model in `payments/models.py` as follows:

```python
from django.db import models
from django.contrib.auth import get_user_model

User = get_user_model()

class Transaction(models.Model):
    made_by = models.ForeignKey(User, related_name='transactions', 
                                on_delete=models.CASCADE)
    made_on = models.DateTimeField(auto_now_add=True)
    amount = models.IntegerField()
    order_id = models.CharField(unique=True, max_length=100, null=True, blank=True)
    checksum = models.CharField(max_length=100, null=True, blank=True)

    def save(self, *args, **kwargs):
        if self.order_id is None and self.made_on and self.id:
            self.order_id = self.made_on.strftime('PAY2ME%Y%m%dODR') + str(self.id)
        return super().save(*args, **kwargs)

```

The transaction model is defined in such a way that the **order_id** is unique and generated based on date of transaction. We can see there is a **checksum** which will store the checksum generated by python file.

We overrode the `save` method to automatically generate `order_id` from the date and time of the transaction.

Let's run the migrations again to add the Transaction model in the database.

```bash
(env)$ python manage.py makemigration
(env)$ python manage.py migrate
```

## Step 3: Adding Paytm Settings

Add the following settings to `pay2me/settings.py` :

```python
PAYTM_MERCHANT_ID = '<your_merchant_id>'
PAYTM_SECRET_KEY = '<your_paytm_secret_key>'
PAYTM_WEBSITE = 'WEBSTAGING'
PAYTM_CHANNEL_ID = 'WEB'
PAYTM_INDUSTRY_TYPE_ID = 'Retail'
```

## Step 4: Create Views for Payments

Paytm has provided a repository for the checksum generation code [here](https://github.com/Paytm-Payments/Paytm_Web_Sample_Kit_Python) but the code is using a deprecated  library `pycrypto`, the following code contains the modified file compatible with the latest `pycryptodome` library. So [download the file](https://gist.github.com/masterashu/f90b8ba6d4324d0a7d65ab0f2d8306c0) and save it in your `payments` app with file name `paytm.py`.

Now lets begin the create the `initiate_payment` view that would receive the username, password and amount. Open `payments/views.py` and add the following code.

```python
from django.shortcuts import render
from django.contrib.auth import authenticate, login as auth_login
from django.conf import settings
from .models import Transaction
from .paytm import generate_checksum, verify_checksum


def initiate_payment(request):
    if request.method == "GET":
        return render(request, 'payments/pay.html')
    try:
        username = request.POST['username']
        password = request.POST['password']
        amount = int(request.POST['amount'])
        user = authenticate(request, username=username, password=password)
        if user is None:
            raise ValueError
        auth_login(request=request, user=user)
    except:
        return render(request, 'payments/pay.html', context={'error': 'Wrong Accound Details or amount'})

    transaction = Transaction.objects.create(made_by=user, amount=amount)
    transaction.save()
    merchant_key = settings.PAYTM_SECRET_KEY

    params = (
        ('MID', settings.PAYTM_MERCHANT_ID),
        ('ORDER_ID', str(transaction.order_id)),
        ('CUST_ID', str(transaction.made_by.email)),
        ('TXN_AMOUNT', str(transaction.amount)),
        ('CHANNEL_ID', settings.PAYTM_CHANNEL_ID),
        ('WEBSITE', settings.PAYTM_WEBSITE),
        # ('EMAIL', request.user.email),
        # ('MOBILE_N0', '9911223388'),
        ('INDUSTRY_TYPE_ID', settings.PAYTM_INDUSTRY_TYPE_ID),
        ('CALLBACK_URL', 'http://127.0.0.1:8000/callback/'),
        # ('PAYMENT_MODE_ONLY', 'NO'),
    )

    paytm_params = dict(params)
    checksum = generate_checksum(paytm_params, merchant_key)
    
    transaction.checksum = checksum
    transaction.save()

    paytm_params['CHECKSUMHASH'] = checksum
    print('SENT: ', checksum)
    return render(request, 'payments/redirect.html', context=paytm_params)

```

Lets understand the view part by part:

```python
def initiate_payment(request):
    if request.method == "GET":
        return render(request, 'payments/pay.html')
```

The first step is to check the method of the request. When the request method is **GET** then we will just return the payment page.

```python
try:
        username = request.POST['username']
        password = request.POST['password']
        amount = int(request.POST['amount'])
        user = authenticate(request, username=username, password=password)
        if user is None:
            raise ValueError
        auth_login(request=request, 	user=user)
    except:
        return render(request, 'payments/pay.html', context={'error': 'Wrong Account Details or amount'})
```

Then we verify the username and password entered by the user in the form and tries to log in the user. If the login details are invalid then the view returns back the login page with an error message.

```python
    transaction = Transaction.objects.create(made_by=user, amount=amount)
    transaction.save()
    merchant_key = settings.PAYTM_SECRET_KEY
```

Next we create a Transaction object for our user and get our merchant key from `settings.py`.

```python
    params = (
        ('MID', settings.PAYTM_MERCHANT_ID),
        ('ORDER_ID', str(transaction.order_id)),
        ('CUST_ID', str(transaction.made_by.email)),
        ('TXN_AMOUNT', str(transaction.amount)),
        ('CHANNEL_ID', settings.PAYTM_CHANNEL_ID),
        ('WEBSITE', settings.PAYTM_WEBSITE),
        # ('EMAIL', request.user.email),
        # ('MOBILE_N0', '9911223388'),
        ('INDUSTRY_TYPE_ID', settings.PAYTM_INDUSTRY_TYPE_ID),
        ('CALLBACK_URL', 'http://127.0.0.1:8000/callback/'),
        # ('PAYMENT_MODE_ONLY', 'NO'),
    )
    paytm_params = dict(params)

```

Then we create a dictionary for all the settings for the **Paytm Checksum Generator** to create a checksum.

```python
    checksum = generate_checksum(paytm_params, merchant_key)
    
    transaction.checksum = checksum
    transaction.save()

    paytm_params['CHECKSUMHASH'] = checksum

```

Next after generating the checksum we add the checksum to the `paytm_params` dictionary as well as the `Transaction` object.

```python
    return render(request, 'payment/redirect.html', context=paytm_params)

```

At the end we return the `redirect` page. 

## Step 5: Create a Redirect Page
Create a redirect page in `payments/templates/payments/redirect.html`:
```html
<html>
    <head>
        <title>Merchant Check Out Page</title>
    </head>
    <body>
        <h1>Please do not refresh this page...</h1>
        <form method="post" action="https://securegw-stage.paytm.in/order/process/" name="f1">
            <table>
                <tbody>
                    <input type="hidden" name="MID" value="{{ MID }}">
                    <input type="hidden" name="WEBSITE" value="{{ WEBSITE }}">
                    <input type="hidden" name="ORDER_ID" value="{{ ORDER_ID }}">
                    <input type="hidden" name="CUST_ID" value="{{ CUST_ID }}">
                    <input type="hidden" name="INDUSTRY_TYPE_ID" value="{{ INDUSTRY_TYPE_ID }}">
                    <input type="hidden" name="CHANNEL_ID" value="{{ CHANNEL_ID }}">
                    <input type="hidden" name="TXN_AMOUNT" value="{{ TXN_AMOUNT }}">
                    <input type="hidden" name="CALLBACK_URL" value="{{ CALLBACK_URL }}">
                    <input type="hidden" name="CHECKSUMHASH" value="{{ CHECKSUMHASH }}">
                </tbody>
            </table>
        <script type="text/javascript">
            document.f1.submit();
        </script>
        </form>
    </body>
</html>

```

The redirect page contains a simple form that contains the fields from the dictionary. We used the javascript code to automatically post the form to the paytm gateway.

## Step 6: Create the Callback View

The callback view is the view which would be called when paytm will respond with the status of the Transaction. 

Add the following callback view in the `payments/views.py`

```python
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def callback(request):
    if request.method == 'POST':
        received_data = dict(request.POST)
        paytm_params = {}
        paytm_checksum = received_data['CHECKSUMHASH'][0]
        for key, value in received_data.items():
            if key == 'CHECKSUMHASH':
                paytm_checksum = value[0]
            else:
                paytm_params[key] = str(value[0])
        # Verify checksum
        is_valid_checksum = verify_checksum(paytm_params, settings.PAYTM_SECRET_KEY, str(paytm_checksum))
        if is_valid_checksum:
            received_data['message'] = "Checksum Matched"
        else:
            received_data['message'] = "Checksum Mismatched"
    		return render(request, 'payments/callback.html', context=received_data)
        return render(request, 'payments/callback.html', context=received_data)


```

The callback view receives a post request from the paytm server. Then we retrieve the received data in a dictionary and and verify the checksum sent from paytm as to prevent forged requests. We then return the page with the details of the transaction and the message.

## Step 7: Creating the routes.

As the final step, Let's make the *URLs* for the views and get this long tutorial over. Create/open `urls.py` in the payments app. Add the following routes. 

```python
from django.urls import path
from .views import initiate_payment, callback

urlpatterns = [
    path('pay/', initiate_payment, name='pay'),
    path('callback/', callback, name='callback'),
]


```

Now let's include these *URLs* in the `pay2me/urls.py`

```python
from django.urls import include
urlpatterns = [
    # include 
    path('', include('payments.urls'))
]

```

## Test Run: Try out a Payment

Now let's run `python manage.py runserver` and go to http://localhost:8000/pay/ to create a payment.

Fill the form.

![Payment page](https://dev-to-uploads.s3.amazonaws.com/i/dcedz5uy98kd73wyhi86.png)

This will redirect to the paytm payment gateway. Use any dummy payment methods.

![Paytm Payment Gateway](https://dev-to-uploads.s3.amazonaws.com/i/6w8bhumazlah2d65j1ry.png)

You can also choose the status (Success/Failure) of the Transaction

![Bank Demo Page](https://dev-to-uploads.s3.amazonaws.com/i/ijfb19o7mxhyidkkr4vd.png)

Then you will finally land on the callback page which will show you the status of the transaction.

![Callback page](https://dev-to-uploads.s3.amazonaws.com/i/l06mnurwuyhhtwj42e4f.png)

### Github: Sample

[Here](https://github.com/masterashu/paytm_django_sample) is the link to the above sample code on Github.