Coinbase: first step integration, connect account and main view
This commit is contained in:
parent
eea32d3069
commit
af932b3e59
8 changed files with 466 additions and 244 deletions
|
|
@ -1,25 +1,53 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('coinbaseController',
|
||||
function($rootScope, $scope, $timeout, $ionicModal, profileService, configService, storageService, coinbaseService, lodash, platformInfo, ongoingProcess) {
|
||||
angular.module('copayApp.controllers').controller('coinbaseController', function($rootScope, $scope, $timeout, $ionicModal, $log, profileService, configService, storageService, coinbaseService, lodash, platformInfo, ongoingProcess, popupService, gettextCatalog, externalLinkService) {
|
||||
|
||||
var isNW = platformInfo.isNW;
|
||||
var isNW = platformInfo.isNW;
|
||||
|
||||
if (platformInfo.isCordova && StatusBar.isVisible) {
|
||||
StatusBar.backgroundColorByHexString("#4B6178");
|
||||
}
|
||||
var init = function() {
|
||||
ongoingProcess.set('connectingCoinbase', true);
|
||||
coinbaseService.init($scope.accessToken, function(err, data) {
|
||||
console.log('[coinbase.js:9]',data); //TODO)
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
if (err || lodash.isEmpty(data)) {
|
||||
if (err) {
|
||||
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Updating accessToken and accountId
|
||||
$timeout(function() {
|
||||
$scope.accessToken = data.accessToken;
|
||||
$scope.accountId = data.accountId;
|
||||
$scope.updateTransactions();
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
});
|
||||
};
|
||||
|
||||
this.openAuthenticateWindow = function() {
|
||||
var oauthUrl = this.getAuthenticateUrl();
|
||||
if (!isNW) {
|
||||
$rootScope.openExternalLink(oauthUrl, '_system');
|
||||
} else {
|
||||
var self = this;
|
||||
var gui = require('nw.gui');
|
||||
var win = gui.Window.open(oauthUrl, {
|
||||
focus: true,
|
||||
position: 'center'
|
||||
});
|
||||
$scope.updateTransactions = function() {
|
||||
$log.debug('Checking for transactions...');
|
||||
coinbaseService.getPendingTransactions($scope.accessToken, $scope.accountId, function(err, txs) {
|
||||
console.log('[coinbase.js:43]',txs); //TODO)
|
||||
$scope.pendingTransactions = txs;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
this.openAuthenticateWindow = function() {
|
||||
var oauthUrl = this.getAuthenticateUrl();
|
||||
externalLinkService.open(oauthUrl);
|
||||
/*
|
||||
* Not working (NW bug)
|
||||
if (!isNW) {
|
||||
externalLinkService.open(oauthUrl);
|
||||
} else {
|
||||
var self = this;
|
||||
var gui = require('nw.gui');
|
||||
gui.Window.open(oauthUrl, {
|
||||
focus: true,
|
||||
position: 'center'
|
||||
}, function(win) {
|
||||
win.on('loaded', function() {
|
||||
var title = win.title;
|
||||
if (title.indexOf('Coinbase') == -1) {
|
||||
|
|
@ -28,51 +56,47 @@ angular.module('copayApp.controllers').controller('coinbaseController',
|
|||
win.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.getAuthenticateUrl = function() {
|
||||
return coinbaseService.getOauthCodeUrl();
|
||||
};
|
||||
|
||||
this.submitOauthCode = function(code) {
|
||||
var self = this;
|
||||
var coinbaseTestnet = configService.getSync().coinbase.testnet;
|
||||
var network = coinbaseTestnet ? 'testnet' : 'livenet';
|
||||
ongoingProcess.set('connectingCoinbase', true);
|
||||
this.error = null;
|
||||
$timeout(function() {
|
||||
coinbaseService.getToken(code, function(err, data) {
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
} else if (data && data.access_token && data.refresh_token) {
|
||||
storageService.setCoinbaseToken(network, data.access_token, function() {
|
||||
storageService.setCoinbaseRefreshToken(network, data.refresh_token, function() {
|
||||
$scope.$emit('Local/CoinbaseUpdated', data.access_token);
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
this.openTxModal = function(tx) {
|
||||
$scope.tx = tx;
|
||||
|
||||
$ionicModal.fromTemplateUrl('views/modals/coinbase-tx-details.html', {
|
||||
scope: $scope,
|
||||
animation: 'slide-in-up'
|
||||
}).then(function(modal) {
|
||||
$scope.coinbaseTxDetailsModal = modal;
|
||||
$scope.coinbaseTxDetailsModal.show();
|
||||
});
|
||||
};
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
this.getAuthenticateUrl = function() {
|
||||
return coinbaseService.getOauthCodeUrl();
|
||||
};
|
||||
|
||||
this.submitOauthCode = function(code) {
|
||||
var self = this;
|
||||
ongoingProcess.set('connectingCoinbase', true);
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
coinbaseService.getToken(code, function(err, accessToken) {
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
if (err) {
|
||||
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||
return;
|
||||
}
|
||||
$scope.accessToken = accessToken;
|
||||
init();
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
this.openTxModal = function(tx) {
|
||||
$scope.tx = tx;
|
||||
|
||||
$ionicModal.fromTemplateUrl('views/modals/coinbase-tx-details.html', {
|
||||
scope: $scope,
|
||||
animation: 'slide-in-up'
|
||||
}).then(function(modal) {
|
||||
$scope.coinbaseTxDetailsModal = modal;
|
||||
$scope.coinbaseTxDetailsModal.show();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
coinbaseService.setCredentials();
|
||||
$scope.network = coinbaseService.getEnvironment();
|
||||
init();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -926,21 +926,39 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
*
|
||||
*/
|
||||
|
||||
.state('coinbase', {
|
||||
.state('tabs.buyandsell.coinbase', {
|
||||
url: '/coinbase',
|
||||
templateUrl: 'views/coinbase.html'
|
||||
views: {
|
||||
'tab-home@tabs': {
|
||||
controller: 'coinbaseController',
|
||||
controllerAs: 'coinbase',
|
||||
templateUrl: 'views/coinbase.html'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('preferencesCoinbase', {
|
||||
url: '/preferencesCoinbase',
|
||||
templateUrl: 'views/preferencesCoinbase.html'
|
||||
.state('tabs.buyandsell.coinbase.preferences', {
|
||||
url: '/preferences',
|
||||
'tab-home@tabs': {
|
||||
controller: 'preferencesCoinbaseController',
|
||||
controllerAs: 'coinbase',
|
||||
templateUrl: 'views/preferencesCoinbase.html'
|
||||
}
|
||||
})
|
||||
.state('buyCoinbase', {
|
||||
url: '/buycoinbase',
|
||||
templateUrl: 'views/buyCoinbase.html'
|
||||
.state('tabs.buyandsell.coinbase.buy', {
|
||||
url: '/buy',
|
||||
'tab-home@tabs': {
|
||||
controller: 'buyCoinbaseController',
|
||||
controllerAs: 'buy',
|
||||
templateUrl: 'views/buyCoinbase.html'
|
||||
}
|
||||
})
|
||||
.state('sellCoinbase', {
|
||||
url: '/sellcoinbase',
|
||||
templateUrl: 'views/sellCoinbase.html'
|
||||
.state('tabs.buyandsell.coinbase.sell', {
|
||||
url: '/sell',
|
||||
'tab-home@tabs': {
|
||||
controller: 'sellCoinbaseController',
|
||||
controllerAs: 'sell',
|
||||
templateUrl: 'views/sellCoinbase.html'
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1,11 +1,26 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('coinbaseService', function($http, $log, platformInfo, lodash, storageService, configService) {
|
||||
angular.module('copayApp.services').factory('coinbaseService', function($http, $log, $window, platformInfo, lodash, storageService, configService) {
|
||||
var root = {};
|
||||
var credentials = {};
|
||||
var isCordova = platformInfo.isCordova;
|
||||
var isNW = platformInfo.isNW;
|
||||
|
||||
root.setCredentials = function(network) {
|
||||
root.setCredentials = function() {
|
||||
|
||||
if (!$window.externalServices || !$window.externalServices.coinbase) {
|
||||
return;
|
||||
}
|
||||
|
||||
var coinbase = $window.externalServices.coinbase;
|
||||
|
||||
/*
|
||||
* Development: 'testnet'
|
||||
* Production: 'livenet'
|
||||
*/
|
||||
credentials.NETWORK = 'livenet';
|
||||
|
||||
// Coinbase permissions
|
||||
credentials.SCOPE = ''
|
||||
+ 'wallet:accounts:read,'
|
||||
+ 'wallet:addresses:read,'
|
||||
|
|
@ -20,27 +35,45 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
+ 'wallet:transactions:send,'
|
||||
+ 'wallet:payment-methods:read';
|
||||
|
||||
if (isCordova) {
|
||||
credentials.REDIRECT_URI = 'copay://coinbase';
|
||||
// NW has a bug with Window Object
|
||||
if (isCordova && isNW) {
|
||||
credentials.REDIRECT_URI = coinbase.redirect_uri.mobile;
|
||||
} else {
|
||||
credentials.REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob';
|
||||
credentials.REDIRECT_URI = coinbase.redirect_uri.desktop;
|
||||
}
|
||||
|
||||
if (network == 'testnet') {
|
||||
credentials.HOST = 'https://sandbox.coinbase.com';
|
||||
credentials.API = 'https://api.sandbox.coinbase.com';
|
||||
credentials.CLIENT_ID = '6cdcc82d5d46654c46880e93ab3d2a43c639776347dd88022904bd78cd067841';
|
||||
credentials.CLIENT_SECRET = '228cb6308951f4b6f41ba010c7d7981b2721a493c40c50fd2425132dcaccce59';
|
||||
if (credentials.NETWORK == 'testnet') {
|
||||
credentials.HOST = coinbase.sandbox.host;
|
||||
credentials.API = coinbase.sandbox.api;
|
||||
credentials.CLIENT_ID = coinbase.sandbox.client_id;
|
||||
credentials.CLIENT_SECRET = coinbase.sandbox.client_secret;
|
||||
}
|
||||
else {
|
||||
credentials.HOST = 'https://coinbase.com';
|
||||
credentials.API = 'https://api.coinbase.com';
|
||||
credentials.CLIENT_ID = window.coinbase_client_id;
|
||||
credentials.CLIENT_SECRET = window.coinbase_client_secret;
|
||||
credentials.HOST = coinbase.production.host;
|
||||
credentials.API = coinbase.production.api;
|
||||
credentials.CLIENT_ID = coinbase.production.client_id;
|
||||
credentials.CLIENT_SECRET = coinbase.production.client_secret;
|
||||
};
|
||||
};
|
||||
|
||||
var _afterTokenReceived = function(data, cb) {
|
||||
if (data && data.access_token && data.refresh_token) {
|
||||
storageService.setCoinbaseToken(credentials.NETWORK, data.access_token, function() {
|
||||
storageService.setCoinbaseRefreshToken(credentials.NETWORK, data.refresh_token, function() {
|
||||
return cb(null, data.access_token);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return cb('Could not get the access token');
|
||||
}
|
||||
};
|
||||
|
||||
root.getEnvironment = function() {
|
||||
return credentials.NETWORK;
|
||||
};
|
||||
|
||||
root.getOauthCodeUrl = function() {
|
||||
// TODO CHANGE LIMIT BACK TO 1000 *************************************************
|
||||
return credentials.HOST
|
||||
+ '/oauth/authorize?response_type=code&client_id='
|
||||
+ credentials.CLIENT_ID
|
||||
|
|
@ -48,13 +81,13 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
+ credentials.REDIRECT_URI
|
||||
+ '&state=SECURE_RANDOM&scope='
|
||||
+ credentials.SCOPE
|
||||
+ '&meta[send_limit_amount]=1000&meta[send_limit_currency]=USD&meta[send_limit_period]=day';
|
||||
+ '&meta[send_limit_amount]=1&meta[send_limit_currency]=USD&meta[send_limit_period]=day';
|
||||
};
|
||||
|
||||
root.getToken = function(code, cb) {
|
||||
var req = {
|
||||
method: 'POST',
|
||||
url: credentials.API + '/oauth/token',
|
||||
url: credentials.HOST + '/oauth/token',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
|
|
@ -71,18 +104,18 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
$http(req).then(function(data) {
|
||||
$log.info('Coinbase Authorization Access Token: SUCCESS');
|
||||
// Show pending task from the UI
|
||||
storageService.setNextStep('BuyAndSell', true, function(err) {});
|
||||
return cb(null, data.data);
|
||||
storageService.setNextStep('BuyAndSell', 'true', function(err) {});
|
||||
_afterTokenReceived(data.data, cb);
|
||||
}, function(data) {
|
||||
$log.error('Coinbase Authorization Access Token: ERROR ' + data.statusText);
|
||||
return cb(data.data);
|
||||
return cb(data.data || 'Could not get the access token');
|
||||
});
|
||||
};
|
||||
|
||||
root.refreshToken = function(refreshToken, cb) {
|
||||
var _refreshToken = function(refreshToken, cb) {
|
||||
var req = {
|
||||
method: 'POST',
|
||||
url: credentials.API + '/oauth/token',
|
||||
url: credentials.HOST + '/oauth/token',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
|
|
@ -98,10 +131,63 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
|
||||
$http(req).then(function(data) {
|
||||
$log.info('Coinbase Refresh Access Token: SUCCESS');
|
||||
return cb(null, data.data);
|
||||
_afterTokenReceived(data.data, cb);
|
||||
}, function(data) {
|
||||
$log.error('Coinbase Refresh Access Token: ERROR ' + data.statusText);
|
||||
return cb(data.data);
|
||||
return cb(data.data || 'Could not get the access token');
|
||||
});
|
||||
};
|
||||
|
||||
var _getMainAccountId = function(accessToken, cb) {
|
||||
root.getAccounts(accessToken, function(err, a) {
|
||||
if (err) return cb(err);
|
||||
var data = a.data;
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i].primary && data[i].type == 'wallet') {
|
||||
return cb(null, data[i].id);
|
||||
}
|
||||
}
|
||||
coinbaseService.logout(function() {});
|
||||
return cb('Your primary account should be a WALLET. Set your wallet account as primary and try again');
|
||||
});
|
||||
};
|
||||
|
||||
root.init = function(accessToken, cb) {
|
||||
if (lodash.isEmpty(credentials.CLIENT_ID)) {
|
||||
return cb('Coinbase is Disabled');
|
||||
}
|
||||
$log.debug('Init Token...');
|
||||
|
||||
var getToken = function(cb) {
|
||||
if (accessToken) {
|
||||
cb(null, accessToken);
|
||||
} else {
|
||||
storageService.getCoinbaseToken(credentials.NETWORK, cb);
|
||||
}
|
||||
};
|
||||
|
||||
getToken(function(err, accessToken) {
|
||||
if (err || !accessToken) return cb();
|
||||
else {
|
||||
_getMainAccountId(accessToken, function(err, accountId) {
|
||||
if (err) {
|
||||
if (err.errors && err.errors[0] && err.errors[0].id == 'expired_token') {
|
||||
$log.debug('Refresh token');
|
||||
storageService.getCoinbaseRefreshToken(credentials.NETWORK, function(err, refreshToken) {
|
||||
if (err) return cb(err);
|
||||
_refreshToken(refreshToken, function(err, newToken) {
|
||||
if (err) return cb(err);
|
||||
return cb(null, {accessToken: newToken, accountId: accountId});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return cb(err);
|
||||
}
|
||||
} else {
|
||||
return cb(null, {accessToken: accessToken, accountId: accountId});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -124,7 +210,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
return cb(null, data.data);
|
||||
}, function(data) {
|
||||
$log.error('Coinbase Get Accounts: ERROR ' + data.statusText);
|
||||
return cb(data.data);
|
||||
return cb(data.data || 'Could not get the accounts');
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -331,9 +417,8 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
|
||||
// Pending transactions
|
||||
|
||||
root.savePendingTransaction = function(ctx, opts, cb) {
|
||||
var network = configService.getSync().coinbase.testnet ? 'testnet' : 'livenet';
|
||||
storageService.getCoinbaseTxs(network, function(err, oldTxs) {
|
||||
var _savePendingTransaction = function(ctx, opts, cb) {
|
||||
storageService.getCoinbaseTxs(credentials.NETWORK, function(err, oldTxs) {
|
||||
if (lodash.isString(oldTxs)) {
|
||||
oldTxs = JSON.parse(oldTxs);
|
||||
}
|
||||
|
|
@ -350,23 +435,166 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
}
|
||||
tx = JSON.stringify(tx);
|
||||
|
||||
storageService.setCoinbaseTxs(network, tx, function(err) {
|
||||
storageService.setCoinbaseTxs(credentials.NETWORK, tx, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
root.getPendingTransactions = function(cb) {
|
||||
var network = configService.getSync().coinbase.testnet ? 'testnet' : 'livenet';
|
||||
storageService.getCoinbaseTxs(network, function(err, txs) {
|
||||
var _txs = txs ? JSON.parse(txs) : {};
|
||||
return cb(err, _txs);
|
||||
root.getPendingTransactions = function(accessToken, accountId, cb) {
|
||||
var coinbasePendingTransactions;
|
||||
storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) {
|
||||
txs = txs ? JSON.parse(txs) : {};
|
||||
coinbasePendingTransactions = lodash.isEmpty(txs) ? null : txs;
|
||||
lodash.forEach(txs, function(dataFromStorage, txId) {
|
||||
if ((dataFromStorage.type == 'sell' && dataFromStorage.status == 'completed') ||
|
||||
(dataFromStorage.type == 'buy' && dataFromStorage.status == 'completed') ||
|
||||
dataFromStorage.status == 'error' ||
|
||||
(dataFromStorage.type == 'send' && dataFromStorage.status == 'completed')) return;
|
||||
root.getTransaction(accessToken, accountId, txId, function(err, tx) {
|
||||
if (err) {
|
||||
_savePendingTransaction(dataFromStorage, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
_updateCoinbasePendingTransactions(dataFromStorage, tx.data);
|
||||
coinbasePendingTransactions[txId] = dataFromStorage;
|
||||
if (tx.data.type == 'send' && tx.data.status == 'completed' && tx.data.from) {
|
||||
root.sellPrice(accessToken, dataFromStorage.sell_price_currency, function(err, s) {
|
||||
if (err) {
|
||||
_savePendingTransaction(dataFromStorage, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
return cb();
|
||||
}
|
||||
var newSellPrice = s.data.amount;
|
||||
var variance = Math.abs((newSellPrice - dataFromStorage.sell_price_amount) / dataFromStorage.sell_price_amount * 100);
|
||||
if (variance < dataFromStorage.price_sensitivity.value) {
|
||||
_sellPending(tx.data, accessToken, accountId);
|
||||
} else {
|
||||
var error = {
|
||||
errors: [{
|
||||
message: 'Price falls over the selected percentage'
|
||||
}]
|
||||
};
|
||||
_savePendingTransaction(dataFromStorage, {
|
||||
status: 'error',
|
||||
error: error
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (tx.data.type == 'buy' && tx.data.status == 'completed' && tx.data.buy) {
|
||||
_sendToCopay(dataFromStorage, accessToken, accountId);
|
||||
} else {
|
||||
_savePendingTransaction(dataFromStorage, {}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
}
|
||||
return cb(null, coinbasePendingTransactions);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
root.logout = function(network, cb) {
|
||||
storageService.removeCoinbaseToken(network, function() {
|
||||
storageService.removeCoinbaseRefreshToken(network, function() {
|
||||
var _sellPending = function(tx, accessToken, accountId) {
|
||||
if (!tx) return;
|
||||
var data = tx.amount;
|
||||
data['commit'] = true;
|
||||
root.sellRequest(accessToken, accountId, data, function(err, res) {
|
||||
if (err) {
|
||||
_savePendingTransaction(tx, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
} else {
|
||||
if (!res.data.transaction) {
|
||||
_savePendingTransaction(tx, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
_savePendingTransaction(tx, {
|
||||
remove: true
|
||||
}, function(err) {
|
||||
root.getTransaction(accessToken, accountId, res.data.transaction.id, function(err, updatedTx) {
|
||||
_savePendingTransaction(updatedTx.data, {}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _sendToCopay = function(tx, accessToken, accountId) {
|
||||
if (!tx) return;
|
||||
var data = {
|
||||
to: tx.toAddr,
|
||||
amount: tx.amount.amount,
|
||||
currency: tx.amount.currency,
|
||||
description: 'To Copay Wallet'
|
||||
};
|
||||
root.sendTo(accessToken, accountId, data, function(err, res) {
|
||||
if (err) {
|
||||
_savePendingTransaction(tx, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
} else {
|
||||
if (!res.data.id) {
|
||||
_savePendingTransaction(tx, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
root.getTransaction(accessToken, accountId, res.data.id, function(err, sendTx) {
|
||||
_savePendingTransaction(tx, {
|
||||
remove: true
|
||||
}, function(err) {
|
||||
_savePendingTransaction(sendTx.data, {}, function(err) {
|
||||
// TODO
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _updateCoinbasePendingTransactions = function(obj /*, …*/ ) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
for (var prop in arguments[i]) {
|
||||
var val = arguments[i][prop];
|
||||
if (typeof val == "object")
|
||||
_updateCoinbasePendingTransactions(obj[prop], val);
|
||||
else
|
||||
obj[prop] = val ? val : obj[prop];
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
root.logout = function(cb) {
|
||||
storageService.removeCoinbaseToken(credentials.NETWORK, function() {
|
||||
storageService.removeCoinbaseRefreshToken(credentials.NETWORK, function() {
|
||||
return cb();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ angular.module('copayApp.services').factory('configService', function(storageSer
|
|||
},
|
||||
|
||||
coinbase: {
|
||||
enabled: false, //disable coinbase for this release
|
||||
enabled: true,
|
||||
testnet: false
|
||||
},
|
||||
|
||||
|
|
@ -230,10 +230,6 @@ angular.module('copayApp.services').factory('configService', function(storageSer
|
|||
configCache.aliasFor = configCache.aliasFor || {};
|
||||
configCache.emailFor = configCache.emailFor || {};
|
||||
|
||||
// Coinbase
|
||||
// Disabled for testnet
|
||||
configCache.coinbase.testnet = false;
|
||||
|
||||
$log.debug('Preferences read:', configCache)
|
||||
|
||||
lodash.each(root._queue, function(x) {
|
||||
|
|
|
|||
|
|
@ -21,12 +21,9 @@
|
|||
<span class="toggle-label" translate>Enable Glidera Service</span>
|
||||
</ion-toggle>
|
||||
|
||||
<!-- disable coinbase for this release -->
|
||||
|
||||
<!-- <ion-toggle ng-show="!isWP" ng-model="coinbaseEnabled" toggle-class="toggle-balanced" ng-change="coinbaseChange()">
|
||||
<ion-toggle ng-show="!isWP" ng-model="coinbaseEnabled.value" toggle-class="toggle-balanced" ng-change="coinbaseChange()">
|
||||
<span class="toggle-label" translate>Enable Coinbase Service</span>
|
||||
</ion-toggle> -->
|
||||
|
||||
</ion-toggle>
|
||||
|
||||
<div class="item item-divider" translate>Wallet Operation</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@
|
|||
</ion-nav-bar>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item class="item item-icon-right" ui-sref="tabs.buyandsell.coinbase">
|
||||
<img src="img/coinbase-logo.png" width="90">
|
||||
<i class="icon bp-arrow-right"></i>
|
||||
</ion-item>
|
||||
<ion-item class="item item-icon-right" ui-sref="tabs.buyandsell.glidera">
|
||||
<img src="img/glidera-logo.png" width="90">
|
||||
<i class="icon bp-arrow-right"></i>
|
||||
|
|
|
|||
|
|
@ -1,59 +1,30 @@
|
|||
|
||||
<div class="topbar-container">
|
||||
<nav ng-controller="topbarController as topbar"
|
||||
class="tab-bar"
|
||||
ng-style="{'background-color': '#2b71b1'}">
|
||||
<section class="left-small">
|
||||
<a class="p10"
|
||||
ng-click="topbar.goHome()">
|
||||
<span class="text-close">Close</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section class="right-small" ng-show="index.coinbaseAccount">
|
||||
<a class="p10" href ui-sref="preferencesCoinbase">
|
||||
<i class="fi-widget size-24"></i>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis">
|
||||
Buy & Sell Bitcoin
|
||||
</h1>
|
||||
</section>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="content coinbase p20b" ng-controller="coinbaseController as coinbase">
|
||||
<div class="row" ng-show="index.coinbaseError || (index.coinbaseToken && !index.coinbaseAccount)">
|
||||
<div class="m20b box-notification" ng-show="index.coinbaseError">
|
||||
<ul class="no-bullet m0 text-warning size-12">
|
||||
<li ng-repeat="err in index.coinbaseError.errors" ng-bind-html="err.message"></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="m20b box-notification" ng-show="index.coinbaseToken && !index.coinbaseAccount">
|
||||
<div class="text-warning">
|
||||
<span>Your primary account should be a WALLET. Set your wallet account as primary and try again.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m10t text-center">
|
||||
<button
|
||||
class="dark-gray outline round tiny"
|
||||
ng-click="index.initCoinbase(index.coinbaseToken)">
|
||||
Reconnect
|
||||
<ion-view id="coinbase">
|
||||
<ion-nav-bar class="bar-royal">
|
||||
<ion-nav-back-button>
|
||||
</ion-nav-back-button>
|
||||
<ion-nav-title>Coinbase</ion-nav-title>
|
||||
<ion-nav-buttons side="secondary">
|
||||
<button ng-show="accountId" class="button no-border" ui-sref="tabs.buyandsell.coinbase.preferences">
|
||||
<i class="icon ion-ios-settings"></i>
|
||||
</button>
|
||||
<div class="m20t size-12">
|
||||
Or go to <a class="text-gray" href ui-sref="preferencesCoinbase">Preferences</a> and log out manually.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ion-nav-buttons>
|
||||
</ion-nav-bar>
|
||||
|
||||
<div ng-if="!index.coinbaseToken && !index.coinbaseError" class="row">
|
||||
<div class="box-notification text-center size-12 text-warning" ng-show="index.coinbaseTestnet">
|
||||
<i class="fi-info"></i>
|
||||
<ion-content>
|
||||
|
||||
<div class="box-notification warning m0" ng-show="network == 'testnet'">
|
||||
Testnet wallets only work with Coinbase Sandbox Accounts
|
||||
</div>
|
||||
<div class="columns" ng-init="showOauthForm = false">
|
||||
|
||||
<div class="text-center" ng-show="error">
|
||||
<div class="m20b box-notification" ng-show="error">
|
||||
<ul class="no-bullet m0 size-12">
|
||||
<li ng-repeat="err in error.errors" ng-bind-html="err.message"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="!accountId && !error" ng-init="showOauthForm = false">
|
||||
<div class="text-center m20v">
|
||||
<img src="img/coinbase-logo.png" width="200">
|
||||
</div>
|
||||
|
|
@ -61,111 +32,95 @@
|
|||
|
||||
<p class="m20t text-gray size-12">Connect your Coinbase account to get started</p>
|
||||
|
||||
<a class="button light-gray outline round small"
|
||||
<button class="button button-standard button-primary"
|
||||
ng-click="coinbase.openAuthenticateWindow(); showOauthForm = true">
|
||||
Connect to Coinbase
|
||||
</a>
|
||||
<div>
|
||||
</button>
|
||||
<div class="m20t">
|
||||
<a href ng-click="showOauthForm = true" class="text-gray size-12">
|
||||
Do you already have the Oauth Code?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center" ng-show="showOauthForm">
|
||||
<div ng-show="showOauthForm">
|
||||
<div class="text-left box-notification" ng-show="coinbase.error">
|
||||
<ul class="no-bullet m0 text-warning size-12">
|
||||
<li ng-repeat="err in coinbase.error.errors" ng-bind-html="err.message"></li>
|
||||
</ul>
|
||||
</div>
|
||||
<form name="oauthCodeForm" ng-submit="coinbase.submitOauthCode(code)" novalidate>
|
||||
<label>OAuth Code</label>
|
||||
<input type="text" ng-model="code" ng-disabled="coinbase.loading"
|
||||
ng-attr-placeholder="{{'Paste the authorization code here'}}" required>
|
||||
<input
|
||||
class="button expand round"
|
||||
ng-style="{'background-color': '#2b71b1'}"
|
||||
type="submit" value="Get started" ng-disabled="oauthCodeForm.$invalid || coinbase.loading">
|
||||
</form>
|
||||
<button class="button light-gray expand outline round"
|
||||
ng-click="showOauthForm = false; index.coinbaseError = null; coinbase.error = null">
|
||||
<i class="fi-arrow-left"></i> <span class="tu">Back</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="index.coinbaseToken && index.coinbaseAccount && !index.coinbaseError">
|
||||
|
||||
<div class="p20v text-center" ng-show="index.coinbaseAccount" ng-click="index.updateCoinbase({updateAccount: true})">
|
||||
<img src="img/coinbase-logo.png" width="100">
|
||||
</div>
|
||||
|
||||
<ul ng-show="index.coinbaseAccount" class="no-bullet m0 size-12">
|
||||
<li class="line-b line-t p15 coinbase-pointer"
|
||||
href ui-sref="buyCoinbase">
|
||||
<img src="img/buy-bitcoin.svg" alt="buy bitcoin" width="30">
|
||||
<span class="m10 text-normal text-bold">Buy Bitcoin</span>
|
||||
<span class="right text-gray">
|
||||
<i class="icon-arrow-right3 size-24 right"></i>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p15 coinbase-pointer"
|
||||
href ui-sref="sellCoinbase">
|
||||
<img src="img/sell-bitcoin.svg" alt="sell bitcoin" width="30">
|
||||
<span class="m10 text-normal text-bold">Sell Bitcoin</span>
|
||||
<span class="right text-gray">
|
||||
<i class="icon-arrow-right3 size-24 right"></i>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div ng-show="index.coinbasePendingTransactions && !index.coinbaseError">
|
||||
<h4 class="title">Activity</h4>
|
||||
<div class="m20b box-notification" ng-show="index.coinbasePendingError">
|
||||
<ul class="no-bullet m0 text-warning size-12">
|
||||
<li ng-repeat="err in index.coinbasePendingError.errors" ng-bind-html="err.message"></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div ng-repeat="(id, tx) in index.coinbasePendingTransactions | orderObjectBy:'updated_at':true track by $index"
|
||||
ng-click="coinbase.openTxModal(tx)"
|
||||
class="row collapse coinbase-last-transactions-content">
|
||||
<div class="large-2 medium-2 small-2 columns">
|
||||
<img src="img/bought-pending.svg" alt="bought" width="24" ng-show="(tx.type == 'buy' || (tx.to && tx.type == 'send')) && tx.status != 'completed'">
|
||||
<img src="img/bought.svg" alt="bought" width="30" ng-show="(tx.type == 'buy' || (tx.to && tx.type == 'send')) && tx.status == 'completed'">
|
||||
<img src="img/sold-pending.svg" alt="sold" width="24" ng-show="tx.from && tx.type == 'send'">
|
||||
<img src="img/sold.svg" alt="sold" width="30" ng-show="!tx.from && tx.type == 'sell' && tx.status == 'completed'">
|
||||
</div>
|
||||
|
||||
<div class="large-5 medium-5 small-5 columns">
|
||||
<div class="size-12 m5t">
|
||||
<span ng-show="tx.type == 'sell' && tx.status == 'completed'">Sold</span>
|
||||
<span ng-show="tx.type == 'buy' && tx.status == 'completed'">Bought</span>
|
||||
<span class="text-bold">
|
||||
<span ng-if="tx.type == 'sell' || (tx.type == 'send' && tx.from)">-</span>{{tx.amount.amount.replace('-','')}}
|
||||
{{tx.amount.currency}}
|
||||
</span>
|
||||
<div class="list settings-input-group">
|
||||
<label class="item item-input item-stacked-label">
|
||||
<span class="input-label">OAuth Code</span>
|
||||
<input type="text"
|
||||
ng-model="code"
|
||||
ng-attr-placeholder="{{'Paste the authorization code here'}}" required>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-4 medium-4 small-4 columns text-right">
|
||||
<div ng-show="tx.error" class="m5t size-12 text-warning">
|
||||
<input
|
||||
class="button button-standard button-primary"
|
||||
type="submit" value="Connect Coinbase Account" ng-disabled="oauthCodeForm.$invalid">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="accountId && !error">
|
||||
|
||||
<div class="list card"
|
||||
ng-show="accountId">
|
||||
<a class="item item-icon-right"
|
||||
href ui-sref="tabs.buyandsell.coinbase.buy">
|
||||
<img src="img/buy-bitcoin.svg" alt="buy bitcoin" width="35" class="item-img-buy">
|
||||
Buy Bitcoin
|
||||
<i class="icon bp-arrow-right"></i>
|
||||
</a>
|
||||
<a class="item item-icon-right"
|
||||
href ui-sref="tabs.buyandsell.coinbase.sell">
|
||||
<img src="img/sell-bitcoin.svg" alt="buy bitcoin" width="35" class="item-img-sell">
|
||||
Sell Bitcoin
|
||||
<i class="icon bp-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="list card">
|
||||
<div class="item item-heading" ng-click="updateTransactions()">
|
||||
Activity
|
||||
</div>
|
||||
<a class="item"
|
||||
ng-if="pendingTransactions && !error"
|
||||
ng-repeat="(id, tx) in pendingTransactions | orderObjectBy:'updated_at':true track by $index"
|
||||
ng-click="coinbase.openTxModal(tx)">
|
||||
|
||||
<span class="item-note">
|
||||
<div ng-show="tx.error">
|
||||
Error
|
||||
</div>
|
||||
<div ng-show="!tx.error" class="m5t size-12 text-gray">
|
||||
<div ng-show="!tx.error">
|
||||
<div ng-show="tx.status == 'completed'">
|
||||
<time ng-if="tx.created_at">{{tx.created_at | amTimeAgo}}</time>
|
||||
</div>
|
||||
<div ng-show="tx.status == 'pending'">
|
||||
<span class="label outline gray radius text-gray text-info" ng-if="tx.status == 'pending'">Pending</span>
|
||||
<span ng-if="tx.status == 'pending'">Pending</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-1 medium-1 small-1 columns text-right">
|
||||
<i class="icon-arrow-right3 size-18"></i>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<img class="left m10r" src="img/bought-pending.svg" alt="bought" width="24" ng-show="(tx.type == 'buy' || (tx.to && tx.type == 'send')) && tx.status != 'completed'">
|
||||
<img class="left m10r" src="img/bought.svg" alt="bought" width="30" ng-show="(tx.type == 'buy' || (tx.to && tx.type == 'send')) && tx.status == 'completed'">
|
||||
<img class="left m10r" src="img/sold-pending.svg" alt="sold" width="24" ng-show="tx.from && tx.type == 'send'">
|
||||
<img class="left m10r" src="img/sold.svg" alt="sold" width="30" ng-show="!tx.from && tx.type == 'sell' && tx.status == 'completed'">
|
||||
|
||||
<h2>
|
||||
<span ng-show="tx.type == 'sell' && tx.status == 'completed'">Sold</span>
|
||||
<span ng-show="tx.type == 'buy' && tx.status == 'completed'">Bought</span>
|
||||
</h2>
|
||||
<p>
|
||||
<span ng-if="tx.type == 'sell' || (tx.type == 'send' && tx.from)">-</span>{{tx.amount.amount.replace('-','')}}
|
||||
{{tx.amount.currency}}
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="extra-margin-bottom"></div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-view>
|
||||
|
|
|
|||
|
|
@ -124,10 +124,10 @@
|
|||
<img src="img/glidera-logo.png" width="90"/>
|
||||
<i class="icon bp-arrow-right"></i>
|
||||
</a>
|
||||
<!-- disable coinbase for this release -->
|
||||
<!-- <a ng-if="coinbaseEnabled" ui-sref="exchange.coinbase" class="item">
|
||||
<img src="img/coinbase-logo.png" width="90"> TODO
|
||||
</a> -->
|
||||
<a ng-if="coinbaseEnabled" ui-sref="tabs.buyandsell.coinbase" class="item item-extra-padding item-sub item-icon-right">
|
||||
<img src="img/coinbase-logo.png" width="90">
|
||||
<i class="icon bp-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -156,7 +156,7 @@
|
|||
<span translate>Add BitPay Visa® Card</span>
|
||||
<i class="icon bp-arrow-right"></i>
|
||||
</a>
|
||||
<a ng-if="!externalServices.BuyAndSell && (coinbaseEnabled || glideraEnabled)" ui-sref="tabs.buyandsell.glidera" class="item item-sub item-icon-left item-big-icon-left item-icon-right next-step">
|
||||
<a ng-if="!externalServices.BuyAndSell && (coinbaseEnabled || glideraEnabled)" ui-sref="tabs.buyandsell" class="item item-sub item-icon-left item-big-icon-left item-icon-right next-step">
|
||||
<i class="icon big-icon-svg">
|
||||
<div class="bg icon-buy-bitcoin"></div>
|
||||
</i>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue