Merge pull request #5326 from ajp8164/feat/app-identity

Feat/app identity
This commit is contained in:
Gustavo Maximiliano Cortez 2017-01-06 10:52:08 -03:00 committed by GitHub
commit 08447a13fd
9 changed files with 538 additions and 217 deletions

View file

@ -1,13 +1,13 @@
'use strict';
angular.module('copayApp.controllers').controller('bitpayCardController', function($scope, $timeout, $log, $state, lodash, bitpayCardService, moment, popupService, gettextCatalog, $ionicHistory) {
angular.module('copayApp.controllers').controller('bitpayCardController', function($scope, $timeout, $log, $state, lodash, bitpayCardService, moment, popupService, gettextCatalog, $ionicHistory, bitpayService) {
var self = this;
var runningBalance;
$scope.dateRange = {
value: 'last30Days'
};
$scope.network = bitpayCardService.getEnvironment();
$scope.network = bitpayService.getEnvironment().network;
var updateHistoryFromCache = function(cb) {
bitpayCardService.getBitpayDebitCardsHistory($scope.cardId, function(err, data) {

View file

@ -1,64 +1,48 @@
'use strict';
angular.module('copayApp.controllers').controller('bitpayCardIntroController', function($scope, $log, $state, $ionicHistory, storageService, externalLinkService, bitpayCardService, gettextCatalog, popupService) {
var checkOtp = function(obj, cb) {
if (obj.otp) {
var msg = gettextCatalog.getString('Enter Two Factor for BitPay Card');
popupService.showPrompt(null, msg, null, function(res) {
cb(res);
});
} else {
cb();
}
};
angular.module('copayApp.controllers').controller('bitpayCardIntroController', function($scope, $log, $state, $ionicHistory, storageService, externalLinkService, bitpayCardService, gettextCatalog, popupService, appIdentityService, bitpayService) {
$scope.$on("$ionicView.beforeEnter", function(event, data) {
if (data.stateParams && data.stateParams.secret) {
var obj = {
var pairData = {
secret: data.stateParams.secret,
email: data.stateParams.email,
otp: data.stateParams.otp
};
checkOtp(obj, function(otp) {
obj.otp = otp;
bitpayCardService.bitAuthPair(obj, function(err, data) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
var title = gettextCatalog.getString('Add BitPay Card Account?');
var msg = gettextCatalog.getString('Would you like to add this account ({{email}}) to your wallet?', {
email: obj.email
});
var ok = gettextCatalog.getString('Add Account');
var cancel = gettextCatalog.getString('Go back');
popupService.showConfirm(title, msg, ok, cancel, function(res) {
if (res) {
// Set flag for nextStep
storageService.setNextStep('BitpayCard', 'true', function(err) {});
// Save data
bitpayCardService.setBitpayDebitCards(data, function(err) {
if (err) return;
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$state.go('tabs.home').then(function() {
if (data.cards[0]) {
$state.transitionTo('tabs.bitpayCard', {
id: data.cards[0].id
});
}
});
});
var pairingReason = gettextCatalog.getString('add your BitPay Visa<sup>&reg;</sup> card(s)');
bitpayService.pair(pairData, pairingReason, function(err, paired, apiContext) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
if (paired) {
bitpayCardService.fetchBitpayDebitCards(apiContext, function(err, data) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
// Set flag for nextStep
storageService.setNextStep('BitpayCard', 'true', function(err) {});
// Save data
bitpayCardService.setBitpayDebitCards(data, function(err) {
if (err) return;
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$state.go('tabs.home').then(function() {
if (data.cards[0]) {
$state.transitionTo('tabs.bitpayCard', {
id: data.cards[0].id
});
}
});
});
});
});
}
});
} else {
bitpayCardService.getCredentials(function(err, credentials) {
appIdentityService.getIdentity(bitpayService.getEnvironment(), function(err, appIdentity) {
if (err) popupService.showAlert(null, err);
else $log.info('BitPay Debit Card Credentials: Ok.');
else $log.info('App identity: OK');
});
}
});

View file

@ -4,7 +4,9 @@ angular.module('copayApp.controllers').controller('preferencesBitpayCardControll
function($scope, $state, $timeout, $ionicHistory, bitpayCardService, popupService, gettextCatalog) {
$scope.remove = function(card) {
var msg = gettextCatalog.getString('Are you sure you would like to remove your BitPay Card account from this device?');
var msg = gettextCatalog.getString('Are you sure you would like to remove your BitPay Card ({{lastFourDigits}}) from this device?', {
lastFourDigits: card.lastFourDigits
});
popupService.showConfirm(null, msg, null, null, function(res) {
if (res) remove(card);
});

View file

@ -28,11 +28,9 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct
$scope.glideraEnabled = config.glidera.enabled && !isWindowsPhoneApp;
if ($scope.bitpayCardEnabled) {
bitpayCardService.getBitpayDebitCards(function(err, data) {
bitpayCardService.getBitpayDebitCards(function(err, cards) {
if (err) $log.error(err);
if (!lodash.isEmpty(data)) {
$scope.bitpayCards = true;
}
$scope.bitpayCards = cards && cards.length > 0;
});
}

View file

@ -0,0 +1,31 @@
'use strict';
angular.module('copayApp.services').factory('appIdentityService', function($log, lodash, storageService, bitauthService) {
var root = {};
root.getIdentity = function(network, cb) {
var pubkey, sin, isNew;
storageService.getAppIdentity(network, function(err, data) {
if (err) return cb(err);
var appIdentity = data || {};
if (lodash.isEmpty(appIdentity) || (appIdentity && !appIdentity.priv)) {
isNew = true;
appIdentity = bitauthService.generateSin();
}
try {
pubkey = bitauthService.getPublicKeyFromPrivateKey(appIdentity.priv);
sin = bitauthService.getSinFromPublicKey(pubkey);
if (isNew)
storageService.setAppIdentity(network, JSON.stringify(appIdentity), function(err) {});
}
catch (e) {
$log.error(e);
return cb(e);
};
return cb(null, appIdentity);
});
};
return root;
});

View file

@ -1,35 +1,7 @@
'use strict';
angular.module('copayApp.services').factory('bitpayCardService', function($http, $log, lodash, storageService, bitauthService, platformInfo, moment) {
angular.module('copayApp.services').factory('bitpayCardService', function($log, $rootScope, lodash, storageService, bitauthService, platformInfo, moment, appIdentityService, bitpayService) {
var root = {};
var BITPAY_CARD_NETWORK = 'livenet';
var BITPAY_CARD_API_URL = BITPAY_CARD_NETWORK == 'livenet' ? 'https://bitpay.com' : 'https://test.bitpay.com';
var _getCredentials = function(cb) {
var pubkey, sin, isNew;
storageService.getBitpayCardCredentials(BITPAY_CARD_NETWORK, function(err, data) {
if (err) return cb(err);
if (lodash.isString(data)) {
data = JSON.parse(data);
}
var credentials = data || {};
if (lodash.isEmpty(credentials) || (credentials && !credentials.priv)) {
isNew = true;
credentials = bitauthService.generateSin();
}
try {
pubkey = bitauthService.getPublicKeyFromPrivateKey(credentials.priv);
sin = bitauthService.getSinFromPublicKey(pubkey);
if (isNew)
storageService.setBitpayCardCredentials(BITPAY_CARD_NETWORK, JSON.stringify(credentials), function(err) {});
}
catch (e) {
$log.error(e);
return cb(e);
};
return cb(null, credentials);
});
};
var _setError = function(msg, e) {
$log.error(msg);
@ -37,64 +9,6 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
return error;
};
var _get = function(endpoint) {
return {
method: 'GET',
url: BITPAY_CARD_API_URL + endpoint,
headers: {
'content-type': 'application/json'
}
};
};
var _post = function(endpoint, json, credentials) {
var dataToSign = BITPAY_CARD_API_URL + endpoint + JSON.stringify(json);
var signedData = bitauthService.sign(dataToSign, credentials.priv);
return {
method: 'POST',
url: BITPAY_CARD_API_URL + endpoint,
headers: {
'content-type': 'application/json',
'x-identity': credentials.pub,
'x-signature': signedData
},
data: json
};
};
var _postAuth = function(endpoint, json, credentials) {
json['params'].signature = bitauthService.sign(JSON.stringify(json.params), credentials.priv);
json['params'].pubkey = credentials.pub;
json['params'] = JSON.stringify(json.params);
var ret = {
method: 'POST',
url: BITPAY_CARD_API_URL + endpoint,
headers: {
'content-type': 'application/json'
},
data: json
};
$log.debug('post auth:' + JSON.stringify(ret));
return ret;
};
var _afterBitAuthSuccess = function(token, obj, credentials, cb) {
var json = {
method: 'getDebitCards'
};
// Get Debit Cards
$http(_post('/api/v2/' + token, json, credentials)).then(function(data) {
if (data && data.data.error) return cb(data.data.error);
$log.info('BitPay Get Debit Cards: SUCCESS');
return cb(data.data.error, {token: token, cards: data.data.data, email: obj.email});
}, function(data) {
return cb(_setError('BitPay Card Error: Get Debit Cards', data));
});
};
var _processTransactions = function(invoices, history) {
invoices = invoices || [];
for (var i = 0; i < invoices.length; i++) {
@ -125,41 +39,17 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
return history;
};
root.getEnvironment = function() {
return BITPAY_CARD_NETWORK;
};
root.getCredentials = function(cb) {
_getCredentials(function(err, credentials) {
return cb(err, credentials);
});
};
root.bitAuthPair = function(obj, cb) {
var deviceName = 'Unknown device';
if (platformInfo.isNW) {
deviceName = require('os').platform();
} else if (platformInfo.isCordova) {
deviceName = device.model;
}
root.fetchBitpayDebitCards = function(apiContext, cb) {
var json = {
method: 'createToken',
params: {
secret: obj.secret,
version: 2,
deviceName: deviceName,
code: obj.otp
}
method: 'getDebitCards'
};
_getCredentials(function(err, credentials) {
if (err) return cb(err);
$http(_postAuth('/api/v2/', json, credentials)).then(function(data) {
if (data && data.data.error) return cb(data.data.error);
$log.info('BitPay Card BitAuth Create Token: SUCCESS');
_afterBitAuthSuccess(data.data.data, obj, credentials, cb);
}, function(data) {
return cb(_setError('BitPay Card Error Create Token: BitAuth', data));
});
// Get Debit Cards
bitpayService.post('/api/v2/' + apiContext.token, json, function(data) {
if (data && data.data.error) return cb(data.data.error);
$log.info('BitPay Get Debit Cards: SUCCESS');
return cb(data.data.error, {token: apiContext.token, cards: data.data.data, email: apiContext.pairData.email});
}, function(data) {
return cb(_setError('BitPay Card Error: Get Debit Cards', data));
});
};
@ -170,14 +60,14 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
method: 'getInvoiceHistory',
params: JSON.stringify(params)
};
_getCredentials(function(err, credentials) {
appIdentityService.getIdentity(bitpayService.getEnvironment().network, function(err, appIdentity) {
if (err) return cb(err);
root.getBitpayDebitCards(function(err, data) {
if (err) return cb(err);
var card = lodash.find(data, {id : cardId});
if (!card) return cb(_setError('Card not found'));
// Get invoices
$http(_post('/api/v2/' + card.token, json, credentials)).then(function(data) {
bitpayService.post('/api/v2/' + card.token, json, function(data) {
$log.info('BitPay Get Invoices: SUCCESS');
invoices = data.data.data || [];
if (lodash.isEmpty(invoices)) $log.info('No invoices');
@ -186,7 +76,7 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
params: JSON.stringify(params)
};
// Get transactions list
$http(_post('/api/v2/' + card.token, json, credentials)).then(function(data) {
bitpayService.post('/api/v2/' + card.token, json, function(data) {
$log.info('BitPay Get Transactions: SUCCESS');
transactions = data.data.data || {};
transactions['txs'] = _processTransactions(invoices, transactions.transactionList);
@ -195,7 +85,7 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
return cb(_setError('BitPay Card Error: Get Transactions', data));
});
}, function(data) {
return cb(_setError('BitPay Card Error: Get Invoices', data));
return cb(_setError('BitPay Card Error: Get Invoices', data));
});
});
});
@ -207,13 +97,13 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
method: 'generateTopUpInvoice',
params: JSON.stringify(params)
};
_getCredentials(function(err, credentials) {
appIdentityService.getIdentity(bitpayService.getEnvironment().network, function(err, appIdentity) {
if (err) return cb(err);
root.getBitpayDebitCards(function(err, data) {
if (err) return cb(err);
var card = lodash.find(data, {id : cardId});
if (!card) return cb(_setError('Card not found'));
$http(_post('/api/v2/' + card.token, json, credentials)).then(function(data) {
bitpayService.post('/api/v2/' + card.token, json, function(data) {
$log.info('BitPay TopUp: SUCCESS');
if(data.data.error) {
return cb(data.data.error);
@ -228,7 +118,7 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
};
root.getInvoice = function(id, cb) {
$http(_get('/invoices/' + id)).then(function(data) {
bitpayService.get('/invoices/' + id, function(data) {
$log.info('BitPay Get Invoice: SUCCESS');
return cb(data.data.error, data.data.data);
}, function(data) {
@ -237,7 +127,7 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
};
root.getBitpayDebitCards = function(cb) {
storageService.getBitpayDebitCards(BITPAY_CARD_NETWORK, function(err, data) {
storageService.getBitpayDebitCards(bitpayService.getEnvironment().network, function(err, data) {
if (err) return cb(err);
if (lodash.isString(data)) {
data = JSON.parse(data);
@ -249,14 +139,14 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
root.setBitpayDebitCards = function(data, cb) {
data = JSON.stringify(data);
storageService.setBitpayDebitCards(BITPAY_CARD_NETWORK, data, function(err) {
storageService.setBitpayDebitCards(bitpayService.getEnvironment().network, data, function(err) {
if (err) return cb(err);
return cb();
});
};
root.getBitpayDebitCardsHistory = function(cardId, cb) {
storageService.getBitpayDebitCardsHistory(BITPAY_CARD_NETWORK, function(err, data) {
storageService.getBitpayDebitCardsHistory(bitpayService.getEnvironment().network, function(err, data) {
if (err) return cb(err);
if (lodash.isString(data)) {
data = JSON.parse(data);
@ -268,7 +158,7 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
};
root.setBitpayDebitCardsHistory = function(cardId, data, opts, cb) {
storageService.getBitpayDebitCardsHistory(BITPAY_CARD_NETWORK, function(err, oldData) {
storageService.getBitpayDebitCardsHistory(bitpayService.getEnvironment().network, function(err, oldData) {
if (lodash.isString(oldData)) {
oldData = JSON.parse(oldData);
}
@ -282,19 +172,19 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
}
inv = JSON.stringify(inv);
storageService.setBitpayDebitCardsHistory(BITPAY_CARD_NETWORK, inv, function(err) {
storageService.setBitpayDebitCardsHistory(bitpayService.getEnvironment().network, inv, function(err) {
return cb(err);
});
});
};
root.remove = function(card, cb) {
storageService.removeBitpayDebitCard(BITPAY_CARD_NETWORK, card, function(err) {
storageService.removeBitpayDebitCard(bitpayService.getEnvironment().network, card, function(err) {
if (err) {
$log.error('Error removing BitPay debit card: ' + err);
// Continue, try to remove/cleanup card history
}
storageService.removeBitpayDebitCardHistory(BITPAY_CARD_NETWORK, card, function(err) {
storageService.removeBitpayDebitCardHistory(bitpayService.getEnvironment().network, card, function(err) {
if (err) {
$log.error('Error removing BitPay debit card transaction history: ' + err);
return cb(err);
@ -306,7 +196,7 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
};
root.getRates = function(currency, cb) {
$http(_get('/rates/' + currency)).then(function(data) {
bitpayService.get('/rates/' + currency, function(data) {
$log.info('BitPay Get Rates: SUCCESS');
return cb(data.data.error, data.data.data);
}, function(data) {

View file

@ -0,0 +1,195 @@
'use strict';
angular.module('copayApp.services').factory('bitpayService', function($log, $http, platformInfo, appIdentityService, bitauthService, storageService, gettextCatalog, popupService) {
var root = {};
var NETWORK = 'livenet';
var BITPAY_API_URL = NETWORK == 'livenet' ? 'https://bitpay.com' : 'https://test.bitpay.com';
root.getEnvironment = function() {
return {
network: NETWORK
};
};
/*
* Pair this app with the bitpay server using the specified pairing data.
* An app identity will be created if one does not already exist.
* Pairing data is provided by an input URI provided by the bitpay server.
*
* pairData - data needed to complete the pairing process
* {
* secret: shared pairing secret
* email: email address associated with bitpay account
* otp: two-factor one-time use password
* }
*
* pairingReason - text string to be embedded into popup message. If `null` then the reason
* message is not shown to the UI.
* "To {{reason}} you must pair this app with your BitPay account ({{email}})."
*
* cb - callback after completion
* callback(err, paired, apiContext)
*
* err - something unexpected happened which prevented the pairing
*
* paired - boolean indicating whether the pairing was compledted by the user
*
* apiContext - the context needed for making future api calls
* {
* token: api token for use in future calls
* pairData: the input pair data
* appIdentity: the identity of this app
* }
*/
root.pair = function(pairData, pairingReason, cb) {
checkOtp(pairData, function(otp) {
pairData.otp = otp;
var deviceName = 'Unknown device';
if (platformInfo.isNW) {
deviceName = require('os').platform();
} else if (platformInfo.isCordova) {
deviceName = device.model;
}
var json = {
method: 'createToken',
params: {
secret: pairData.secret,
version: 2,
deviceName: deviceName,
code: pairData.otp
}
};
appIdentityService.getIdentity(root.getEnvironment().network, function(err, appIdentity) {
if (err) return cb(err);
$http(_postAuth('/api/v2/', json, appIdentity)).then(function(data) {
if (data && data.data.error) return cb(data.data.error);
$log.info('BitPay service BitAuth create token: SUCCESS');
var title = gettextCatalog.getString('Add BitPay Account?');
var msgDetail = 'Add this BitPay account ({{email}})?';
if (pairingReason) {
msgDetail = 'To {{reason}} you must first add your BitPay account.<br/><br/>{{email}}';
}
var msg = gettextCatalog.getString(msgDetail, {
reason: pairingReason,
email: pairData.email
});
var ok = gettextCatalog.getString('Add Account');
var cancel = gettextCatalog.getString('Go back');
popupService.showConfirm(title, msg, ok, cancel, function(res) {
if (res) {
var acctData = {
token: data.data.data,
email: pairData.email
};
setBitpayAccount(acctData, function(err) {
if (err) return cb(err);
return cb(null, true, {
token: acctData.token,
pairData: pairData,
appIdentity: appIdentity
});
});
} else {
$log.info('User cancelled BitPay pairing process');
return cb(null, false);
}
});
}, function(data) {
return cb(_setError('BitPay service BitAuth create token: ERROR ', data));
});
});
});
};
root.get = function(endpoint, successCallback, errorCallback) {
$http(_get(endpoint)).then(function(data) {
successCallback(data);
}, function(data) {
errorCallback(data);
});
};
root.post = function(endpoint, json, successCallback, errorCallback) {
appIdentityService.getIdentity(root.getEnvironment().network, function(err, appIdentity) {
if (err) return errorCallback(err);
$http(_post(endpoint, json, appIdentity)).then(function(data) {
successCallback(data);
}, function(data) {
errorCallback(data);
});
});
};
var checkOtp = function(pairData, cb) {
if (pairData.otp) {
var msg = gettextCatalog.getString('Enter Two Factor for your BitPay account');
popupService.showPrompt(null, msg, null, function(res) {
cb(res);
});
} else {
cb();
}
};
var setBitpayAccount = function(accountData, cb) {
var data = JSON.stringify(accountData);
storageService.setBitpayAccount(root.getEnvironment().network, data, function(err) {
if (err) return cb(err);
return cb();
});
};
var _get = function(endpoint) {
return {
method: 'GET',
url: BITPAY_API_URL + endpoint,
headers: {
'content-type': 'application/json'
}
};
};
var _post = function(endpoint, json, appIdentity) {
var dataToSign = BITPAY_API_URL + endpoint + JSON.stringify(json);
var signedData = bitauthService.sign(dataToSign, appIdentity.priv);
return {
method: 'POST',
url: BITPAY_API_URL + endpoint,
headers: {
'content-type': 'application/json',
'x-identity': appIdentity.pub,
'x-signature': signedData
},
data: json
};
};
var _postAuth = function(endpoint, json, appIdentity) {
json['params'].signature = bitauthService.sign(JSON.stringify(json.params), appIdentity.priv);
json['params'].pubkey = appIdentity.pub;
json['params'] = JSON.stringify(json.params);
var ret = {
method: 'POST',
url: BITPAY_API_URL + endpoint,
headers: {
'content-type': 'application/json'
},
data: json
};
$log.debug('post auth:' + JSON.stringify(ret));
return ret;
};
var _setError = function(msg, e) {
$log.error(msg);
var error = (e && e.data && e.data.error) ? e.data.error : msg;
return error;
};
return root;
});

View file

@ -135,15 +135,22 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
var secret = getParameterByName('secret', data);
var email = getParameterByName('email', data);
var otp = getParameterByName('otp', data);
var reason = getParameterByName('r', data);
$state.go('tabs.home', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.home' ? false : true
}).then(function() {
$state.transitionTo('tabs.bitpayCardIntro', {
secret: secret,
email: email,
otp: otp
});
switch (reason) {
default:
case '0': /* For BitPay card binding */
$state.transitionTo('tabs.bitpayCardIntro', {
secret: secret,
email: email,
otp: otp
});
break;
}
});
return true;

View file

@ -78,6 +78,11 @@ angular.module('copayApp.services')
//
// UPGRADING STORAGE
//
// Upgraders are executed in numerical order per the '##_' object key prefix. Each upgrader will run.
// Each upgrader should detect storage configuraton and fail-safe; no upgrader should damage the ability
// of another to function properly (in order). Each upgrader should upgrade storage incrementally; storage
// upgrade is not complete until all upgraders have run.
//
// 1. Write a function to upgrade the desired storage key(s). The function should have the protocol:
//
// _upgrade_x(key, network, cb), where:
@ -91,11 +96,15 @@ angular.module('copayApp.services')
// when a storage key is involved in multiple upgraders as well as predicte the order in which upgrades
// occur the `_upgrader` object key should be prefixed with '##_' (e.g., '01_') to create a unique and
// sortable name. This format is interpreted by the _upgrade() function.
//
// Upgraders are executed in numerical order per the '##_' object key prefix.
//
// 3. Any dependency functions called by upgraders should be copied/factored out and remain unchanged as
// long as the upgrader remains in effect. By convention the dependency function is prefixed by '##_' to
// match the upgrader key.
//
var _upgraders = {
'00_bitpayDebitCards': _upgrade_bitpayDebitCards // 2016-11: Upgrade bitpayDebitCards-x to bitpayAccounts-x
'00_bitpayDebitCards' : _upgrade_bitpayDebitCards, // 2016-11: Upgrade bitpayDebitCards-x to bitpayAccounts-x
'01_bitpayCardCredentials' : _upgrade_bitpayCardCredentials, // 2016-11: Upgrade bitpayCardCredentials-x to appIdentity-x
'02_bitpayAccounts' : _upgrade_bitpayAccounts // 2016-12: Upgrade tpayAccounts-x to bitpayAccounts-v2-x
};
function _upgrade_bitpayDebitCards(key, network, cb) {
@ -108,7 +117,7 @@ angular.module('copayApp.services')
data = JSON.parse(data);
}
data = data || {};
root.setBitpayDebitCards(network, data, function(err) {
_00_setBitpayDebitCards(network, data, function(err) {
if (err) return cb(err);
storage.remove(key, function() {
cb(null, 'replaced with \'bitpayAccounts\'');
@ -119,6 +128,126 @@ angular.module('copayApp.services')
}
});
};
function _upgrade_bitpayCardCredentials(key, network, cb) {
key += '-' + network;
storage.get(key, function(err, data) {
if (err) return cb(err);
if (data != null) {
// Needs upgrade
_01_setAppIdentity(network, data, function(err) {
if (err) return cb(err);
storage.remove(key, function() {
cb(null, 'replaced with \'appIdentity\'');
});
});
} else {
cb();
}
});
};
function _upgrade_bitpayAccounts(key, network, cb) {
key += '-' + network;
storage.get(key, function(err, data) {
if (err) return cb(err);
if (lodash.isString(data)) {
data = JSON.parse(data);
}
data = data || {};
var upgraded = '';
Object.keys(data).forEach(function(key) {
// Keys are account emails
if (!data[key]['bitpayApi-' + network]) {
// Needs upgrade
upgraded += ' ' + key;
var acctData = {
token: data[key]['bitpayDebitCards-' + network].token,
email: key
};
_02_setBitpayAccount(network, acctData, function(err) {
if (err) return cb(err);
_02_setBitpayDebitCards(network, data[key]['bitpayDebitCards-' + network], function(err) {
if (err) return cb(err);
});
});
}
});
// Remove obsolete key.
storage.remove('bitpayAccounts-' + network, function() {
if (upgraded.length > 0) {
cb(null, 'upgraded to \'bitpayAccounts-v2-' + network + '\':' + upgraded);
} else {
cb();
}
});
});
};
//
////////////////////////////////////////////////////////////////////////////
//
// UPGRADER DEPENDENCIES
// These functions remain as long as the upgrader remains in effect.
//
var _00_setBitpayDebitCards = function(network, data, cb) {
if (lodash.isString(data)) {
data = JSON.parse(data);
}
data = data || {};
if (lodash.isEmpty(data) || !data.email) return cb('No card(s) to set');
storage.get('bitpayAccounts-' + network, function(err, bitpayAccounts) {
if (err) return cb(err);
if (lodash.isString(bitpayAccounts)) {
bitpayAccounts = JSON.parse(bitpayAccounts);
}
bitpayAccounts = bitpayAccounts || {};
bitpayAccounts[data.email] = bitpayAccounts[data.email] || {};
bitpayAccounts[data.email]['bitpayDebitCards-' + network] = data;
storage.set('bitpayAccounts-' + network, JSON.stringify(bitpayAccounts), cb);
});
};
var _01_setAppIdentity = function(network, data, cb) {
storage.set('appIdentity-' + network, data, cb);
};
var _02_setBitpayAccount = function(network, data, cb) {
if (lodash.isString(data)) {
data = JSON.parse(data);
}
data = data || {};
if (lodash.isEmpty(data) || !data.email) return cb('No account to set');
storage.get('bitpayAccounts-' + network, function(err, bitpayAccounts) {
if (err) return cb(err);
if (lodash.isString(bitpayAccounts)) {
bitpayAccounts = JSON.parse(bitpayAccounts);
}
bitpayAccounts = bitpayAccounts || {};
bitpayAccounts[data.email] = bitpayAccounts[data.email] || {};
bitpayAccounts[data.email]['bitpayApi-' + network] = bitpayAccounts[data.email]['bitpayApi-' + network] || {};
bitpayAccounts[data.email]['bitpayApi-' + network].token = data.token;
storage.set('bitpayAccounts-v2-' + network, JSON.stringify(bitpayAccounts), cb);
});
};
var _02_setBitpayDebitCards = function(network, data, cb) {
if (lodash.isString(data)) {
data = JSON.parse(data);
}
data = data || {};
if (lodash.isEmpty(data) || !data.email) return cb('Cannot set cards: no account to set');
storage.get('bitpayAccounts-v2-' + network, function(err, bitpayAccounts) {
if (err) return cb(err);
if (lodash.isString(bitpayAccounts)) {
bitpayAccounts = JSON.parse(bitpayAccounts);
}
bitpayAccounts = bitpayAccounts || {};
bitpayAccounts[data.email] = bitpayAccounts[data.email] || {};
bitpayAccounts[data.email]['bitpayDebitCards-' + network] = data.cards;
storage.set('bitpayAccounts-v2-' + network, JSON.stringify(bitpayAccounts), cb);
});
};
//
////////////////////////////////////////////////////////////////////////////
@ -148,11 +277,11 @@ angular.module('copayApp.services')
var storagekey = key.split('_')[1];
_upgraders[key](storagekey, network, function(err, msg) {
if (err) {
_handleUpgradeError(storagekey, err);
_handleUpgradeError(storagekey + '-' + network, err);
errorCount++;
errorMessage = errorCount + ' storage upgrade failures';
}
if (msg) _handleUpgradeSuccess(storagekey, msg);
if (msg) _handleUpgradeSuccess(storagekey + '-' + network, msg);
});
});
});
@ -444,26 +573,44 @@ angular.module('copayApp.services')
});
};
// data: {
// cards: [
// eid: card id
// id: card id
// lastFourDigits: card number
// token: card token
// ]
// email: account email
// token: account token
// }
root.setBitpayDebitCards = function(network, data, cb) {
if (lodash.isString(data)) {
data = JSON.parse(data);
}
data = data || {};
if (lodash.isEmpty(data) || !data.email) return cb('No card(s) to set');
storage.get('bitpayAccounts-' + network, function(err, bitpayAccounts) {
if (lodash.isEmpty(data) || !data.email) return cb('Cannot set cards: no account to set');
storage.get('bitpayAccounts-v2-' + network, function(err, bitpayAccounts) {
if (err) return cb(err);
if (lodash.isString(bitpayAccounts)) {
bitpayAccounts = JSON.parse(bitpayAccounts);
}
bitpayAccounts = bitpayAccounts || {};
bitpayAccounts[data.email] = bitpayAccounts[data.email] || {};
bitpayAccounts[data.email]['bitpayDebitCards-' + network] = data;
storage.set('bitpayAccounts-' + network, JSON.stringify(bitpayAccounts), cb);
bitpayAccounts[data.email]['bitpayDebitCards-' + network] = data.cards;
storage.set('bitpayAccounts-v2-' + network, JSON.stringify(bitpayAccounts), cb);
});
};
// cb(err, cards)
// cards: [
// eid: card id
// id: card id
// lastFourDigits: card number
// token: card token
// email: account email
// ]
root.getBitpayDebitCards = function(network, cb) {
storage.get('bitpayAccounts-' + network, function(err, bitpayAccounts) {
storage.get('bitpayAccounts-v2-' + network, function(err, bitpayAccounts) {
if (lodash.isString(bitpayAccounts)) {
bitpayAccounts = JSON.parse(bitpayAccounts);
}
@ -471,7 +618,7 @@ angular.module('copayApp.services')
var cards = [];
Object.keys(bitpayAccounts).forEach(function(email) {
// For the UI, add the account email to the card object.
var acctCards = bitpayAccounts[email]['bitpayDebitCards-' + network].cards;
var acctCards = bitpayAccounts[email]['bitpayDebitCards-' + network] || [];
for (var i = 0; i < acctCards.length; i++) {
acctCards[i].email = email;
}
@ -481,24 +628,32 @@ angular.module('copayApp.services')
});
};
// card: {
// eid: card id
// id: card id
// lastFourDigits: card number
// token: card token
// }
root.removeBitpayDebitCard = function(network, card, cb) {
if (lodash.isString(card)) {
card = JSON.parse(card);
}
card = card || {};
if (lodash.isEmpty(card) || !card.eid) return cb('No card to remove');
storage.get('bitpayAccounts-' + network, function(err, bitpayAccounts) {
storage.get('bitpayAccounts-v2-' + network, function(err, bitpayAccounts) {
if (err) cb(err);
if (lodash.isString(bitpayAccounts)) {
bitpayAccounts = JSON.parse(bitpayAccounts);
}
bitpayAccounts = bitpayAccounts || {};
Object.keys(bitpayAccounts).forEach(function(userId) {
var data = bitpayAccounts[userId]['bitpayDebitCards-' + network];
var newCards = lodash.reject(data.cards, {
Object.keys(bitpayAccounts).forEach(function(email) {
var data = bitpayAccounts[email]['bitpayDebitCards-' + network];
var newCards = lodash.reject(data, {
'eid': card.eid
});
data = {};
data.cards = newCards;
data.email = email;
root.setBitpayDebitCards(network, data, function(err) {
if (err) cb(err);
// If there are no more cards in storage then re-enable the next step entry.
@ -515,16 +670,75 @@ angular.module('copayApp.services')
});
};
root.setBitpayCardCredentials = function(network, data, cb) {
storage.set('bitpayCardCredentials-' + network, data, cb);
// data: {
// email: account email
// token: account token
// }
root.setBitpayAccount = function(network, data, cb) {
if (lodash.isString(data)) {
data = JSON.parse(data);
}
data = data || {};
if (lodash.isEmpty(data) || !data.email) return cb('No account to set');
storage.get('bitpayAccounts-v2-' + network, function(err, bitpayAccounts) {
if (err) return cb(err);
if (lodash.isString(bitpayAccounts)) {
bitpayAccounts = JSON.parse(bitpayAccounts);
}
bitpayAccounts = bitpayAccounts || {};
bitpayAccounts[data.email] = bitpayAccounts[data.email] || {};
bitpayAccounts[data.email]['bitpayApi-' + network] = bitpayAccounts[data.email]['bitpayApi-' + network] || {};
bitpayAccounts[data.email]['bitpayApi-' + network].token = data.token;
storage.set('bitpayAccounts-v2-' + network, JSON.stringify(bitpayAccounts), cb);
});
};
root.getBitpayCardCredentials = function(network, cb) {
storage.get('bitpayCardCredentials-' + network, cb);
// cb(err, accounts)
// accounts: {
// email_1: {
// bitpayApi-<network>: {
// token: account token
// }
// bitpayDebitCards-<network>: {
// <card-data>
// }
// }
// ...
// email_n: {
// bitpayApi-<network>: {
// token: account token
// }
// bitpayDebitCards-<network>: {
// <card-data>
// }
// }
// }
root.getBitpayAccounts = function(network, cb) {
storage.get('bitpayAccounts-v2-' + network, function(err, bitpayAccounts) {
if (err) return cb(err);
if (lodash.isString(bitpayAccounts)) {
bitpayAccounts = JSON.parse(bitpayAccounts);
}
cb(err, bitpayAccounts);
});
};
root.removeBitpayCardCredentials = function(network, cb) {
storage.remove('bitpayCardCredentials-' + network, cb);
root.setAppIdentity = function(network, data, cb) {
storage.set('appIdentity-' + network, data, cb);
};
root.getAppIdentity = function(network, cb) {
storage.get('appIdentity-' + network, function(err, data) {
if (err) return cb(err);
if (lodash.isString(data)) {
data = JSON.parse(data);
}
cb(err, data);
});
};
root.removeAppIdentity = function(network, cb) {
storage.remove('appIdentity-' + network, cb);
};
root.removeAllWalletData = function(walletId, cb) {