Sei sulla pagina 1di 12

Paypal Integration in Web2py

Paypal Integration in Web2py

This document is meant to be an introduction to Paypal integration in web2py,


it does, by no means, cover all the possible integrations with Paypal, and is mostly
centered on what Paypal names Standard Integration. The examples given are proven
at the time of writing this article, but they should be taken only as a lead and
starting point rather than reference. For that, please use both paypal’s and web2py’s
documentation.

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

<img a l t=" " border=" 0 " 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 / s c r / p i x e l .


g i f " width=" 1 " h e i g h t=" 1 ">
<form a c t i o n=" h t t p : / /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 " />
<i n p u t type=" h i d d e n " name="cmd" v a l u e=" _ c a r t " />
<i n p u t type=" h i d d e n " name=" u p l o a d " v a l u e=" 1 " />
<i n p u t type=" h i d d e n " name=" c h a r s e t " v a l u e=" u t f −8">
<i n p u t type=" h i d d e n " name=" c u r r e n c y _ c o d e " v a l u e="EUR" />
<i n p u t type=" h i d d e n " name=" d i s p l a y " v a l u e=" 1 " />
<i n p u t type=" h i d d e n " name=" s h o p p i n g _ u r l " v a l u e=" h t t p : / /www. m i c r o p o l i x s h o p . com
/ g i f t l i s t / d e f a u l t / g l i s t " /> <!−− Not r e a l l y n e c e s s a r y , o n l y i f want t o
a l l o w c o n t i n u e S h o p p i n g −−>
<i n p u t type=" h i d d e n " name=" n o t i f y _ u r l " v a l u e=" h t t p : / /www. m i c r o p o l i x s h o p . com/
g i f t l i s t / d e f a u l t / i p n _ h a n d l e r " /> <!−− Or l e a v e b l a n k and s e t u p d e f a u l t u r l
a t p a y p a l −−>
<i n p u t type=" h i d d e n " name=" r e t u r n " v a l u e=" h t t p : / /www. m i c r o p o l i x s h o p . com/
g i f t l i s t / d e f a u l t / c o n f i r m " /> <!−− Or l e a v e b l a n k and s e t u p d e f a u l t u r l a t
p a y p a l −−>
<i n p u t type=" h i d d e n " name=" custom " v a l u e="{{= s e s s i o n . e v e n t _ c o d e }} " />
{{ k=1}}
{{ f o r i d , p r o d u c t i n p r o d u c t s . i t e m s ( ) : } }
<i n p u t type=" h i d d e n " name=" item_number_{{=k }} " v a l u e="{{= p r o d u c t . e x t _ c o d e }}
" />
<i n p u t type=" h i d d e n " name=" item_name_{{=k }} " v a l u e="{{= p r o d u c t . name }} " />
<i n p u t type=" h i d d e n " name=" q u a n t i t y _ {{=k }} " v a l u e="{{= s e s s i o n . c a r t [ s t r ( i d )
] } } " />
<i n p u t type=" h i d d e n " name=" d i s c o u n t _ r a t e _ {{=k }} " v a l u e=" 15 " /> <!−− i e ,
wants a 15 % on a l l a r t i c l e s a l w a y s −−>
<i n p u t type=" h i d d e n " name=" tax_{{=k }} " v a l u e="{{= p r o d u c t . p r i c e ∗ p r o d u c t .
t a x _ r a t e }} " />
<i n p u t type=" h i d d e n " name=" amount_{{=k }} " v a l u e="{{= p r o d u c t . p r i c e }} " />
{{ k+=1}}
{{ p a s s }}
</ form>

A couple of comments regarding Listing 1:


In all cases, to move from sandbox to production, the url to use only needs to change
from https://www.sandbox.paypal.com to https://www.paypal.com
You can create the buttons using the create new button functionality at your seller account,
and then reuse the code it would give you having chosen language and the type of button
to use. That way, you will get the correct link to the image to be used for your paypal
button.
The field cmd with value _cart is very important, read the documentation to see the
possible values of this field depending on what you want to do. I am assuming a cart
scenario on this example.
The fields shopping_url, notify_ulr and return, can be omited if you setup your seller
account profile. If you set it up here, this takes precedence over the default values setup
in your seller account.
The field custom I think is rather important, as is one of the few fields that let you intro-

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).

2.2. Checkout Confirmation / Payment Data Transfer


