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

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

我正在开发一个 django webapp。我将 paytm payment gateway 与 django 应用程序连接起来。我按照文档做了一切,一切正常。差不多。

我在付款结束后调用回调 URL 时遇到问题。

这是代码

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>

但是付款结束后,网站无法正确地从 views.py 调用 handlerequest。这就是为什么我添加了 @csrf_exempt 以便外部页面可以毫无问题地调用 url 的原因。但我仍然收到 403 错误。我不确定我做错了什么

编辑1

我已将 paytm.html 代码添加到问题中。我个人不觉得这个页面有问题,因为该页面所做的只是重定向到 paytm 的支付网关页面。我面临的问题是回到我的 url 即。 paymentstatus.html。那就是通过 handlerequest 视图。捐赠流程如下

  1. 用户在 donate.html 中填写表格并点击捐赠按钮。
  2. paytm.html获取信息并自动路由到paytm支付网关
  3. 用户捐款。
  4. URL 从 paytm 支付网关路由回我的 URL。
  5. 显示 paymentstatus.html 页面。

由于从外部 url 调用 paymentstatus.html 页面,因此需要 csrf_exempt,我已经提供了。但由于某些原因不起作用

[编辑 2]

当我与 Paytm 的技术团队交谈时,他们告诉我必须在 POST 中接受回调 URL。与我交谈的人在 django 方面几乎没有经验,无法进一步帮助我。我不太确定接受POST中的响应是什么意思。有人可以帮我吗?

[编辑 3]

编辑了 handlerequest view

我认为您应该重定向而不是在捐赠视图中呈现该请求。我认为您的 handlerequest 视图甚至没有被触及。这是我在捐赠视图中看到的:

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

在 paymentstatus.html 的模板中,您有:

{% if response_dict.RESPCODE == 01 %}

但是你传递了 paytm_params 的上下文,而不是 response_dict。除非,你从来没有告诉我 paytm.html 到底是什么。

编辑:

感谢您添加 paytm.html。您应该尝试做几件事。

  1. 确保您的浏览器没有阻止 cookie。有些人会阻止 cookie,这会搞砸您的浏览器。
  2. 在表单顶部添加 {% csrf_token %} 标签。 CSRF 令牌很重要,因为它无处不在,无论页面如何。
  3. 尝试从您的视图中删除 csrf_exempt
  4. 将 settings.py 中的 csrf 令牌名称更改为其他名称,例如 csrftokenasdfasdfasdf
  5. 还有一种叫做 corsheader 的东西。我相信他们的包叫做 django-cors-headers 并且他们有一个你可以使用的中间件,因为你在视图之间来回跳转。

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

这个 403 错误是来自您的服务器吗?或者您只是在浏览器中看到 403...?

当您的回调 url 为 http://127.0.0.1:8000 时,我认为 Paytm 无法访问您的服务器。为了 Paytm 访问您的服务器,您需要在回调 url 中提供您的 public IP 地址,然后将您的路由器配置为打开端口 8000,然后将所有请求从端口 8000 转发到你的机器。但是既然你没有提到你已经这样做了,我猜你还没有这样做。

设置回调url:

这件事很简单,你只需要在你的Django应用中添加一个新的url然后注册到你正在调用的API,我一点都不熟悉使用 PAYTM,但是你肯定会找到一个地方通过你的仪表板或者如果他们有 CLI 界面来注册它。

进入代码示例:

#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

顺便说一下,如果您在本地进行测试,那么您需要将您的网站公开给外部世界,检查 ngrok https://ngrok.com/

处理在线支付需要使用 SSL、HTTPS 处理。

您可以在提交后强制重定向,例如:

将以下存根放入付款表格

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

然后从你提交来看

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

我们来看两行代码

if request.method == 'POST':

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

这说明什么?

  1. 回调 URL 被发送到 Paytm 后端服务器。
  2. 在您的服务器上设置请求处理程序以接受 POST 请求。
  3. 最重要的是,您发送到 Paytm 服务器的服务器地址是 127.0.0.1
  4. 当支付网关在浏览器中重定向时,它始终是 GET 请求。 (浏览器中所有post个请求都是Ajax个请求)

根据以上事实,让我们看看您的代码发生了什么。

可能会发生两种情况

案例 1:Paytm 正在重定向到您在浏览器上的回调,但是这将是一个 GET 请求,并且您的代码仅适用于 POST 请求。如果是这样,请将方法更改为 GET。

情况 2:Paytm 服务器正在向您的服务器发出 POST API 调用,但是,您已将 127.0.0.1 作为服务器 IP,PayTm 服务器将无法使用环回 IP 连接到本地计算机上 运行 的服务器,因此您在服务器上看不到任何 POST 请求。如果这是真的,请尝试将您的应用程序托管在某个云上并使用该机器的 public IP,或者使用 ngrok 创建到您本地机器的隧道,并在回调 URL 中使用 ngrok URL.

我个人认为你的实现是基于案例1。

回调可能有问题 URL。您可以通过自己调用 URL 来测试视图,看看它是否真的在连接。您的回调 URL 应该使用 POST 方法调用:

http://127.0.0.1:8000/payment/status

如果这不起作用,请尝试添加结尾“/”并进行回调调用 'url/ payment/status/'

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

POST方法可以这样:

urls.py

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

views.py

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