An Introduction to PayPal and IPN for Developers


My inbox is full of spam trying to get me to apply for a merchant account, so my web site can accept credit card payments. Who needs that when you've got PayPal?

If your web site needs to accept electronic payments, PayPal is your best partner. You don't need to contact any sales people at PayPal, and you don't need to buy or install any software. You just need a PayPal account and some basic HTML and web scripting skills. You can use PayPal as your payment solution and leave all the complex payment processing to them.

Any web site can start collecting payments using PayPal Merchant Services. Simply fill out a simple web form and PayPal will give you a button you can stick on your web site to start collecting money. No programming required. This component of PayPal's Merchant Services is called Web Accept.

PayPal's Web Accept Wizard

Web Accept works well by itself, and becomes even more powerful when combined with another component of PayPal's Merchant Services, called Instant Payment Notification (IPN). With IPN, your web site collects money via PayPal, and your server is notified of all payments as they happen. Using a simple CGI interface, PayPal sends your server all the payment details you need to log, process, and deliver transactions.

PayPal's Instant Payment Notification

With the combination of Web Accept plus Instant Payment Notification, PayPal truly acts as a valuable, easy-to-use web service. Payments from your users are deposited directly into your PayPal account; no middleman ever holds the money. PayPal collects shipping information for you, if necessary. And you can vary the level to which your organization automates its payment handling. You can begin with a simple payment process, and gradually integrate and automate more PayPal features and services over time as your business grows.

The easiest way to get started is to run through PayPal's Web Accept wizard, and copy the HTML it spits out. Then modify the HTML, either manually or programmatically, to suit your site's needs. The following example steps through the process. Note: this example assumes I already have a web site up and running, and that I already have a PayPal account.

Say I produce doodads, and I want to start selling doodads from my personal web site using PayPal. I start by using PayPal's Web Accept form to fill in the details for my product, and PayPal gives me the following chunk of HTML for my web site:

<!-- Begin PayPal Logo -->
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="alanb@alanb.com">
<input type="hidden" name="undefined_quantity" value="1">
<input type="hidden" name="item_name" value="doodad from alanb.com">
<input type="hidden" name="item_number" value="dd01">
<input type="hidden" name="amount" value="4.99">
<input type="hidden" name="return" value="http://alanb.com/doodads/thanks.html">
<input type="hidden" name="cancel_return" value="http://alanb.com/doodads/canceled.html">
<input type="image" border="0" name="submit" src="http://images.paypal.com/images/x-click-but5.gif" alt="Make payments with PayPal - it's fast, free and secure!">
</form>
<!-- End PayPal Logo -->

Note that the business variable points to my email address, which also represents my PayPal account. I can edit the item name, item number, and amount (price) as needed.

The form also includes two URLs, called return and cancel_return. These are used to send the user back to my web site after he completes or cancels the PayPal transaction. I've created two simple web pages called thanks.html and canceled.html for this example.

I could stop right here, slap that HTML on a web page, and start taking orders for doodads. Whenever someone clicks on the PayPal button and buys a doodad, PayPal will email the transaction details and the customer's contact information to me. But, say I want to automatically enter that customer and transaction data into my own database. I can easily extend the above block of HTML to notify my web server of all transactions as they happen. Simply add one more hidden input field to the HTML form:

<input type="hidden" name="notify_url" value="http://alanb.com/doodads/notify.cgi">

Now PayPal's servers will call my CGI script, notify.cgi, with the details of each doodad order as it occurs. This is Instant Payment Notification. PayPal uses the HTTP POST method to send transaction details to notify.cgi. Then notify.cgi echoes that transaction data back to PayPal to confirm the validity of the payment. For this simple example, notify.cgi then logs the payment details to a log file. PayPal does not require any special response code from notify.cgi, so I just output the word "foo" to end the HTTP communication.

Here is my HTML PayPal button, modified to use Instant PayPal Notification, along with a couple other tweaks:

<!-- Begin PayPal Button -->
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="alanb@alanb.com">
<input type="hidden" name="undefined_quantity" value="1">
<input type="hidden" name="item_name" value="doodad from alanb.com">
<input type="hidden" name="item_number" value="dd01">
<input type="hidden" name="amount" value="1.99">
<input type="hidden" name="return">
value="http://alanb.com/doodads/thanks.cgi">
<input type="hidden" name="cancel_return">
value="http://alanb.com/doodads/canceled.html">
<input type="hidden" name="notify_url" value="http://alanb.com/doodads/notify.cgi">
<input type="image" border="0" name="submit" src="http://images.paypal.com/images/x-click-but5.gif" alt="Buy doodads with PayPal">
</form>
<!-- End PayPal Button -->

And here is a simple Python script, notify.cgi:

#!/usr/local/bin/python

# notify.cgi -- CGI script that receives PayPal instant payment notifications
# Alan Braverman
# 11/27/2001

import sys, time, urllib

def confirmPayment():
"Ask PayPal to confirm this payment, return status and detail strings"
paymentDetails = sys.stdin.read() # formatted as a CGI query
confirmPostData = paymentDetails + '&cmd=_notify-validate'
confirmPostRequest = urllib.urlopen(
'http://www.paypal.com/cgi-bin/webscr', confirmPostData)
paymentStatus = confirmPostRequest.read() # either VERIFIED or INVALID
return paymentStatus, paymentDetails

def logPayment(paymentStatus, paymentDetails):
"Write payment status and details to a log file"
logFile = open('payments.log', 'a')
logFile.write('%s %s %s\n' % (
time.ctime(time.time()), paymentStatus, paymentDetails))
logFile.close()

def sendResponse():
"Send a simple HTTP response back to PayPal"
print 'Content-type: text/plain\n\nfoo'

def main(argc, argv):
"Process PayPal Instant Payment Notification CGI call"
paymentStatus, paymentDetails = confirmPayment()
logPayment(paymentStatus, paymentDetails)
sendResponse()

if __name__ == "__main__":
main(len(sys.argv), sys.argv)

Of course, you could extend this concept in myriad ways beyond dumping a log file, from writing to a database to automatically processing the order and delivering a product or service. I recommend keeping your PayPal interface routines simple, and allowing another process to pick up and fulfill the order. If your CGI script has bugs, you could lose a transaction. However, until your script confirms each payment PayPal will continue trying to notify you. You can try a simple example and you'll see PayPal hitting your access logs. Along those same lines, you'll recover from errors more gracefully if your script can distinguish unique transactions from repeat transactions.

Once the user completes a transaction, PayPal posts the transaction details to my return URL. So I replaced thanks.html with a thanks.cgi script. In this example, thanks.cgi simply redirects the user to thanks.html. You could extend this to use the order details in the "thanks" page, to produce an online receipt or what-have-you.

Here is another simple Python script, thanks.cgi:

#!/usr/local/bin/python

def main():
print 'Content-type: text/plain'
print 'Status: 302'
print 'Location: http://alanb.com/doodads/thanks.html'
print

if __name__ == "__main__":
main()

I won't delve into the details of transaction data in this article. Please refer to the table of Instant Payment Notification variables on PayPal's website.

When PayPal tells you that a payment is completed or cancelled, you'll know how to handle it on your end. But sometimes PayPal will tell you that a payment's status is "pending." Pending payments are a little more difficult to handle, and PayPal help is limited for this case. According to PayPal's IPN documentation regarding pending payments, your server will be notified once when a payment is initiated, and a second time once the payment's status has changed from "pending" to "completed."

Experienced web programmers already know that CGI scripts can be difficult to debug. Unfortunately, PayPal will not allow you to make a payment to yourself. You might need to create test items and debug with a friend in order to test the end-to-end product flow. Go recruit another developer to buddy up with for PayPal testing.

My doodad example is up and running at http://alanb.com/doodads, so go ahead and buy a doodad from me if you want to see it in action from the buyer's perspective. I won't actually send you anything, of course, but my doodads are reasonably priced :-)


DISCLAIMER: The following content is provided by each article's individual author and not by PayPal, Inc. or any of its affiliates or subsidiaries. The articles may contain errors, and PayPal does not endorse or provide support for their content. PayPal advises that you contact the authors of the articles for questions, information about technical support, warranties and/or licenses, if any are necessary. If you choose rely on the content in any of these articles, you do so at your own risk. PayPal will take no responsibility for any errors, harm or other damage that may occur as a result of your reliance on this content, including any incidental, special, indirect or consequential damages. PayPal EXPRESSLY DISCLAIMS ALL WARRANTIES regarding this content and the use of this content.