diff --git a/.gitignore b/.gitignore index 16ebb09f3..18d9fa5ea 100644 --- a/.gitignore +++ b/.gitignore @@ -50,7 +50,7 @@ build/Release node_modules bower_components angular-bitcore-wallet-client/angular-bitcore-wallet-client.js -angular-pbkdf2/angular-pbkdf2.js +angular-bitauth/angular-bitauth.js # Users Environment Variables .lock-wscript diff --git a/Gruntfile.js b/Gruntfile.js index bd234152c..cd74aed4b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -128,7 +128,7 @@ module.exports = function(grunt) { 'bower_components/angular-md5/angular-md5.js', 'bower_components/angular-mocks/angular-mocks.js', 'bower_components/ngtouch/src/ngTouch.js', - 'angular-pbkdf2/angular-pbkdf2.js', + 'angular-bitauth/angular-bitauth.js', 'angular-bitcore-wallet-client/angular-bitcore-wallet-client.js' ], dest: 'www/lib/angular.js' @@ -251,7 +251,7 @@ module.exports = function(grunt) { dist: { files: { 'angular-bitcore-wallet-client/angular-bitcore-wallet-client.js': ['angular-bitcore-wallet-client/index.js'], - 'angular-pbkdf2/angular-pbkdf2.js': ['angular-pbkdf2/index.js'] + 'angular-bitauth/angular-bitauth.js': ['angular-bitauth/index.js'] }, } } diff --git a/angular-bitauth/index.js b/angular-bitauth/index.js new file mode 100644 index 000000000..cb6ebaa2e --- /dev/null +++ b/angular-bitauth/index.js @@ -0,0 +1,18 @@ +var bitauthModule = angular.module('bitauthModule', []); +var bitauth = require('../node_modules/bitauth'); + +bitauthModule.constant('MODULE_VERSION', '1.0.0'); + +bitauthModule.provider("bitauthService", function() { + var provider = {}; + + provider.$get = function() { + var service = {}; + + service = bitauth; + + return service; + }; + + return provider; +}); diff --git a/angular-pbkdf2/index.js b/angular-pbkdf2/index.js deleted file mode 100644 index 4e1e0d9b9..000000000 --- a/angular-pbkdf2/index.js +++ /dev/null @@ -1,18 +0,0 @@ -var pbkdf2Module = angular.module('pbkdf2Module', []); -var pbkdf2Sync = require('../node_modules/pbkdf2').pbkdf2Sync; - -pbkdf2Module.constant('MODULE_VERSION', '1.0.0'); - -pbkdf2Module.provider("pbkdf2Service", function() { - var provider = {}; - - provider.$get = function() { - var service = {}; - - service.pbkdf2Sync = pbkdf2Sync; - - return service; - }; - - return provider; -}); diff --git a/app-template/package.json b/app-template/package.json index d62d1ff5d..0aa436dc1 100644 --- a/app-template/package.json +++ b/app-template/package.json @@ -46,6 +46,7 @@ "angular": "1.4.6", "angular-mocks": "1.4.10", "bhttp": "^1.2.1", + "bitauth": "^0.3.2", "bitcore-wallet-client": "4.2.1", "bower": "^1.7.9", "chai": "^3.5.0", @@ -80,7 +81,6 @@ "karma-sinon": "^1.0.5", "load-grunt-tasks": "^3.5.0", "mocha": "^2.4.5", - "pbkdf2": "^3.0.4", "phantomjs-prebuilt": "^2.1.7", "shelljs": "^0.3.0", "xcode": "^0.8.2" diff --git a/src/js/app.js b/src/js/app.js index 91466a757..784745fdd 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -12,7 +12,7 @@ var modules = [ 'ngCsv', 'angular-md5', 'bwcModule', - 'pbkdf2Module', + 'bitauthModule', 'copayApp.filters', 'copayApp.services', 'copayApp.controllers', diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index dd8623cfa..0f18ebcfc 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -17,13 +17,13 @@ angular.module('copayApp.controllers').controller('amountController', function($ $scope.$on("$ionicView.beforeEnter", function(event, data) { $scope.isWallet = data.stateParams.isWallet; - $scope.isCard = data.stateParams.isCard; + $scope.cardId = data.stateParams.cardId; $scope.toAddress = data.stateParams.toAddress; $scope.toName = data.stateParams.toName; $scope.toEmail = data.stateParams.toEmail; - $scope.showAlternativeAmount = !!$scope.isCard; + $scope.showAlternativeAmount = !!$scope.cardId; - if (!$scope.isCard && !$stateParams.toAddress) { + if (!$scope.cardId && !$stateParams.toAddress) { $log.error('Bad params at amount') throw ('bad params'); } @@ -189,7 +189,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ $scope.finish = function() { var _amount = evaluate(format($scope.amount)); - if ($scope.isCard) { + if ($scope.cardId) { var amountUSD = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount)); var dataSrc = { @@ -199,7 +199,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ ongoingProcess.set('Processing Transaction...', true); $timeout(function() { - bitpayCardService.topUp(dataSrc, function(err, invoiceId) { + bitpayCardService.topUp($scope.cardId, dataSrc, function(err, invoiceId) { if (err) { ongoingProcess.set('Processing Transaction...', false); popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err)); @@ -215,7 +215,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ var payProUrl = data.paymentUrls.BIP73; $state.transitionTo('tabs.bitpayCard.confirm', { - isCard: $scope.isCard, + cardId: $scope.cardId, toName: $scope.toName, paypro: payProUrl }); diff --git a/src/js/controllers/bitpayCard.js b/src/js/controllers/bitpayCard.js index 03d1d123f..8dee60315 100644 --- a/src/js/controllers/bitpayCard.js +++ b/src/js/controllers/bitpayCard.js @@ -1,40 +1,18 @@ 'use strict'; -angular.module('copayApp.controllers').controller('bitpayCardController', function($scope, $timeout, $log, lodash, bitpayCardService, configService, profileService, walletService, ongoingProcess, pbkdf2Service, moment, popupService, gettextCatalog, bwcError) { +angular.module('copayApp.controllers').controller('bitpayCardController', function($scope, $timeout, $log, $state, lodash, bitpayCardService, configService, profileService, walletService, ongoingProcess, moment, popupService, gettextCatalog, bwcError, $ionicHistory) { var self = this; $scope.dateRange = 'last30Days'; $scope.network = bitpayCardService.getEnvironment(); + /* bitpayCardService.getCacheData(function(err, data) { if (err || lodash.isEmpty(data)) return; - $scope.bitpayCardCached = true; self.bitpayCardTransactionHistory = data.transactions; self.bitpayCardCurrentBalance = data.balance; }); - - var processTransactions = function(invoices, history) { - for (var i = 0; i < invoices.length; i++) { - var matched = false; - for (var j = 0; j < history.length; j++) { - if (history[j].description[0].indexOf(invoices[i].id) > -1) { - matched = true; - } - } - if (!matched && ['paid', 'confirmed', 'complete'].indexOf(invoices[i].status) > -1) { - - history.unshift({ - timestamp: invoices[i].invoiceTime, - description: invoices[i].itemDesc, - amount: invoices[i].price, - type: '00611 = Client Funded Deposit', - pending: true, - status: invoices[i].status - }); - } - } - return history; - }; + */ var setDateRange = function(preset) { var startDate, endDate; @@ -63,92 +41,23 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi this.update = function() { var dateRange = setDateRange($scope.dateRange); - self.loadingSession = true; - bitpayCardService.isAuthenticated(function(err, bpSession) { - self.loadingSession = false; - if (err) { + + $scope.loadingHistory = true; + bitpayCardService.getHistory($scope.cardId, dateRange, function(err, history) { + $scope.loadingHistory = false; + if (err || history.error) { + $log.error(err || history.error); + $scope.error = gettextCatalog.getString('Could not get transactions'); return; } - self.bitpayCardAuthenticated = bpSession.isAuthenticated; - self.bitpayCardTwoFactorPending = bpSession.twoFactorPending ? true : false; + self.bitpayCardTransactionHistory = history.transactionList; + self.bitpayCardCurrentBalance = history.currentCardBalance; - if (self.bitpayCardTwoFactorPending) return; - - if (self.bitpayCardAuthenticated) { - $scope.loadingHistory = true; - bitpayCardService.invoiceHistory(function(err, invoices) { - if (err) $log.error(err); - bitpayCardService.transactionHistory(dateRange, function(err, history) { - $scope.loadingHistory = false; - if (err) { - popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not get transactions')); - return; - } - - self.bitpayCardTransactionHistory = processTransactions(invoices, history.transactionList); - self.bitpayCardCurrentBalance = history.currentCardBalance; - - var cacheData = { - balance: self.bitpayCardCurrentBalance, - transactions: self.bitpayCardTransactionHistory - }; - bitpayCardService.setCacheData(cacheData, function(err) { - $scope.bitpayCardCached = true; - if (err) $log.error(err); - }); - }); - }); - } - $timeout(function() { - $scope.$apply(); - }); - }); - }; - - this.authenticate = function(email, password) { - - var data = { - emailAddress : email, - hashedPassword : pbkdf2Service.pbkdf2Sync(password, '..............', 200, 64).toString('hex') - }; - - // POST /authenticate - // emailAddress: - // hashedPassword: - self.authenticating = true; - bitpayCardService.authenticate(data, function(err, auth) { - self.authenticating = false; - if (err && err.data && err.data.error && !err.data.error.twoFactorPending) { - popupService.showAlert(gettextCatalog.getString('Error'), err.statusText || err.data.error || 'Authentiation error'); - return; - } - - self.update(); - $timeout(function() { - $scope.$apply(); - }, 100); - }); - }; - - this.authenticate2FA = function(twoFactorCode) { - - var data = { - twoFactorCode : twoFactorCode - }; - - self.authenticating = true; - bitpayCardService.authenticate2FA(data, function(err, auth) { - self.authenticating = false; - if (err) { - popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Authentiation error')); - return; - } - - self.update(); - $timeout(function() { - $scope.$apply(); - }, 100); + var cacheData = { + balance: self.bitpayCardCurrentBalance, + transactions: self.bitpayCardTransactionHistory + }; }); }; @@ -174,8 +83,18 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi return tx.description; }; - $scope.$on("$ionicView.beforeEnter", function(event, data){ - self.update(); + $scope.$on("$ionicView.beforeEnter", function(event, data) { + $scope.cardId = data.stateParams.id; + if (!$scope.cardId) { + var msg = gettextCatalog.getString('Bad param'); + $ionicHistory.nextViewOptions({ + disableAnimate: true + }); + $state.go('tabs.home'); + popupService.showAlert(null, msg); + } else { + self.update(); + } }); }); diff --git a/src/js/controllers/bitpayCardIntro.js b/src/js/controllers/bitpayCardIntro.js index 943a1c0fd..042195951 100644 --- a/src/js/controllers/bitpayCardIntro.js +++ b/src/js/controllers/bitpayCardIntro.js @@ -1,5 +1,5 @@ 'use strict'; -angular.module('copayApp.controllers').controller('bitpayCardIntroController', function($scope, $state, $timeout, $ionicHistory, storageService, externalLinkService, bitpayCardService) { +angular.module('copayApp.controllers').controller('bitpayCardIntroController', function($scope, $state, $timeout, $ionicHistory, storageService, externalLinkService, bitpayCardService, gettextCatalog, popupService) { $scope.$on("$ionicView.beforeEnter", function(event, data) { $scope.data = { @@ -13,6 +13,39 @@ angular.module('copayApp.controllers').controller('bitpayCardIntroController', f spaceBetween: 100 }; + if (data.stateParams && data.stateParams.secret) { + var obj = { + secret: data.stateParams.secret, + email: data.stateParams.email, + otp: data.stateParams.otp + }; + bitpayCardService.bitAuthPair(obj, function(err, data) { + if (err) { + popupService.showAlert(null, err); + return; + } + var title = gettextCatalog.getString('Add BitPay Cards?'); + var msg = gettextCatalog.getString('Would you like to add this account to your wallet?'); + var ok = gettextCatalog.getString('Add cards'); + var cancel = gettextCatalog.getString('Go back'); + popupService.showConfirm(title, msg, ok, cancel, function(res) { + if (res) { + // Save data + bitpayCardService.setBitpayDebitCards(data, function(err) { + if (err) return; + $ionicHistory.nextViewOptions({ + disableAnimate: true + }); + $state.go('tabs.home'); + }); + } + }); + }); + } else { + // TODO + } + + /* storageService.getNextStep('BitpayCard', function(err, value) { if (value) { $ionicHistory.nextViewOptions({ @@ -24,6 +57,7 @@ angular.module('copayApp.controllers').controller('bitpayCardIntroController', f }, 100); } }); + */ }); $scope.orderBitPayCard = function() { @@ -32,7 +66,11 @@ angular.module('copayApp.controllers').controller('bitpayCardIntroController', f externalLinkService.open(url, target); }; - $scope.connectBitPayCard = function() {}; + $scope.connectBitPayCard = function() { + var url = 'https://bitpay.com/visa/login'; + var target = '_system'; + externalLinkService.open(url, target); + }; $scope.goBack = function() { if ($scope.data.index != 0) $scope.slider.slidePrev(); diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index 465257e7c..5ee30b1b9 100644 --- a/src/js/controllers/confirm.js +++ b/src/js/controllers/confirm.js @@ -8,7 +8,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( $scope.$on("$ionicView.beforeEnter", function(event, data) { $scope.isWallet = data.stateParams.isWallet; - $scope.isCard = data.stateParams.isCard; + $scope.cardId = data.stateParams.cardId; $scope.toAmount = data.stateParams.toAmount; $scope.toAddress = data.stateParams.toAddress; $scope.toName = data.stateParams.toName; diff --git a/src/js/controllers/tab-home.js b/src/js/controllers/tab-home.js index c4fa9b106..7e40a4e2c 100644 --- a/src/js/controllers/tab-home.js +++ b/src/js/controllers/tab-home.js @@ -6,7 +6,6 @@ angular.module('copayApp.controllers').controller('tabHomeController', var listeners = []; var notifications = []; $scope.externalServices = {}; - $scope.bitpayCardEnabled = true; // TODO $scope.openTxpModal = txpModalService.open; $scope.version = $window.version; $scope.name = $window.appConfig.nameCase; @@ -203,10 +202,16 @@ angular.module('copayApp.controllers').controller('tabHomeController', }; var bitpayCardCache = function() { + bitpayCardService.getBitpayDebitCards(function(err, data) { + if (err) return; + $scope.bitpayCards = data.cards; + }); + /* bitpayCardService.getCacheData(function(err, data) { if (err ||  lodash.isEmpty(data)) return; $scope.bitpayCard = data; }); + */ }; $scope.onRefresh = function() { @@ -215,7 +220,6 @@ angular.module('copayApp.controllers').controller('tabHomeController', }; $scope.$on("$ionicView.enter", function(event, data) { - $scope.bitpayCard = null; nextStep(); updateAllWallets(); diff --git a/src/js/routes.js b/src/js/routes.js index 0782c14f2..72c269060 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -847,7 +847,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr */ .state('tabs.bitpayCardIntro', { - url: '/bitpay-card-intro', + url: '/bitpay-card-intro/:secret/:email/:otp', views: { 'tab-home@tabs': { controller: 'bitpayCardIntroController', @@ -856,7 +856,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) .state('tabs.bitpayCard', { - url: '/bitpay-card', + url: '/bitpay-card/:id', views: { 'tab-home@tabs': { controller: 'bitpayCardController', @@ -866,7 +866,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) .state('tabs.bitpayCard.amount', { - url: '/amount/:isCard/:toName', + url: '/amount/:cardId/:toName', views: { 'tab-home@tabs': { controller: 'amountController', @@ -875,7 +875,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) .state('tabs.bitpayCard.confirm', { - url: '/confirm/:isCard/:toAddress/:toName/:toAmount/:toEmail/:description/:paypro', + url: '/confirm/:cardId/:toAddress/:toName/:toAmount/:toEmail/:description/:paypro', views: { 'tab-home@tabs': { controller: 'confirmController', diff --git a/src/js/services/bitpayCardService.js b/src/js/services/bitpayCardService.js index c49bc5ffa..1bce179f1 100644 --- a/src/js/services/bitpayCardService.js +++ b/src/js/services/bitpayCardService.js @@ -1,9 +1,10 @@ 'use strict'; -angular.module('copayApp.services').factory('bitpayCardService', function($http, $log, lodash, storageService) { +angular.module('copayApp.services').factory('bitpayCardService', function($http, $log, lodash, storageService, bitauthService, platformInfo) { var root = {}; var credentials = {}; var bpSession = {}; + var pubkey, sin; var _setCredentials = function() { /* @@ -12,16 +13,26 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http, */ credentials.NETWORK = 'livenet'; if (credentials.NETWORK == 'testnet') { + credentials.BITPAY_PRIV_KEY = ''; credentials.BITPAY_API_URL = 'https://test.bitpay.com'; } else { + credentials.BITPAY_PRIV_KEY = ''; credentials.BITPAY_API_URL = 'https://bitpay.com'; + } + try { + pubkey = bitauthService.getPublicKeyFromPrivateKey(credentials.BITPAY_PRIV_KEY); + sin = bitauthService.getSinFromPublicKey(pubkey); + } + catch (e) { + $log.error(e); }; }; var _setError = function(msg, e) { $log.error(msg); - return e; + var error = e.data ? e.data.error : msg; + return error; }; var _getUser = function(cb) { @@ -92,40 +103,163 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http, return credentials.NETWORK; }; - root.topUp = function(data, cb) { - var dataSrc = { - amount: data.amount, - currency: data.currency + root.getApiUrl = function() { + _setCredentials(); + return credentials.BITPAY_API_URL; + }; + + var _postBitAuth = function(endpoint, data) { + var dataToSign = credentials.BITPAY_API_URL + endpoint + JSON.stringify(data); + var signedData = bitauthService.sign(dataToSign, credentials.BITPAY_PRIV_KEY); + + return { + method: 'POST', + url: credentials.BITPAY_API_URL + endpoint, + headers: { + 'content-type': 'application/json', + 'x-identity': pubkey, + 'x-signature': signedData + }, + data: data }; - $http(_postBitPay('/visa-api/topUp', dataSrc)).then(function(data) { - $log.info('BitPay TopUp: SUCCESS'); - return cb(null, data.data.data.invoice); + }; + + var _afterBitAuthSuccess = function(obj, cb) { + var data = { + method: 'getTokens' + }; + // Get tokens + $http(_postBitAuth('/api/v2/', data)).then(function(data) { + $log.info('BitPay Get Tokens: SUCCESS'); + var token = lodash.find(data.data.data, 'visaUser'); + if (lodash.isEmpty(token)) return cb(_setError('No token for visaUser')); + token = token.visaUser; + data['method'] = 'getDebitCards'; + // Get Debit Cards + $http(_postBitAuth('/api/v2/' + token, data)).then(function(data) { + $log.info('BitPay Get Debit Cards: SUCCESS'); + var cards = data.data.data; + return cb(null, {token: token, cards: cards, email: obj.email}); + }, function(data) { + return cb(_setError('BitPay Card Error: Get Debit Cards', data)); + }); }, function(data) { - return cb(_setError('BitPay Card Error: TopUp', data)); + return cb(_setError('BitPay Card Error: Get Token', data)); }); }; - root.transactionHistory = function(dateRange, cb) { - var params; - if (!dateRange.startDate) { - params = ''; - } else { - params = '/?startDate=' + dateRange.startDate + '&endDate=' + dateRange.endDate; + root.bitAuthPair = function(obj, cb) { + _getSession(function(err, session) { + if (err) return cb(err); + var deviceName = 'Unknow device'; + if (platformInfo.isNW) { + deviceName = require('os').platform(); + } else if (platformInfo.isCordova) { + deviceName = device.model; + } + var userData = { + csrf: session.csrfToken, + secret: obj.secret, + deviceName: deviceName, + code: obj.otp + }; + + var dataToSign = credentials.BITPAY_API_URL + '/visa-api/validateBitAuthPairingCode' + JSON.stringify(userData); + var signedData = bitauthService.sign(dataToSign, credentials.BITPAY_PRIV_KEY); + + $http({ + method: 'POST', + url: credentials.BITPAY_API_URL + '/visa-api/validateBitAuthPairingCode', + headers: { + 'content-type': 'application/json', + 'x-csrf-token': session.csrfToken, + 'x-identity': pubkey, + 'x-signature': signedData + }, + data: userData + }).then(function(data) { + $log.info('BitPay Card BitAuth: SUCCESS'); + // Set an UI flag + storageService.setNextStep('BitpayCard', true, function(err) {}); + // Get cards + _afterBitAuthSuccess(obj, cb); + }, function(data) { + return cb(_setError('BitPay Card Error: BitAuth', data)); + }); + + }); + }; + + var _processTransactions = function(invoices, history) { + invoices = invoices || []; + for (var i = 0; i < invoices.length; i++) { + var matched = false; + for (var j = 0; j < history.length; j++) { + if (history[j].description[0].indexOf(invoices[i].id) > -1) { + matched = true; + } + } + if (!matched && ['paid', 'confirmed', 'complete'].indexOf(invoices[i].status) > -1) { + + history.unshift({ + timestamp: invoices[i].invoiceTime, + description: invoices[i].itemDesc, + amount: invoices[i].price, + type: '00611 = Client Funded Deposit', + pending: true, + status: invoices[i].status + }); + } } - $http(_getBitPay('/visa-api/transactionHistory' + params)).then(function(data) { - $log.info('BitPay Get Transaction History: SUCCESS'); - return cb(null, data.data.data); - }, function(data) { - return cb(_setError('BitPay Card Error: Get Transaction History', data)); + return history; + }; + + root.getHistory = function(cardId, params, cb) { + params = params || {}; + var json = {}; +// json = { +// method: 'getInvoiceHistory' +// }; + root.getBitpayDebitCards(function(err, data) { + var card = lodash.find(data.cards, {id : cardId}); + if (!card) return cb(_setError('No card available')); + // Get invoices +// $http(_postBitAuth('/api/v2/' + card.token, json)).then(function(data) { +// var invoices = data.data.data; + json = { + method: 'getTransactionHistory', + params: JSON.stringify(params) + }; + // Get transactions list + $http(_postBitAuth('/api/v2/' + card.token, json)).then(function(data) { + $log.info('BitPay Get Transactions: SUCCESS'); + var history = data.data.data || data.data; +// history['txs'] = _processTransactions(invoices, history.transactionList); + return cb(null, history); + }, function(data) { + return cb(_setError('BitPay Card Error: Get Transactions', data)); + }); +// }, function(data) { +// return cb(_setError('BitPay Card Error: Get Invoices', data)); +// }); }); }; - root.invoiceHistory = function(cb) { - $http(_getBitPay('/visa-api/invoiceHistory')).then(function(data) { - $log.info('BitPay Get Invoice History: SUCCESS'); - return cb(null, data.data.data); - }, function(data) { - return cb(_setError('BitPay Card Error: Get Invoice History', data)); + root.topUp = function(cardId, params, cb) { + var json = { + method: 'generateTopUpInvoice', + params: JSON.stringify(params) + }; + root.getBitpayDebitCards(function(err, data) { + var card = lodash.find(data.cards, {id : cardId}); + if (!card) return cb(_setError('No card available')); + $http(_postBitAuth('/api/v2/' + card.token, json)).then(function(data) { + $log.info('BitPay TopUp: SUCCESS'); + var invoiceId = data.data.data.invoice; + return cb(null, invoiceId); + }, function(data) { + return cb(_setError('BitPay Card Error: TopUp', data)); + }); }); }; @@ -138,58 +272,9 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http, }); }; - root.authenticate = function(userData, cb) { - _setUser(userData, function(err) { - $http(_postBitPay('/visa-api/authenticate', userData)).then(function(data) { - $log.info('BitPay Authenticate: SUCCESS'); - _getSession(function(err, session) { - if (err) return cb(err); - return cb(null, session); - }); - }, function(data) { - if (data && data.data && data.data.error.twoFactorPending) { - $log.error('BitPay Card needs 2FA Authentication'); - _getSession(function(err, session) { - if (err) return cb(err); - return cb(null, session); - }); - } else { - return cb(data); - } - }); - }); - }; - - root.authenticate2FA = function(userData, cb) { - $http(_postBitPay('/visa-api/verify-two-factor', userData)).then(function(data) { - $log.info('BitPay 2FA: SUCCESS'); - return cb(null, data); - }, function(data) { - return cb(_setError('BitPay Card Error: 2FA', data)); - }); - }; - - root.isAuthenticated = function(cb) { - _getSession(function(err, session) { - if (err) return cb(err); - if (!session.isAuthenticated) { - _getUser(function(err, user) { - if (err) return cb(err); - if (lodash.isEmpty(user)) return cb(null, session); - root.authenticate(user, function(err, session) { - if (err) return cb(err); - return cb(null, session); - }); - }); - } else { - return cb(null, session); - } - }); - }; - - root.getCacheData = function(cb) { + root.getBitpayDebitCards = function(cb) { _setCredentials(); - storageService.getBitpayCardCache(credentials.NETWORK, function(err, data) { + storageService.getBitpayDebitCards(credentials.NETWORK, function(err, data) { if (err) return cb(err); if (lodash.isString(data)) { data = JSON.parse(data); @@ -199,18 +284,18 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http, }); }; - root.setCacheData = function(data, cb) { + root.setBitpayDebitCards = function(data, cb) { _setCredentials(); data = JSON.stringify(data); - storageService.setBitpayCardCache(credentials.NETWORK, data, function(err) { + storageService.setBitpayDebitCards(credentials.NETWORK, data, function(err) { if (err) return cb(err); return cb(); }); }; - root.removeCacheData = function(cb) { + root.removeBitpayDebitCards = function(cb) { _setCredentials(); - storageService.removeBitpayCardCache(credentials.NETWORK, function(err) { + storageService.removeBitpayDebitCards(credentials.NETWORK, function(err) { if (err) return cb(err); return cb(); }); @@ -218,14 +303,8 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http, root.logout = function(cb) { _setCredentials(); - root.removeCacheData(function() {}); - storageService.removeBitpayCard(credentials.NETWORK, function(err) { - $http(_getBitPay('/visa-api/logout')).then(function(data) { - $log.info('BitPay Logout: SUCCESS'); - return cb(data); - }, function(data) { - return cb(_setError('BitPay Card Error: Logout ', data)); - }); + storageService.removeBitpayDebitCards(credentials.NETWORK, function(err) { + $log.info('BitPay Logout: SUCCESS'); }); }; diff --git a/src/js/services/incomingData.js b/src/js/services/incomingData.js index fc363590c..33c2de8a9 100644 --- a/src/js/services/incomingData.js +++ b/src/js/services/incomingData.js @@ -23,6 +23,16 @@ angular.module('copayApp.services').factory('incomingData', function($log, $ioni return newUri; }; + function getParameterByName(name, url) { + if (!url) return; + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), + results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, " ")); + } + // data extensions for Payment Protocol with non-backwards-compatible request if ((/^bitcoin:\?r=[\w+]/).exec(data)) { data = decodeURIComponent(data.replace('bitcoin:?r=', '')); @@ -33,7 +43,6 @@ angular.module('copayApp.services').factory('incomingData', function($log, $ioni return true; } - data = sanitizeUri(data); // BIP21 @@ -87,6 +96,21 @@ angular.module('copayApp.services').factory('incomingData', function($log, $ioni } else if (data && data.indexOf($window.appConfig.name + '://coinbase')==0) { return $state.go('uricoinbase', {url: data}); + // BitPayCard Authentication + } else if (data && data.indexOf($window.appConfig.name + '://')==0) { + var secret = getParameterByName('secret', data); + var email = getParameterByName('email', data); + var otp = getParameterByName('otp', data); + $state.go('tabs.home'); + $timeout(function() { + $state.transitionTo('tabs.bitpayCardIntro', { + secret: secret, + email: email, + otp: otp + }); + }, 100); + return true; + // Join } else if (data && data.match(/^copay:[0-9A-HJ-NP-Za-km-z]{70,80}$/)) { $state.go('tabs.home'); diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index a0e0f6503..bbbcea5a0 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -337,16 +337,16 @@ angular.module('copayApp.services') storage.remove('bitpayCard-' + network, cb); }; - root.setBitpayCardCache = function(network, data, cb) { - storage.set('bitpayCardCache-' + network, data, cb); + root.setBitpayDebitCards = function(network, data, cb) { + storage.set('bitpayDebitCards-' + network, data, cb); }; - root.getBitpayCardCache = function(network, cb) { - storage.get('bitpayCardCache-' + network, cb); + root.getBitpayDebitCards = function(network, cb) { + storage.get('bitpayDebitCards-' + network, cb); }; - root.removeBitpayCardCache = function(network, cb) { - storage.remove('bitpayCardCache-' + network, cb); + root.removeBitpayDebitCards = function(network, cb) { + storage.remove('bitpayDebitCards-' + network, cb); }; root.removeAllWalletData = function(walletId, cb) { diff --git a/www/views/amount.html b/www/views/amount.html index 8e961d3b7..9754e8867 100644 --- a/www/views/amount.html +++ b/www/views/amount.html @@ -12,15 +12,15 @@
Recipient
-
+
- - + +
- {{toName || toAddress}} + {{toName || toAddress}}
diff --git a/www/views/bitpayCard.html b/www/views/bitpayCard.html index b5d3cd836..12c972d3e 100644 --- a/www/views/bitpayCard.html +++ b/www/views/bitpayCard.html @@ -3,8 +3,8 @@ BitPay Card - - @@ -16,147 +16,76 @@ Sandbox version. Only for testing purpose
-
- Loading... -
- -
-
- +
+
+
+
${{bitpayCard.bitpayCardCurrentBalance}}
+ + {{'Add Funds'|translate}} +
-

