Paypal slice question

106 views
Skip to first unread message

Andrew Evans

unread,
Mar 14, 2011, 12:07:22 AM3/14/11
to web2py-users
So I have finally gotten around to trying to implement paypal into my site. But I am facing some issues and was hoping some one could help me figure out my questions.


I am trying to figure out how to use this example http://www.web2pyslices.com/main/slices/take_slice/106 but I need to know: What is the database schema for sitesettings, also how do I include the full_url() function also what is db.song_purchases in the ipn.

Any ideas

I would appreciate any help ty

Cheers


pbreit

unread,
Mar 14, 2011, 12:18:49 AM3/14/11
to web...@googlegroups.com
I'll put this into a slice when the new slice site comes. But here's an example of PayPal's Express Checkout interface which is more modern. There are two main functions in the controller and a module.

def paypal():
    session.recipient_id = ''
    item = db(db.item.id==request.args(0)).select().first()
    item.update_record(status='pending')
    item.seller = db(item.created_by==db.auth_user.id).select().first()
    purchase = db.purchase.insert(
        status = 'pending',
        item = item.id,
        amount = item.current_price,
        currency ='USD',
        item_amount = item.current_price,
        payment_method = 'paypal',
        recipient_id = item.seller.email)
    paypal = local_import('paypal', reload=True)
    row = db(db.purchase.id==purchase).select().first()
    res = paypal.setec(paypal_config, row)
    if res['ACK'][0]=='Success':
        session.recipient_id = item.seller.email
        token = res['TOKEN'][0]
        redirect('%s%s' % (url, token))
    else:
        return dict(res=res)

def paypal_return():
    token = request.vars.token
    recipient_id = session.recipient_id
    paypal = local_import('paypal', reload=True)
    payment = paypal.getec(paypal_config, token, recipient_id)
    if payment['ACK'][0]=='Success':
        invoice = int(payment['PAYMENTREQUEST_0_INVNUM'][0].split('-')[1])
        purchase = db(db.purchase.id==invoice).select().first()
        purchase.update_record(email=payment['EMAIL'][0],
                               name=payment['PAYMENTREQUEST_0_SHIPTONAME'][0],
                               first_name=payment['FIRSTNAME'][0],
                               last_name=payment['LASTNAME'][0],
                               street1=payment['PAYMENTREQUEST_0_SHIPTOSTREET'][0],
                               street2='',
                               city=payment['PAYMENTREQUEST_0_SHIPTOCITY'][0],
                               state=payment['PAYMENTREQUEST_0_SHIPTOSTATE'][0],
                               zip=payment['PAYMENTREQUEST_0_SHIPTOZIP'][0],
                               country=payment['PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE'][0],
                               address_status=payment['ADDRESSSTATUS'][0],
                               amount=payment['PAYMENTREQUEST_0_AMT'][0],
                               tax_amount=payment['PAYMENTREQUEST_0_TAXAMT'][0])                          
        payerid = request.vars.PayerID
        res = paypal.doec(paypal_config, token, purchase, payerid)
        if res['ACK'][0]=='Success':
            purchase.update_record(status='completed',
                                   completed_on=request.now,
                                   payment_id=res['PAYMENTINFO_0_TRANSACTIONID'][0])
            item = db(db.item.id==purchase.item.id).select().first()
            item.update_record(status='sold')
            process_sale(item=item, purchase=purchase)
            return dict(item=item)
        else:
            app_logging.info(payment)
            session.flash = 'error'
            return dict(item=False)
    else:
        app_logging.info(payment)
        session.flash = 'error'
        return dict(item=False)


import urllib
import cgi

paypal_config = {
'user': 'API USERNAME',
'pwd': 'API PASSWORD',
'signature': 'API SIGNATURE',
'version': '66.0',
'button_source': ''
}

