Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
1. Paypal background
Paypal offers different levels of integration, which, depending on what you need to do, might be
better suited for your needs. It is important that you get to know at least the basic integration
concepts that paypal provides before starting to program anything so that you plan in advance
what is best suited to your needs.
That said, let me try to give you a rough idea of the different levels involved before going any
further so as to better understand the little area that this article covers. It is, however, an area
in which most of the small and middle sized projects may fall into.
In broad lines, there are 3 levels of integration that one may achieve with Paypal1 :
Express Checkout: Within a seller account in paypal, you can create buttons with infor-
mation related to each item that you may be selling, (name, description, item number, and
pricing). You can have up to 1.000 different buttons or items, defined in this way. After
that, it is a matter of setting the buttons on the html to go along with the application.
Regarding web2py, it is really simple to just copy the code that paypal creates for each
button in a text field in your product db, and then just present it on the screen whenever
its needed.
Using this method, one can opt for different purchase experiences including straight che-
ckout or cart management (managed by paypal) which would let you add or remove items
from within the checkout screen in paypal.
I do not like this method, unless you would be selling very very few item codes, as it may
get to be a pain to maintain your articles in paypal. If you are selling a few services or
1
Again, please understand that I am over simplifying the different methods to try to explain in a few lines the
different possibilities
1
Paypal Integration in Web2py
whatever with a small set of prices, it might very well be worth it, as you don’t have to
work much from the programming point of view and its really simple to set up.
Standard Integration: This is the one that we will be covering in this article. It basically
lets you manage your own product database etc, and send all the data to paypal, at the
moment of payment, so that the whole checkout process is managed at paypal. After the
transaction has been completed, you can choose (as per configuration of your profile in
your paypal seller account) wether the customer is redirected back to your domain (you
can setup a default URL to return to, or send that URL dinamically each time you send the
data for the checkout, but the functionality needs to be activated in your seller account).
Two things need to be mentioned here, which I feel are part of the Standard Integration,
although they are not required in order to have your basic site working:
• PDT: Payment Data Transfer, which would be the process by which the customer
is sent back to your domain, which lets you capture the transaction data (payment
confirmation data from paypal), and show it in a confirmation screen in your own do-
main, with any further information you may want to show, or redirect him to continue
his shopping. It is not completelly safe, as nothing garanties that the customer will
be redirected, this may well happen, because on some cases, paypal doesn’t execute
the redirection, but forces the customer to click on an extra button to return to your
domain, so as to give the opportunity to the customer to join paypal. This happens
whenever the customer pays by credit card and not using his paypal account.
• IPN: Instant Payment Notification, which is a messaging service that connects to
your domain to send the information of each transaction processed at Paypal. It
doesn’t stop sending the message until you acknowledge its reception (or 4 days
pass without acknowledgement). This is the safest way to collect all the data from
all the transactions processed at paypal, and trigger any internal process that you
may have, ussually you will want to do the shipping of your products at this point.
Detailed Integration: In here I am really grouping a number of other methods ands APIs,
that I will not be detailing, some of them for very specific uses. The only that that I would
like to mention more specifically is NVP (Name Value Pairs), as I feel gives you a very sim-
ple programing interface with wich you can do very detailed processes controlling all your
data, and all your transaction flow from your domain. Using NVP, you can for example,
capture all the data related to a payment in your domain, and only at that point, send all
the information to paypal to process the payment (as opposed to processing the checkout
which is what we are doing in the previous items). You have a good example as to how
to implement this at http://mdp.cti.depaul.edu/appliances/default/show/28 or
go to main webpage www.web2py.com and find it under free applications, PaypalEngine
developed by Matt Sellers. You should however check the detailed documentation at
paypal as the process involves many steps in order to ensure the maximum security of
your transactions.
So basically, in Express Checkout paypal manages your cart (and master data), the checkout
process and of course, payments. With Standard Integration paypal manages checkout and
payments, and with further detailed integration, you can make it so that it manages only the
2
Paypal Integration in Web2py
payments.
2. First Steps
Before moving on, all the technical documentation regarding integration with paypal, can be
found at:
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/
library_documentation.
A link to this URL in case this changes can be found by clicking on the Documentation link at
https://developer.paypal.com/.
So moving on to how to use the standard integration, the first thing you should do, is create
yourself a sandbox account. You do this at https://developer.paypal.com/ create yourself
an account, and once logged in, create at least two test accounts, seller and a buyer respectivelly.
There is a good guide on all the necessary steps called PP sandbox user guide which you can find
at the documentation link provided before, or on an html version at https://cms.paypal.com/
us/cgi-bin/?cmd=_render-content&content_ID=developer/howto_testing_sandbox. Everyt-
hing on how to set your account up and start running is described there.
Once you have that setup and running, you will have your seller ID and email (you can use any
of them to identify yourself to paypal on the code below, although I prefer the ID, if only to
avoid possible spam).
2.1. Checkout
Ok, so now, we can already create the checkout button that will take our customers to the
paypal site with all our cart data. Before moving further, you can find all documentation related
to this point at the documentation link provided before under Website Payments Standard
Integration Guide or directly in html format at:
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/
howto_html_wp_standard_overview
Namelly check the information about Third-Party Shopping Carts. Anyway, creating the
button to send all the information is actually very simple, all that is needed is the following code
in your checkout page view:
Listing 1: Checkout Button Code
<form a c t i o n=" h t t p s : / /www. s a n d b o x . p a y p a l . com/ c g i −b i n / w e b s c r " method=" p o s t ">
<!−− S e l e c t t h e c o r r e c t button d e p e n d i n g on c o u n t r y e t c .
I f you can do i t w i t h p r e g e n e r a t e d b u t t o n s ( w i t h p r i c e s i n c l u d e d e t c )
t h e n s o much t h e b e t t e r f o r s e c u r i t y −−>
<i n p u t type=" h i d d e n " name=" b u s i n e s s " v a l u e="{{= p a y p a l _ i d }} " />
<i n p u t type=" image " s r c=" h t t p s : / /www. s a n d b o x . p a y p a l . com/es_XC/ i / b t n /
btn_buynowCC_LG . g i f " border=" 0 " name=" s u b m i t " a l t=" PayPal − The s a f e r ,
e a s i e r way t o pay o n l i n e ! ">
3
Paypal Integration in Web2py
4
Paypal Integration in Web2py
Figura 1: PDT
duce data not shown to the customer, that may allow you to track any extra information.
It is per transaction (not per item). In this case, I choose to use an internal event code
to track all purchases related to an event (special promotion if you like or whatever).
As you can see, I create a loop with all the cart items to do the checkout by passing a
dictionary with all the product data. I have the information of the items purchased in the
session. They get named and numbered following the paypal rules.
Regarding the discount, even though you set the discounts per item, paypal, only shows
a discount total, I do not know if this is different in the Pro version.
For more information, you should check the documentation named before, which includes a list
of all the available fields to you (which include shipping charges etc).
5
Paypal Integration in Web2py
Figure 1 (picture taken from paypal docs) so as to give you a detailed view of the process flow.
In Listing 2 I include a number of generic functions that I have used in setting up the interface.
The Connection class definition is a modified version of a generic connection example I found sur-
fing the web, but I cannot really recall where. The add_to_cart, remove_from_cart, empty_cart
and checkout I include as an example on how to setup your cart, which are taken from EStore
which you can find at http://www.web2py.com/appliances/default/show/24 created by
Massimo di Pierro.
Listing 2: Generic Classes and Function definitions
# db . py f i l e
#########################################################################
## G l o b a l V a r i a b l e s d e f i n i t i o n
#########################################################################
domain= ’www. s a n d b o x . p a y p a l . com ’
p r o t o c o l= ’ h t t p s : / / ’
u s e r=None
passwd=None
r e a l m=None
h e a d e r s = { ’ Content −Type ’ : ’ a p p l i c a t i o n /x−www−form−u r l e n c o d e d ’ }
# T h i s t o k e n s h o u l d a l s o be s e t i n a t a b l e s o t h a t t h e s e l l e r can s e t i t up
# d i n a m i c a l l y and n o t t h r o u g h t h e code . Same g o e s f o r t h e PAGINATE .
p a y p a l _ t o k e n="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
PAGINATE = 20
#########################################################################
# d e f a u l t . py f i l e
#########################################################################
# coding : utf8
import d a t e t i m e
import s t r i n g
i f not s e s s i o n . c a r t : s e s s i o n . c a r t , s e s s i o n . b a l a n c e ={} ,0
app=r e q u e s t . a p p l i c a t i o n
class Connection :
def __init__ ( s e l f , b a s e _ u r l , username , pa ss wor d , r e a l m = None , h e a d e r = { } )
:
s e l f . base_url = base_url
s e l f . username = username
s e l f . password = password
s e l f . realm = realm
s e l f . header = header
6
Paypal Integration in Web2py
i f args :
p a t h += " ? " + ( a r g s )
# c r e a t e a p a s s w o r d manager
password_mgr = u r l l i b 2 . HTTPPasswordMgrWithDefaultRealm ( )
if s e l f . username and s e l f . p a s s w o r d :
# Add t h e username and p a s s w o r d .
password_mgr . add_password ( s e l f . r e a l m , s e l f . b a s e _ u r l , s e l f . username ,
s e l f . password )
h a n d l e r = u r l l i b 2 . HTTPBasicAuthHandler ( password_mgr )
# I n s t a l l the opener .
# Now a l l c a l l s t o u r l l i b 2 . u r l o p e n u s e o u r o p e n e r .
u r l l i b 2 . i n s t a l l _ o p e n e r ( opener )
#C r e a t e a R e q u e s t
r e q= u r l l i b 2 . R e q u e s t ( s e l f . b a s e _ u r l + path , data , s e l f . h e a d e r )
# u s e t h e o p e n e r t o f e t c h a URL
error = ’ ’
try :
r e t=o p e n e r . open ( r e q )
except u r l l i b 2 . HTTPError , e :
ret = e
e r r o r = ’ u r l l i b 2 . HTTPError ’
except u r l l i b 2 . URLError , e :
ret = e
e r r o r = ’ u r l l i b 2 . URLError ’
return ret , e r r o r
def a d d _ t o _ c a r t ( ) :
"""
Add d a t a i n t o t h e s e s s i o n . c a r t d i c t i o n a r y
S e s s i o n . c a r t i s a d i c t i o n a r y w i t h i d p r o d u c t _ i d and v a l u e = q u a n t i t y
Session . balance i s a value with the t o t a l of the t r a n s a c i o n .
A ft e r updating values , r e d i r e c t to checkout
"""
p i d=r e q u e s t . a r g s [ 0 ]
p r o d u c t=db ( db . p r o d u c t . i d==p i d ) . s e l e c t ( ) [ 0 ]
p r o d u c t . u p d a t e _ r e c o r d ( c l i c k e d=p r o d u c t . c l i c k e d +1)
t r y : q t y=s e s s i o n . c a r t [ p i d ]+1
except : q t y=1
s e s s i o n . c a r t [ p i d ]= q t y
s e s s i o n . b a l a n c e+=p r o d u c t . p r i c e
r e d i r e c t (URL( r=r e q u e s t , f= ’ c h e c k o u t ’ ) )
def r e m o v e _ f r o m _ c a r t ( ) :
"""
a l l o w add t o c a r t
7
Paypal Integration in Web2py
"""
p i d=r e q u e s t . a r g s [ 0 ]
p r o d u c t=db ( db . p r o d u c t . i d==p i d ) . s e l e c t ( ) [ 0 ]
i f s e s s i o n . c a r t . has_key ( p i d ) :
s e s s i o n . b a l a n c e −=p r o d u c t . p r i c e
s e s s i o n . c a r t [ p i d ]−=1
i f not s e s s i o n . c a r t [ p i d ] : d e l s e s s i o n . c a r t [ p i d ]
r e d i r e c t (URL( r=r e q u e s t , f= ’ c h e c k o u t ’ ) )
def e m p t y _ c a r t ( ) :
"""
a l l o w add t o c a r t
"""
s e s s i o n . c a r t , s e s s i o n . b a l a n c e ={} ,0
r e d i r e c t (URL( r=r e q u e s t , f= ’ c h e c k o u t ’ ) )
def c h e c k o u t ( ) :
"""
Checkout
"""
p i d s=s e s s i o n . c a r t . k e y s ( )
c a r t ={}
p r o d u c t s ={}
for pid in pids :
p r o d u c t s [ p i d ]= db ( db . p r o d u c t . i d==p i d ) . s e l e c t ( ) [ 0 ]
r e t u r n d i c t ( p r o d u c t s=p r o d u c t s , p a y p a l _ i d=s e s s i o n . p a y p a l _ i d )
Finally, Confirm, at Listing 3 will process the information sent from paypal, with the four step
process described in Figure 1 steps 2,3,4 and 5.
Listing 3: Generic Classes and Function definitions
def c o n f i r m ( ) :
"""
T h i s i s s e t s o a s t o c a p t u r e t h e t r a n s a c t i o n d a t a from p a y p a l
I t c a p t u r e s t h e t r a n s a c t i o n ID from t h e HTTP GET t h a t p a y p a l s e n d s .
And u s i n g t h e t o k e n from v e n d o r p r o f i l e PDT, i t d o e s a form p o s t .
The d a t a from t h e h t t p g e t comes a s v a r s Name V a l u e P a i r s .
"""
i f r e q u e s t . v a r s . has_key ( ’ t x ’ ) :
trans = request . vars . get ( ’ tx ’ )
# Establish connection .
conn = C o n n e c t i o n ( b a s e _ u r l=p r o t o c o l+domain , username=u s e r , p a s s w o r d =
passwd , r e a l m = r e a l m , h e a d e r = h e a d e r s )
d a t a = "cmd=_ n o t i f y −s y n c h&t x="+t r a n s+"&a t="+p a y p a l _ t o k e n
r e s p , e r r o r=conn . r e q u e s t ( ’ / c g i −b i n / w e b s c r ’ , d a t a )
d a t a ={}
i f e r r o r==’ ’ :
respu = resp . read ()
respuesta = respu . s p l i t l i n e s ()
d a t a [ ’ s t a t u s ’ ]= r e s p u e s t a [ 0 ]
i f r e s p u e s t a [0]== ’ SUCCESS ’ :
for r in respuesta [ 1 : ] :
key , v a l = r . s p l i t ( ’= ’ )
8
Paypal Integration in Web2py
d a t a [ k e y ]= v a l
msg= ’ ’
i f d a t a . has_key ( ’memo ’ ) : msg=d a t a [ ’memo ’ ]
form = FORM( " Q u i e r e d e j a r un m e n s a j e con l o s r e g a l o s ? " ,
INPUT ( _name=T( ’ message ’ ) , _type=" t e x t " , _ v a l u e=msg ) ,
INPUT ( _type=" s u b m i t " ) )
i f form . a c c e p t s ( r e q u e s t . v a r s , s e s s i o n ) :
e m a i l=d a t a [ ’ p a y e r _ e m a i l ’ ] . r e p l a c e ( ’ %40 ’ , ’@ ’ )
i d = db . g i f t _ m s g . i n s e r t ( b u y e r=d a t a [ ’ p a y e r _ e m a i l ’ ] , t r a n s a c t=t r a n s ,
msg=form . v a r s . message )
r e s p o n s e . f l a s h=T( ’ Your message w i l l be p a s s e d on t o t h e r e c i p i e n t ’ )
r e d i r e c t (URL( r=r e q u e s t , f= ’ i n d e x ’ ) )
r e t u r n d i c t ( d a t a=data , form=form )
r e t u r n d i c t ( d a t a=d a t a )
else :
d a t a [ ’ s t a t u s ’ ]= ’ FAIL ’
else :
r e d i r e c t (URL( r=r e q u e s t , f= ’ i n d e x ’ ) )
r e t u r n d i c t ( t r a n s=t r a n s )
Just for the shake of completion I am adding a very basic example of confirm.html which you
can see in Listing 4.
Listing 4: default/confirm.html
{{ e x t e n d ’ l a y o u t . html ’ } }
{{ i f d a t a [ ’ s t a t u s ’ ] == ’ SUCCESS ’ : } }
<p><h3>{{=T( ’ Your o r d e r h a s been r e c e i v e d . ’ ) }}</h3></p>
<hr>
<b>{{=T( ’ D e t a i l s ’ ) }}</b><br>
< l i >{{=T( ’ Name : ’ ) }} {{= d a t a [ ’ f i r s t _ n a m e ’ ] } } {{= d a t a [ ’ last_name ’ ] } }</ l i >
< l i >{{=T( ’ P u r c h a s e s f o r e v e n t : ’ ) } } : {{= d a t a [ ’ t r a n s a c t i o n _ s u b j e c t ’ ] } }</ l i >
< l i >{{=T( ’ Amount ’ ) } } : {{= d a t a [ ’ mc_currency ’ ] } } {{= d a t a [ ’ mc_gross ’ ] } }</ l i >
<hr>
{{=form }}
{{ e l s e : } }
{{=T( ’ No c o n f i r m a t i o n r e c e i v e d from p a y p a l . T h i s can be due t o a number o f
r e a s o n s , p l e a s e c h e c k y o u r e m a i l t o s e e i f t h e t r a n s a c t i o n was s u c c e s s f u l
. ’ ) }}
{{ p a s s }}
9
Paypal Integration in Web2py
process if you need to do additional processing of the information from your sales, or if you want
to keep a local database of the actual sales processed.
This is done with IPN. You can find all the documentation related at the documentation site URL
given previously. You will need to turn on the IPN functionality at your seller account, as well as
give a default URL to receive those messages which should be equal to the view in which you pro-
cess them. In the case of this example it would be: http://www.yourdomain.com/yourapp/default/ipn_handler
The process is quite similar to that of PDT, even the variables are the same. The main diffe-
rence is that IPN are sent from paypal until you acknowledge them. The view for this function
(default/ipn_handler.html) can very well be left blank. I am including also the table definition
for logging the messages from paypal.
Anyway, find in Listing 5 is an example of how to set them up
Listing 5: IPN Handler
# At m o d e l s / db . py
######################################################################
db . d e f i n e _ t a b l e ( ’ ipn_msgs ’ ,
F i e l d ( ’ t r a n s _ i d ’ , l a b e l=T( ’ t r a n s a c t i o n i d ’ ) ) ,
F i e l d ( ’ t i m e s t a m p ’ , ’ d a t e t i m e ’ , l a b e l=T( ’ t i m e s t a m p ’ ) ) ,
F i e l d ( ’ t y p e ’ , l a b e l=T( ’ t y p e ’ ) ) ,
F i e l d ( ’ msg ’ , ’ t e x t ’ , l a b e l=T( ’ message ’ ) ) ,
F i e l d ( ’ p r o c e s s e d ’ , ’ b o o l e a n ’ , l a b e l=T( ’ p r o c e s s e d ’ ) ) ,
F i e l d ( ’ t o t a l ’ , ’ d o u b l e ’ , l a b e l=T( ’ t o t a l ’ ) ) ,
F i e l d ( ’ f e e ’ , ’ d o u b l e ’ , l a b e l=T( ’ f e e ’ ) ) ,
F i e l d ( ’ c u r r e n c y ’ , l e n g t h =3, l a b e l=T( ’ c u r r e n c y ’ ) ) ,
F i e l d ( ’ s e c u r i t y _ m s g ’ , l a b e l=T( ’ s e c u r i t y message ’ ) )
)
# At c o n t r o l l e r s / d e f a u l t . py
######################################################################
def i p n _ h a n d l e r ( ) :
"""
Manages t h e i p n c o n n e c t i o n w i t h P a y p a l
Ask PayPal t o c o n f i r m t h i s payment , r e t u r n s t a t u s and d e t a i l s t r i n g s
"""
p a r a m e t e r s = None
parameters = request . vars
i f parameters :
p a r a m e t e r s [ ’ cmd ’ ] = ’ _ n o t i f y −v a l i d a t e ’
params = u r l l i b . u r l e n c o d e ( p a r a m e t e r s )
conn = C o n n e c t i o n ( b a s e _ u r l=p r o t o c o l+domain , username=u s e r , p a s s w o r d =
passwd , r e a l m = r e a l m , h e a d e r = h e a d e r s )
r e s p , e r r o r =conn . r e q u e s t ( ’ / c g i −b i n / w e b s c r ’ , params )
t i m e s t a m p=d a t e t i m e . d a t e t i m e . now ( )
# We a r e g o i n g t o l o g a l l m e s s a g e s c o n f i r m e d by p a y p a l .
i f e r r o r ==’ ’ :
ipn_msg_id = db . ipn_msgs . i n s e r t ( t r a n s _ i d=p a r a m e t e r s [ ’ t x n _ i d ’ ] , t i m e s t a m p
=timestamp , t y p e=r e s p . r e a d ( ) , msg=params ,
t o t a l=p a r a m e t e r s [ ’ mc_gross ’ ] , f e e=p a r a m e t e r s [ ’ mc_fee ’ ] , c u r r e n c y=
p a r a m e t e r s [ ’ mc_currency ’ ] )
# But o n l y i n t e r e s t e d i n p r o c e s s i n g m e s s a g e s t h a t h av e payment s t a t u s
c o m p l e t e d and a r e VERIFIED by p a y p a l .
10
Paypal Integration in Web2py
i f p a r a m e t e r s [ ’ p a y m e n t _ s t a t u s ’]== ’ Completed ’ :
p r o c e s s _ i p n ( ipn_msg_id , p a r a m e t e r s )
Only thing missing would be to process the information received and check for errors or pos-
sible fraud attempts. You can see an example function in Listing 6. Although this is probably
something that would change quite a bit from one project to the next, I hope that it may serve
you as a rough guide.
Listing 6: IPN message processing
def p r o c e s s _ i p n ( ipn_msg_id , param ) :
"""
We p r o c e s s t h e p a r a m e t e r s s e n t from IPN p a y p a l , t o c o r r e c t l y s t o r e t h e
confirmed s a l e s
in the database .
11
Paypal Integration in Web2py
3. Closing up
I hope that this guide has helped you to set up your paypal site using web2py, or at least helped
you understand the basic concepts behind setting up one and the different possibilities that you
have available.
Benigno Calvo Adiego, author of the present article, is co-founder of AlbenDas http://www.
albendas.com and executive director of the IT division at AlbenDas . Has a 11 year career as
system analyst, project manager and IT Director managing external outsourced companies in
various industrial and leisure business. Currently uses Web2Py as a quick integration tool to
quickly adapt to constant changing requirements.
12