Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Split out identity verification from bank account connecting #3250

Merged
merged 14 commits into from
Mar 18, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions gratipay/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
# This is shared via class inheritance with jinja2_htmlescaped.
'b64encode': base64.b64encode,
'enumerate': enumerate,
'filter': filter,
'filter_profile_subnav': utils.filter_profile_subnav,
'float': float,
'len': len,
'map': map,
'range': range,
'str': str,
'to_javascript': utils.to_javascript,
Expand Down
63 changes: 12 additions & 51 deletions js/gratipay/forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,6 @@

Gratipay.forms = {};

Gratipay.forms.clearFeedback = function() {
$('#feedback').empty();
};

Gratipay.forms.showFeedback = function(msg, details) {
if (msg === null)
msg = "Failure";
msg = '<h2><span class="highlight">' + msg + '</span></h2>';
msg += '<ul class="details"></ul>';
$('#feedback').html(msg);
if (details !== undefined)
for (var i=0; i < details.length; i++)
$('#feedback .details').append('<li>' + details[i] + '</li>');
};

Gratipay.forms.submit = function(url, data, success, error) {
if (success === undefined) {
success = function() {
Gratipay.forms.showFeedback("Success!");
};
}

if (error === undefined) {
error = function(data) {
Gratipay.forms.showFeedback(data.problem);
};
}

function _success(data) {
if (data.problem === "" || data.problem === undefined)
success(data);
else
error(data);
}

function _error(xhr, foo, bar) {
Gratipay.forms.showFeedback( "So sorry!!"
, ["There was a fairly drastic error with your request."]
);
console.log("failed", xhr, foo, bar);
}

jQuery.ajax({ url: url
, type: "POST"
, data: data
, dataType: "json"
, success: _success
, error: _error
});
};

Gratipay.forms.initCSRF = function() { // https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
jQuery(document).ajaxSend(function(event, xhr, settings) {
function sameOrigin(url) {
Expand Down Expand Up @@ -151,3 +100,15 @@ Gratipay.forms.jsEdit = function(params) {
$form.on('submit', post);

};

Gratipay.forms.clearInvalid = function($form) {
$form.find('.invalid').removeClass('invalid');
};

Gratipay.forms.focusInvalid = function($form) {
$form.find('.invalid').eq(0).focus();
};

Gratipay.forms.setInvalid = function($input, invalid) {
$input.toggleClass('invalid', invalid);
};
209 changes: 66 additions & 143 deletions js/gratipay/payments.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Gratipay.payments = {};

Gratipay.payments.init = function(participantId) {
Gratipay.participantId = participantId;
$('#delete form').submit(Gratipay.payments.submitDeleteForm);
$('#delete').submit(Gratipay.payments.submitDeleteForm);

// Lazily depend on Balanced.
var balanced_js = "https://js.balancedpayments.com/1.1/balanced.min.js";
Expand All @@ -25,47 +25,35 @@ Gratipay.payments.init = function(participantId) {
}

Gratipay.payments.submitDeleteForm = function(e) {
var item = $("#payout").length ? "bank account" : "credit card";
var slug = $("#payout").length ? "bank-account" : "credit-card";
var msg = "Really disconnect your " + item + "?";
if (!confirm(msg)) {
e.stopPropagation();
e.preventDefault();
e.stopPropagation();
e.preventDefault();

var $form = $(this);
if (!confirm($form.data('confirm'))) {
return false;
}

jQuery.ajax(
{ url: '/' + slug + '.json'
{ url: $form.attr('action')
, data: {action: "delete"}
, type: "POST"
, success: function() {
window.location.href = '/' + slug + '.html';
}
, success: function() { window.location.reload(); }
, error: Gratipay.error
}
);
return false;
};

Gratipay.payments.onError = function(response) {
$('button#save').css('opacity', 1);
$('button#save').prop('disabled', false);
var msg = response.status_code + ": " +
$.map(response.errors, function(obj) { return obj.description }).join(', ');
Gratipay.forms.showFeedback(null, [msg]);
Gratipay.notification(msg, 'error', -1);
return msg;
};

Gratipay.payments.onSuccess = function(data) {
$('#status').text('working').addClass('highlight');
setTimeout(function() {
$('#status').removeClass('highlight');
}, 8000);
$('#delete').show();
Gratipay.forms.clearFeedback();
$('button#save').css('opacity', 1);
setTimeout(function() {
window.location.href = '/' + Gratipay.participantId + '/';
}, 1000);
$('button#save').prop('disabled', false);
window.location.reload();
};


Expand All @@ -76,87 +64,35 @@ Gratipay.payments.ba = {};

Gratipay.payments.ba.init = function(participantId) {
Gratipay.payments.init(participantId);
$('#payout').submit(Gratipay.payments.ba.submit);
$('form#bank-account').submit(Gratipay.payments.ba.submit);
};

Gratipay.payments.ba.submit = function (e) {
e.preventDefault();

$('button#save').css('opacity', 0.5);
Gratipay.forms.clearFeedback();
$('button#save').prop('disabled', true);
Gratipay.forms.clearInvalid($(this));

var bankAccount = {
name: $('#account_name').val(),
account_number: $('#account_number').val(),
routing_number: $('#routing_number').val()
};

Gratipay.payments.ba.merchantData = {
//type: 'person', // Oooh, may need to vary this some day?
street_address: $('#address_1').val(),
postal_code: $('#zip').val(),
phone_number: $('#phone_number').val(),
region: $('#state').val(),
dob_month: $('#dob-month').val(),
dob_year: $('#dob-year').val(),
dob_day: $('#dob-day').val(),
name: $('#name').val()
};
var errors = [];


// Require some fields.
// ====================
// We only require fields that are actually on the page. Since we don't
// load the identity verification fields if they're already verified, not
// all of these will necessarily be present at all.

var requiredFields = {
name: 'Your legal name is required.',
address_1: 'Your street address is required.',
zip: 'Your ZIP or postal code is required.',
phone_number: 'A phone number is required.',
account_name: 'The name on your bank account is required.',
account_number: 'Your bank account number is required.',
routing_number: 'A routing number is required.'
};
for (var field in requiredFields) {
var $f = $('#' + field);
if (!$f.length) // Only validate if it's on the page.
continue;
var value = $f.val();

if (!value) {
$f.closest('div').addClass('error');
errors.push(requiredFields[field]);
} else {
$f.closest('div').removeClass('error');
}
}


// Validate routing number.
// ========================

var $rn = $('#routing_number');
if (bankAccount.routing_number) {
if (!balanced.bankAccount.validateRoutingNumber(bankAccount.routing_number)) {
$rn.closest('div').addClass('error');
errors.push("That routing number is invalid.");
} else {
$rn.closest('div').removeClass('error');
Gratipay.forms.setInvalid($('#routing_number'));
Gratipay.forms.focusInvalid($(this));
$('button#save').prop('disabled', false);
return false
}
}


if (errors.length) {
$('button#save').css('opacity', 1);
Gratipay.forms.showFeedback(null, errors);
} else {
balanced.bankAccount.create( bankAccount
, Gratipay.payments.ba.handleResponse
);
}
// Okay, send the data to Balanced.
balanced.bankAccount.create( bankAccount
, Gratipay.payments.ba.handleResponse
);
};

Gratipay.payments.ba.handleResponse = function (response) {
Expand All @@ -167,32 +103,21 @@ Gratipay.payments.ba.handleResponse = function (response) {
}

/* The request to tokenize the bank account succeeded. Now we need to
* validate the merchant information. We'll submit it to
* /bank-accounts.json and check the response code to see what's going
* on there.
* associate it to the Customer on Balanced and to the participant in
* our DB.
*/

function detailedFeedback(data) {
$('#status').text('failing');
$('#delete').show();
var messages = [data.error];
if (data.problem == 'More Info Needed') {
messages = [ "Sorry, we couldn't verify your identity. Please "
+ "check, correct, and resubmit your details."
];
}
Gratipay.forms.showFeedback(data.problem, messages);
$('button#save').css('opacity', 1);
}

var detailsToSubmit = Gratipay.payments.ba.merchantData;
detailsToSubmit.bank_account_uri = response.bank_accounts[0].href;

Gratipay.forms.submit( "/bank-account.json"
, detailsToSubmit
, Gratipay.payments.onSuccess
, detailedFeedback
);
jQuery.ajax({
url: "/bank-account.json",
type: "POST",
data: {bank_account_uri: response.bank_accounts[0].href},
dataType: "json",
success: Gratipay.payments.onSuccess,
error: [
Gratipay.error,
function() { $('button#save').prop('disabled', false); },
],
});
};


Expand All @@ -203,7 +128,7 @@ Gratipay.payments.cc = {};

Gratipay.payments.cc.init = function(participantId) {
Gratipay.payments.init(participantId);
$('form#payment').submit(Gratipay.payments.cc.submit);
$('form#credit-card').submit(Gratipay.payments.cc.submit);
Gratipay.payments.cc.formatInputs(
$('#card_number'),
$('#expiration_month'),
Expand Down Expand Up @@ -340,13 +265,13 @@ Gratipay.payments.cc.submit = function(e) {

e.stopPropagation();
e.preventDefault();
$('button#save').css('opacity', 0.5);
Gratipay.forms.clearFeedback();
$('button#save').prop('disabled', true);
Gratipay.forms.clearInvalid($(this));

// Adapt our form lingo to balanced nomenclature.

function val(field) {
return $('form#payment input[id="' + field + '"]').val();
return $('form#credit-card #'+field).val();
}

var credit_card = {}; // holds CC info
Expand Down Expand Up @@ -378,23 +303,23 @@ Gratipay.payments.cc.submit = function(e) {
var year = val('expiration_year');
credit_card.expiration_year = year.length == 2 ? '20' + year : year;

if (!balanced.card.isCardNumberValid(credit_card.number)) {
$('button#save').css('opacity', 1);
Gratipay.forms.showFeedback(null, ["Your card number is bad."]);
} else if (!balanced.card.isExpiryValid( credit_card.expiration_month
, credit_card.expiration_year
)) {
$('button#save').css('opacity', 1);
Gratipay.forms.showFeedback(null, ["Your expiration date is bad."]);
} else if (!balanced.card.isSecurityCodeValid( credit_card.number
, credit_card.cvv
)) {
$('button#save').css('opacity', 1);
Gratipay.forms.showFeedback(null, ["Your CVV is bad."]);
} else {
balanced.card.create(credit_card, Gratipay.payments.cc.handleResponse);
var is_card_number_invalid = !balanced.card.isCardNumberValid(credit_card.number);
var is_expiry_invalid = !balanced.card.isExpiryValid(credit_card.expiration_month,
credit_card.expiration_year);
var is_cvv_invalid = !balanced.card.isSecurityCodeValid(credit_card.number,
credit_card.cvv);

Gratipay.forms.setInvalid($('#card_number'), is_card_number_invalid);
Gratipay.forms.setInvalid($('#expiration_month'), is_expiry_invalid);
Gratipay.forms.setInvalid($('#cvv'), is_cvv_invalid);

if (is_card_number_invalid || is_expiry_invalid || is_cvv_invalid) {
$('button#save').prop('disabled', false);
Gratipay.forms.focusInvalid($(this));
return false;
}

balanced.card.create(credit_card, Gratipay.payments.cc.handleResponse);
return false;
};

Expand All @@ -413,17 +338,15 @@ Gratipay.payments.cc.handleResponse = function(response) {
* card is good.
*/

function detailedFeedback(data) {
$('#status').text('failing');
$('#delete').show();
var details = [];
Gratipay.forms.showFeedback(data.problem, [data.error]);
$('button#save').css('opacity', 1);
}

Gratipay.forms.submit( "/credit-card.json"
, {card_uri: response.cards[0].href}
, Gratipay.payments.onSuccess
, detailedFeedback
);
jQuery.ajax({
url: "/credit-card.json",
type: "POST",
data: {card_uri: response.cards[0].href},
dataType: "json",
success: Gratipay.payments.onSuccess,
error: [
Gratipay.error,
function() { $('button#save').prop('disabled', false); },
],
});
};
Loading