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/apply.js b/app-template/apply.js index 14fdaab57..7ecad93d8 100755 --- a/app-template/apply.js +++ b/app-template/apply.js @@ -5,6 +5,7 @@ // var templates = { + 'package.json': '/', 'Makefile': 'cordova/', 'ProjectMakefile': 'cordova/', 'config-template.xml': '/', diff --git a/app-template/bitpay/appConfig.json b/app-template/bitpay/appConfig.json index 79eb58f52..54faf2203 100644 --- a/app-template/bitpay/appConfig.json +++ b/app-template/bitpay/appConfig.json @@ -5,12 +5,13 @@ "purposeLine": "Secure Bitcoin Wallet", "bundleName": "wallet", "appUri": "bitpay", - "name": "bitpay", "nameNoSpace": "bitpay", "nameCase": "BitPay", "nameCaseNoSpace": "BitPay", "gitHubRepoName": "bitpay-wallet", + "gitHubRepoUrl": "git://github.com/bitpay/bitpay-wallet.git", + "gitHubRepoBugs": "https://github.com/bitpay/bitpay-wallet/issues", "disclaimerUrl": "", "url": "https://bitpay.com", "appDescription": "Secure Bitcoin Wallet", diff --git a/app-template/bitpay/img/favicon.ico b/app-template/bitpay/img/favicon.ico new file mode 100644 index 000000000..166c0bcaf Binary files /dev/null and b/app-template/bitpay/img/favicon.ico differ diff --git a/app-template/bitpay/img/icon-128.png b/app-template/bitpay/img/icon-128.png new file mode 100644 index 000000000..6958667cd Binary files /dev/null and b/app-template/bitpay/img/icon-128.png differ diff --git a/app-template/config-template.xml b/app-template/config-template.xml index 1db63c59a..faa71f449 100644 --- a/app-template/config-template.xml +++ b/app-template/config-template.xml @@ -56,8 +56,9 @@ - - + + + diff --git a/app-template/copay/appConfig.json b/app-template/copay/appConfig.json index 7a6ee7ef3..6e66bd2bf 100644 --- a/app-template/copay/appConfig.json +++ b/app-template/copay/appConfig.json @@ -3,14 +3,15 @@ "packageDescription": "Copay Bitcoin Wallet", "userVisibleName": "Copay", "purposeLine": "Copay Bitcoin Wallet", + "bundleName": "copay", "appUri": "copay", - "name": "copay", "nameNoSpace": "copay", "nameCase": "Copay", "nameCaseNoSpace": "Copay", - "bundleName": "copay", "gitHubRepoName": "copay", + "gitHubRepoUrl": "git://github.com/bitpay/copay.git", + "gitHubRepoBugs": "https://github.com/bitpay/copay/issues", "disclaimerUrl": "https://copay.io/disclaimer", "url": "https://copay.io", "appDescription": "Copay Bitcoin Wallet", diff --git a/app-template/copay/img/favicon.ico b/app-template/copay/img/favicon.ico new file mode 100644 index 000000000..e2f92139d Binary files /dev/null and b/app-template/copay/img/favicon.ico differ diff --git a/app-template/copay/img/icon-128.png b/app-template/copay/img/icon-128.png new file mode 100644 index 000000000..8624a0189 Binary files /dev/null and b/app-template/copay/img/icon-128.png differ diff --git a/app-template/package.json b/app-template/package.json index d62d1ff5d..6f535b9d5 100644 --- a/app-template/package.json +++ b/app-template/package.json @@ -14,14 +14,14 @@ "main": "www/index.html", "window": { "title": "*USERVISIBLENAME* - *PURPOSELINE*", - "icon": "./www/img/icons/icon-256.png", + "icon": "www/img/icon-128.png", "toolbar": false, "show": true, "visible": true, "resizable": true, "frame": true, "width": 400, - "height": 600, + "height": 650, "position": "center", "fullscreen": false }, @@ -32,25 +32,27 @@ }, "dom_storage_quota": 200, "id": "jid1-x7bV5evAaI1P9Q", - "homepage": "https://github.com/bitpay/copay", + "homepage": "*URL*", "license": "MIT", "repository": { - "url": "git://github.com/bitpay/copay.git", + "url": "*GITHUBREPOURL*", "type": "git" }, "bugs": { - "url": "https://github.com/bitpay/copay/issues" + "url": "*GITHUBREPOBUGS*" }, "dependencies": { "adm-zip": "^0.4.7", "angular": "1.4.6", "angular-mocks": "1.4.10", + "bezier-easing": "^2.0.3", "bhttp": "^1.2.1", - "bitcore-wallet-client": "4.2.1", + "bitauth": "^0.3.2", + "bitcore-wallet-client": "4.3.1", "bower": "^1.7.9", "chai": "^3.5.0", - "cordova": "5.4.1", "cordova-android": "5.1.1", + "cordova-custom-config": "^3.0.5", "cordova-plugin-qrscanner": "^2.3.1", "coveralls": "^2.11.9", "express": "^4.11.2", @@ -80,24 +82,40 @@ "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" + "shelljs": "^0.3.0" }, "scripts": { - "preinstall": "bower install && npm i fs-extra", - "build": "grunt", - "apply:copay": "cd app-template && node apply.js", - "apply:bitpay-wallet": "cd app-template && node apply.js bitpay-wallet", - "start": "npm run build && node app.js", - "watch": "grunt watch", - "test": "./node_modules/.bin/grunt test-coveralls", - "clean": "git clean -dfx", - "start:ios": "npm run build && cd cordova && trash project-ios && make ios && open project-ios/platforms/ios/BitPay\\ Wallet.xcodeproj", - "start:android": "npm run build && cd cordova && trash project-android && make android && open -a /Applications/Android\\ Studio.app project-android/platforms/android" + "preinstall": "bower install && npm i fs-extra", + "postinstall": "npm run apply:bitpay && cordova prepare", + "start": "npm run build:www && ionic serve --nolivereload --nogulp -s", + "start:ios": "npm run build:www && npm run build:ios && npm run open:ios", + "start:android": "npm run build:www && npm run build:android && npm run run:android", + "watch": "grunt watch", + "build:www": "grunt", + "build:www-release": "grunt prod", + "build:ios": "cordova prepare ios && cordova build ios --debug", + "build:android": "cordova prepare android && cordova build android --debug", + "build:ios-release": "cordova prepare ios && cordova build ios --release", + "build:android-release": "cordova prepare android && cordova build android --release", + "open:ios": "open platforms/ios/*.xcodeproj", + "open:android": "open -a open -a /Applications/Android\\ Studio.app platforms/android", + "final:www": "npm run clean-all && npm run build:www-release", + "final:ios": "npm run final:www && npm run build:ios-release && npm run open:ios", + "final:android": "npm run final:www && npm run build:android-release && npm run run:android", + "run:android": "cordova run android --device", + "log:android": "adb logcat | grep chromium", + "apply:copay": "cd app-template && node apply.js copay", + "apply:bitpay": "cd app-template && node apply.js bitpay", + "test": "./node_modules/.bin/grunt test-coveralls", + "clean": "trash platforms && trash plugins && npm run postinstall", + "clean-all": "git clean -dfx && npm install" }, "devDependencies": { - "trash-cli": "^1.4.0" + "cordova": "^6.3.1", + "grunt": "^1.0.1", + "ionic": "^2.1.0", + "trash-cli": "^1.4.0", + "lodash": "^4.3.0" } } diff --git a/app-template/setup-win.iss b/app-template/setup-win.iss index d9f6eaa30..d2443afef 100755 --- a/app-template/setup-win.iss +++ b/app-template/setup-win.iss @@ -8,7 +8,7 @@ #define MyAppExeName "*PACKAGENAME.exe" [Setup] -AppId={{804636ee-b017-4cad-8719-e58ac97ffa5c} +AppId={804636ee-b017-4cad-8719-e58ac97ffa5c} AppName={#MyAppName} AppVersion={#MyAppVersion} ;AppVerName={#MyAppName} {#MyAppVersion} diff --git a/package.json b/package.json index c690d4989..fd30b3f2e 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,9 @@ -{ + { + "//":"PLEASE! Do not edit this file directly", + "//":" Modify it at app-template/", + + "name": "bitpay", + "description": "Secure Bitcoin Wallet", "author": "BitPay", "version": "0.14.0", "keywords": [ @@ -9,14 +14,35 @@ "multisignature", "bitcore" ], - "homepage": "https://github.com/bitpay/copay", + "main": "www/index.html", + "window": { + "title": "BitPay - Secure Bitcoin Wallet", + "icon": "www/img/icon-128.png", + "toolbar": false, + "show": true, + "visible": true, + "resizable": true, + "frame": true, + "width": 400, + "height": 650, + "position": "center", + "fullscreen": false + }, + "webkit": { + "page-cache": false, + "java": false, + "plugin": false + }, + "dom_storage_quota": 200, + "id": "jid1-x7bV5evAaI1P9Q", + "homepage": "https://bitpay.com", "license": "MIT", "repository": { - "url": "git://github.com/bitpay/copay.git", + "url": "git://github.com/bitpay/bitpay-wallet.git", "type": "git" }, "bugs": { - "url": "https://github.com/bitpay/copay/issues" + "url": "https://github.com/bitpay/bitpay-wallet/issues" }, "dependencies": { "adm-zip": "^0.4.7", @@ -24,6 +50,7 @@ "angular-mocks": "1.4.10", "bezier-easing": "^2.0.3", "bhttp": "^1.2.1", + "bitauth": "^0.3.2", "bitcore-wallet-client": "4.3.1", "bower": "^1.7.9", "chai": "^3.5.0", @@ -58,7 +85,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" }, diff --git a/public/views/bitpayCardIntro.html b/public/views/bitpayCardIntro.html new file mode 100644 index 000000000..7b42f3611 --- /dev/null +++ b/public/views/bitpayCardIntro.html @@ -0,0 +1,45 @@ + + + + + + + + + + +
+ +
+ + +