def setec(config, purchase):
    invoice = '%s-%s' % ('INVOICE', purchase.id)
    values =   {'USER': config['user'],
                'PWD': config['pwd'],
                'SIGNATURE': config['signature'],
                'VERSION': config['version'],
                'METHOD': 'SetExpressCheckout',
                'SUBJECT': purchase.recipient_id,
                'PAYMENTREQUEST_0_AMT': purchase.amount,
                'PAYMENTREQUEST_0_CURRENCYCODE': purchase.currency,
                'RETURNURL': 'http://127.0.0.1:8000/order/paypal_return',
                'CANCELURL': 'http://127.0.0.1:8000/order/cancel/%s' % (purchase.item),
                'PAYMENTREQUEST_0_PAYMENTACTION': 'Sale',
                'PAYMENTREQUEST_0_INVNUM': invoice,
                'PAYMENTREQUEST_0_NOTIFYURL': 'http://mysite.com/ipn'}
    return cgi.parse_qs(urllib.urlopen('https://api-3t.paypal.com/nvp', urllib.urlencode(values)).read())

def getec(config, token, recipient_id):
    values =   {'USER': config['user'],
                'PWD': config['pwd'],
                'SIGNATURE': config['signature'],
                'VERSION': config['version'],
                'SUBJECT': recipient_id,
                'METHOD': 'GetExpressCheckoutDetails',
                'TOKEN': token}
    return cgi.parse_qs(urllib.urlopen('https://api-3t.paypal.com/nvp', urllib.urlencode(values)).read())

def doec(config, token, purchase, payerid):
    values =   {'USER': config['user'],
                'PWD': config['pwd'],
                'SIGNATURE': config['signature'],
                'VERSION': config['version'],
                'METHOD': 'DoExpressCheckoutPayment',
                'BUTTONSOURCE': config['button_source'],
                'SUBJECT': purchase.recipient_id,
                'TOKEN': token,
                'PAYMENTREQUEST_0_PAYMENTACTION': 'Sale',
                'PAYMENTREQUEST_0_DESC': 'Order #%s' % (purchase.id),
                'PAYMENTREQUEST_0_AMT': purchase.amount,
                'PAYMENTREQUEST_0_CURRENCYCODE': purchase.currency,
                'PAYERID': payerid}
    return cgi.parse_qs(urllib.urlopen('https://api-3t.paypal.com/nvp', urllib.urlencode(values)).read())

Andrew Evans

unread,
Mar 14, 2011, 12:23:39 AM3/14/11
to web...@googlegroups.com
I assume at import urlib and below is the module does it matter what I name the controller or module?

*cheers

Andrew Evans

unread,
Mar 14, 2011, 12:33:13 AM3/14/11
to web...@googlegroups.com
Do I need to add the button code in my view?

what is this file  'PAYMENTREQUEST_0_NOTIFYURL': 'http://mysite.com/ipn'}


how do I add this to my site... sorry I have been wrecking my head over this the past week and am going crazy your code looks way simpler though imo just some questions I have :-)


*cheers

Andrew

pbreit

unread,
Mar 14, 2011, 3:36:06 AM3/14/11
to web...@googlegroups.com
Sorry for the brevity. I think can put together a whole app and post it. Maybe Monday.

Massimo Di Pierro

unread,
Mar 14, 2011, 9:30:37 AM3/14/11
to web2py-users
:-)

Andrew Evans

unread,
Mar 14, 2011, 2:07:36 PM3/14/11
to web...@googlegroups.com
Cool can you post a link to this post :-)

would be greatly appreciated

*cheers

Andrew

howesc

unread,
Mar 14, 2011, 5:17:12 PM3/14/11
to web...@googlegroups.com
Andrew,

I have not looked into the express checkout solution - but i think that is because i wanted full cart functionality.

sorry about my sloppy code on the slice.  guess i did not sanitize it well enough...

sitesettings is a table that i have that is just name, value pairs (the following is in my db.py):

