I am having problems with django-paypal, the problem occurs at the moment of receiving the paypal IPN when the payment is successfully completed.
Error:
Forbidden (CSRF cookie not set.): /paypal/
[15/Sep/2019 22:53:04] "POST /paypal/ HTTP/1.1" 403 2896
Forbidden (CSRF cookie not set.): /paypal/
[15/Sep/2019 22:53:19] "POST /paypal/ HTTP/1.1" 403 2896
Forbidden (CSRF cookie not set.): /paypal/
[15/Sep/2019 22:53:42] "POST /paypal/ HTTP/1.1" 403 2896
Forbidden (CSRF cookie not set.): /paypal/
[15/Sep/2019 22:54:24] "POST /paypal/ HTTP/1.1" 403 2896
Forbidden (CSRF cookie not set.): /paypal/
[15/Sep/2019 22:55:45] "POST /paypal/ HTTP/1.1" 403 2896
I don't know what is happening, I started to investigate the error but I did not achieve much, investigating I put in the file settings.py
CSRF_COOKIE_SECURE
as True
, and even then it did not work, any solution ?.
This is the code that makes the payment:
def process_payment(request, pk):
course = get_object_or_404(Course, pk = pk)
host = request.get_host()
paypal_dict = {
'business': settings.PAYPAL_RECEIVER_EMAIL,
'item_name': course.title,
'amount': course.price,
'currency_code': 'USD',
'notify_url': 'http://{}{}'.format(host, reverse('paypal-ipn')),
'return_url': 'http://{}{}'.format(host, reverse('course:list')),
'cancel_return': 'http://{}{}'.format(host, reverse('payment_cancelled')),
}
form = PayPalPaymentsForm(initial = paypal_dict)
return render(request, 'carts/process_payment.html', {'form': form, 'course': course})
UPDATE
The view that gives error was not made by me, is from django-paypal, which is responsible for managing the paypal IPN, the view is called ipn
. The view is as follows:
https://github.com/spookylukey/django-paypal/blob/master/paypal/standard/ipn/views.py
That view is the notify_url
.
For more information, this is the guide I am doing: https://overiq.com/django-paypal-integration-with-django-paypal/
These are the urls.py of my application:
urlpatterns = [
path('cart/', CartDetailView.as_view(), name = 'cart'),
path('cart-add/', views.add_course, name = 'add_course'),
path('process-payment/<int:pk>/', views.process_payment, name='process_payment'),
path('payment-cancelled/', views.payment_canceled, name='payment_cancelled'),
]
These are the urls.py of the django-paypal
ipn
application
urlpatterns = [
url(r'^$', views.ipn, name="paypal-ipn"),
]
This would be my urlconf:
urlpatterns = [
path('django-admin/', admin.site.urls),
# Paths of My Apps
path('admin/', include(administration_patterns)),
path('', include('core.urls')),
path('', include('carts.urls')),
path('', include(course_patterns)),
path('paypal/', include('paypal.standard.ipn.urls')),
# Paths of Auth
path('accounts/', include('django.contrib.auth.urls')),
path('accounts/', include('registration.urls')),
]
Try moving the Paypal URL pattern to the top to ensure that the request is actually routed to django-paypal
:
urlpatterns = [
path('django-admin/', admin.site.urls),
# Paths of My Apps
path('paypal/', include('paypal.standard.ipn.urls')),
path('admin/', include(administration_patterns)),
path('', include('core.urls')),
path('', include('carts.urls')),
path('', include(course_patterns)),
# Paths of Auth
path('accounts/', include('django.contrib.auth.urls')),
path('accounts/', include('registration.urls')),
]
Why are you seeing this error?
The request is then routed to a view, which is not csrf_exempt
, so the CSRF middleware expects a CSRF cookie.
As the request is sent from the Paypal server, it does not contain a CSRF cookie, so you are getting the error Forbidden (CSRF cookie not set.)
Why is the request routed to the wrong view?
Patterns are matched in a depth-first approach, meaning Django first tries all patterns included from 'core.urls'
, 'carts.urls'
and course_patterns
before trying to match path('paypal/', include('paypal.standard.ipn.urls'))
One of the included URL patterns matches the path /paypal/
, maybe something like path('<slug>/', course_view)
in course_patterns
.
Moving path('paypal/', include('paypal.standard.ipn.urls')),
up in the urlpatterns
list ensures that Django tries to match it first.