Once the customer finishes paying through paypal, he will be redirected to your website auto-
matically if it is setup in the account and he is already a paypal user (else he will have to click
on an button to return to your site). This section shows you how to set your application so
that it will receive the payment data confirmation from paypal and show a confirmation to your
customer.
You can read detailed documentation on this subject here:
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/
howto_html_paymentdatatransfer
where you can see how to set it up in detail, so that you know where to get your token from,
which you need to identify yourself to paypal to confirm and get the data. In an case, refer to

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

# Setup paypal l o g i n e m a i l ( s e l l e r i d ) i n the s e s s i o n


# I store paypal_id in a table
s e s s i o n . p a y p a l _ i d=myorg . p a y p a l _ i d
import u r l l i b 2 , u r l l i b
import d a t e t i m e

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

def r e q u e s t ( s e l f , r e s o u r c e , d a t a = None , a r g s = None ) :


path = r e s o u r c e

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 )

# c r ea t e " opener " ( OpenerDirector i n s t a n c e )


opener = u r l l i b 2 . build_opener ( handler )

# 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 }}

{{=T( ’ Your t r a n s a c t i o n h a s f i n i s h e d , you s h o u l d r e c e i v e an e m a i l o f y o u r


p u r c h a s e . ’ ) }}<br>
{{=T( ’ I n c a s e you ha ve an a c c o u n t a t p a y p a l , you can c h e c k y o u r t r a n s a c t i o n
d e t a i l s at ’ ) }} <a h r e f =’ h t t p s : / /www. p a y p a l . es ’>www. p a y p a l . e s</a>

2.3. IPN: Instant Payment Notification


As mentioned before, one cannot trust the PDT process to receive the information from all
transactions as a great number of things can happen. Thus, you need to implement an additional

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 .

param −− r e q u e s t . v a r s from IPN message from p a y p a l


"""
# Check i f t r a n s a c t i o n _ i d h a s a l r e a d y been p r o c e s s e d .
q u e r y 1 = db . ipn_msgs . t r a n s _ i d==param [ ’ t x n _ i d ’ ]
q u e r y 2 = db . ipn_msgs . p r o c e s s e d == True
rows = db ( q u e r y 1 & q u e r y 2 ) . s e l e c t ( )
i f not rows :
t r a n s = param [ ’ t x n _ i d ’ ]
p a y e r _ e m a i l = param [ ’ p a y e r _ e m a i l ’ ]
n_ item s = i n t ( param [ ’ num_cart_items ’ ] )
pay_date = param [ ’ payment_date ’ ]
t o t a l = param [ ’ mc_gross ’ ]
c u r r = param [ ’ mc_currency ’ ]
e v e n t _ c o d e = param [ ’ custom ’ ]
i f param . has_key ( ’memo ’ ) : memo=param [ ’memo ’ ]
e v e n t _ i d = db ( db . e v e n t . code==e v e n t _ c o d e ) . s e l e c t ( db . e v e n t . i d )
i f not e v e n t _ i d :
db . ipn_msgs [ ipn_msg_id ]= d i c t ( s e c u r i t y _ m s g=T( ’ E v e n t d o e s n o t e x i s t ’ ) )
else :
e r r o r=F a l s e
f o r i i n r a n g e ( 1 , n_i tems +1) :
p r o d u c t _ c o d e = param [ ’ item_number ’+s t r ( i ) ]
q t t y = param [ ’ q u a n t i t y ’+s t r ( i ) ]
l i n e _ t o t a l = f l o a t ( param [ ’ mc_gross_ ’+s t r ( i ) ] ) + f l o a t ( param [ ’ mc_tax ’+
str ( i ) ])
p r o d u c t=db ( db . p r o d u c t . e x t _ c o d e==p r o d u c t _ c o d e ) . s e l e c t ( db . p r o d u c t . i d )
i f not p r o d u c t :
db . ipn_msgs [ ipn_msg_id ]= d i c t ( s e c u r i t y _ m s g=T( ’ P r o d u c t code d o e s n o t
exist ’))
e r r o r=True
else :
db . g l i s t . i n s e r t ( e v e n t=e v e n t _ i d [ 0 ] , p r o d u c t=p r o d u c t [ 0 ] , b u y e r=
p a y e r _ e m a i l , t r a n s a c t=t r a n s ,
p u r c h a s e _ d a t e=pay_date , q u a n t i t y _ s o l d=q t t y , p r i c e=l i n e _ t o t a l ,
o b s e r v a t i o n s=memo)
i f not e r r o r : db . ipn_msgs [ ipn_msg_id ]= d i c t ( p r o c e s s e d=True )

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

Potrebbero piacerti anche