References
1.
PayPal IPN Python Code from Django Aware blog.
2.
A Google Group discussion
3.
Pro PayPal E-Commerce. I recommend this book.
4.
Professional Web APIs with PHP. This book is good for non-PayPal stuffs too. A good book.
My Tutorial
(1) Make a sandbox/testing account with PayPal.
(2) Log in to that account. Get to IPN Simulator at the bottom of the page.
(3) In the form that shows up, make sure
1. notify url = http://your_app.appspot.com/path-to-your_ipn_handler
2. "receiver_email" = "ACCOUNT_EMAIL" that you use in your IPN handler code.
3. You should vary "payment_status" from "Completed" to "Failed" to "Pending" etc.
Send the form a few times.
(4) Paypal says it can't send.
Change " notify url" to www.google.com etc. You get the same failure result.
It is as expected: you haven't written the code to handle this POST request.
The code you need to write is:
import os
import sys
import logging
import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from util.sessions import Session
from google.appengine.ext import db
sys.path.append(os.path.join(os.pardir,os.getcwd()))
from helper_rendering import doRender
import urllib, urllib2
from google.appengine.api import mail
from google.appengine.api import urlfetch
from google.appengine.ext.webapp.util import run_wsgi_app
PP_URL = "https://www.sandbox.paypal.com/cgi-bin/webscr"
ACCOUNT_EMAIL= "my_email_for_sandbox@nice.com"
#PP_URL = "https://www.paypal.com/cgi-bin/webscr"
#ACCOUNT_EMAIL = "my_email_for_real-paypal_acct@good.com"
class IPNHandler1(webapp.RequestHandler):
def post(self):
parameters = None
# Check payment is completed, not Pending or Failed.
if self.request.get('payment_status') == 'Completed':
if self.request.POST:
parameters = self.request.POST.copy()
if self.request.GET:
parameters = self.request.GET.copy()
logging.debug('IPN 1,
or your own message for yourself to read in log.')
else:
self.response.out.write('Error, sorry.
The parameter payment_status was not Completed.')
# Check the IPN POST request came from real PayPal,
# not from a fraudster.
if parameters:
parameters['cmd']='_notify-validate'
params = urllib.urlencode(parameters)
status = urlfetch.fetch(
url = PP_URL,
method = urlfetch.POST,
payload = params,
).content
if not status == "VERIFIED":
self.response.out.write('Error.
The request could not be verified, check for fraud.')
parameters['homemadeParameterValidity']=False
# parameters = None
# You may log this data in your database,
# for later investigation.
# Check the money is really to go to your account,
# not to a fraudster's account.
if parameters['receiver_email'] == ACCOUNT_EMAIL:
transaction_id = parameters['txn_id']
# Check if this is a new, unique txn,
# not a fraudster re-using an old, verified txn.
invoice_id = parameters['invoice']
currency = parameters['mc_currency']
amount = parameters['mc_gross']
fee = parameters['mc_fee']
# Check if they are the right product/item, right price,
# right currency, right amount, etc.
email = parameters['payer_email']
identifier = parameters['payer_id']
# Email/notify/inform the user for whatever reason.
parameters['your_parm']= "It is ok on 19 September, 2010."
logging.debug('IPN 100. All OK.')
logging.debug(parameters['txn_id'])
logging.debug(parameters['invoice'])
logging.debug(parameters['payer_email'])
# With this IPN testing, you can't see results on the browser.
# See results on the log file maintained by Google AppEngine.
#template_values = { 'params': parameters, }
#doRender(self,'ipn_ok.htm', template_values )
if __name__ == '__main__':
print "IPNHandler1 is being run as main()."
(1) Now send the IPN Simulator form again as explained in the beginning of this post.
Now Paypal says it has sent it successfully.
(2) Log in to your Google AppEngine developer account.
Get to the log. Set logging level to "debug"
Check the log entries. you will see something like:
1. 09-29 01:54AM 28.745 /ipn_handle1 200 2141ms 408cpu_ms 4kb gzip(gfe)
See details
216.113.191.33 - - [29/Sep/2010:01:54:30 -0700] "POST /ipn_handle1 HTTP/1.1"
200 4842 - "gzip(gfe)"
"yourstruly.appspot.com"
ms=1234 cpu_ms=123 api_cpu_ms=0 cpm_usd=0.123456 loading_request=1
2. D 09-29 01:54AM 29.395 IPN 1
3. D 09-29 01:54AM 30.682 IPN 100. All OK.
4. D 09-29 01:54AM 30.682 31929814
5. D 09-29 01:54AM 30.682 abc1234
6. D 09-29 01:54AM 30.682 buyer@alan.htet.com
7. I 09-29 01:54AM 30.705 Creating session session-575150356408086346
8. I 09-29 01:54AM 30.884 This request caused a new process to be started
for your application, and thus caused your application code to be loaded
for the first time. This request ...
If you have any question, just ask by way of commenting below.