# a table of settings.
db.define_table('settings',
                SQLField('name', 'text', notnull=True,
                         unique=True,
                         requires=[IS_NOT_EMPTY(),
                                   IS_NOT_IN_DB(db,'settings.name')]),
                SQLField('value', 'text'),
                migrate=migrate)

sitesettings = buildDict(db.settings, "value")

#@TODO: create global dictionaries that have the lookup tables, and don't
# reload them on every page hit.
def buildDict(table, field="id"):
    """
    Creates a dictionary, actually a L{gluon.sql.SQLStorage} object from
    a lookup table.

    @type table: SQLTable
    @param table: the SQLTable to make into a SQLStorage object.  The table
    is expected to have an ID and a name field.

    @rtype: SQLStorage
    @return: SQLStorage object with the names of the rows as keys, and id's as
      values.
    """
    rows = db(db[table].id>0).select()
    enum_dict = SQLStorage()
    for r in rows:
        enum_dict[r.name] = r[field]
    return enum_dict

I put the full_url() method in a file in the models directory so it is available to all controllers.

the song_purchases table is where i track what users have purchased:
#a table of purchases, one record per user per cue_point(song) purchased
db.define_table('song_purchases',
                SQLField('cue_point', db.cue_point, ondelete='RESTRICT',
                         requires=IS_NULL_OR(IS_IN_DB(db, db.cue_point.id, '%(name)s')),
                         notnull=False),
                SQLField('song_media', db.song_media, notnull=False,
                         ondelete='RESTRICT',
                         requires=IS_NULL_OR(IS_IN_DB(db,db.song_media.id,'%(name)s'))),
                SQLField('auth_user', db.auth_user, ondelete='RESTRICT',
                         requires=IS_IN_DB(db, db.auth_user.id,
                                           '%(first_name)s %(last_name)s'),
                         notnull=True),
                SQLField('date', 'datetime', notnull=True, default=now),
                #when the download expires.
                #@TODO: make the default expires time configurable.
                SQLField('expires', 'datetime', notnull=True,
                         default=now + datetime.timedelta(2)),
                #the paypal transaction id in case we need to look this up
                SQLField('transaction_id', 'text', notnull=True,
                         requires=IS_NOT_EMPTY()),
                migrate=migrate)

I hope that helps.  let me know if you decide to go this route and are still having problems.  

christian

Andrew Evans

unread,
Mar 14, 2011, 10:23:33 PM3/14/11
to web...@googlegroups.com
Cool

ok so when I pass the sitesettings attributes to my view what am I passing?


{{     attributes = {"cert_id":sitesettings.paypal_cert_id,
"cmd":"_cart",
"business":sitesettings.cart_business,
"add":"1",
"custom":auth.user.id,
"item_name":artist_name + ": " + song['name'],
"item_number":"song-"+str(song['cue_point_id']),
"amount":song['cost'],
"currency_code":"USD",
"shopping_url":full_url('http',r=request,args=request.args),
"return":full_url('https', r=request, c='account', \
f='alldownloads'),
}
encattrs = paypal_encrypt(attributes, sitesettings)
}}
or is that just as an example...show I be using my paypal values?

*cheers

Andrew

howesc

unread,
Mar 15, 2011, 4:50:55 PM3/15/11
to web...@googlegroups.com
yes, you should be putting your paypal values in there.  i abstracted them to a site-settings table so an admin could update them (and we could switch them easily between test and production values).

make sense?

cfh

pbreit

unread,
Mar 15, 2011, 6:35:41 PM3/15/11
to web...@googlegroups.com
I'm struggling to complete a re-write of the code to be more generic. Still working. And I'm going to try to put it in Massimo's tutorial format.

Andrew Evans

unread,
Mar 15, 2011, 11:29:31 PM3/15/11
to web...@googlegroups.com
Makes sense I wanted them in a table also. But the site settings table is already in existance, with that code you provided how does that work? Or am I totally missing something?

*cheers
Reply all
Reply to author
Forward
0 new messages