+ Turn bitcoin into dollars, swipe anywhere Visa® is accepted. +

+
+ +

+ Get local cash anywhere you go, from any Visa®-compatible ATM. +

+ *ATM bank fees may apply +
+

+
+ +

+ Pay 0% fees to turn bitcoin into dollars. +

+
+
+
+ + +
+
+
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 54a9c09a5..ef5364b0b 100644 --- a/src/js/controllers/bitpayCard.js +++ b/src/js/controllers/bitpayCard.js @@ -1,39 +1,19 @@ '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, moment, popupService, gettextCatalog, $ionicHistory) { var self = this; - $scope.dateRange = 'last30Days'; + $scope.dateRange = { value: '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 getFromCache = function(cb) { + bitpayCardService.getBitpayDebitCardsHistory($scope.cardId, function(err, data) { + if (err || lodash.isEmpty(data)) return cb(); + $scope.historyCached = true; + self.bitpayCardTransactionHistory = data.transactions; + self.bitpayCardCurrentBalance = data.balance; + return cb(); + }); }; var setDateRange = function(preset) { @@ -62,41 +42,35 @@ 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; + var dateRange = setDateRange($scope.dateRange.value); + + $scope.loadingHistory = true; + bitpayCardService.getHistory($scope.cardId, dateRange, function(err, history) { + $scope.loadingHistory = false; if (err) { + $log.error(err); + $scope.error = gettextCatalog.getString('Could not get transactions'); return; } - self.bitpayCardAuthenticated = bpSession.isAuthenticated; - self.bitpayCardTwoFactorPending = bpSession.twoFactorPending ? true : false; + var txs = lodash.clone(history.txs); + for (var i = 0; i < txs.length; i++) { + txs[i] = _getMerchantInfo(txs[i]); + txs[i].icon = _getIconName(txs[i]); + txs[i].desc = _processDescription(txs[i]); + } + self.bitpayCardTransactionHistory = txs; + self.bitpayCardCurrentBalance = history.currentCardBalance; - if (self.bitpayCardTwoFactorPending) return; - - if (self.bitpayCardAuthenticated) { - $scope.loadingHistory = true; - bitpayCardService.invoiceHistory(function(err, invoices) { + if ($scope.dateRange.value == 'last30Days') { + $log.debug('BitPay Card: store cache history'); + var cacheHistory = { + balance: history.currentCardBalance, + transactions: history.txs + }; + bitpayCardService.setBitpayDebitCardsHistory($scope.cardId, cacheHistory, {}, function(err) { 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) { - if (err) $log.error(err); - }); - }); + $scope.historyCached = true; }); } $timeout(function() { @@ -105,76 +79,43 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi }); }; - 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); - }); - }; - - this.getMerchantInfo = function(tx) { + var _getMerchantInfo = function(tx) { var bpTranCodes = bitpayCardService.bpTranCodes; lodash.keys(bpTranCodes).forEach(function(code) { if (tx.type.indexOf(code) === 0) { lodash.assign(tx, bpTranCodes[code]); } }); + return tx; }; - this.getIconName = function(tx) { + var _getIconName = function(tx) { var icon = tx.mcc || tx.category || null; if (!icon) return 'default'; return bitpayCardService.iconMap[icon]; }; - this.processDescription = function(tx) { + var _processDescription = function(tx) { if (lodash.isArray(tx.description)) { return tx.description[0]; } 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 { + getFromCache(function() { + self.update(); + }); + } }); }); diff --git a/src/js/controllers/bitpayCardIntro.js b/src/js/controllers/bitpayCardIntro.js new file mode 100644 index 000000000..95d5cbfb2 --- /dev/null +++ b/src/js/controllers/bitpayCardIntro.js @@ -0,0 +1,72 @@ +'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 Cards'); + popupService.showPrompt(null, msg, null, function(res) { + cb(res); + }); + } else { + cb(); + } + }; + + $scope.$on("$ionicView.beforeEnter", function(event, data) { + + if (data.stateParams && data.stateParams.secret) { + var obj = { + 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(null, err); + return; + } + var title = gettextCatalog.getString('Add BitPay Cards?'); + var msg = gettextCatalog.getString('Would you like to add this account ({{email}}) to your wallet?', {email: obj.email}); + var ok = gettextCatalog.getString('Add cards'); + 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}); + } + }); + }); + } + }); + }); + }); + } else { + bitpayCardService.getCredentials(function(err, credentials) { + if (err) popupService.showAlert(null, err); + else $log.info('BitPay Debit Card Credentials: Ok.'); + }); + } + }); + + $scope.orderBitPayCard = function() { + var url = 'https://bitpay.com/visa/'; + externalLinkService.open(url); + }; + + $scope.connectBitPayCard = function() { + var url = 'https://bitpay.com/visa/login'; + externalLinkService.open(url); + }; +}); + diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index 465257e7c..0b8949f3a 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; @@ -374,11 +374,22 @@ angular.module('copayApp.controllers').controller('confirmController', function( }; $scope.onSuccessConfirm = function() { + var previousView = $ionicHistory.viewHistory().backView && $ionicHistory.viewHistory().backView.stateName; + var fromBitPayCard = previousView.match(/tabs.bitpayCard/) ? true : false; + $ionicHistory.nextViewOptions({ disableAnimate: true }); + $ionicHistory.removeBackView(); $scope.sendStatus = ''; - $state.go('tabs.send'); + + if (fromBitPayCard) { + $timeout(function() { + $state.transitionTo('tabs.bitpayCard', {id: $stateParams.cardId}); + }, 100); + } else { + $state.go('tabs.send'); + } }; function publishAndSign(wallet, txp, onSendStatusChange) { diff --git a/src/js/controllers/preferencesBitpayCard.js b/src/js/controllers/preferencesBitpayCard.js index 2abf6a8c4..0fb74d228 100644 --- a/src/js/controllers/preferencesBitpayCard.js +++ b/src/js/controllers/preferencesBitpayCard.js @@ -1,17 +1,17 @@ 'use strict'; angular.module('copayApp.controllers').controller('preferencesBitpayCardController', - function($scope, $state, $timeout, $ionicHistory, bitpayCardService, popupService) { + function($scope, $state, $timeout, $ionicHistory, bitpayCardService, popupService, gettextCatalog) { - $scope.logout = function() { - var title = 'Are you sure you would like to log out of your Bitpay Card account?'; - popupService.showConfirm(title, null, null, null, function(res) { - if (res) logout(); + $scope.remove = function() { + var msg = gettextCatalog.getString('Are you sure you would like to remove your BitPay Card account from this device?'); + popupService.showConfirm(null, msg, null, null, function(res) { + if (res) remove(); }); }; - var logout = function() { - bitpayCardService.logout(function() { + var remove = function() { + bitpayCardService.remove(function() { $ionicHistory.removeBackView(); $timeout(function() { $state.go('tabs.home'); @@ -19,4 +19,11 @@ angular.module('copayApp.controllers').controller('preferencesBitpayCardControll }); }; + $scope.$on("$ionicView.beforeEnter", function(event, data) { + bitpayCardService.getBitpayDebitCards(function(err, data) { + if (err) return; + $scope.bitpayCards = data.cards; + }); + }); + }); diff --git a/src/js/controllers/tab-home.js b/src/js/controllers/tab-home.js index c4fa9b106..3d67a56f4 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,9 +202,21 @@ angular.module('copayApp.controllers').controller('tabHomeController', }; var bitpayCardCache = function() { - bitpayCardService.getCacheData(function(err, data) { - if (err ||  lodash.isEmpty(data)) return; - $scope.bitpayCard = data; + bitpayCardService.getBitpayDebitCards(function(err, data) { + if (err) return; + if (lodash.isEmpty(data)) { + $scope.bitpayCards = null; + return; + } + $scope.bitpayCards = data.cards; + }); + bitpayCardService.getBitpayDebitCardsHistory(null, function(err, data) { + if (err) return; + if (lodash.isEmpty(data)) { + $scope.cardsHistory = null; + return; + } + $scope.cardsHistory = data; }); }; @@ -215,7 +226,6 @@ angular.module('copayApp.controllers').controller('tabHomeController', }; $scope.$on("$ionicView.enter", function(event, data) { - $scope.bitpayCard = null; nextStep(); updateAllWallets(); diff --git a/src/js/controllers/tabsController.js b/src/js/controllers/tabsController.js index 3e93df45d..642948ab6 100644 --- a/src/js/controllers/tabsController.js +++ b/src/js/controllers/tabsController.js @@ -35,6 +35,7 @@ angular.module('copayApp.controllers').controller('tabsController', function($ro 'tabs.receive.backup', 'tabs.bitpayCard.amount', 'tabs.bitpayCard.confirm', + 'tabs.bitpayCardIntro' ]; $rootScope.$on('$ionicView.beforeEnter', function() { diff --git a/src/js/routes.js b/src/js/routes.js index caa3df17f..b4c68d926 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -846,8 +846,17 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr * */ - .state('tabs.bitpayCard', { - url: '/bitpay-card', + .state('tabs.bitpayCardIntro', { + url: '/bitpay-card-intro/:secret/:email/:otp', + views: { + 'tab-home@tabs': { + controller: 'bitpayCardIntroController', + templateUrl: 'views/bitpayCardIntro.html' + } + } + }) + .state('tabs.bitpayCard', { + url: '/bitpay-card/:id', views: { 'tab-home@tabs': { controller: 'bitpayCardController', @@ -857,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', @@ -866,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', @@ -878,6 +887,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr url: '/preferences', views: { 'tab-home@tabs': { + controller: 'preferencesBitpayCardController', templateUrl: 'views/preferencesBitpayCard.html' } } diff --git a/src/js/services/bitpayCardService.js b/src/js/services/bitpayCardService.js index c49bc5ffa..884b57cbe 100644 --- a/src/js/services/bitpayCardService.js +++ b/src/js/services/bitpayCardService.js @@ -1,195 +1,236 @@ '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, moment) { var root = {}; - var credentials = {}; - var bpSession = {}; + var BITPAY_CARD_NETWORK = 'livenet'; + var BITPAY_CARD_API_URL = BITPAY_CARD_NETWORK == 'livenet' ? 'https://bitpay.com' : 'https://test.bitpay.com'; - var _setCredentials = function() { - /* - * Development: 'testnet' - * Production: 'livenet' - */ - credentials.NETWORK = 'livenet'; - if (credentials.NETWORK == 'testnet') { - credentials.BITPAY_API_URL = 'https://test.bitpay.com'; - } - else { - credentials.BITPAY_API_URL = 'https://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); - return e; + var error = e.data ? e.data.error : msg; + return error; }; - var _getUser = function(cb) { - _setCredentials(); - storageService.getBitpayCard(credentials.NETWORK, function(err, user) { - if (err) return cb(err); - if (lodash.isString(user)) { - user = JSON.parse(user); - } - return cb(null, user); - }); - }; - - var _setUser = function(user, cb) { - _setCredentials(); - user = JSON.stringify(user); - storageService.setBitpayCard(credentials.NETWORK, user, function(err) { - return cb(err); - }); - // Show pending task from the UI - storageService.setNextStep('BitpayCard', true, function(err) {}); - }; - - var _getSession = function(cb) { - _setCredentials(); - $http({ + var _get = function(endpoint) { + return { method: 'GET', - url: credentials.BITPAY_API_URL + '/visa-api/session', + url: BITPAY_CARD_API_URL + endpoint, headers: { 'content-type': 'application/json' } - }).then(function(data) { - $log.info('BitPay Get Session: SUCCESS'); - bpSession = data.data.data; - return cb(null, bpSession); + }; + }; + + 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); + + return { + method: 'POST', + url: BITPAY_CARD_API_URL + endpoint, + headers: { + 'content-type': 'application/json' + }, + data: json + }; + }; + + 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 Session', data)); + return cb(_setError('BitPay Card Error: Get Debit Cards', data)); }); }; - var _getBitPay = function(endpoint) { - _setCredentials(); - return { - method: 'GET', - url: credentials.BITPAY_API_URL + endpoint, - headers: { - 'content-type': 'application/json', - 'x-csrf-token': bpSession.csrfToken + 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; + } } - }; - }; + var isInvoiceLessThanOneDayOld = moment() < moment(new Date(invoices[i].invoiceTime)).add(1, 'day'); + if (!matched && isInvoiceLessThanOneDayOld) { + var isInvoiceUnderpaid = invoices[i].exceptionStatus === 'paidPartial'; - var _postBitPay = function(endpoint, data) { - _setCredentials(); - return { - method: 'POST', - url: credentials.BITPAY_API_URL + endpoint, - headers: { - 'Content-Type': 'application/json', - 'x-csrf-token': bpSession.csrfToken - }, - data: data - }; + if(['paid', 'confirmed', 'complete'].indexOf(invoices[i].status) >= 0 + || (invoices[i].status === 'invalid' || isInvoiceUnderpaid)) { + + history.unshift({ + timestamp: new Date(invoices[i].invoiceTime), + description: invoices[i].itemDesc, + amount: invoices[i].price, + type: '00611 = Client Funded Deposit', + pending: true, + status: invoices[i].status + }); + } + } + } + return history; }; root.getEnvironment = function() { - _setCredentials(); - return credentials.NETWORK; + return BITPAY_CARD_NETWORK; }; - root.topUp = function(data, cb) { - var dataSrc = { - amount: data.amount, - currency: data.currency - }; - $http(_postBitPay('/visa-api/topUp', dataSrc)).then(function(data) { - $log.info('BitPay TopUp: SUCCESS'); - return cb(null, data.data.data.invoice); - }, function(data) { - return cb(_setError('BitPay Card Error: TopUp', data)); + root.getCredentials = function(cb) { + _getCredentials(function(err, credentials) { + return cb(err, credentials); }); }; - root.transactionHistory = function(dateRange, cb) { - var params; - if (!dateRange.startDate) { - params = ''; - } else { - params = '/?startDate=' + dateRange.startDate + '&endDate=' + dateRange.endDate; + root.bitAuthPair = function(obj, cb) { + var deviceName = 'Unknow device'; + if (platformInfo.isNW) { + deviceName = require('os').platform(); + } else if (platformInfo.isCordova) { + deviceName = device.model; } - $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)); + var json = { + method: 'createToken', + params: { + secret: obj.secret, + version: 2, + deviceName: deviceName, + code: obj.otp + } + }; + _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)); + }); }); }; - 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.getHistory = function(cardId, params, cb) { + var invoices, transactions; + params = params || {}; + var json = { + method: 'getInvoiceHistory', + params: JSON.stringify(params) + }; + _getCredentials(function(err, credentials) { + if (err) return cb(err); + root.getBitpayDebitCards(function(err, data) { + if (err) return cb(err); + var card = lodash.find(data.cards, {id : cardId}); + if (!card) return cb(_setError('Not card found')); + // Get invoices + $http(_post('/api/v2/' + card.token, json, credentials)).then(function(data) { + $log.info('BitPay Get Invoices: SUCCESS'); + invoices = data.data.data || []; + if (lodash.isEmpty(invoices)) $log.info('No invoices'); + json = { + method: 'getTransactionHistory', + params: JSON.stringify(params) + }; + // Get transactions list + $http(_post('/api/v2/' + card.token, json, credentials)).then(function(data) { + $log.info('BitPay Get Transactions: SUCCESS'); + transactions = data.data.data || {}; + transactions['txs'] = _processTransactions(invoices, transactions.transactionList); + return cb(data.data.error, transactions); + }, function(data) { + return cb(_setError('BitPay Card Error: Get Transactions', data)); + }); + }, function(data) { + return cb(_setError('BitPay Card Error: Get Invoices', data)); + }); + }); + }); + }; + + root.topUp = function(cardId, params, cb) { + params = params || {}; + var json = { + method: 'generateTopUpInvoice', + params: JSON.stringify(params) + }; + _getCredentials(function(err, credentials) { + if (err) return cb(err); + root.getBitpayDebitCards(function(err, data) { + if (err) return cb(err); + var card = lodash.find(data.cards, {id : cardId}); + if (!card) return cb(_setError('Not card found')); + $http(_post('/api/v2/' + card.token, json, credentials)).then(function(data) { + $log.info('BitPay TopUp: SUCCESS'); + return cb(data.data.error, data.data.data.invoice); + }, function(data) { + return cb(_setError('BitPay Card Error: TopUp', data)); + }); + }); }); }; root.getInvoice = function(id, cb) { - $http(_getBitPay('/invoices/' + id)).then(function(data) { + $http(_get('/invoices/' + id)).then(function(data) { $log.info('BitPay Get Invoice: SUCCESS'); - return cb(null, data.data.data); + return cb(data.data.error, data.data.data); }, function(data) { return cb(_setError('BitPay Card Error: Get Invoice', data)); }); }; - 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) { - _setCredentials(); - storageService.getBitpayCardCache(credentials.NETWORK, function(err, data) { + root.getBitpayDebitCards = function(cb) { + storageService.getBitpayDebitCards(BITPAY_CARD_NETWORK, function(err, data) { if (err) return cb(err); if (lodash.isString(data)) { data = JSON.parse(data); @@ -199,32 +240,54 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http, }); }; - root.setCacheData = function(data, cb) { - _setCredentials(); + root.setBitpayDebitCards = function(data, cb) { data = JSON.stringify(data); - storageService.setBitpayCardCache(credentials.NETWORK, data, function(err) { + storageService.setBitpayDebitCards(BITPAY_CARD_NETWORK, data, function(err) { if (err) return cb(err); return cb(); }); }; - root.removeCacheData = function(cb) { - _setCredentials(); - storageService.removeBitpayCardCache(credentials.NETWORK, function(err) { + root.getBitpayDebitCardsHistory = function(cardId, cb) { + storageService.getBitpayDebitCardsHistory(BITPAY_CARD_NETWORK, function(err, data) { if (err) return cb(err); - return cb(); + if (lodash.isString(data)) { + data = JSON.parse(data); + } + data = data || {}; + if (cardId) data = data[cardId]; + return cb(null, data); }); }; - 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)); + root.setBitpayDebitCardsHistory = function(cardId, data, opts, cb) { + storageService.getBitpayDebitCardsHistory(BITPAY_CARD_NETWORK, function(err, oldData) { + if (lodash.isString(oldData)) { + oldData = JSON.parse(oldData); + } + if (lodash.isString(data)) { + data = JSON.parse(data); + } + var inv = oldData || {}; + inv[cardId] = data; + if (opts && opts.remove) { + delete(inv[cardId]); + } + inv = JSON.stringify(inv); + + storageService.setBitpayDebitCardsHistory(BITPAY_CARD_NETWORK, inv, function(err) { + return cb(err); + }); + }); + }; + + root.remove = function(cb) { + storageService.removeBitpayCardCredentials(BITPAY_CARD_NETWORK, function(err) { + storageService.removeBitpayDebitCards(BITPAY_CARD_NETWORK, function(err) { + storageService.removeBitpayDebitCardsHistory(BITPAY_CARD_NETWORK, function(err) { + $log.info('BitPay Debit Cards Removed: SUCCESS'); + return cb(); + }); }); }); }; diff --git a/src/js/services/incomingData.js b/src/js/services/incomingData.js index fc363590c..b157704b8 100644 --- a/src/js/services/incomingData.js +++ b/src/js/services/incomingData.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.services').factory('incomingData', function($log, $ionicModal, $state, $window, $timeout, bitcore) { +angular.module('copayApp.services').factory('incomingData', function($log, $state, $window, bitcore, lodash) { var root = {}; @@ -23,17 +23,25 @@ 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=', '')); - $state.go('tabs.send'); - $timeout(function() { + $state.go('tabs.send').then(function() { $state.transitionTo('tabs.send.confirm', {paypro: data}); - }, 100); + }); return true; } - data = sanitizeUri(data); // BIP21 @@ -45,8 +53,7 @@ angular.module('copayApp.services').factory('incomingData', function($log, $ioni var amount = parsed.amount ? parsed.amount : ''; - $state.go('tabs.send'); - $timeout(function() { + $state.go('tabs.send').then(function() { if (parsed.r) { $state.transitionTo('tabs.send.confirm', {paypro: parsed.r}); } else { @@ -56,29 +63,26 @@ angular.module('copayApp.services').factory('incomingData', function($log, $ioni $state.transitionTo('tabs.send.amount', {toAddress: addr}); } } - }, 100); + }); return true; // Plain URL } else if (/^https?:\/\//.test(data)) { - $state.go('tabs.send'); - $timeout(function() { + $state.go('tabs.send').then(function() { $state.transitionTo('tabs.send.confirm', {paypro: data}); - }, 100); + }); return true; // Plain Address } else if (bitcore.Address.isValid(data, 'livenet')) { - $state.go('tabs.send'); - $timeout(function() { + $state.go('tabs.send').then(function() { $state.transitionTo('tabs.send.amount', {toAddress: data}); - }, 100); + }); return true; } else if (bitcore.Address.isValid(data, 'testnet')) { - $state.go('tabs.send'); - $timeout(function() { + $state.go('tabs.send').then(function() { $state.transitionTo('tabs.send.amount', {toAddress: data}); - }, 100); + }); return true; // Protocol @@ -87,20 +91,32 @@ 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').then(function() { + $state.transitionTo('tabs.bitpayCardIntro', { + secret: secret, + email: email, + otp: otp + }); + }); + return true; + // Join } else if (data && data.match(/^copay:[0-9A-HJ-NP-Za-km-z]{70,80}$/)) { - $state.go('tabs.home'); - $timeout(function() { + $state.go('tabs.home').then(function() { $state.transitionTo('tabs.add.join', {url: data}); - }, 100); + }); return true; // Old join } else if (data && data.match(/^[0-9A-HJ-NP-Za-km-z]{70,80}$/)) { - $state.go('tabs.home'); - $timeout(function() { + $state.go('tabs.home').then(function() { $state.transitionTo('tabs.add.join', {url: data}); - }, 100); + }); return true; } diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index a0e0f6503..60f01a3c8 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -325,28 +325,40 @@ angular.module('copayApp.services') storage.remove('coinbaseTxs-' + network, cb); }; - root.setBitpayCard = function(network, data, cb) { - storage.set('bitpayCard-' + network, data, cb); + root.setBitpayDebitCardsHistory = function(network, data, cb) { + storage.set('bitpayDebitCardsHistory-' + network, data, cb); }; - root.getBitpayCard = function(network, cb) { - storage.get('bitpayCard-' + network, cb); + root.getBitpayDebitCardsHistory = function(network, cb) { + storage.get('bitpayDebitCardsHistory-' + network, cb); }; - root.removeBitpayCard = function(network, cb) { - storage.remove('bitpayCard-' + network, cb); + root.removeBitpayDebitCardsHistory = function(network, cb) { + storage.remove('bitpayDebitCardsHistory-' + 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.setBitpayCardCredentials = function(network, data, cb) { + storage.set('bitpayCardCredentials-' + network, data, cb); + }; + + root.getBitpayCardCredentials = function(network, cb) { + storage.get('bitpayCardCredentials-' + network, cb); + }; + + root.removeBitpayCardCredentials = function(network, cb) { + storage.remove('bitpayCardCredentials-' + network, cb); }; root.removeAllWalletData = function(walletId, cb) { diff --git a/src/sass/views/bitpayCard.scss b/src/sass/views/bitpayCard.scss index 5d7b045b6..563554f15 100644 --- a/src/sass/views/bitpayCard.scss +++ b/src/sass/views/bitpayCard.scss @@ -3,12 +3,15 @@ width: 100%; text-align: center; padding: 2rem 1rem 1.5rem 1rem; - min-height: 140px; + height: 140px; border-color: #172565; background-color: #1e3186; background-image: linear-gradient(0deg, #172565, #172565 0%, transparent 0%); color: #fff; } + .wallet-details-wallet-info { + bottom: 5px; + } strong { line-height: 100%; } diff --git a/src/sass/views/bitpayCardIntro.scss b/src/sass/views/bitpayCardIntro.scss new file mode 100644 index 000000000..3ce232b0f --- /dev/null +++ b/src/sass/views/bitpayCardIntro.scss @@ -0,0 +1,70 @@ +#bitpayCard-intro { + .slider-pager .slider-pager-page { + color: #fff; + } + .cta-button{ + text-align: center; + position: absolute; + bottom: 55px; + padding: 0 1.5rem; + width: 100%; + } + background: rgba(30, 49, 134, 1); + background: -moz-linear-gradient(top, rgba(30, 49, 134, 1) 0%, rgba(17, 27, 73, 1) 100%); + background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(30, 49, 134, 1)), color-stop(100%, rgba(17, 27, 73, 1))); + background: -webkit-linear-gradient(top, rgba(30, 49, 134, 1) 0%, rgba(17, 27, 73, 1) 100%); + background: -o-linear-gradient(top, rgba(30, 49, 134, 1) 0%, rgba(17, 27, 73, 1) 100%); + background: -ms-linear-gradient(top, rgba(30, 49, 134, 1) 0%, rgba(17, 27, 73, 1) 100%); + background: linear-gradient(to bottom, rgba(30, 49, 134, 1) 0%, rgba(17, 27, 73, 1) 100%); + color: #fff; + height: 100%; + .bar.bar-header { + background: rgb(30, 49, 134); + color: #fff; + button { + color: #fff; + } + .secondary-buttons { + button { + color: rgba(255, 255, 255, .5); + } + } + } + .bar.bar-stable{ + border-color: transparent; + border:none; + } + .button-transparent{ + background: none !important; + } + .button-translucent{ + background: rgba(215, 215, 215, 0.1) + } + .button-primary{ + background: rgb(100, 124, 232) !important; + color:#fff; + } + .light-blue{ + color:rgb(100, 124, 232); + } + .text-white{ + color: #ffffff; + } + ion-content { + background: url(../img/onboarding-welcome-bg.png); + background-position: top center; + background-size: contain; + background-repeat: repeat-x; + height: 100%; + .scroll{ + height: 100%; + } + color: #fff; + p { + text-align: center; + margin: 40px 20px; + font-size: 1.2rem; + color: rgba(255, 255, 255, .5); + } + } +} diff --git a/src/sass/views/views.scss b/src/sass/views/views.scss index 0112476f3..de6ee8d30 100644 --- a/src/sass/views/views.scss +++ b/src/sass/views/views.scss @@ -12,6 +12,7 @@ @import "walletDetails"; @import "advancedSettings"; @import "bitpayCard"; +@import "bitpayCardIntro"; @import "address-book"; @import "wallet-backup-phrase"; @import "zero-state"; diff --git a/webkitbuilds/setup-win.iss b/webkitbuilds/setup-win.iss index 8ae2a631f..f495bd8db 100755 --- a/webkitbuilds/setup-win.iss +++ b/webkitbuilds/setup-win.iss @@ -8,7 +8,7 @@ #define MyAppExeName "*PACKAGENAME.exe" [Setup] -AppId={{804636ee-b017-4cad-8719-e58ac97ffa5c} +AppId={804636ee-b017-4cad-8719-e58ac97ffa5c} AppName={#MyAppName} AppVersion={#MyAppVersion} ;AppVerName={#MyAppName} {#MyAppVersion} diff --git a/www/img/favicon.ico b/www/img/favicon.ico index e2f92139d..166c0bcaf 100644 Binary files a/www/img/favicon.ico and b/www/img/favicon.ico differ diff --git a/www/img/icon-128.png b/www/img/icon-128.png new file mode 100644 index 000000000..6958667cd Binary files /dev/null and b/www/img/icon-128.png differ 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 ab8457b30..557b06869 100644 --- a/www/views/bitpayCard.html +++ b/www/views/bitpayCard.html @@ -2,9 +2,9 @@ - BitPay Card - - @@ -16,145 +16,74 @@ 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"> +
+
-
-
- -
-
-
- {{tx.merchant.name}} -
-
- {{tx.merchant.city}}, {{tx.merchant.state}} -
+
+
+ {{tx.merchant.name}}
-
- {{desc}} +
+ {{tx.merchant.city}}, {{tx.merchant.state}}
-
- - -
-
-
- {{tx.amount | currency:'$':2 }} -
- +
+
+ {{tx.desc}} +
+
+ + +
+
+
+ {{tx.amount | currency:'$':2 }}
+ + Pending
diff --git a/www/views/bitpayCardIntro.html b/www/views/bitpayCardIntro.html new file mode 100644 index 000000000..262a5f710 --- /dev/null +++ b/www/views/bitpayCardIntro.html @@ -0,0 +1,45 @@ + + + + + + + + + + +
+ +
+ + +

+ Turn bitcoin into dollars, swipe anywhere Visa® is accepted. +

+
+ +

+ Get local cash anywhere you go, from any Visa®-compatible ATM. +

+ *ATM bank fees may apply +
+

+
+ +

+ Pay 0% fees to turn bitcoin into dollars. +

+
+
+
+ + +
+
+
diff --git a/www/views/confirm.html b/www/views/confirm.html index 953dc89fb..49c70ce85 100644 --- a/www/views/confirm.html +++ b/www/views/confirm.html @@ -23,8 +23,15 @@
To - - {{toAddress}} + + + {{toName}} + {{toAddress}} +
+ + + {{_paypro.domain}} +
diff --git a/www/views/preferencesBitpayCard.html b/www/views/preferencesBitpayCard.html index 12ae0a455..c20545f7b 100644 --- a/www/views/preferencesBitpayCard.html +++ b/www/views/preferencesBitpayCard.html @@ -2,14 +2,21 @@ - Preferences + BitPay Visa® Cards - -
    -
  • - Log out -
  • -
+ +
+
+ Cards +
+
+ xxxx-xxxx-xxxx-{{card.lastFourDigits}} +
+
+
+ Removes all data from this device +
+
diff --git a/www/views/tab-home.html b/www/views/tab-home.html index 9b9cbdb14..0a0aa9233 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,26 @@

- + +
-

BitPay Card

-

Add funds to get started

- ${{bitpayCard.balance}} +

BitPay Visa® Card

+

{{cardsHistory[card.id].balance ? '$' + cardsHistory[card.id].balance : 'Add funds to get started'|translate}}

+ +
+ + +
+
+

BitPay Visa® Card

+

Add your cards

@@ -138,7 +148,7 @@