From 612d7067799e52605d3c847321578e7bf979b414 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Mon, 19 Dec 2016 11:50:49 -0300 Subject: [PATCH] Complete buying flow --- src/js/controllers/amount.js | 7 +- src/js/controllers/coinbase.js | 6 +- src/js/controllers/confirm.js | 126 +++++++++++- .../controllers/modals/coinbaseTxDetails.js | 4 +- src/js/routes.js | 2 +- src/js/services/coinbaseService.js | 177 +++++++++++++++-- www/views/confirm.html | 29 ++- www/views/modals/coinbase-tx-details.html | 180 +++++++++--------- 8 files changed, 406 insertions(+), 125 deletions(-) diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index f9c5a554e..55da74976 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -52,7 +52,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ if ($scope.isCoinbase) { var currency = 'USD'; - coinbaseService.init($scope.coinbaseAccessToken, function(err, data) { + coinbaseService.init(function(err, data) { if ($scope.isCoinbase == 'buy') { coinbaseService.buyPrice(data.accessToken, currency, function(err, b) { $scope.coinbaseBuyPrice = b.data || null; @@ -394,11 +394,14 @@ angular.module('copayApp.controllers').controller('amountController', function($ popupService.showAlert(gettextCatalog.getString('Error'), 'No Payment Method Selected'); return; } + var amountUSD = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount)); var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; $state.transitionTo('tabs.buyandsell.coinbase.confirm', { toAmount: (amount * unitToSatoshi).toFixed(0), isCoinbase: $scope.isCoinbase, - coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethod.id + coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethod.id, + coinbaseAmount: amountUSD, + coinbaseAmountCurrency: 'USD' }); } else { var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; diff --git a/src/js/controllers/coinbase.js b/src/js/controllers/coinbase.js index ef2ef3749..333ab1ee6 100644 --- a/src/js/controllers/coinbase.js +++ b/src/js/controllers/coinbase.js @@ -6,8 +6,8 @@ angular.module('copayApp.controllers').controller('coinbaseController', function var init = function() { ongoingProcess.set('connectingCoinbase', true); - coinbaseService.init($scope.accessToken, function(err, data) { -console.log('[coinbase.js:9]',data); //TODO) + coinbaseService.init(function(err, data) { +console.log('[coinbase.js:9]',err, data); //TODO) ongoingProcess.set('connectingCoinbase', false); if (err || lodash.isEmpty(data)) { if (err) { @@ -28,7 +28,7 @@ console.log('[coinbase.js:9]',data); //TODO) $scope.updateTransactions = function() { $log.debug('Checking for transactions...'); coinbaseService.getPendingTransactions($scope.accessToken, $scope.accountId, function(err, txs) { -console.log('[coinbase.js:43]',txs); //TODO) +console.log('[coinbase.js:43]',txs); //TODO $scope.pendingTransactions = txs; }); diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index 11e2f7444..9cc0e8ec1 100644 --- a/src/js/controllers/confirm.js +++ b/src/js/controllers/confirm.js @@ -28,6 +28,8 @@ angular.module('copayApp.controllers').controller('confirmController', function( // Coinbase parameters $scope.isCoinbase = data.stateParams.isCoinbase; $scope.coinbasePaymentMethodId = data.stateParams.coinbasePaymentMethodId; + $scope.coinbaseAmount = data.stateParams.coinbaseAmount; + $scope.coinbaseAmountCurrency = data.stateParams.coinbaseAmountCurrency; toAmount = data.stateParams.toAmount; cachedSendMax = {}; @@ -180,6 +182,10 @@ angular.module('copayApp.controllers').controller('confirmController', function( } if ($scope.isGlidera == 'buy') $scope.getBuyPrice(); if ($scope.isGlidera == 'sell') $scope.getSellPrice(); + + if ($scope.isCoinbase == 'buy') { + coinbaseBuyRequest($scope.coinbaseAmount, $scope.coinbaseAmountCurrency, $scope.coinbasePaymentMethodId); + } }; function resetValues() { @@ -532,6 +538,50 @@ angular.module('copayApp.controllers').controller('confirmController', function( } }); return; + } + + if ($scope.isCoinbase) { + + ongoingProcess.set('buyingBitcoin', true, onSendStatusChange); + coinbaseService.init(function(err, res) { + if (err) { + $log.error(err); + return; + } + var token = res.accessToken; + var accountId = res.accountId; + coinbaseService.buyCommit(token, accountId, $scope.coinbaseBuyRequest.id, function(err, b) { +console.log('[confirm.js:508] BUY COMMIT',b); //TODO + if (err) { + $log.error(err); + return; + } + var tx = b.data.transaction; + if (!tx) return; + + coinbaseService.getTransaction(token, accountId, tx.id, function(err, updatedTx) { +console.log('[confirm.js:517] GET TRANSACTION',updatedTx); //TODO + if (err) $log.debug(err); + walletService.getAddress($scope.wallet, false, function(err, walletAddr) { +console.log('[confirm.js:521] GET ADDRESS',walletAddr); //TODO + if (err) { + return; + } + updatedTx.data['toAddr'] = walletAddr; + coinbaseService.savePendingTransaction(updatedTx.data, {}, function(err) { + if (err) $log.debug(err); + if (updatedTx.data.status == 'completed') { + coinbaseSendToCopay(token, accountId, updatedTx.data, onSendStatusChange); + } else { + ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); + $scope.coinbaseBuySuccess = updatedTx.data; + } + }); + }); + }); + }); + }); + return; } ongoingProcess.set('creatingTx', true, onSendStatusChange); @@ -582,12 +632,15 @@ angular.module('copayApp.controllers').controller('confirmController', function( $log.debug('statusChangeHandler: ', processName, showName, isOn); if ( ( - processName === 'broadcastingTx' || - ((processName === 'signingTx') && $scope.wallet.m > 1) || - (processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal()) + processName === 'broadcastingTx' || + ((processName === 'signingTx') && $scope.wallet.m > 1) || + (processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal()) || + (processName == 'buyingBitcoin') ) && !isOn) { $scope.sendStatus = 'success'; - $scope.$digest(); + $timeout(function() { + $scope.$digest(); + }, 100) } else if (showName) { $scope.sendStatus = showName; } @@ -604,6 +657,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( var fromBitPayCard = previousView.match(/tabs.bitpayCard/) ? true : false; var fromAmazon = previousView.match(/tabs.giftcards.amazon/) ? true : false; var fromGlidera = previousView.match(/tabs.buyandsell.glidera/) ? true : false; + var fromCoinbase = previousView.match(/tabs.buyandsell.coinbase/) ? true : false; $ionicHistory.nextViewOptions({ disableAnimate: true @@ -637,6 +691,15 @@ angular.module('copayApp.controllers').controller('confirmController', function( $state.go('tabs.home').then(function() { $state.transitionTo('tabs.buyandsell.glidera'); }); + } else if (fromCoinbase) { + $ionicHistory.nextViewOptions({ + disableAnimate: true, + historyRoot: true + }); + $ionicHistory.clearHistory(); + $state.go('tabs.home').then(function() { + $state.transitionTo('tabs.buyandsell.coinbase'); + }); } else { $ionicHistory.nextViewOptions({ disableAnimate: true, @@ -897,10 +960,61 @@ angular.module('copayApp.controllers').controller('confirmController', function( } if (lodash.isEmpty(res)) return; if (unitName == 'bits') { - $scope.exchangeRate = '1,000,000 bits ~ ' + res.rate + ' ' + alternativeIsoCode; + $scope.exchangeRate = '1,000,000 bits ~ ' + res.data.rate + ' ' + alternativeIsoCode; } else { - $scope.exchangeRate = '1 BTC ~ ' + res.rate + ' ' + alternativeIsoCode; + $scope.exchangeRate = '1 BTC ~ ' + res.data.rate + ' ' + alternativeIsoCode; } }); }; + + var coinbaseBuyRequest = function(amount, currency, paymentMethodId) { + var dataSrc = { + amount: amount, + currency: currency, + payment_method: paymentMethodId + }; + coinbaseService.init(function(err, res) { + if (err) { + $log.error(err); + return; + } + coinbaseService.buyRequest(res.accessToken, res.accountId, dataSrc, function(err, data) { +console.log('[confirm.js:931] BUY REQUEST',data); //TODO + if (err) { + $log.error(err); + return; + } + $scope.coinbaseBuyRequest = data.data; + }); + }); + }; + + var coinbaseSendToCopay = function(token, accountId, tx, onSendStatusChange) { + var data = { + to: tx.toAddr, + amount: tx.amount.amount, + currency: tx.amount.currency, + description: 'Copay Wallet: ' + $scope.wallet.name + }; + coinbaseService.sendTo(token, accountId, data, function(err, res) { +console.log('[confirm.js:938] SEND TO',res); //TODO + if (err) { + return; + } + $scope.coinbaseReceiveInfo = res.data; + if (!res.data.id) return; + coinbaseService.getTransaction(token, accountId, res.data.id, function(err, sendTx) { +console.log('[confirm.js:945] GET TRANSACTION',sendTx); //TODO + coinbaseService.savePendingTransaction(tx, { + remove: true + }, function(err) { + coinbaseService.savePendingTransaction(sendTx.data, {}, function(err) { + ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); +console.log('[confirm.js:948] LISTO',err); //TODO + // TODO + }); + }); + }); + }); + }; }); diff --git a/src/js/controllers/modals/coinbaseTxDetails.js b/src/js/controllers/modals/coinbaseTxDetails.js index feebfd47d..3d952b168 100644 --- a/src/js/controllers/modals/coinbaseTxDetails.js +++ b/src/js/controllers/modals/coinbaseTxDetails.js @@ -7,11 +7,11 @@ angular.module('copayApp.controllers').controller('coinbaseTxDetailsController', remove: true }, function(err) { $rootScope.$emit('Local/CoinbaseTx'); - $scope.cancel(); + $scope.close(); }); }; - $scope.cancel = function() { + $scope.close = function() { $scope.coinbaseTxDetailsModal.hide(); }; diff --git a/src/js/routes.js b/src/js/routes.js index d466a17eb..ad0bcca17 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -955,7 +955,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) .state('tabs.buyandsell.coinbase.confirm', { - url: '/confirm/:toAmount/:isCoinbase/:coinbasePaymentMethodId', + url: '/confirm/:toAmount/:isCoinbase/:coinbasePaymentMethodId/:coinbaseAmount/:coinbaseAmountCurrency', views: { 'tab-home@tabs': { controller: 'confirmController', diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 6a05b99cb..6bccb318d 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -180,21 +180,13 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }); }; - root.init = function(accessToken, cb) { + root.init = function(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) { + storageService.getCoinbaseToken(credentials.NETWORK, function(err, accessToken) { if (err || !accessToken) return cb(); else { _getMainAccountId(accessToken, function(err, accountId) { @@ -279,6 +271,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.getTransaction = function(token, accountId, transactionId, cb) { + if (isFake) return cb(null, get_transaction); if (!token) return cb('Invalid Token'); $http(_get('/accounts/' + accountId + '/transactions/' + transactionId, token)).then(function(data) { $log.info('Coinbase Transaction: SUCCESS'); @@ -392,6 +385,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.buyRequest = function(token, accountId, data, cb) { + if (isFake) return cb(null, buy_request); var data = { amount: data.amount, currency: data.currency, @@ -408,6 +402,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.buyCommit = function(token, accountId, buyId, cb) { + if (isFake) return cb(null, buy_commit); $http(_post('/accounts/' + accountId + '/buys/' + buyId + '/commit', token)).then(function(data) { $log.info('Coinbase Buy Commit: SUCCESS'); return cb(null, data.data); @@ -431,6 +426,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.sendTo = function(token, accountId, data, cb) { + if (isFake) return cb(null, send_to_copay); var data = { type: 'send', to: data.to, @@ -448,6 +444,10 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; // Pending transactions + + root.savePendingTransaction = function(ctx, opts, cb) { + _savePendingTransaction(ctx, opts, cb); + }; var _savePendingTransaction = function(ctx, opts, cb) { storageService.getCoinbaseTxs(credentials.NETWORK, function(err, oldTxs) { @@ -478,11 +478,12 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) { txs = txs ? JSON.parse(txs) : {}; coinbasePendingTransactions = lodash.isEmpty(txs) ? null : txs; - lodash.forEach(txs, function(dataFromStorage, txId) { + lodash.forEach(coinbasePendingTransactions, 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; + (dataFromStorage.type == 'send' && dataFromStorage.status == 'completed')) + return cb(null, coinbasePendingTransactions); root.getTransaction(accessToken, accountId, txId, function(err, tx) { if (err) { _savePendingTransaction(dataFromStorage, { @@ -491,7 +492,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }, function(err) { if (err) $log.debug(err); }); - return; + return cb(err); } _updateCoinbasePendingTransactions(dataFromStorage, tx.data); coinbasePendingTransactions[txId] = dataFromStorage; @@ -504,7 +505,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }, function(err) { if (err) $log.debug(err); }); - return cb(); + return cb(err); } var newSellPrice = s.data.amount; var variance = Math.abs((newSellPrice - dataFromStorage.sell_price_amount) / dataFromStorage.sell_price_amount * 100); @@ -632,6 +633,46 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }); }; + var buy_request = { + "data" : { + "id": "a333743d-184a-5b5b-abe8-11612fc44ab5", + "status": "created", + "payment_method": { + "id": "83562370-3e5c-51db-87da-752af5ab9559", + "resource": "payment_method", + "resource_path": "/v2/payment-methods/83562370-3e5c-51db-87da-752af5ab9559" + }, + "transaction": { + "id": "763d1401-fd17-5a18-852a-9cca5ac2f9c0", + "resource": "transaction", + "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/441b9494-b3f0-5b98-b9b0-4d82c21c252a" + }, + "amount": { + "amount": "10.00000000", + "currency": "BTC" + }, + "total": { + "amount": "102.01", + "currency": "USD" + }, + "subtotal": { + "amount": "101.00", + "currency": "USD" + }, + "created_at": "2015-04-01T18:43:37-07:00", + "updated_at": "2015-04-01T18:43:37-07:00", + "resource": "buy", + "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/buys/a333743d-184a-5b5b-abe8-11612fc44ab5", + "committed": false, + "instant": false, + "fee": { + "amount": "1.01", + "currency": "USD" + }, + "payout_at": "2015-04-07T18:43:37-07:00" + } + }; + var payment_methods = { "pagination": { "ending_before": null, @@ -686,6 +727,114 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ ] }; + var get_transaction = { + "data" : { + "id": "57ffb4ae-0c59-5430-bcd3-3f98f797a66c", + "type": "send", + "status": "completed", + "amount": { + "amount": "-0.00100000", + "currency": "BTC" + }, + "native_amount": { + "amount": "-0.01", + "currency": "USD" + }, + "description": null, + "created_at": "2015-03-11T13:13:35-07:00", + "updated_at": "2015-03-26T15:55:43-07:00", + "resource": "transaction", + "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/57ffb4ae-0c59-5430-bcd3-3f98f797a66c", + "network": { + "status": "off_blockchain", + "name": "bitcoin" + }, + "to": { + "id": "a6b4c2df-a62c-5d68-822a-dd4e2102e703", + "resource": "user", + "resource_path": "/v2/users/a6b4c2df-a62c-5d68-822a-dd4e2102e703" + }, + "details": { + "title": "Send bitcoin", + "subtitle": "to User 2" + } + } + }; + + var buy_commit = { + "data" : { + "id": "a333743d-184a-5b5b-abe8-11612fc44ab5", + "status": "created", + "payment_method": { + "id": "83562370-3e5c-51db-87da-752af5ab9559", + "resource": "payment_method", + "resource_path": "/v2/payment-methods/83562370-3e5c-51db-87da-752af5ab9559" + }, + "transaction": { + "id": "763d1401-fd17-5a18-852a-9cca5ac2f9c0", + "resource": "transaction", + "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/441b9494-b3f0-5b98-b9b0-4d82c21c252a" + }, + "amount": { + "amount": "10.00000000", + "currency": "BTC" + }, + "total": { + "amount": "102.01", + "currency": "USD" + }, + "subtotal": { + "amount": "101.00", + "currency": "USD" + }, + "created_at": "2015-04-01T18:43:37-07:00", + "updated_at": "2015-04-01T18:43:37-07:00", + "resource": "buy", + "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/buys/a333743d-184a-5b5b-abe8-11612fc44ab5", + "committed": true, + "instant": false, + "fee": { + "amount": "1.01", + "currency": "USD" + }, + "payout_at": "2015-04-07T18:43:37-07:00" + } + }; + + var send_to_copay = { + "data" : { + "id": "3c04e35e-8e5a-5ff1-9155-00675db4ac02", + "type": "send", + "status": "pending", + "amount": { + "amount": "-0.10000000", + "currency": "BTC" + }, + "native_amount": { + "amount": "-1.00", + "currency": "USD" + }, + "description": null, + "created_at": "2015-01-31T20:49:02Z", + "updated_at": "2015-03-31T17:25:29-07:00", + "resource": "transaction", + "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/3c04e35e-8e5a-5ff1-9155-00675db4ac02", + "network": { + "status": "unconfirmed", + "hash": "463397c87beddd9a61ade61359a13adc9efea26062191fe07147037bce7f33ed", + "name": "bitcoin" + }, + "to": { + "resource": "bitcoin_address", + "address": "1AUJ8z5RuHRTqD1eikyfUUetzGmdWLGkpT" + }, + "details": { + "title": "Send bitcoin", + "subtitle": "to User 2" + } + } + }; + return root; }); diff --git a/www/views/confirm.html b/www/views/confirm.html index 9bfdcdeeb..4cf9e823f 100644 --- a/www/views/confirm.html +++ b/www/views/confirm.html @@ -116,6 +116,17 @@ +
+
+ Subtotal: {{coinbaseBuyRequest.subtotal.amount}} {{coinbaseBuyRequest.subtotal.currency}} +
+
+ Total: {{coinbaseBuyRequest.total.amount}} {{coinbaseBuyRequest.total.currency}} +
+
+ Payout at: {{coinbaseBuyRequest.payout_at | amCalendar}} +
+
No wallets available
@@ -147,15 +158,25 @@ slide-success-show="sendStatus === 'success'" slide-success-on-confirm="onSuccessConfirm()" slide-success-hide-on-confirm="true"> - Payment Sent - Proposal Created - Transaction created + Bought +
+ Payment Sent + Proposal Created + Transaction created +
A transfer has been initiated from your bank account. Your bitcoins should arrive to your wallet in 2-4 business day A transfer has been initiated to your bank account. Should arrive in 4-6 business days
- Bitcoin purchase completed. Coinbase has queued the transfer to your selected Copay wallet + + + Bitcoin purchase completed. Coinbase has queued the transfer to your selected Copay wallet + + + Buy confirmed. Funds will be send soon to your selected wallet + + Sale initiated
diff --git a/www/views/modals/coinbase-tx-details.html b/www/views/modals/coinbase-tx-details.html index 181312a7f..1ff0969ef 100644 --- a/www/views/modals/coinbase-tx-details.html +++ b/www/views/modals/coinbase-tx-details.html @@ -1,104 +1,98 @@ - -
- - - Back - + + +
+ Details
-

Details

-
-
-
- bought - bought - bought - bought -
-
- Bought - Sold -
-
- Receiving purchased bitcoin - Sending bitcoin to sell -
-
- Buying bitcoin - Selling bitcoin -
-
- -{{tx.amount.amount.replace('-','')}} - {{tx.amount.currency}} -
-
- -{{tx.native_amount.amount.replace('-','')}} - {{tx.native_amount.currency}} -
+
+
+ bought + bought + bought + bought
- - -
-
    -
  • -
+
+ Bought + Sold
- -
    - -
  • - {{tx.details.title}} - {{tx.details.subtitle}} -
  • - -
  • - Status - Completed - Pending - Error -
  • - -
  • - Date - {{tx.created_at | amCalendar}} -
  • - -
  • - Price Sensitivity - {{tx.price_sensitivity.name}} -
  • - -
  • - Sell Price - {{tx.sell_price_amount}} {{tx.sell_price_currency}} -
  • - -
  • - Sent bitcoin from - Receive bitcoin in - {{tx.description}} -
  • -
- -
-
-

- This action will remove the transaction. -

- -
+
+ Receiving purchased bitcoin + Sending bitcoin to sell +
+
+ Buying bitcoin + Selling bitcoin +
+
+ -{{tx.amount.amount.replace('-','')}} + {{tx.amount.currency}} +
+
+ -{{tx.native_amount.amount.replace('-','')}} + {{tx.native_amount.currency}}
- -
+ +
+
    +
  • +
+
+ +
    +
  • + {{tx.details.title}} + {{tx.details.subtitle}} +
  • + +
  • + Status + + Completed + Pending + Error + +
  • + +
  • + Date + {{tx.created_at | amCalendar}} +
  • + +
  • + Price Sensitivity + {{tx.price_sensitivity.name}} +
  • + +
  • + Sell Price + {{tx.sell_price_amount}} {{tx.sell_price_currency}} +
  • + +
  • + Sent bitcoin from + Receive bitcoin in + {{tx.description}} +
  • +
+ +
+
+ This action will remove the transaction. +
+ +
+