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

别说谁变了你拦得住时间么 提交于 2021-02-07 07:30:38

问题


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


回答1:


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')




回答2:


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.




回答3:


I think you should be redirecting instead of rendering that request in the donate view. I don't think your handlerequest view is even being touched. This is what I'm seeing in the donate view:

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

In your template for paymentstatus.html, you have:

{% if response_dict.RESPCODE == 01 %}

But you passed a context of paytm_params, not response_dict. UNLESS, you never showed me what paytm.html really is.

Edit:

Thanks for adding paytm.html. There are a couple things you should try to do.

  1. Make sure your browser is not blocking cookies. Some people block cookies and that screws up your browser.
  2. Add the {% csrf_token %} tag at the top of your form. CSRF token is important because it travels everywhere, no matter the page.
  3. Try removing the csrf_exempt from your view.
  4. Change the name of your csrf token in settings.py to something else like csrftokenasdfasdfasdf
  5. There's another thing called corsheader. I believe their package is called django-cors-headers and they have a middle wear that you can use since your doing a bunch of jumping back and forth between views.



回答4:


Let's take a look at two lines of code

if request.method == 'POST':

and

"CALLBACK_URL" : "http://127.0.0.1:8000/payment/status",

What does this indicate?

  1. A callback URL is sent to Paytm backend servers.
  2. Setup a request handler to accept POST requests on your server.
  3. Most importantly, your server address that is sent to Paytm servers is 127.0.0.1
  4. When a payment gateway redirects in the browsers it's always a GET request. (All the post requests in the browsers are Ajax requests)

With the above facts, let's see what's happening with your code.

There are two cases that might be happening

Case 1: Paytm is redirecting to your callback your on the browser, but it would be a GET request then, and your code only works for POST request. If this is true, change the method to GET.

Case 2: Paytm servers are making a POST API call to your servers, however, you have passed 127.0.0.1 as the server IP, and PayTm servers won't be able to connect to the server that is running on your local machine with loopback IP, hence you don't see any POST request on your server. If this is true, try to host your application on some cloud and use the public IP of that machine, or use ngrok to create a tunnel to your local machine, and in the callback URL use the ngrok URL.

My personal opinion is your implementation is based on case 1.




回答5:


Might be an issue with the callback URL. You can test the view by calling the URL yourself to see if it is actually connecting. Your call back URL should be calling this with the POST method:

http://127.0.0.1:8000/payment/status

If that doesn't work try add ending '/' and make the callback call 'url/ payment/status/'

path('payment/status/', views.handlerequest, name='handlerequest') http://127.0.0.1:8000/payment/status/




回答6:


The POST method can be like this:

urls.py

urlpatterns = [
   url(r'^callback$', views.CallbackView.as_view())
]

views.py

class CallbackViews(APIView):
    @csrf_exempt
    def post(self, request):
        # do something


来源:https://stackoverflow.com/questions/60584432/forbidden-403-error-when-calling-the-callback-url-in-django

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!