How to accept credit card payments (video)

313 views
Skip to first unread message

Massimo Di Pierro

unread,
Mar 6, 2011, 12:57:53 PM3/6/11
to web2py-users
http://www.youtube.com/watch?v=xW5xOdDk-BE

Here are the steps:


# let us go into our web2py folder
cd /Users/mdipierro/Desktop/demo/web2py

# and we create a new app called pos (point of sale)
cd applications
mkdir pos
rm -r pos/*

# we make it a clone of the scaffolding application
cp -r welcome/* pos/
cd pos

open http://127.0.0.1:8000/admin/default/design/welcome

# lets get janrain working!
edit models/db.py
# -*- coding: utf-8 -*-
# this file is released under public domain and you can use without
limitations

#########################################################################
## This scaffolding model makes your app work on Google App Engine too
#########################################################################

if request.env.web2py_runtime_gae: # if running on Google
App Engine
db = DAL('gae') # connect to Google
BigTable
# optional DAL('gae://
namespace')
session.connect(request, response, db = db) # and store sessions
and tickets there
### or use the following lines to store sessions in Memcache
# from gluon.contrib.memdb import MEMDB
# from google.appengine.api.memcache import Client
# session.connect(request, response, db = MEMDB(Client()))
else: # else use a normal
relational database
db = DAL('sqlite://storage.sqlite') # if not, use SQLite or
other DB
## if no need for session
# session.forget()

#########################################################################
## Here is sample code if you need for
## - email capabilities
## - authentication (registration, login, logout, ... )
## - authorization (role based authorization)
## - services (xml, csv, json, xmlrpc, jsonrpc, amf, rss)
## - crud actions
## (more options discussed in gluon/tools.py)
#########################################################################

from gluon.tools import *
mail = Mail() # mailer
auth = Auth(globals(),db) # authentication/
authorization
crud = Crud(globals(),db) # for CRUD helpers
using auth
service = Service(globals()) # for json, xml,
jsonrpc, xmlrpc, amfrpc
plugins = PluginManager()

mail.settings.server = 'logging' or 'smtp.gmail.com:587' # your SMTP
server
mail.settings.sender = 'y...@gmail.com' # your email
mail.settings.login = 'username:password' # your credentials or
None

auth.settings.hmac_key = '<your secret key>' # before
define_tables()
auth.define_tables() # creates all needed
tables
auth.settings.mailer = mail # for user email
verification
auth.settings.registration_requires_verification = False
auth.settings.registration_requires_approval = False
auth.messages.verify_email = 'Click on the link
http://'+request.env.http_host+URL('default','user',args=['verify_email'])+'/%(key)s
to verify your email'
auth.settings.reset_password_requires_verification = True
auth.messages.reset_password = 'Click on the link
http://'+request.env.http_host+URL('default','user',args=['reset_password'])+'/%(key)s
to reset your password'

#########################################################################
## If you need to use OpenID, Facebook, MySpace, Twitter, Linkedin,
etc.
## register with janrain.com, uncomment and customize following
from gluon.contrib.login_methods.rpx_account import RPXAccount
auth.settings.actions_disabled=['register','change_password','request_reset_password']
api_key = open('/Users/mdipierro/
janrain_api_key.txt','r').read().strip()
auth.settings.login_form = RPXAccount(request,
api_key=api_key,domain='web2py',
url = "http://localhost:8000/%s/default/user/login" %
request.application)
## other login methods are in gluon/contrib/login_methods
#########################################################################

crud.settings.auth = None # =auth to enforce
authorization on crud
@@END@@

open http://127.0.0.1:8000/admin/default/peek/welcome/models/db.py

# then we create a custom model
edit models/db_pos.py
# we need a table to store products
db.define_table('product',
Field('name',notnull=True,unique=True),
Field('price','double'),
Field('description','text'),
Field('image','upload'),
Field('sortable','integer'),
auth.signature,
format='%(name)s')

# and one table to store sales of products to users
db.define_table('sale',
Field('invoice'),
Field('creditcard'),
Field('buyer',db.auth_user),
Field('product',db.product),
Field('quantity','integer'),
Field('price','double'),
Field('shipped','boolean',default=False),
auth.signature),

# we also make a session cart, just in case
session.cart = session.cart or {}
@@END@@

# we can now already access the model via app-admin
# try create some products
open http://127.0.0.1:8000/pos/appadmin

# we can also create prducts programmatically here is one way
mkdir private
cd private
# let's get an image
wget http://www.clker.com/cliparts/5/1/9/a/11954319101307220149molumen_cardboard_box.svg.med.png
sh:convert 11954319101307220149molumen_cardboard_box.svg.med.png
box.jpg
open box.jpg
cd ..
edit private/script.py
import os

# find the box
path = os.path.join(request.folder,'private','box.jpg')

# insert it in a database record
id = db.product.insert(name='Box',price=2.99,description='the best
box',

image=db.product.image.store(open(path,'rb'),path))
print 'record created, id=',id

# and do not forget to commit
db.commit()
@@END@@

# now let's run it
cd ../..
python web2py.py -S pos -M -N -R applications/pos/private/script.py
cd applications/pos

# let us write a controller with the actions we need
rm controllers/default.py
edit controllers/default.py
# list of products
def index():
products = db(db.product).select(orderby=db.product.sortable)
return locals()

# login, registration, etcetera
def user():
return dict(form=auth())

# an action to download uploaded images
def download():
return response.download(request,db)

# an action to expose web services
def call():
session.forget()
return service()
@@END@@

# let us try listing the products we created
open http://127.0.0.1:8000/pos/default/index

# let us make this better by creating a view
rm views/default/index.html
edit views/default/index.html
{{extend 'layout.html'}}
<h1>Products</h1>
{{for p in products:}}
<div>
<h2>{{=p.name}} - ${{=p.price}}</h2>
<img src="{{=URL('download',args=p.image)}}" align="right"/>
{{=MARKMIN(p.description)}}
</div>
{{pass}}
@@END@@

open http://127.0.0.1:8000/pos/default/index

# and we make a simple menu
edit models/menu.py
response.menu=[
(T('Home'),False,URL('default','index')),
(T('Cart'),False,URL('default','cart')),
(T('Buy'),False,URL('default','buy')),
]
@@END@@

open http://127.0.0.1:8000/pos/default/index

# now we create some more logic for our app
edit controllers/default.py
# list of products
def index():
products = db(db.product).select(orderby=db.product.sortable)
return locals()

# login, registration, etcetera
def user():
return dict(form=auth())

# an action to download uploaded images
def download():
return response.download(request,db)

# an action to expose web services
def call():
session.forget()
return service()

# an action to see and process a shopping cart
@auth.requires_login()
def cart():
return dict(cart=session.cart)

# this is for paying
@auth.requires_login()
def pay():
form = SQLFORM.factory(Field('creditcard'))
if form.accepts(request,session):
for key, value in session.cart.items():
db.sale.insert(buyer=auth.user.id,
product = key,
quantity = value,
price = db.product(key).price,
creditcard = form.vars.creditcard)
session.cart.clear()
session.flash = 'Thank you for your order'
redirect(URL('index'))
return dict(cart=session.cart,form=form)

# an action to add and remove items from the shopping cart
def cart_callback():
id = int(request.vars.id)
if request.vars.action == 'add':
session.cart[id]=session.cart.get(id,0)+1
if request.vars.action == 'sub':
session.cart[id]=max(0,session.cart.get(id,0)-1)
return str(session.cart[id])
@@END@@

# we add a button from the index page
edit views/default/index.html
{{extend 'layout.html'}}
<h1>Products</h1>
{{for p in products:}}
<div>
<h2>{{=p.name}} - ${{=p.price}}</h2>
<img src="{{=URL('download',args=p.image)}}" align="right"/>
{{=MARKMIN(p.description)}}
<span id="item{{=p.id}}">{{=session.cart.get(p.id,0)}}</span> in
cart - <button
onclick="ajax('{{=URL('cart_callback',vars=dict(id=p.id,action='add'))}}',
[],'item{{=p.id}}')">add to cart</button>
</div>
{{pass}}
@@END@@

# and a view for the cart so visitors can see their products
edit views/default/cart.html
{{extend 'layout.html'}}
<h1>Your Shopping Cart</h1>
<table width="100%">
{{for id, qty in cart.items():}}{{p=db.product(id)}}
<tr>
<td>{{=p.name}}</td>
<td>${{=p.price}}</td>
<td id="item{{=id}}">{{=qty}}</td>
<td><button
onclick="ajax('{{=URL('cart_callback',vars=dict(id=p.id,action='add'))}}',
[],'item{{=id}}')">add</button></td>
<td><button
onclick="ajax('{{=URL('cart_callback',vars=dict(id=p.id,action='sub'))}}',
[],'item{{=id}}')">sub</button></td>
</tr>
{{pass}}
</table>
<a href="{{=URL('pay')}}">checkout</a>
@@END@@

# and a view for the buy action
edit views/default/pay.html
{{extend 'layout.html'}}
<h1>Checkout</h1>
{{=form}}
@@END@@

# now try to add something to your cart and buy the products
open http://127.0.0.1:8000/pos

# Let's change the layout
wget http://web2py.com/layouts/static/plugin_layouts/plugins/web2py.plugin.layout_Commission.w2p
tar zxvf web2py.plugin.layout_Commission.w2p

# time to accept real payments
edit models/aim.py
from gluon.contrib.AuthorizeNet import AIM

def process(creditcard,expiration,total,cvv,tax,invoice):
payment = AIM('cnpdev4289', 'SR2P8g4jdEn7vFLQ', True)
payment.setTransaction(creditcard, expiration.replace('/',''),
total, cvv, tax, invoice)
payment.process()
return payment.isApproved()
@@END@@

# check out the new layout
open http://127.0.0.1:8000/pos

edit controllers/default.py
# list of products
def index():
products = db(db.product).select(orderby=db.product.sortable)
return locals()

# login, registration, etcetera
def user():
return dict(form=auth())

# an action to download uploaded images
def download():
return response.download(request,db)

# an action to expose web services
def call():
session.forget()
return service()


# an action to see and process a shopping cart
@auth.requires_login()
def cart():
return dict(cart=session.cart)

# time to pay ... now for real
@auth.requires_login()
def pay():
import uuid
invoice = str(uuid.uuid4())
total = sum(db.product(id).price*qty for id,qty in
session.cart.items())
form =
SQLFORM.factory(Field('creditcard',default='4427802641004797'),
Field('expiration',default='12/2012'),
Field('cvv',default='123'),
Field('total','double',default=total,writable=False))

if form.accepts(request,session):
if process(form.vars.creditcard,form.vars.expiration,
total,form.vars.cvv,0.0,'invoice'):
for key, value in session.cart.items():
db.sale.insert(invoice=invoice,
buyer=auth.user.id,
product = key,
quantity = value,
price = db.product(key).price,
creditcard = form.vars.creditcard)
session.cart.clear()
session.flash = 'Thank you for your order'
redirect(URL('index'))
else:
response.flash = "payment rejected (please call XXX)"
return dict(cart=session.cart,form=form)

# an action to add and remove items from the shopping cart
def cart_callback():
id = int(request.vars.id)
if request.vars.action == 'add':
session.cart[id]=session.cart.get(id,0)+1
if request.vars.action == 'sub':
session.cart[id]=max(0,session.cart.get(id,0)-1)
return str(session.cart[id])
@@END@@

# done. now we have a functional web store

Plumo

unread,
Mar 6, 2011, 5:02:05 PM3/6/11
to web...@googlegroups.com
haha - TTS and Civ 4 soundtrack!

rochacbruno

unread,
Mar 6, 2011, 5:10:21 PM3/6/11
to web...@googlegroups.com
What did you use for the voice sintetization?

Enviado via iPhone

Massimo Di Pierro

unread,
Mar 6, 2011, 5:16:28 PM3/6/11
to web2py-users
OSX comes a os utility called say

example:

$ say hello world

speaks better than me.

On Mar 6, 4:10 pm, rochacbruno <rochacbr...@gmail.com> wrote:
> What did you use for the voice sintetization?
>
> Enviado via iPhone
>
> Em 06/03/2011, às 14:57, Massimo Di Pierro <massimo.dipie...@gmail.com> escreveu:
>
>
>
>
>
>
>
> >http://www.youtube.com/watch?v=xW5xOdDk-BE
>
> > Here are the steps:
>
> > # let us go into our web2py folder
> > cd /Users/mdipierro/Desktop/demo/web2py
>
> > # and we create a new app called pos (point of sale)
> > cd applications
> > mkdir pos
> > rm -r pos/*
>
> > # we make it a clone of the scaffolding application
> > cp -r welcome/* pos/
> > cd pos
>
> > openhttp://127.0.0.1:8000/admin/default/design/welcome
> > mail.settings.sender = '...@gmail.com'         # your email
> > openhttp://127.0.0.1:8000/admin/default/peek/welcome/models/db.py
> > openhttp://127.0.0.1:8000/pos/appadmin
>
> > # we can also create prducts programmatically here is one way
> > mkdir private
> > cd private
> > #  let's get an image
> > wgethttp://www.clker.com/cliparts/5/1/9/a/11954319101307220149molumen_car...
> > openhttp://127.0.0.1:8000/pos/default/index
>
> > # let us make this better by creating a view
> > rm views/default/index.html
> > edit views/default/index.html
> > {{extend 'layout.html'}}
> > <h1>Products</h1>
> > {{for p in products:}}
> >  <div>
> >    <h2>{{=p.name}} - ${{=p.price}}</h2>
> >    <img src="{{=URL('download',args=p.image)}}" align="right"/>
> >    {{=MARKMIN(p.description)}}
> >  </div>
> > {{pass}}
> > @@END@@
>
> > openhttp://127.0.0.1:8000/pos/default/index
>
> > # and we make a simple menu
> > edit models/menu.py
> > response.menu=[
> >   (T('Home'),False,URL('default','index')),
> >   (T('Cart'),False,URL('default','cart')),
> >   (T('Buy'),False,URL('default','buy')),
> > ]
> > @@END@@
>
> > openhttp://127.0.0.1:8000/pos/default/index
> >    <td><button...
>
> read more »

villas

unread,
Mar 6, 2011, 5:48:14 PM3/6/11
to web2py-users
> speaks better than me.

LOL, no way -- we missed the accent! :)

Marin Pranjic

unread,
Mar 6, 2011, 6:21:32 PM3/6/11
to web...@googlegroups.com
+1 :D

howesc

unread,
Mar 6, 2011, 6:36:43 PM3/6/11
to web...@googlegroups.com
I see that you are storing the credit card number in the database, can anyone comment on web2py servers and PCI compliance?  I'm using other methods to integrate with paypal, and now authorize.net so that i don't store the credit card info on my server.  This is more out of fear that real knowledge about how to do it right.

thanks,

christian

Massimo Di Pierro

unread,
Mar 6, 2011, 6:38:43 PM3/6/11
to web2py-users
The only problem is that it reads this:

say web2py

the same as this

say web two p why

but it should be more like this:

say web2pie

(it may be technically right and I wrong... or just need a stack).

also

say appadmin

sonds better if I say

say app-admin

Jonathan Lundell

unread,
Mar 6, 2011, 7:12:04 PM3/6/11
to web...@googlegroups.com
On Mar 6, 2011, at 3:38 PM, Massimo Di Pierro wrote:
>
> The only problem is that it reads this:
>
> say web2py
>
> the same as this
>
> say web two p why
>
> but it should be more like this:
>
> say web2pie
>
> (it may be technically right and I wrong... or just need a stack).
>
> also
>
> say appadmin
>
> sonds better if I say
>
> say app-admin

I've done some work with this, and I generally create a plain-text reading script, making spelling and punctuation changes as necessary to get the pronunciations and rhythm right.

So yes, change web2py to web2-pi.

You'll also find that it always pronounces 'read' as 'reed', so if you want the past tense, change it to 'red'. Likewise 'lead'.

You can also sometimes improve things by adding commas strategically.

I'm not sure if this is standard, but I've got the system set up to speak selected text when I hit shift-option-control-s. Set that up in the Text-to-Speech system preference pane if necessary; it's quite handy. I've also got a script that converts a text file to mp3.

Jonathan Lundell

unread,
Mar 6, 2011, 7:23:56 PM3/6/11
to web...@googlegroups.com
On Mar 6, 2011, at 4:12 PM, Jonathan Lundell wrote:
>
> I'm not sure if this is standard, but I've got the system set up to speak selected text when I hit shift-option-control-s. Set that up in the Text-to-Speech system preference pane if necessary; it's quite handy. I've also got a script that converts a text file to mp3.

I see that 'say' will also send audio to a file.

BTW, go to http://www.cepstral.com/demos/ and try Vittoria.

rochacbruno

unread,
Mar 6, 2011, 8:35:39 PM3/6/11
to web...@googlegroups.com
I found a utility called Festival that runs on ubuntu.

sudo apt-get festival

That sounds good too.

Brian M

unread,
Mar 6, 2011, 9:23:18 PM3/6/11
to web...@googlegroups.com
Yeah, I'm no PCI expert, but I'm pretty sure that storing the credit card number in the DB (un-encrypted/un-masked) is a no-no and in the very least is a bad idea. But Massimo's just illustrating how to use Authorize.Net


BTW, Massimo you can put GPL in as the license for the contrib/AuthorizeNet.py - The original source declared it GPL last month (and linked to your version) http://www.johnconde.net/blog/integrate-the-authorizenet-aim-api-with-python-3-2/

weheh

unread,
Apr 17, 2012, 8:52:59 PM4/17/12
to web...@googlegroups.com
You guys should be using my website for voiceover: YAKiToMe.com. The version on the web is old, but the new one is coming online very shortly (as soon as I can finish it) and it will be 100% web2py.


On Monday, March 7, 2011 9:35:39 AM UTC+8, rochacbruno wrote:
I found a utility called Festival that runs on ubuntu.

sudo apt-get festival

That sounds good too.

Reply all
Reply to author
Forward
0 new messages