Skip to content

Instantly share code, notes, and snippets.

@matthewfl
Created August 28, 2013 04:41
Show Gist options
  • Save matthewfl/6362199 to your computer and use it in GitHub Desktop.
Save matthewfl/6362199 to your computer and use it in GitHub Desktop.
var Balanced = (function () {
var capabilities = {
system_timezone:-(new Date()).getTimezoneOffset() / 60,
user_agent:navigator.userAgent,
language:navigator.language
};
function validateData (requiredKeys, data, errors) {
for (var i = 0; i < requiredKeys.length; i++) {
var key = requiredKeys[i];
if (!data || !(key in data) || !data[key]) {
errors[key] = 'Missing field';
}
}
}
function preparePayload(data) {
if(!data.meta) data.meta = {};
for(var k in capabilities) {
if(!('capabilities_'+k in data.meta)) {
data.meta['capabilities_'+k] = capabilities[k];
}
}
return data;
}
function addEvent(obj, type, fn) {
if (obj.addEventListener)
obj.addEventListener(type, fn, false);
else if (obj.attachEvent) {
obj["e" + type + fn] = fn;
obj[type + fn] = function () {
obj["e" + type + fn](window.event);
};
obj.attachEvent("on" + type, obj[type + fn]);
}
}
var shifted = false;
function icl(e) {
e = (e) ? e : window.event;
var shifton = false;
if (e.shiftKey) {
shifton = e.shiftKey;
} else if (e.modifiers) {
shifton = !!(e.modifiers & 4);
}
if (shifton) {
shifted = true;
}
return shifted;
}
addEvent(window, 'keydown', function (e) {
if (!capabilities.cl) {
capabilities.cl = icl(e);
}
});
addEvent(window, 'paste', function () {
capabilities.ps = true;
});
function str_trim(str) {
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
};
// validation stuff copied out of the old balanced.js
function validate (details, requiredKeys, validationMethod) {
var errors = {};
validateData(requiredKeys, details, errors);
var additionalErrors = validationMethod(details);
for (var k in additionalErrors) {
errors[k] = additionalErrors[k];
}
return errors;
}
var cc = {
isCardNumberValid:function (cardNumber) {
if (!cardNumber) {
return false;
}
cardNumber = (cardNumber + '').replace(/\D+/g, '').split('').reverse();
if (!cardNumber.length || cardNumber.length < 12) {
return false;
}
var total = 0, i;
for (i = 0; i < cardNumber.length; i++) {
cardNumber[i] = parseInt(cardNumber[i], 10);
total += i % 2 ? 2 * cardNumber[i] - (cardNumber[i] > 4 ? 9 : 0) : cardNumber[i];
}
return (total % 10) === 0;
},
cardType:function (cardNumber) {
var p = {};
p['51'] = p['52'] = p['53'] = p['54'] = p['55'] = 'Mastercard';
p['34'] = p['37'] = 'American Express';
p['4'] = 'VISA';
p['6'] = 'Discover Card';
if (cardNumber) {
for (var k in p) {
if (cardNumber.indexOf(k) === 0) {
return p[k];
}
}
}
return null;
},
isSecurityCodeValid:function (cardNumber, securityCode) {
var cardType = cc.cardType(cardNumber);
if (!cardType) {
return false;
}
var requiredLength = (cardType === 'American Express' ? 4 : 3);
return securityCode && securityCode.toString().replace(/\D+/g, '').length === requiredLength;
},
isExpiryValid:function (expiryMonth, expiryYear) {
if (!expiryMonth || !expiryYear) {
return false;
}
expiryMonth = parseInt(expiryMonth, 10);
expiryYear = parseInt(expiryYear, 10);
if (isNaN(expiryMonth) || isNaN(expiryYear) || expiryMonth > 12 || expiryMonth < 1) {
return false;
}
var today = new Date();
return !(today.getFullYear() > expiryYear ||
(today.getFullYear() === expiryYear && today.getMonth() >= expiryMonth));
},
validate:function (cardData) {
if (cardData.number) {
cardData.number = str_trim(cardData.number.toString());
}
var cardNumber = cardData.number,
securityCode = cardData.security_code,
expiryMonth = cardData.expiration_month,
expiryYear = cardData.expiration_year;
var errors = {};
if (!cc.isCardNumberValid(cardNumber)) {
errors.number = '"' + cardNumber + '" is not a valid credit card number';
}
if (typeof securityCode !== 'undefined' && securityCode !== null && !cc.isSecurityCodeValid(cardNumber, securityCode)) {
errors.security_code = '"' + securityCode + '" is not a valid credit card security code';
}
if (!cc.isExpiryValid(expiryMonth, expiryYear)) {
errors.expiration = '"' + expiryMonth + '-' + expiryYear + '" is not a valid credit card expiration date';
}
return errors;
},
create:function (data, callback) {
if (!data) {
noDataError(callback);
return;
}
var requiredKeys = ['number', 'expiration_month',
'expiration_year'];
var errors = validate(data, requiredKeys, cc.validate);
var ec = 0;
for (var p in errors) {
ec++;
break;
}
if (ec > 0) {
callback({
error:errors,
status:400
});
} else {
jsonp(make_url('/jsonp/cards', preparePayload(data)), make_callback(callback));
}
}
};
var em = {
validate:function (emailAddress) {
var match = emailAddress &&
emailAddress.match(/[a-z0-9!#$%&'*+\/=?\^_`{|}~\-]+(?:\.[a-z0-9!#$%&'*+\/=?\^_`{|}~\-]+)*@(?:[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?/i);
return match && match.toString() === emailAddress;
}
};
var ba = {
types: ['savings', 'checking'],
validate:function (accountData) {
var noun = ('routing_number' in accountData) ? 'routing_number' : 'bank_code';
var bankCode = accountData[noun];
var errors = {};
if (!ba.validateRoutingNumber(bankCode)) {
errors[noun] = '"' + bankCode + '" is not a valid ' + noun.replace('_', ' ');
}
if ('type' in accountData && !ba.validateType(accountData.type)) {
errors.type = '"' + accountData.type + '" must be one of: "' + ba.types.join('", "') + '"';
}
return errors;
},
validateRoutingNumber:function (routingNumber) {
if (!routingNumber) {
return false;
}
routingNumber = routingNumber.match(/\d+/g);
if (!routingNumber) {
return false;
}
routingNumber = routingNumber.join('');
if (!routingNumber || routingNumber.length != 9) {
return false;
}
var a = routingNumber.toString().split('');
var d = [];
for (var i = 0; i < a.length; i++) {
d.push(parseInt(a[i], 10));
}
return d[8] === (
7 * (d[0] + d[3] + d[6]) +
3 * (d[1] + d[4] + d[7]) +
9 * (d[2] + d[5])
) % 10;
},
lookupRoutingNumber:function (routingNumber, callback) {
if (!routingNumber) {
noDataError(callback);
return;
}
var uri = ROUTING_NUMBER_URI + routingNumber;
sendWhenReady(uri, null, callback, 'GET');
},
validateType: function (type) {
if (!type) {
return true;
}
return ba.types.indexOf(type) >= 0;
},
create:function (data, callback) {
if (!data) {
noDataError(callback);
return;
}
var requiredKeys = ['name', 'account_number', 'routing_number'];
var errors = validate(data, requiredKeys, ba.validate);
var ec = 0;
for (var p in errors) {
ec++;
break;
}
if (ec > 0) {
callback({
error:errors,
status:400
});
} else {
jsonp(make_url('/jsonp/bank_accounts', preparePayload(data)), make_callback(callback));
}
}
};
var root_url = 'http://localhost:7777';
function jsonp(path, callback) {
var funct = "balanced_jsonp_"+Math.random().toString().substr(2);
var tag = document.createElement('script');
tag.type = 'text/javascript';
tag.async = true;
tag.src = path.replace('{callback}', funct);
var where = document.getElementsByTagName('script')[0];
where.parentNode.insertBefore(tag, where);
window[funct] = function(result) {
try {
callback(result);
} catch(e) { console && console.error && console.error(e); }
tag.parentNode.removeChild(tag);
};
}
function make_url(path, data) {
return root_url + path + "?callback={callback}&data="+encodeURI(JSON.stringify(data));
}
function make_callback(callback) {
var called_back = false;
var ret = function(data) {
if(called_back) return;
if(!data || !data.status || data.status >= 400) {
callback(data && data.body ? JSON.parse(data.body) : {
status_code: 500,
description: "Unable to connect to the balanced servers"
});
return;
}
var body = JSON.parse(data.body);
if(!('href' in body)) {
callback(body);
return;
}
callback(body);
};
return ret;
}
if(!JSON || !JSON.stringify || JSON.stringify({}) != '{}') {
// load JSON parser
jsonp('https://js.balancedpayments.com/v2/json.js');
}
return {
card: cc,
bankAccount: ba,
emailAddress: em,
init: function (args) {
if('root_url' in args) {
root_url = args.root_url;
}
}
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment