According to the PayPal IPN documentation, you have to respond to an IPN by first sending an empty HTTP 200 response to https://www.sandbox.paypal.com/cgi-bin/webscr
and then send an HTTP POST request. The only way I could figure out to send an HTTP 200 response was by returning HttpResponse('')
in the view, which works since the IPN simulator says the "IPN was sent and the handshake was verified." But then how would I send the HTTP POST request when the view has already returned? My plan was to use urllib2 to generate the POST request.
I also would like to send an HTTP 200 response without using HttpResponse('')
since I don't know where the original request is coming from, and I want to be sure that I'm sending the response to https://www.sandbox.paypal.com/cgi-bin/webscr
. I've looked at urllib2, requests, and PyCurl, and I've been unable to find a way to create an empty response to a particular URL.
You want to use requests
or urllib2
to make a POST request to verify the IPN before you return the HttpResponse('')
; the docs imply you have to do it in the opposite order, but you don't actually have to, and this way is a lot simpler in most web frameworks. So your view should look vaguely like
def ipn(request):
verification = requests.POST(PAYPAL_URL, data=request.body)
if verification.text == 'VERIFIED':
# process IPN
return HttpResponse('')
else:
return HttpResponseForbidden()
Note that if this errors somewhere, you will return a 500 to PayPal, and they will retry. If you do this too much, they will get annoyed at you and eventually deactivate your account, so make sure you are getting alerted of the error in some fashion. (You could also wrap things in a try
/except
block, but then you risk returning a 200 without actually doing your processing if there's an error, which may be worse.)