diff --git a/gratipay/homepage.py b/gratipay/homepage.py
index a183288803..e189998998 100644
--- a/gratipay/homepage.py
+++ b/gratipay/homepage.py
@@ -13,10 +13,10 @@ def _parse(raw):
"""
errors = []
- x = lambda f: raw.get(f, '').strip()
+ x = lambda f: raw[f].strip() # KeyError -> 400
# amount
- amount = x('amount') or '0'
+ amount = x('amount')
if (not amount.isdigit()) or (int(amount) < 10):
errors.append('amount')
amount = ''.join(x for x in amount.split('.')[0] if x.isdigit())
@@ -105,6 +105,7 @@ def pay_for_open_source(app, raw):
payment_method_nonce = parsed.pop('payment_method_nonce')
payment_for_open_source = _store(parsed)
if not errors:
+ import pdb; pdb.set_trace()
result = _charge(parsed['amount'], payment_method_nonce)
payment_for_open_source.process_result(result)
if not payment_for_open_source.succeeded:
diff --git a/gratipay/models/payment_for_open_source.py b/gratipay/models/payment_for_open_source.py
index 905a122e2a..e8f6e1fc9e 100644
--- a/gratipay/models/payment_for_open_source.py
+++ b/gratipay/models/payment_for_open_source.py
@@ -11,7 +11,7 @@ class PaymentForOpenSource(Model):
typname = "payments_for_open_source"
def __repr__(self):
- return ''.format(repr(self.amount))
+ return ''.format(repr(self.amount))
@property
diff --git a/js/gratipay/homepage.js b/js/gratipay/homepage.js
index d07ca9be7e..469c3ad4fd 100644
--- a/js/gratipay/homepage.js
+++ b/js/gratipay/homepage.js
@@ -3,38 +3,33 @@ Gratipay.homepage = {}
Gratipay.homepage.initForm = function(clientAuthorization) {
$form = $('#homepage #content form');
- $submit= $form.find('button[type=submit]');
- $submit.click(Gratipay.homepage.submitForm);
-
- $chooseEcosystem = $form.find('.ecosystem-chooser button');
- $chooseEcosystem.click(function(e) {
- e.preventDefault();
- Gratipay.notification('Not implemented.', 'error');
- });
-
$promote = $form.find('.promotion-gate button');
$promote.click(Gratipay.homepage.openPromote);
+ function callback(createErr, instance) {
+ $submit = $form.find('button[type=submit]');
+ $submit.click(function(e) {
+ e.preventDefault();
+ instance.requestPaymentMethod(function(requestPaymentMethodErr, payload) {
+ Gratipay.homepage.submitFormWithNonce(payload.nonce);
+ });
+ });
+ }
+
braintree.dropin.create({
authorization: clientAuthorization,
container: '#braintree-container'
- }, function (createErr, instance) {
- $submit.click(function () {
- instance.requestPaymentMethod(function (requestPaymentMethodErr, payload) {
- // Submit payload.nonce to your server
- });
- });
- });
-}
+ }, callback);
+};
-Gratipay.homepage.submitForm = function(e) {
- e.preventDefault();
- $input = $(this)
- $form = $(this).parent('form');
+Gratipay.homepage.submitFormWithNonce = function(nonce) {
+ $submit = $form.find('button[type=submit]');
+ $form = $('#homepage #content form');
var data = new FormData($form[0]);
+ data.set('payment_method_nonce', nonce);
- $input.prop('disable', true);
+ $submit.prop('disable', true);
$.ajax({
url: $form.attr('action'),
@@ -43,16 +38,21 @@ Gratipay.homepage.submitForm = function(e) {
processData: false,
contentType: false,
dataType: 'json',
- success: function (d) {
+ success: function(d) {
$('a.team_url').attr('href', d.team_url).text(d.team_url);
$('a.review_url').attr('href', d.review_url).text(d.review_url);
$('form').slideUp(500, function() {
$('.application-complete').slideDown(250);
});
},
- error: [Gratipay.error, function() { $input.prop('disable', false); }]
+ error: function(e) {
+ console.log(e);
+ debugger;
+ Gratipay.error(e);
+ $submit.prop('disable', false);
+ }
});
-}
+};
Gratipay.homepage.openPromote = function(e) {
e.preventDefault();
@@ -60,4 +60,4 @@ Gratipay.homepage.openPromote = function(e) {
$('.promotion-fields').slideDown(function() {
$('.promotion-fields input:first').focus();
});
-}
+};
diff --git a/tests/py/test_www_homepage.py b/tests/py/test_www_homepage.py
index edab4c09ac..3b22c5daba 100644
--- a/tests/py/test_www_homepage.py
+++ b/tests/py/test_www_homepage.py
@@ -167,7 +167,21 @@ def test_flags_failures_with_transaction_id(self):
def test_post_gets_json(self):
- response = self.client.POST('/', data=GOOD)
+ response = self.client.POST('/', data=GOOD, HTTP_ACCEPT=b'application/json')
assert response.code == 200
assert response.headers['Content-Type'] == 'application/json'
assert json.loads(response.body) == {'parsed': {}, 'errors': []}
+
+ def test_bad_post_gets_400(self):
+ response = self.client.PxST('/', data=BAD, HTTP_ACCEPT=b'application/json')
+ assert response.code == 400
+ assert response.headers['Content-Type'] == 'application/json'
+ scrubbed = SCRUBBED.copy()
+ scrubbed.pop('payment_method_nonce') # consumed
+ assert json.loads(response.body) == {'parsed': scrubbed, 'errors': ALL}
+
+ def test_really_bad_post_gets_plain_400(self):
+ response = self.client.PxST('/', data={}, HTTP_ACCEPT=b'application/json')
+ assert response.code == 400
+ assert response.headers == {}
+ assert response.body == "Missing key: u'amount'"
diff --git a/tests/ttw/test_homepage.py b/tests/ttw/test_homepage.py
index bcfb1dbe7b..29848ad953 100644
--- a/tests/ttw/test_homepage.py
+++ b/tests/ttw/test_homepage.py
@@ -5,6 +5,28 @@
class Tests(BrowserHarness):
+ def fill_form(self, amount, credit_card_number, expiration, cvv, name, email_address,
+ follow_up, promotion_name, promotion_url, promotion_twitter, promotion_message):
+ self.wait_for('.braintree-form-number')
+ self.fill('amount', amount)
+ with self.get_iframe('braintree-hosted-field-number') as iframe:
+ iframe.fill('credit-card-number', credit_card_number)
+ with self.get_iframe('braintree-hosted-field-expirationDate') as iframe:
+ iframe.fill('expiration', expiration)
+ with self.get_iframe('braintree-hosted-field-cvv') as iframe:
+ iframe.fill('cvv', cvv)
+ self.fill('name', name)
+ self.fill('email_address', email_address)
+ if promotion_name:
+ self.css('.promotion-gate button').type('\n')
+ # stackoverflow.com/q/11908249#comment58577676_19763087
+ self.wait_for('#promotion-message')
+ self.fill('promotion_name', promotion_name)
+ self.fill('promotion_url', promotion_url)
+ self.fill('promotion_twitter', promotion_twitter)
+ self.fill('promotion_message', promotion_message)
+
+
def test_loads_for_anon(self):
assert self.css('#banner h1').html == 'Pay for open source.'
assert self.css('#header .sign-in button').html.strip()[:17] == 'Sign in / Sign up'
@@ -15,3 +37,18 @@ def test_redirects_for_authed_exclamation_point(self):
self.reload()
assert self.css('#banner h1').html == 'Browse'
assert self.css('.you-are a').html.strip()[:6] == '~alice'
+
+ def test_anon_can_post(self):
+ self.fill_form('537', '4242424242424242', '1020', '123', 'Alice Liddell',
+ 'alice@example.com', 'monthly', 'Wonderland', 'http://www.example.com/',
+ 'thebestbutter', 'Love me! Love me! Say that you love me!')
+ self.css('fieldset.submit button').type('\n')
+ import pdb; pdb.set_trace()
+ assert self.db.one('SELECT * FROM payments_for_open_source').promotion_name == 'Wonderland'
+
+ def est_validation_works(self):
+ self.fill_form('537', '4242424242424242', '', '', 'Alice Liddell', 'alice@example.com',
+ 'monthly', 'Wonderland', 'http://www.example.com/', 'thebestbutter',
+ 'Love me! Love me! Say that you love me!')
+ self.css('fieldset.submit button').type('\n')
+ assert self.db.one('select * from moral_license_payments').promotion_name == 'Wonderland'
diff --git a/www/index.spt b/www/index.spt
index 73015fa217..9d65d86b05 100644
--- a/www/index.spt
+++ b/www/index.spt
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
+import json
+from aspen import Response
from gratipay.homepage import pay_for_open_source
[---]
if not user.ANON:
@@ -7,6 +9,9 @@ suppress_sidebar = True
page_id = "homepage"
banner = "Pay for Open Source"
result = pay_for_open_source(website.app, request.body) if request.method == 'POST' else {}
+if result.get('errors'):
+ # Hmmm ... bit of an Aspen rough spot ... interaction w/ error.spt, skip it
+ raise Response(400, json.dumps(result), {'Content-Type': 'application/json'})
[---] application/json via json_dump
result
[---] text/html
@@ -37,7 +42,7 @@ $(document).ready(function() {
{% endblock %}
{% block content %}
-
-
-
-
-