- Login to your account - 2-Step Verification -

- -
- -
- - - -
- - -
- -

- Enter the verification code generated by the authenticator app on your phone. -

- -
- -
- -
- - -
-
- -
-
-
-
-
${{bitpayCard.bitpayCardCurrentBalance}}
- - {{'Add Funds'|translate}} - -
-
- ... -
-
-
- +
+ ...
+
+ +
+
+
+ {{error}} +
+ +
+ +

Get started

+

Your BitPay Card is ready. Add funds to your card to start using your card at stores and ATMs worldwide.

+
+ +
+
+ +
- -

Get started

-

Your BitPay Card is ready. Add funds to your card to start using your card at stores and ATMs worldwide.

-
+ ng-repeat="tx in bitpayCard.bitpayCardTransactionHistory | orderBy: ['pending','-timestamp']" + class="item row" + ng-init="bitpayCard.getMerchantInfo(tx)"> +
+ +
-
-
- +
+
+ {{tx.merchant.name}} +
+
+ {{tx.merchant.city}}, {{tx.merchant.state}} +
-
- -
- -
-
- {{tx.merchant.name}} -
-
- {{tx.merchant.city}}, {{tx.merchant.state}} -
-
-
- {{desc}} -
-
- - -
-
-
- {{tx.amount | currency:'$':2 }} -
- + ng-init="desc = bitpayCard.processDescription(tx)" + class="col size-12"> + {{desc}} +
+
+ + +
+
+
+ {{tx.amount | currency:'$':2 }}
+
diff --git a/www/views/confirm.html b/www/views/confirm.html index 953dc89fb..9886ca270 100644 --- a/www/views/confirm.html +++ b/www/views/confirm.html @@ -23,7 +23,16 @@
To - + + +
+
+ {{toName}} +
+ + + {{_paypro.domain}} +
{{toAddress}} diff --git a/www/views/tab-home.html b/www/views/tab-home.html index 531cc72eb..81b5551ca 100644 --- a/www/views/tab-home.html +++ b/www/views/tab-home.html @@ -78,7 +78,6 @@ {{wallet.m}}-of-{{wallet.n}}
-

Incomplete @@ -91,15 +90,15 @@

- +

BitPay Card

-

Add funds to get started

- ${{bitpayCard.balance}} +

{{card.lastFourDigits}}