Merge branch 'newpayment' into relaunch

Conflicts:
	frontend/views.py
	payment/manager.py
pull/1/head
Raymond Yee 2012-09-07 13:46:09 -07:00
commit f639aa02dd
19 changed files with 1094 additions and 1 deletions

7
payment/forms.py Normal file
View File

@ -0,0 +1,7 @@
from django import forms
import logging
logger = logging.getLogger(__name__)
class StripePledgeForm(forms.Form):
stripe_token = forms.CharField(required=False, widget=forms.HiddenInput())

268
payment/stripelib.py Normal file
View File

@ -0,0 +1,268 @@
# https://github.com/stripe/stripe-python
# https://stripe.com/docs/api?lang=python#top
from datetime import datetime
from pytz import utc
import stripe
try:
import unittest
from unittest import TestCase
except:
from django.test import TestCase
from django.utils import unittest
# if customer.id doesn't exist, create one and then charge the customer
# we probably should ask our users whether they are ok with our creating a customer id account -- or ask for credit
# card info each time....
# should load the keys for Stripe from db -- but for now just hardcode here
# moving towards not having the stripe api key for the non profit partner in the unglue.it code -- but in a logically
# distinct application
try:
from regluit.core.models import Key
STRIPE_PK = Key.objects.get(name="STRIPE_PK").value
STRIPE_SK = Key.objects.get(name="STRIPE_SK").value
STRIPE_PARTNER_PK = Key.objects.get(name="STRIPE_PARTNER_PK").value
STRIPE_PARTNER_SK = Key.objects.get(name="STRIPE_PARTNER_SK").value
logger.info('Successful loading of STRIPE_*_KEYs')
except Exception, e:
# currently test keys for Gluejar and for raymond.yee@gmail.com as standin for non-profit
STRIPE_PK = 'pk_0EajXPn195ZdF7Gt7pCxsqRhNN5BF'
STRIPE_SK = 'sk_0EajIO4Dnh646KPIgLWGcO10f9qnH'
STRIPE_PARTNER_PK ='pk_0AnIkNu4WRiJYzxMKgruiUwxzXP2T'
STRIPE_PARTNER_SK = 'sk_0AnIvBrnrJoFpfD3YmQBVZuTUAbjs'
# set default stripe api_key to that of unglue.it
stripe.api_key = STRIPE_SK
# https://stripe.com/docs/testing
TEST_CARDS = (
('4242424242424242', 'Visa'),
('4012888888881881', 'Visa'),
('5555555555554444', 'MasterCard'),
('5105105105105100', 'MasterCard'),
('378282246310005', 'American Express'),
('371449635398431', 'American Express'),
('6011111111111117', 'Discover'),
('6011000990139424', 'Discover'),
('30569309025904', "Diner's Club"),
('38520000023237', "Diner's Club"),
('3530111333300000', 'JCB'),
('3566002020360505','JCB')
)
ERROR_TESTING = dict((
('ADDRESS1_ZIP_FAIL', ('4000000000000010', 'address_line1_check and address_zip_check will both fail')),
('ADDRESS1_FAIL', ('4000000000000028', 'address_line1_check will fail.')),
('ADDRESS_ZIP_FAIL', ('4000000000000036', 'address_zip_check will fail.')),
('CVC_CHECK_FAIL', ('4000000000000101', 'cvc_check will fail.')),
('BAD_ATTACHED_CARD', ('4000000000000341', 'Attaching this card to a Customer object will succeed, but attempts to charge the customer will fail.')),
('CHARGE_DECLINE', ('4000000000000002', 'Charges with this card will always be declined.'))
))
# types of errors / when they can be handled
#card_declined: Use this special card number - 4000000000000002.
#incorrect_number: Use a number that fails the Luhn check, e.g. 4242424242424241.
#invalid_expiry_month: Use an invalid month e.g. 13.
#invalid_expiry_year: Use a year in the past e.g. 1970.
#invalid_cvc: Use a two digit number e.g. 99.
def filter_none(d):
return dict([(k,v) for (k,v) in d.items() if v is not None])
# if you create a Customer object, then you'll be able to charge multiple times. You can create a customer with a token.
# https://stripe.com/docs/tutorials/charges
def card (number=TEST_CARDS[0][0], exp_month='01', exp_year='2020', cvc=None, name=None,
address_line1=None, address_line2=None, address_zip=None, address_state=None, address_country=None):
card = {
"number": number,
"exp_month": str(exp_month),
"exp_year": str(exp_year),
"cvc": str(cvc) if cvc is not None else None,
"name": name,
"address_line1": address_line1,
"address_line2": address_line2,
"address_zip": address_zip,
"address_state": address_state,
"address_country": address_country
}
return filter_none(card)
class StripeClient(object):
def __init__(self, api_key=STRIPE_SK):
self.api_key = api_key
# key entities: Charge, Customer, Token, Event
@property
def charge(self):
return stripe.Charge(api_key=self.api_key)
@property
def customer(self):
return stripe.Customer(api_key=self.api_key)
@property
def token(self):
return stripe.Token(api_key=self.api_key)
@property
def transfer(self):
return stripe.Transfer(api_key=self.api_key)
@property
def event(self):
return stripe.Event(api_key=self.api_key)
def create_token(self, card):
return stripe.Token(api_key=self.api_key).create(card=card)
def create_customer (self, card=None, description=None, email=None, account_balance=None, plan=None, trial_end=None):
"""card is a dictionary or a token"""
# https://stripe.com/docs/api?lang=python#create_customer
customer = stripe.Customer(api_key=self.api_key).create(
card=card,
description=description,
email=email,
account_balance=account_balance,
plan=plan,
trial_end=trial_end
)
# customer.id is useful to save in db
return customer
def create_charge(self, amount, currency="usd", customer=None, card=None, description=None ):
# https://stripe.com/docs/api?lang=python#create_charge
# customer or card required but not both
# charge the Customer instead of the card
# amount in cents
charge = stripe.Charge(api_key=self.api_key).create(
amount=int(100*amount), # in cents
currency=currency,
customer=customer.id,
description=description
)
return charge
def refund_charge(self, charge_id):
# https://stripe.com/docs/api?lang=python#refund_charge
ch = stripe.Charge(api_key=self.api_key).retrieve(charge_id)
ch.refund()
return ch
def list_all_charges(self, count=None, offset=None, customer=None):
# https://stripe.com/docs/api?lang=python#list_charges
return stripe.Charge(api_key=self.api_key).all(count=count, offset=offset, customer=customer)
# what to work through?
# can't test Transfer in test mode: "There are no transfers in test mode."
#pledge scenario
# bad card -- what types of erros to handle?
# https://stripe.com/docs/api#errors
# what errors are handled in the python library and how?
#
# Account?
# https://stripe.com/docs/api#event_types
# events of interest -- especially ones that do not directly arise immediately (synchronously) from something we do -- I think
# especially: charge.disputed
# I think following (charge.succeeded, charge.failed, charge.refunded) pretty much sychronous to our actions
# customer.created, customer.updated, customer.deleted
# transfer
# I expect the ones related to transfers all happen asynchronously: transfer.created, transfer.updated, transfer.failed
# When will the money I charge with Stripe end up in my bank account?
# Every day, we transfer the money that you charged seven days previously?that is, you receive the money for your March 1st charges on March 8th.
# pending payments?
# how to tell whether money transferred to bank account yet
# best practices for calling Events -- not too often.
class PledgeScenarioTest(TestCase):
@classmethod
def setUpClass(cls):
print "in setUp"
cls._sc = StripeClient(api_key=STRIPE_SK)
# valid card
card0 = card()
cls._good_cust = cls._sc.create_customer(card=card0, description="test good customer", email="raymond.yee@gmail.com")
# bad card
test_card_num_to_get_BAD_ATTACHED_CARD = ERROR_TESTING['BAD_ATTACHED_CARD'][0]
card1 = card(number=test_card_num_to_get_BAD_ATTACHED_CARD)
cls._cust_bad_card = cls._sc.create_customer(card=card1, description="test bad customer", email="rdhyee@gluejar.com")
def test_charge_good_cust(self):
charge = self._sc.create_charge(10, customer=self._good_cust, description="$10 for good cust")
print charge.id
def test_error_creating_customer_with_declined_card(self):
# should get a CardError upon attempt to create Customer with this card
_card = card(number=card(ERROR_TESTING['CHARGE_DECLINE'][0]))
self.assertRaises(stripe.CardError, self._sc.create_customer, card=_card)
def test_charge_bad_cust(self):
# expect the card to be declined -- and for us to get CardError
self.assertRaises(stripe.CardError, self._sc.create_charge, 10,
customer = self._cust_bad_card, description="$10 for bad cust")
@classmethod
def tearDownClass(cls):
# clean up stuff we create in test
print "in tearDown"
cls._good_cust.delete()
print "list of customers"
print [(i, c.id, c.description, datetime.fromtimestamp(c.created, tz=utc), c.account_balance) for(i, c) in enumerate(cls._sc.customer.all()["data"])]
print "list of charges"
print [(i, c.id, c.amount, c.currency, c.description, datetime.fromtimestamp(c.created, tz=utc), c.paid, c.fee, c.disputed, c.amount_refunded, c.failure_message, c.card.fingerprint, c.card.last4) for (i, c) in enumerate(cls._sc.charge.all()['data'])]
# can retrieve events since a certain time
print "list of events", cls._sc.event.all()
# [(i, e.id, e.type, e.created, e.pending_webhooks, e.data) for (i,e) in enumerate(s.event.all()['data'])]
def suite():
testcases = [PledgeScenarioTest]
#testcases = []
suites = unittest.TestSuite([unittest.TestLoader().loadTestsFromTestCase(testcase) for testcase in testcases])
#suites.addTest(LibraryThingTest('test_cache'))
#suites.addTest(SettingsTest('test_dev_me_alignment')) # give option to test this alignment
return suites
# IPNs/webhooks: https://stripe.com/docs/webhooks
# how to use pending_webhooks ?
# all events
# https://stripe.com/docs/api?lang=python#list_events
if __name__ == '__main__':
#unittest.main()
suites = suite()
#suites = unittest.defaultTestLoader.loadTestsFromModule(__import__('__main__'))
unittest.TextTestRunner().run(suites)

View File

@ -0,0 +1,119 @@
{% extends "basepledge.html" %}
{% load humanize %}
{% block title %}Balanced{% endblock %}
{% block extra_extra_head %}
<link type="text/css" rel="stylesheet" href="/static/css/campaign.css" />
<link type="text/css" rel="stylesheet" href="/static/css/pledge.css" />
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css" type="text/css">
<style type="text/css">
[name="marketplace_eid"] {
width: 300px;
}
[name^="expiration"] {
width: 50px;
}
[name="security_code"] {
width: 50px;
}
code { display: block; }
pre { color: green; }
</style>
{% endblock %}
{% block doccontent %}
<h1>Balanced Sample - Collect Credit Card Information</h1>
<div class="row">
<div class="span6">
<form id="payment" method="POST">
{% csrf_token %}
<div>
<label>Card Number</label>
<input name="card_number" value="4111111111111111" autocomplete="off">
</div>
<div>
<label>Expiration</label>
<input name="expiration_month" value="1"> / <input name="expiration_year" value="2020">
</div>
<div>
<label>Security Code</label>
<input name="security_code" value="123" autocomplete="off">
</div>
<button>Submit Payment Data</button>
</form>
</div>
</div>
<div id="result"></div>
<script type="text/javascript" src="https://js.balancedpayments.com/v1/balanced.js"></script>
<script type="text/javascript">
var $j = jQuery.noConflict();
var marketplaceUri = '{{MARKETPLACE_URI}}';
var debug = function (tag, content) {
$j('<' + tag + '>' + content + '</' + tag + '>').appendTo('#result');
};
try {
balanced.init(marketplaceUri);
} catch (e) {
debug('code', 'You need to set the marketplaceUri variable');
}
function balancedCallback(response) {
var tag = (response.status < 300) ? 'pre' : 'code';
debug(tag, JSON.stringify(response));
switch (response.status) {
case 201:
// response.data.uri == uri of the card resource, submit to your server
var form$ = $j('form#payment');
var card_uri = response.data.uri;
// insert the token into the form so it gets submitted to the server
form$.append("<input type='hidden' name='card_uri' value='" + card_uri + "' />");
// and submit
form$.get(0).submit();
case 400:
case 403:
// missing/malformed data - check response.error for details
break;
case 402:
// we couldn't authorize the buyer's credit card - check response.error for details
break;
case 404:
// your marketplace URI is incorrect
break;
default:
// we did something unexpected - check response.error for details
break;
}
}
var tokenizeCard = function(e) {
e.preventDefault();
var $form = $j('form#payment');
var cardData = {
card_number: $form.find('[name="card_number"]').val(),
expiration_month: $form.find('[name="expiration_month"]').val(),
expiration_year: $form.find('[name="expiration_year"]').val(),
security_code: $form.find('[name="security_code"]').val()
};
balanced.card.create(cardData, balancedCallback);
// prevent the form from submitting with the default action
return false;
};
$j('#payment').submit(tokenizeCard);
if (window.location.protocol === 'file:') {
alert("balanced.js does not work when included in pages served over file:// URLs. Try serving this page over a webserver. Contact support@balancedpayments.com if you need assistance.");
}
</script>
{% endblock %}

View File

@ -0,0 +1,27 @@
{% extends "basepledge.html" %}
{% load humanize %}
{% block title %}Stripe{% endblock %}
{% block extra_extra_head %}
<link type="text/css" rel="stylesheet" href="/static/css/campaign.css" />
<link type="text/css" rel="stylesheet" href="/static/css/pledge.css" />
<link href="/static/stripe/tag.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="/static/stripe/tag.js"></script>
{% endblock %}
{% block doccontent %}
Stripe Test!:
<span class="payment-errors"></span>
<form action="" method="post" id="payment-form">
{% csrf_token %}
<payment key="{{STRIPE_PK}}"></payment>
<input type="submit" value="Submit">
</form>
{% endblock %}

View File

@ -0,0 +1,24 @@
{% extends "basepledge.html" %}
{% load humanize %}
{% block title %}WePay{% endblock %}
{% block extra_extra_head %}
<link type="text/css" rel="stylesheet" href="/static/css/campaign.css" />
<link type="text/css" rel="stylesheet" href="/static/css/pledge.css" />
<script type="text/javascript" src="https://www.wepay.com/js/iframe.wepay.js"></script>{% endblock %}
{% block doccontent %}
<div id="checkout_div"></div>
<script type="text/javascript">
WePay.iframe_checkout("checkout_div", "{{checkout_uri}}");
</script>
{% endblock %}

View File

@ -1,5 +1,6 @@
from django.conf.urls.defaults import *
from django.conf import settings
from regluit.payment.views import StripeView
urlpatterns = patterns(
"regluit.payment.views",
@ -21,6 +22,7 @@ if settings.DEBUG:
url(r"^testfinish", "testFinish"),
url(r"^testrefund", "testRefund"),
url(r"^testmodify", "testModify"),
url(r"^stripe/test", StripeView.as_view())
)

View File

@ -1,6 +1,11 @@
from regluit.payment.manager import PaymentManager
from regluit.payment.models import Transaction
from regluit.core.models import Campaign, Wishlist
from regluit.payment.stripelib import STRIPE_PK
from regluit.payment.forms import StripePledgeForm
from django.conf import settings
from django.core.urlresolvers import reverse
from django.shortcuts import render_to_response
@ -12,6 +17,9 @@ from django.views.decorators.csrf import csrf_exempt
from django.test.utils import setup_test_environment
from django.template import RequestContext
from django.views.generic.edit import FormView
from django.views.generic.base import TemplateView
from unittest import TestResult
@ -283,5 +291,25 @@ def checkStatus(request):
def _render(request, template, template_vars={}):
return render_to_response(template, template_vars, RequestContext(request))
class StripeView(FormView):
template_name="stripe.html"
form_class = StripePledgeForm
def get_context_data(self, **kwargs):
context = super(StripeView, self).get_context_data(**kwargs)
context.update({
'STRIPE_PK':STRIPE_PK
})
return context
def form_valid(self, form):
stripe_token = form.cleaned_data["stripe_token"]
# e.g., tok_0C0k4jG5B2Oxox
#
return HttpResponse("stripe_token: {0}".format(stripe_token))

