简体   繁体   English

在 django 中调用回调 URL 时出现 Forbidden (403) 错误

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

I am working on a django webapp.我正在开发 django webapp。 I connected the paytm payment gateway with the django app.我将paytm 支付网关与 django 应用程序连接起来。 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.付款结束后,我在调用回调 URL 时遇到问题。

Here is the code这是代码

views.py视图.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网址.py

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

donate.html捐赠.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

<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付款状态.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.但是一旦付款结束,该网站就不会正确地从views.py调用handlerequest That is why I had added the @csrf_exempt so that an outside page can call the url without any issues.这就是为什么我添加了@csrf_exempt以便外部页面可以毫无问题地调用 url。 But I am still getting the 403 error.但我仍然收到 403 错误。 I am not sure what I am doing wrong我不确定我做错了什么

EDIT1编辑1

I have added the paytm.html code to the question.我已将paytm.html代码添加到问题中。 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.我个人不认为问题出在这个页面上,因为该页面所做的只是重定向到 paytm 的支付网关页面。 The problem I am facing is when returning back to my url ie.我面临的问题是返回到我的 url 时,即。 paymentstatus.html . paymentstatus.html That is through the handlerequest view.那是通过handlerequest视图。 The donation process is as follows捐赠流程如下

  1. user fills out form in donate.html and clicks the donate button.用户在donate.html填写表单并单击捐赠按钮。
  2. paytm.html gets the information and automatically routes to paytm payment gateway paytm.html获取信息并自动路由到paytm支付网关
  3. User makes the donation.用户进行捐赠。
  4. The URL routes back from the paytm payment gateway back to my URL. URL 从 paytm 支付网关路由回我的 URL。
  5. The paymentstatus.html page is displayed.将显示paymentstatus.html页面。

As the paymentstatus.html page is being called from an external url, csrf_exempt is required, which I have provided.由于从外部 url 调用paymentstatus.html页面, csrf_exempt需要csrf_exempt ,这是我提供的。 But for some reason that does not work但由于某种原因不起作用

[EDIT 2] [编辑 2]

When I spoke to the technical team at Paytm they told me that I had to accept the callback URL in POST.当我与 Paytm 的技术团队交谈时,他们告诉我必须在 POST 中接受回调 URL。 The person I spoke to, had little experience in django and could not help me further.我与之交谈的人对 Django 几乎没有经验,无法进一步帮助我。 I am not really sure what accepting the response in POST means.我不太确定接受 POST 中的响应意味着什么。 Can someone help me out?有人可以帮我吗?

[EDIT 3] [编辑 3]

Edited the handlerequest view编辑了handlerequest view

But once the payment is over, The website is not calling handlerequest from views.py correctly.但是一旦付款结束,该网站就不会正确地从 views.py 调用 handlerequest。 That is why I had added the @csrf_exempt so that an outside page can call the url without any issues.这就是为什么我添加了 @csrf_exempt 以便外部页面可以毫无问题地调用 url。 But I am still getting the 403 error.但我仍然收到 403 错误。 I am not sure what I am doing wrong我不确定我做错了什么

Is this 403 error coming from your server ?这个 403 错误是否来自您的服务器? Or you are just seeing 403 in the browser... ?或者你只是在浏览器中看到 403 ......?

I don't believe Paytm can access your server when your callback url is http://127.0.0.1:8000 .当您的回调网址为http://127.0.0.1:8000时,我不相信Paytm可以访问您的服务器。 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.为了让Paytm访问您的服务器,您需要在回调 url 中提供您的公共 IP 地址,然后将您的路由器配置为打开端口 8000,然后将所有请求从端口 8000 转发到您的机器。 But since you didn't mention you have done that, my guess is you haven't done it.但既然你没有提到你已经这样做了,我猜你还没有这样做。

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.这是非常简单的事情,你所要做的就是在你的 Django 应用程序中添加一个新的 url,然后用你正在调用的 API 注册它,我对 PAYTM 一点也不熟悉,但是你肯定会找到一个通过它注册的地方您的仪表板或者他们是否有 CLI 界面。

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/顺便说一句,如果您在本地进行测试,那么您需要公开您的网站以供外部世界访问,请查看 ngrok https://ngrok.com/

