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

Commit

Permalink
Merge pull request #3250 from gratipay/identity-check-split
Browse files Browse the repository at this point in the history
Split out identity verification from bank account connecting
  • Loading branch information
chadwhitacre committed Mar 18, 2015
2 parents 073960e + d1d2738 commit a86e08e
Show file tree
Hide file tree
Showing 11 changed files with 407 additions and 517 deletions.
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

0 comments on commit a86e08e

Please sign in to comment.