View File

@ -28,3 +28,4 @@ pycrypto
django-maintenancemode
django-smtp-ssl
django-ckeditor
stripe

View File

@ -53,4 +53,5 @@ requests==0.9.1
selenium==2.24.0
South==0.7.3
ssh==1.7.13
stripe==1.7.4
wsgiref==0.1.2

58
static/stripe/tag.css Executable file
View File

@ -0,0 +1,58 @@
payment, .payment {
position: relative;
display: block;
padding: 15px 20px;
max-width: 300px;
overflow: hidden;
box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-sizing: border-box;
}
payment label, .payment label {
display: block;
padding: 5px 0;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
payment input, .payment input {
padding: 5px 5px;
box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-sizing: border-box;
}
payment .number input, .payment .number input {
padding: 7px 40px 7px 7px;
width: 100%;
}
payment .expiry input, payment .cvc input {
width: 45px;
}
payment .expiry em {
display: none;
}
payment .cvc,
.payment .cvc {
float: right;
text-align: right;
}
payment .expiry,
.payment .expiry {
float: left;
}
payment .message,
.payment .message {
display: block;
}

405
static/stripe/tag.dev.js Executable file
View File

@ -0,0 +1,405 @@
(function() {
var $, global, script,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__slice = [].slice;
$ = this.jQuery || this.Zepto;
if (!$) {
throw 'jQuery/Zepto required';
}
this.PaymentTag = (function() {
PaymentTag.replaceTags = function(element) {
var _this = this;
if (element == null) {
element = document.body;
}
return $('payment, .payment-tag', element).each(function(i, tag) {
return new _this({
el: tag
}).render();
});
};
PaymentTag.prototype.defaults = {
tokenName: 'stripe_token',
token: true,
cvc: true
};
function PaymentTag(options) {
var _ref, _ref1;
if (options == null) {
options = {};
}
this.changeCardType = __bind(this.changeCardType, this);
this.restrictNumeric = __bind(this.restrictNumeric, this);
this.formatNumber = __bind(this.formatNumber, this);
this.handleToken = __bind(this.handleToken, this);
this.submit = __bind(this.submit, this);
this.$el = options.el || '<payment />';
this.$el = $(this.$el);
options.key || (options.key = this.$el.attr('key') || this.$el.attr('data-key'));
if ((_ref = options.cvc) == null) {
options.cvc = !((this.$el.attr('nocvc') != null) || (this.$el.attr('data-nocvc') != null));
}
if ((_ref1 = options.token) == null) {
options.token = !((this.$el.attr('notoken') != null) || (this.$el.attr('data-notoken') != null));
}
options.form || (options.form = this.$el.parents('form'));
this.options = $.extend({}, this.defaults, options);
if (this.options.key) {
this.setKey(this.options.key);
}
this.setForm(this.options.form);
this.$el.delegate('.number input', 'keydown', this.formatNumber);
this.$el.delegate('.number input', 'keyup', this.changeCardType);
this.$el.delegate('input[type=tel]', 'keypress', this.restrictNumeric);
}
PaymentTag.prototype.render = function() {
this.$el.html(this.constructor.view(this));
this.$number = this.$('.number input');
this.$cvc = this.$('.cvc input');
this.$expiryMonth = this.$('.expiry input.expiryMonth');
this.$expiryYear = this.$('.expiry input.expiryYear');
this.$message = this.$('.message');
return this;
};
PaymentTag.prototype.renderToken = function(token) {
this.$token = $('<input type="hidden">');
this.$token.attr('name', this.options.tokenName);
this.$token.val(token);
return this.$el.html(this.$token);
};
PaymentTag.prototype.setForm = function($form) {
this.$form = $($form);
return this.$form.bind('submit.payment', this.submit);
};
PaymentTag.prototype.setKey = function(key) {
this.key = key;
return Stripe.setPublishableKey(this.key);
};
PaymentTag.prototype.validate = function() {
var expiry, valid;
valid = true;
this.$('div').removeClass('invalid');
this.$message.empty();
if (!Stripe.validateCardNumber(this.$number.val())) {
valid = false;
this.handleError({
code: 'invalid_number'
});
}
expiry = this.expiryVal();
if (!Stripe.validateExpiry(expiry.month, expiry.year)) {
valid = false;
this.handleError({
code: 'expired_card'
});
}
if (this.options.cvc && !Stripe.validateCVC(this.$cvc.val())) {
valid = false;
this.handleError({
code: 'invalid_cvc'
});
}
if (!valid) {
this.$('.invalid input:first').select();
}
return valid;
};
PaymentTag.prototype.createToken = function(callback) {
var complete, expiry,
_this = this;
complete = function(status, response) {
if (response.error) {
return callback(response.error);
} else {
return callback(null, response);
}
};
expiry = this.expiryVal();
return Stripe.createToken({
number: this.$number.val(),
cvc: this.$cvc.val() || null,
exp_month: expiry.month,
exp_year: expiry.year
}, complete);
};
PaymentTag.prototype.submit = function(e) {
if (e != null) {
e.preventDefault();
}
if (e != null) {
e.stopImmediatePropagation();
}
if (!this.validate()) {
return;
}
if (this.pending) {
return;
}
this.pending = true;
this.disableInputs();
this.trigger('pending');
this.$el.addClass('pending');
return this.createToken(this.handleToken);
};
PaymentTag.prototype.handleToken = function(err, response) {
this.enableInputs();
this.trigger('complete');
this.$el.removeClass('pending');
this.pending = false;
if (err) {
return this.handleError(err);
} else {
this.trigger('success', response);
this.$el.addClass('success');
if (this.options.token) {
this.renderToken(response.id);
}
this.$form.unbind('submit.payment', this.submit);
return this.$form.submit();
}
};
PaymentTag.prototype.formatNumber = function(e) {
var digit, lastDigits, value;
digit = String.fromCharCode(e.which);
if (!/^\d+$/.test(digit)) {
return;
}
value = this.$number.val();
if (Stripe.cardType(value) === 'American Express') {
lastDigits = value.match(/^(\d{4}|\d{4}\s\d{6})$/);
} else {
lastDigits = value.match(/(?:^|\s)(\d{4})$/);
}
if (lastDigits) {
return this.$number.val(value + ' ');
}
};
PaymentTag.prototype.restrictNumeric = function(e) {
var char;
if (e.shiftKey || e.metaKey) {
return true;
}
if (e.which === 0) {
return true;
}
char = String.fromCharCode(e.which);
return !/[A-Za-z]/.test(char);
};
PaymentTag.prototype.cardTypes = {
'Visa': 'visa',
'American Express': 'amex',
'MasterCard': 'mastercard',
'Discover': 'discover',
'Unknown': 'unknown'
};
PaymentTag.prototype.changeCardType = function(e) {
var map, name, type, _ref;
type = Stripe.cardType(this.$number.val());
if (!this.$number.hasClass(type)) {
_ref = this.cardTypes;
for (name in _ref) {
map = _ref[name];
this.$number.removeClass(map);
}
return this.$number.addClass(this.cardTypes[type]);
}
};
PaymentTag.prototype.handleError = function(err) {
if (err.message) {
this.$message.text(err.message);
}
switch (err.code) {
case 'card_declined':
this.invalidInput(this.$number);
break;
case 'invalid_number':
case 'incorrect_number':
this.invalidInput(this.$number);
break;
case 'invalid_expiry_month':
this.invalidInput(this.$expiryMonth);
break;
case 'invalid_expiry_year':
case 'expired_card':
this.invalidInput(this.$expiryYear);
break;
case 'invalid_cvc':
this.invalidInput(this.$cvc);
}
this.$('label.invalid:first input').select();
this.trigger('error', err);
return typeof console !== "undefined" && console !== null ? console.error('Stripe error:', err) : void 0;
};
PaymentTag.prototype.invalidInput = function(input) {
input.parent().addClass('invalid');
return this.trigger('invalid', [input.attr('name'), input]);
};
PaymentTag.prototype.expiryVal = function() {
var month, prefix, trim, year;
trim = function(s) {
return s.replace(/^\s+|\s+$/g, '');
};
month = trim(this.$expiryMonth.val());
year = trim(this.$expiryYear.val());
if (year.length === 2) {
prefix = (new Date).getFullYear();
prefix = prefix.toString().slice(0, 2);
year = prefix + year;
}
return {
month: month,
year: year
};
};
PaymentTag.prototype.enableInputs = function() {
var $elements;
$elements = this.$el.add(this.$form).find(':input');
return $elements.each(function() {
var $item, _ref;
$item = $(this);
return $elements.attr('disabled', (_ref = $item.data('olddisabled')) != null ? _ref : false);
});
};
PaymentTag.prototype.disableInputs = function() {
var $elements;
$elements = this.$el.add(this.$form).find(':input');
return $elements.each(function() {
var $item;
$item = $(this);
$item.data('olddisabled', $item.attr('disabled'));
return $item.attr('disabled', true);
});
};
PaymentTag.prototype.trigger = function() {
var data, event, _ref;
event = arguments[0], data = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
return (_ref = this.$el).trigger.apply(_ref, ["" + event + ".payment"].concat(__slice.call(data)));
};
PaymentTag.prototype.$ = function(sel) {
return $(sel, this.$el);
};
return PaymentTag;
})();
document.createElement('payment');
if (typeof module !== "undefined" && module !== null) {
module.exports = PaymentTag;
}
global = this;
if (global.Stripe) {
$(function() {
return typeof PaymentTag.replaceTags === "function" ? PaymentTag.replaceTags() : void 0;
});
} else {
script = document.createElement('script');
script.onload = script.onreadystatechange = function() {
if (!global.Stripe) {
return;
}
if (script.done) {
return;
}
script.done = true;
return typeof PaymentTag.replaceTags === "function" ? PaymentTag.replaceTags() : void 0;
};
script.src = 'https://js.stripe.com/v1/';
$(function() {
var sibling;
sibling = document.getElementsByTagName('script')[0];
return sibling != null ? sibling.parentNode.insertBefore(script, sibling) : void 0;
});
}
}).call(this);
(function() {
this.PaymentTag || (this.PaymentTag = {});
this.PaymentTag["view"] = function(__obj) {
if (!__obj) __obj = {};
var __out = [], __capture = function(callback) {
var out = __out, result;
__out = [];
callback.call(this);
result = __out.join('');
__out = out;
return __safe(result);
}, __sanitize = function(value) {
if (value && value.ecoSafe) {
return value;
} else if (typeof value !== 'undefined' && value != null) {
return __escape(value);
} else {
return '';
}
}, __safe, __objSafe = __obj.safe, __escape = __obj.escape;
__safe = __obj.safe = function(value) {
if (value && value.ecoSafe) {
return value;
} else {
if (!(typeof value !== 'undefined' && value != null)) value = '';
var result = new String(value);
result.ecoSafe = true;
return result;
}
};
if (!__escape) {
__escape = __obj.escape = function(value) {
return ('' + value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};
}
(function() {
(function() {
__out.push('<span class="message"></span>\n\n<div class="number">\n <label for="paymentNumber">Card number</label>\n\n <input type="tel" id="paymentNumber" placeholder="4242 4242 4242 4242" autofocus required>\n</div>\n\n<div class="expiry">\n <label for="paymentExpiryMonth">Expiry date <em>(mm/yy)</em></label>\n\n <input class="expiryMonth" type="tel" id="paymentExpiryMonth" placeholder="mm" required>\n <input class="expiryYear" type="tel" id="paymentExpiryYear" placeholder="yy" required>\n</div>\n\n');
if (this.options.cvc) {
__out.push('\n <div class="cvc">\n <label for="paymentCVC">Security code</label>\n <input type="tel" id="paymentCVC" placeholder="123" maxlength="4" required>\n </div>\n');
}
__out.push('\n');
}).call(this);
}).call(__obj);
__obj.safe = __objSafe, __obj.escape = __escape;
return __out.join('');
};
}).call(this);

1
static/stripe/tag.js Executable file

File diff suppressed because one or more lines are too long

BIN
static/stripe/themes/amex.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
static/stripe/themes/discover.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
static/stripe/themes/generic.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
static/stripe/themes/spinner.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

152
static/stripe/themes/stripe.css Executable file
View File

@ -0,0 +1,152 @@
payment, .payment {
position: relative;
display: block;
border-radius: 5px;
padding: 15px 20px;
max-width: 300px;
overflow: hidden;
box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-sizing: border-box;
font-size: 12px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
font-family: 'Helvetica Neue', Helvetica, Arial Geneva, sans-serif;
background: #FFF;
background-image: -o-linear-gradient(#FFF, #F9FAFA);
background-image: -ms-linear-gradient(#FFF, #F9FAFA);
background-image: -moz-linear-gradient(#FFF, #F9FAFA);
background-image: -webkit-linear-gradient(#FEFEFE, #F9FAFA);
background-image: linear-gradient(#FFF, #F9FAFA);
-moz-box-shadow: 0 0 2px rgba(80,84,92,0.3), 0 1px 1px rgba(80,84,92,0.5);
-webkit-box-shadow: 0 0 2px rgba(80, 84, 92, 0.3), 0 1px 1px rgba(80, 84, 92, 0.5);
-ms-box-shadow: 0 0 2px rgba(80, 84, 92, 0.3), 0 1px 1px rgba(80, 84, 92, 0.5);
box-shadow: 0 0 2px rgba(80, 84, 92, 0.3), 0 1px 1px rgba(80, 84, 92, 0.5);
}
payment ::-webkit-input-placeholder,
.payment ::-webkit-input-placeholder {
text-transform: uppercase;
}
payment label, .payment label {
display: block;
color: #999;
font-size: 13px;
padding: 5px 0;
text-transform: uppercase;
text-shadow: 0 1px 0 #FFF;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
payment input, .payment input {
font-size: 13px;
padding: 5px 5px;
border: 1px solid #BBB;
border-top-color: #999;
box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1);
border-radius: 3px;
-webkit-transition: -webkit-box-shadow 0.1s ease-in-out;
-moz-transition: -moz-box-shadow 0.1s ease-in-out;
transition: -moz-box-shadow 0.1s ease-in-out;
box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-sizing: border-box;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
font-family: 'Helvetica Neue', Helvetica, Arial Geneva, sans-serif;
font-size: 14px;
}
payment input:focus, .payment input:focus {
border: 1px solid #5695DB;
outline: none;
-webkit-box-shadow: inset 0 1px 2px #DDD, 0px 0 5px #5695DB;
-moz-box-shadow: 0 0 5px #5695db;
box-shadow: inset 0 1px 2px #DDD, 0px 0 5px #5695DB;
}
payment .invalid input, .payment .invalid input {
outline: none;
border-color: rgba(255, 0, 0, 0.5);
-moz-box-shadow: inset 0 1px 2px rgba(0,0,0,0.20), 0 1px 5px 0 rgba(255, 0, 0, 0.4);
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.20), 0 1px 5px 0 rgba(255, 0, 0, 0.4);
-ms-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.20), 0 1px 5px 0 rgba(255, 0, 0, 0.4);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.20), 0 1px 5px 0 rgba(255, 0, 0, 0.4);
}
payment input:disabled, .payment input:disabled {
opacity: 0.5;
}
payment .number,
.payment .number {
margin-bottom: 8px;
}
payment .number input, .payment .number input {
padding: 7px 40px 7px 7px;
background: #FFF url(generic.png) 98.5% 20% no-repeat;
width: 100%;
}
payment .number input.visa, .payment .number input.visa {
background-image: url(visa.png);
}
payment .number input.mastercard, .payment .number input.mastercard {
background-image: url(mastercard.png);
}
payment .number input.discover, .payment .number input.discover {
background-image: url(discover.png);
}
payment .number input.amex, .payment .number input.amex {
background-image: url(amex.png);
}
payment .expiry input, payment .cvc input {
width: 45px;
}
payment .expiry em {
font-size: 10px;
font-style: normal;
display: none;
}
payment .cvc,
.payment .cvc {
float: right;
text-align: right;
}
payment .expiry,
.payment .expiry {
float: left;
}
payment .message,
.payment .message {
display: block;
}
payment.pending,
.payment.pending,
payment.success,
.payment.success {
background: #FFF url(spinner.gif) center center no-repeat;
min-height: 130px;
}

BIN
static/stripe/themes/visa.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB