【问题标题】:Forbidden (403) error when calling the callback URL in django在 django 中调用回调 URL 时出现 Forbidden (403) 错误
【发布时间】:2020-06-20 09:23:31
【问题描述】:

我正在开发一个 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 以便外部页面可以毫无问题地调用该网址。但我仍然收到 403 错误。我不确定我做错了什么

编辑1

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

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

由于 paymentstatus.html 页面是从外部 url 调用的,csrf_exempt 是必需的,这是我提供的。但是由于某种原因不起作用

[编辑 2]

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

[编辑 3]

编辑handlerequest view

【问题讨论】:

  • 我想知道您是在本地主机上运行应用程序还是通过 HTTP 在实际的 Web 服务器上运行应用程序?
  • 两种方式都试过了。两者都不起作用
  • 这可能是 CORS 标头问题。检查浏览器开发者控制台 - 在 paytm 支付页面上提交支付时会发出什么请求 - OPTIONS 请求是否发送到您的回调 url?
  • 从支付网关,一个 POST 请求被发送到 URL:127.0.0.1:8000/handlerequest。请求的状态码是403。我在浏览器控制台中没有看到任何 OPTIONS 请求
  • 运行Checksum.verify_checksum 时会发生什么。我找不到那个的python包

标签: python django csrf paytm


【解决方案1】:

POST方法可以是这样的:

urls.py

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

views.py

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

【讨论】:

  • 考虑在您的代码周围添加更多描述以解释为什么这是解决方案。
【解决方案2】:

可能是回调 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/

【讨论】:

    【解决方案3】:

    我们来看两行代码

    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 请求。如果这是真的,请尝试将您的应用程序托管在某个云上并使用该机器的公共 IP,或者使用 ngrok 创建到本地机器的隧道,并在回调 URL 中使用 ngrok URL。

    我个人的看法是你的实现是基于案例 1。

    【讨论】:

      【解决方案4】:

      设置回调地址:

      这很简单,你只需在你的 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')
      
      

      【讨论】:

      • 嗨!我尝试在pythonanywhere 中测试托管它。我尝试将if request.method == 'POST': 添加到handlerequest 视图中。我仍然会遇到同样的错误。我也编辑了这个问题。可以看看吗?
      • 并且 Paytm 在其门户中似乎没有用于注册回调 URL 的字段。必须在views.py 文件中定义URL。这就是我所做的。
      • 再次与他们的支持取得联系,询问付款退货网址,可能会统一。您不应该手动重定向到回调视图,它应该在您发布付款并希望收集有关付款状态的信息后完成,但它应该是可选的。在提交付款时检查网络选项卡,您可能会发现一些有用的信息。无论如何你都需要 ssl。
      • 当您说ssl 时,您的意思是该网站必须是HTTPS 而不是HTTP 网站。对不起这个问题。我是个菜鸟
      • 是的,ssl 我的意思是 HTTPS 连接,你可以通过 ngrok 服务在本地开发中实现,它是免费的,只需下载它并输入 ngrok http -bind-tls=true http://127.0.0.1:8000 并且不要忘记将生成的 url 添加到ALLOWED_HOSTS
      【解决方案5】:

      但是一旦付款结束,网站并没有调用handlerequest 正确地来自views.py。这就是为什么我添加了@csrf_exempt 所以 外部页面可以毫无问题地调用 url。但我是 仍然收到 403 错误。我不确定我做错了什么

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

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

      【讨论】:

        【解决方案6】:

        我认为您应该重定向而不是在捐赠视图中呈现该请求。我认为您的 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 并且他们有一个你可以使用的中间件,因为你在视图之间来回跳转。

        【讨论】:

        • 我已将 paytm.html 添加到问题中。请看一下。我个人不觉得问题出在 paytm.html 上。但你可能知道得更清楚。
        • @Sashaank 我添加了更多建议,因为您没有完全关注您链接到的视频。
        • 嗨。我已经尝试了以上所有方法。似乎没有任何效果。就支付网关而言,我完全按照视频中的那个人所做的。但这似乎不起作用。我现在该怎么办?
        • 嗨。我尝试使用django-cors-headers。我已经编辑了这个问题。你能看一下吗?
        猜你喜欢
        • 1970-01-01
        • 2014-05-13
        • 2017-01-05
        • 2013-07-18
        • 2020-12-22
        • 2019-05-26
        • 1970-01-01
        • 2016-06-04
        • 2022-01-16
        相关资源
        最近更新 更多