I use the attached modules/stripeform.py, which is mostly based upon the contrib/stripe.py It imports the python stripe module : https://github.com/stripe/stripe-python.git
I use it like : @auth.requires_login(otherwise=lambda: helper.flash( T("You need to log in to extend your subscription")))def buy(): from stripeform import clean_stripe_session clean_stripe_session() #To prevent reuse of idempotent uuid for stripe transactions form = SQLFORM.factory( # snipped … ) form = form.process(keepvalues=True) if form.accepted: # error logic else: session.buyvars = form.vars redirect(URL('pay')) elif form.errors: # pragma: no branch response.flash = T('form has errors') return dict(form=form) @auth.requires_login(otherwise=lambda: helper.flash( T("You need to log in to extend your subscription")))def pay(): if not session.buyvars: redirect(URL('buy')) from stripeform import StripeForm total = # TODO calculate total from session.buyvars content cart_form = TABLE(# snipped : table displaying what is buyed (from session.buyvars ) payment_id = db.payments.insert(amount=total) form = StripeForm( pk=STRIPE_PUBLISHABLE_KEY, sk=STRIPE_SECRET_KEY, amount=total, # (amount is in cents) currency='eur', currency_symbol='€', description="…", more = { 'metadata': {'payment_id':payment_id}} ).process() if form.accepted: pi = form.response from datetime import datetime import pytz db.payments[payment_id]= dict(…) # Update db with buyed items if necessary session.flash = T("Thank you!") redirect(URL(c='default', f='index')) elif form.errors: # pragma: no branch response.flash = form.response.errors del db.payments[payment_id] return dict(cart_form=cart_form, form=form) And the views for pay looks like: {{extend 'layout.html'}} <div class="container"> <div class="row"> <h2>{{=T("Payment")}}</h2> <div class="col-md-7 col-sm-12 col-md-push-5"> <h3>{{=T("Cart Content")}}</h3> <div class="table-responsive" id="payment"> {{=cart_form}} </div> </div> <div class="col-md-5 col-md-pull-7 col-sm-12"> {{=form}} </div> … On Thu, Mar 23, 2017 at 8:47 PM Joe Barnhart joe.barnh...@gmail.com <http://mailto:joe.barnh...@gmail.com> wrote: I use Stripe all the time. My sites (web2py and rails) have charged over > $10M thru stripe so I'd have to say it works pretty well. > > My favorite way to use it is to use their Javascript pop-up box, which > prevents any CC info from even getting into my server logs. I actually > started using it before the stripe.py contrib, so I haven't used the > contrib library very much. > > I'll look over the question here and post later when I have a minute. > > --- Joe B. > > > On Tuesday, March 21, 2017 at 8:19:21 PM UTC-7, Scott Hunter wrote: > > Has anyone been able to use Stripe's Checkout with web2py? If so, how did > you do it? I'm having trouble getting the token it generates back. > > - Scott > > -- > Resources: > - http://web2py.com > - http://web2py.com/book (Documentation) > - http://github.com/web2py/web2py (Source code) > - https://code.google.com/p/web2py/issues/list (Report Issues) > --- > You received this message because you are subscribed to the Google Groups > "web2py-users" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to web2py+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- Resources: - http://web2py.com - http://web2py.com/book (Documentation) - http://github.com/web2py/web2py (Source code) - https://code.google.com/p/web2py/issues/list (Report Issues) --- You received this message because you are subscribed to the Google Groups "web2py-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to web2py+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
import logging import urllib from hashlib import sha1 import gluon.contrib.simplejson from gluon.storage import Storage from gluon.html import DIV, A, P, XML, INPUT import stripe logger = logging.getLogger("web2py.app.foundit.stripeform") logger.setLevel(logging.DEBUG) def clean_stripe_session(): from gluon import current current.session.stripe_uuid = None class Stripe: """ Use in WEB2PY (guaranteed PCI compliant) def pay(): from gluon.contrib.stripe import StripeForm form = StripeForm( pk=STRIPE_PUBLISHABLE_KEY, sk=STRIPE_SECRET_KEY, amount=150, # $1.5 (amount is in cents) description="Nothing").process() if form.accepted: payment_id = form.response['id'] redirect(URL('thank_you')) elif form.errors: redirect(URL('pay_error')) return dict(form=form) Low level API: key='<api key>' d = Stripe(key).charge( amount=100, # 1 dollar!!!! currency='usd', description='test charge') print d print Stripe(key).check(d['id']) print Stripe(key).refund(d['id']) Sample output (python dict): {u'fee': 0, u'description': u'test charge', u'created': 1321242072, u'refunded': False, u'livemode': False, u'object': u'charge', u'currency': u'usd', u'amount': 100, u'paid': True, u'id': u'ch_sdjasgfga83asf', u'card': {u'exp_month': 5, u'country': u'US', u'object': u'card', u'last4': u'4242', u'exp_year': 2012, u'type': u'Visa'}} if paid is True than transaction was processed """ def __init__(self, key): self.key = key stripe.api_key = key def charge(self, amount, # in cents currency='usd', idempotency_key=None, token=None, description='test charge', more=None): if not token: return Storage() # We should only get token from script.js res = Storage() from gluon import current T = current.T res.paid = False try: customer = Customer() if not customer.has_card(token): token = customer.create_card(token) d = { 'amount': amount, 'currency': currency, 'customer': customer.id, 'source': token, 'description': description, 'idempotency_key': idempotency_key } if more: d.update(more) res = stripe.Charge.create(**d) except stripe.error.CardError, e: # it's a decline, stripe.error.CardError will be caught from gluon import current T = current.T res.errors = T(e.message) res.reason = T(e.json_body['error'].get('decline_code', '')) except stripe.error.StripeError, e: logger.error("%s", e) res.errors = str(XML(T("Unexpected error when communicating with Stripe. If the problem persists, " "let us know at <a href='mailto://webmaster@%s'>webmaster@%s</a>", current.SITE_NAME))) return res def check(self, charge_id): return stripe.Charge.retrieve(charge_id) def refund(self, charge_id): return stripe.Refund.create(charge=charge_id) class Customer(object): from gluon import current def __init__(self): pass def __getattr__(self, name): return self._stripe_customer.get(name) @property @current.cache('_stripe_customer%s' % current.session.auth.user.id, time_expire=3600, cache_model=current.cache.ram) def _stripe_customer(self): """ Get stripe customer as stored in the stripe_id extra field of the auth_user table. Create a customer if non exists """ from gluon import current user = current.session.auth.user db = current.globalenv['db'] stripe_id = db.auth_user(user.id).stripe_id if not stripe_id: # get one logger.debug("Create Stripe customer") customer = stripe.Customer.create( description="%s %s" % (user.first_name, user.last_name), email=user.email, metadata={'user_id': user.id}, ) logger.debug("Done created Stripe customer") db.auth_user[user.id] = dict(stripe_id=customer.id) else: logger.debug("Retrieve Stripe customer") customer = stripe.Customer.retrieve(stripe_id) logger.debug("Done retrieving Stripe customer") return customer @property @current.cache('valid_cards%s' % current.session.auth.user.id, time_expire=3600, cache_model=current.cache.ram) def valid_cards(self): cust_id = self._stripe_customer.id cards = stripe.Customer.retrieve(cust_id).sources.all() from datetime import datetime now = datetime.now() res = [c for c in cards['data'] if c['exp_year'] > now.year or (c['exp_year'] == now.year and c['exp_month'] >= now.month)] return res def create_card(self, card_data): """ card_data may be a dict or a token returned by stripe.js """ from gluon import current card = self._stripe_customer.sources.create(source=card_data) # clear cache for valid_cards current.cache.ram.clear('valid_cards%s' % current.session.auth.user.id) return card def has_card(self, card_token): return card_token in [c.id for c in self.valid_cards] class StripeForm(object): def __init__(self, pk, sk, amount, # in cents description, currency='usd', currency_symbol='$', security_notice=True, disclosure_notice=True, template=None, extraenv=None, more=None): from gluon import current, redirect, URL from gluon.utils import web2py_uuid if not (current.request.is_local or current.request.is_https): redirect(URL(args=current.request.args, scheme='https')) self.pk = pk self.sk = sk self.stripe = Stripe(sk) self.amount = amount self.description = description self.currency = currency self.currency_symbol = currency_symbol self.security_notice = security_notice self.disclosure_notice = disclosure_notice self.template = template or TEMPLATE self.accepted = None self.more = more self.errors = None self.signature = sha1(repr((current.session.auth.user.id, self.amount, self.description))).hexdigest() self.uuid = current.session.stripe_uuid or web2py_uuid() self.extraenv = extraenv self.customer = None if not current.session.stripe_uuid: current.session.stripe_uuid = self.uuid def process(self): from gluon import current request = current.request if request.post_vars: if self.signature == request.post_vars.signature: self.response = self.stripe.charge( token=request.post_vars.stripeToken, amount=self.amount, idempotency_key=self.uuid, description=self.description, more=self.more, currency=self.currency) if self.response.get('paid', False): current.session.stripe_uuid = None self.accepted = True return self self.errors = True return self def xml(self): from gluon.template import render from gluon import current T = current.T if self.accepted: return str(T("Your payment was processed successfully")) cards = default_card = None try: self.customer = Customer() cards=self.customer.valid_cards default_card=self.customer.default_source except stripe.error.APIConnectionError, e: return str(DIV(XML(T("We've got some connexion error when communicating with Stripe. If the problem persists, " "let us know at <a href='mailto://webmaster@%s'>webmaster@%s</a>", current.SITE_NAME, current.SITE_NAME)), _class="error")) except stripe.error.StripeError, e: logger.error("%s", e) return str(DIV(XML(T("Unexpected error when communicating with Stripe. If the problem persists, " "let us know at <a href='mailto://webmaster@%s'>webmaster@%s</a>", current.SITE_NAME, current.SITE_NAME)), _class="error")) context = dict(amount=self.amount, signature=self.signature, pk=self.pk, currency_symbol=self.currency_symbol, security_notice=self.security_notice, disclosure_notice=self.disclosure_notice, cards=cards, error=(self.errors and self.response.errors) or '', default_card=default_card) context.update({'T':T, 'A':A, 'DIV':DIV, 'XML':XML, 'INPUT':INPUT}) return render(content=self.template, context=context) # TODO use web2py FOMR in order to protect from double submission TEMPLATE = """ <script type="text/javascript" src="https://js.stripe.com/v2/"></script> <script> jQuery(function(){ // This identifies your website in the createToken call below Stripe.setPublishableKey('{{=pk}}'); var stripeResponseHandler = function(status, response) { var jQueryform = jQuery('#payment-form'); if (response.error) { // Show the errors on the form jQuery('.payment-errors').text(response.error.message).show(); jQueryform.find('button').prop('disabled', false); } else { // token contains id, last4, and card type var token = response.id; // Insert the token into the form so it gets submitted to the server var tokenInput = jQuery('<input type="hidden" name="stripeToken" />'); jQueryform.append(tokenInput.val(token)); // and re-submit jQueryform.get(0).submit(); } }; jQuery(function(jQuery) { jQuery('#payment-form').submit(function(e) { var jQueryform = jQuery(this); // Disable the submit button to prevent repeated clicks jQueryform.find('button').prop('disabled', true); Stripe.createToken(jQueryform, stripeResponseHandler); // Prevent the form from submitting with the default action return false; }); }); }); </script> <h3>{{=T("Payment Amount: %(currency)s %(amount)s", dict(currency=currency_symbol, amount="%.2f" % (0.01*amount)))}}</h3> {{ if cards:}} <div class="panel panel-default"> <div class="panel-body"> <h4>{{=T("Pay With an Existing Card")}}</h4> <form action="" method="POST" id="payment-existing-card" class="form-horizontal"> {{for i,card in enumerate(cards):}} <div class="form-row form-group"> <div class="controls col-sm-offset-1"> {{=INPUT(_type='radio', _name='stripeToken', _id='stripeToken%d' % i, _value=card.id, value=default_card)}} {{=card.brand}} ************{{=card.last4}} {{=card.exp_month}}/{{=card.exp_year}} </div></div> {{pass}} <div class="form-row form-group"> <div class="controls col-sm-offset-4 col-lg-8"> <button type="submit" value="submitcard" class="btn btn-primary">{{=T("Submit Payment")}}</button> </div></div> <input type="hidden" name="signature" value="{{=signature}}" /> </form> </div> </div> {{pass}} <div class="panel panel-default"> <div class="panel-body"> <h4>{{=T("Pay With a New Card")}}</h4> <form action="" method="POST" id="payment-form" class="form-horizontal"> <div class="form-row form-group"> <label class="col-sm-4 control-label">{{=T("Card Number")}}</label> <div class="controls col-sm-8"> <input type="text" size="20" style="width:200px" data-stripe="number" name="number" placeholder="4242424242424242" class="form-control"/> </div> </div> <div class="form-row form-group"> <label class="col-sm-4 control-label">{{=T("Expiration")}}</label> <div class="controls col-sm-8"> <input type="text" size="2" style="width:60px; display:inline-block" name='exp_month' data-stripe="exp-month" placeholder="MM" class="form-control"/> / <input type="text" size="4" style="width:80px; display:inline-block" name='exp_year' data-stripe="exp-year" placeholder="YYYY" class="form-control"/> </div> </div> <div class="form-row form-group"> <label class="col-sm-4 control-label">{{=T("CVC")}}</label> <div class="controls col-sm-8"> <input type="text" size="4" style="width:80px" data-stripe="cvc" name='cvc' placeholder="XXX" class="form-control"/> <a href="http://en.wikipedia.org/wiki/Card_Verification_Code" target="_blank">{{=T("What is this?")}}</a> </div> </div> <div class="form-row form-group"> <div class="controls col-sm-offset-4 col-lg-8"> <button type="submit" value="submit" class="btn btn-primary">{{=T("Submit Payment")}}</button> <div class="payment-errors error">{{=error}}</div> </div> </div> <input type="hidden" name="signature" value="{{=signature}}" /> </form> </div> </div> """