Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forbidden (403) error when calling the callback URL in django

I am working on a django webapp. I connected the paytm payment gateway with the django app. I did everything according to the docs, and everything works. almost.

I am having a problem when calling the callback URL once the payment is over.

Here is the code

views.py

def donate(request):
    if request.method == "POST":
        form = DonateForm(request.POST)

        name = request.POST.get('firstName')
        phone = request.POST.get('phone')
        email = request.POST.get('email')
        amount = float("{0:.2f}".format(int(request.POST.get('amount'))))
        ord_id = OrdID()
        cust_id = CustID()

        paytm_params = {
            "MID" : MERCHANTID,
            "WEBSITE" : "WEBSTAGING",
            "INDUSTRY_TYPE_ID" : "Retail",
            "CHANNEL_ID" : "WEB",
            "ORDER_ID" : ord_id,
            "CUST_ID" : cust_id,
            "MOBILE_NO" : phone,
            "EMAIL" : email,
            "TXN_AMOUNT" : str(amount),
            "CALLBACK_URL" : "http://127.0.0.1:8000/payment/status",

            }

        paytm_params['CHECKSUMHASH'] = Checksum.generate_checksum(paytm_params, MERCHANTKEY)

        return render(request, 'paytm.html', {'paytm_params': paytm_params})

    else:
        form = DonateForm()
        context = {'Donate': form}
        return render(request, 'donate.html', context=context)

@csrf_exempt
def handlerequest(request):
    if request.method == 'POST':
        form = request.POST
        response_dict = {}

        for i in form.keys():
            response_dict[i] = form[i]

            if i == 'CHECKSUMHASH':
                checksum = form[i]
                print(checksum)

        verify = Checksum.verify_checksum(response_dict, MERCHANTKEY, checksum)

        if verify:
            if response_dict['RESPCODE'] == '01':
                print('order successful')
            else:
                print('error: ' + response_dict['RESPMSG'])

        return render(request, 'paymentstatus.html', {'response': response_dict})

urls.py

path('donate', views.donate, name='donate'),
path('payment/status', views.handlerequest, name='handlerequest'),

donate.html

<form class="test_paytm" action="{% url 'donate' %}" method="post">
    {% csrf_token %}
    <div class="row">
        <div class="col">
            {{ Donate.firstName|as_crispy_field }}
        </div>
        <div class="col">
            {{ Donate.lastName|as_crispy_field }}
        </div>
    </div>
    <div class="row">
        <div class="col">
            {{ Donate.email|as_crispy_field }}
        </div>
        <div class="col">
            {{ Donate.phone|as_crispy_field }}
        </div>
    </div>
    <div class="row">
        <div class="col">
            {{ Donate.amount|as_crispy_field }}
        </div>
    </div>
    <button type="submit" name="button" class="btn btn-lg mb-5 contact_submit">Donate</button>
</form>

paytm.html

<html>

<head>
  <title>Merchant Check Out Page</title>
</head>

<body>
  <center>
    <h1>Please do not refresh this page...</h1>
  </center>
  <form method="post" action="https://securegw.paytm.in/order/process" name="paytm">
    {% for key, value in paytm_params.items %}
    <input type="hidden" name="{{key}}" value="{{value}}">
    {% endfor %}
  </form>
</body>
<script type="text/javascript">
  document.paytm.submit()

</script>

</html>

paymentstatus.html

<div class="container">
  {% if response_dict.RESPCODE == 01 %}
  <center>
    <h2>Thank you for your donation</h2>
    <p>
      We are thrilled to have your support. Through your donation we will be able to accomplish our goal. You truly make the difference for us, and we are
      extremely grateful!
    </p>
  </center>

  <h3>Order ID: {{response_dict.ORDERID}}</h3>
  <h3>Order Date: {{response_dict.TXNDATE}}</h3>
  <h3>Amount: {{response_dict.TXNAMOUNT}}</h3>
  <h3>Payment Mode: {{response_dict.PAYMENTMODE}}</h3>

  {% else %}
  <center>
    <p>
      There seems to be a problem. We will try to fix this from our end.
    </p>
  </center>
  {% endif %}
</div>

But once the payment is over, The website is not calling handlerequest from views.py correctly. That is why I had added the @csrf_exempt so that an outside page can call the url without any issues. But I am still getting the 403 error. I am not sure what I am doing wrong

EDIT1

I have added the paytm.html code to the question. I personally dont feel that the problem is with this page, as all that the page does is redirect to the payment gateway page of paytm. The problem I am facing is when returning back to my url ie. paymentstatus.html. That is through the handlerequest view. The donation process is as follows

  1. user fills out form in donate.html and clicks the donate button.
  2. paytm.html gets the information and automatically routes to paytm payment gateway
  3. User makes the donation.
  4. The URL routes back from the paytm payment gateway back to my URL.
  5. The paymentstatus.html page is displayed.

As the paymentstatus.html page is being called from an external url, csrf_exempt is required, which I have provided. But for some reason that does not work

[EDIT 2]

When I spoke to the technical team at Paytm they told me that I had to accept the callback URL in POST. The person I spoke to, had little experience in django and could not help me further. I am not really sure what accepting the response in POST means. Can someone help me out?

[EDIT 3]

Edited the handlerequest view

like image 636
Sashaank Avatar asked Mar 08 '20 04:03

Sashaank


2 Answers

Setting callback url:

it is very simple matter, all you have to do is to add a new url in your Django app then register it with the API you are calling, I am not familiar at all with PAYTM however definitely you will find a place to register it through your dashboard or if they have CLI interface.

get to code example:

#urls.py
path('payment/status/', views.check_status, name='payment_status') # the same full url to register in callback url in their website

#views.py
@csrf_exempt # API doesn't know how to send you csrf token
def check_status(request):
    if request.method == 'POST':
        print(request.POST)# examine the data returned from the API

by the way if you are testing locally then you need to expose your website to be reachable to the outer world, check ngrok https://ngrok.com/

Handling online payments requires to be handled with SSL, HTTPS.

you can force redirection after submission like:

place the following stub inside the payment form

<input type="hidden" name="next" value="{% url 'payment_status' %}" />

And then from you submission view

# force the redirect to
return redirect(request.POST.get('next') or 'where_ever_you_send_your_user')

like image 90
Ahmed Shehab Avatar answered Oct 20 '22 14:10

Ahmed Shehab


But once the payment is over, The website is not calling handlerequest from views.py correctly. That is why I had added the @csrf_exempt so that an outside page can call the url without any issues. But I am still getting the 403 error. I am not sure what I am doing wrong

Is this 403 error coming from your server ? Or you are just seeing 403 in the browser... ?

I don't believe Paytm can access your server when your callback url is http://127.0.0.1:8000. In order for Paytm to access your server you need to provide your public IP address in the callback url and then configure your router to open port 8000 and then forward all requests from port 8000 to your machine. But since you didn't mention you have done that, my guess is you haven't done it.

like image 2
Todor Avatar answered Oct 20 '22 13:10

Todor