Handling online payments requires to be handled with SSL, HTTPS.处理在线支付需要使用 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')

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.我认为您的 handlerequest 视图甚至没有被触及。 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:在您的 paymentstatus.html 模板中,您有:

{% if response_dict.RESPCODE == 01 %}

But you passed a context of paytm_params , not response_dict .但是您传递了paytm_params的上下文,而不是response_dict UNLESS, you never showed me what paytm.html really is.除非,你从来没有向我展示过paytm.html是什么。

Edit:编辑:

Thanks for adding paytm.html.感谢您添加 paytm.html。 There are a couple things you should try to do.您应该尝试做几件事。

  1. Make sure your browser is not blocking cookies.确保您的浏览器没有阻止 cookie。 Some people block cookies and that screws up your browser.有些人会阻止 cookie,这会破坏您的浏览器。
  2. Add the {% csrf_token %} tag at the top of your form.在表单顶部添加{% csrf_token %}标签。 CSRF token is important because it travels everywhere, no matter the page. CSRF 令牌很重要,因为它无处不在,无论页面如何。
  3. Try removing the csrf_exempt from your view.尝试从您的视图中删除csrf_exempt
  4. Change the name of your csrf token in settings.py to something else like csrftokenasdfasdfasdf将 settings.py 中的 csrf 令牌的名称更改为其他名称,例如 csrftokenasdfasdfasdf
  5. There's another thing called corsheader.还有一种叫做 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.我相信他们的包被称为 django-cors-headers 并且他们有一个中间穿,你可以使用它,因为你在视图之间来回跳跃。

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.回调 URL 被发送到 Paytm 后端服务器。
  2. Setup a request handler to accept POST requests on your server.设置请求处理程序以在您的服务器上接受 POST 请求。
  3. Most importantly, your server address that is sent to Paytm servers is 127.0.0.1最重要的是,您发送到 Paytm 服务器的服务器地址是 127.0.0.1
  4. When a payment gateway redirects in the browsers it's always a GET request.当支付网关在浏览器中重定向时,它总是一个 GET 请求。 (All the post requests in the browsers are Ajax requests) (浏览器中所有的post请求都是ajax请求)

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.案例 1:Paytm 正在重定向到您在浏览器上的回调,但这将是一个 GET 请求,并且您的代码仅适用于 POST 请求。 If this is true, change the method to GET.如果这是真的,请将方法更改为 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.案例 2:Paytm 服务器正在向您的服务器发出 POST API 调用,但是,您已将 127.0.0.1 作为服务器 IP,并且 Paytm 服务器将无法通过环回连接到在您的本地机器上运行的服务器IP,因此您在服务器上看不到任何 POST 请求。 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.如果这是真的,请尝试将您的应用程序托管在某个云上并使用该机器的公共 IP,或者使用 ngrok 创建到您本地机器的隧道,并在回调 URL 中使用 ngrok URL。

My personal opinion is your implementation is based on case 1.我个人的意见是您的实施基于案例 1。

Might be an issue with the callback URL.可能是回调 URL 的问题。 You can test the view by calling the URL yourself to see if it is actually connecting.您可以通过自己调用 URL 来测试视图,看看它是否真正连接。 Your call back URL should be calling this with the POST method:您的回调 URL 应该使用 POST 方法调用它:

http://127.0.0.1:8000/payment/status http://127.0.0.1:8000/payment/status

If that doesn't work try add ending '/' and make the callback call 'url/ payment/status/'如果这不起作用,请尝试添加结尾“/”并进行回调调用“url/payment/status/”

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

The POST method can be like this: POST 方法可以是这样的:

urls.py网址.py

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

views.py视图.py

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM