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.