Refactor buying process

This commit is contained in:
Gustavo Maximiliano Cortez 2017-01-11 19:38:05 -03:00
parent 420a063017
commit e42d09574b
No known key found for this signature in database
GPG Key ID: 15EDAD8D9F2EB1AF
8 changed files with 329 additions and 123 deletions

View File

@ -20,8 +20,8 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.isGlidera = data.stateParams.isGlidera;
$scope.glideraAccessToken = data.stateParams.glideraAccessToken;
// Coinbase parameters
$scope.isCoinbase = data.stateParams.isCoinbase;
// Coinbase
$scope.coinbase = data.stateParams.coinbase;
$scope.cardId = data.stateParams.cardId;
$scope.showMenu = $ionicHistory.backView() && $ionicHistory.backView().stateName == 'tabs.send';
@ -30,13 +30,13 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.toAddress = data.stateParams.toAddress;
$scope.toName = data.stateParams.toName;
$scope.toEmail = data.stateParams.toEmail;
$scope.showAlternativeAmount = !!$scope.cardId || !!$scope.isGiftCard || !!$scope.isGlidera || !!$scope.isCoinbase;
$scope.showAlternativeAmount = !!$scope.cardId || !!$scope.isGiftCard || !!$scope.isGlidera || !!$scope.coinbase;
$scope.toColor = data.stateParams.toColor;
$scope.showSendMax = false;
$scope.customAmount = data.stateParams.customAmount;
if (!$scope.cardId && !$scope.isGiftCard && !$scope.isGlidera && !$scope.isCoinbase && !data.stateParams.toAddress) {
if (!$scope.cardId && !$scope.isGiftCard && !$scope.isGlidera && !$scope.coinbase && !data.stateParams.toAddress) {
$log.error('Bad params at amount')
throw ('bad params');
}
@ -50,51 +50,6 @@ angular.module('copayApp.controllers').controller('amountController', function($
});
}
if ($scope.isCoinbase) {
var currency = 'USD';
coinbaseService.init(function(err, data) {
if ($scope.isCoinbase == 'buy') {
coinbaseService.buyPrice(data.accessToken, currency, function(err, b) {
$scope.coinbaseBuyPrice = b.data || null;
});
} else {
coinbaseService.sellPrice(data.accessToken, currency, function(err, s) {
$scope.coinbaseSellPrice = s.data || null;
});
var dataSrc = {
name: 'Received from ' + appConfigService.nameCase
};
coinbaseService.createAddress(data.accessToken, data.accountId, dataSrc, function(err, data) {
$scope.toAddress = data.data.address;
});
}
$scope.coinbasePaymentMethods = [];
$scope.coinbaseSelectedPaymentMethodId = { value : null };
coinbaseService.getPaymentMethods(data.accessToken, function(err, p) {
lodash.each(p.data, function(pm) {
if ($scope.isCoinbase == 'sell') {
if (pm.allow_sell) {
$scope.coinbasePaymentMethods.push(pm);
}
if (pm.allow_sell && pm.primary_sell) {
$scope.coinbaseSelectedPaymentMethodId.value = pm.id;
}
} else {
if (pm.allow_buy) {
$scope.coinbasePaymentMethods.push(pm);
}
if (pm.allow_buy && pm.primary_buy) {
$scope.coinbaseSelectedPaymentMethodId.value = pm.id;
}
}
});
});
});
}
var reNr = /^[1234567890\.]$/;
var reOp = /^[\*\+\-\/]$/;
@ -120,7 +75,11 @@ angular.module('copayApp.controllers').controller('amountController', function($
var config = configService.getSync().wallet.settings;
$scope.unitName = config.unitName;
$scope.alternativeIsoCode = !!$scope.cardId || !!$scope.isGiftCard ? 'USD' : config.alternativeIsoCode;
if (data.stateParams.currency) {
$scope.alternativeIsoCode = data.stateParams.currency;
} else {
$scope.alternativeIsoCode = !!$scope.cardId || !!$scope.isGiftCard ? 'USD' : config.alternativeIsoCode;
}
$scope.specificAmount = $scope.specificAlternativeAmount = '';
$scope.isCordova = platformInfo.isCordova;
unitToSatoshi = config.unitToSatoshi;
@ -398,29 +357,17 @@ angular.module('copayApp.controllers').controller('amountController', function($
isGlidera: $scope.isGlidera,
glideraAccessToken: $scope.glideraAccessToken
});
} else if ($scope.isCoinbase) {
if (lodash.isEmpty($scope.coinbaseSelectedPaymentMethodId.value)) {
popupService.showAlert(gettextCatalog.getString('Error'), 'No Payment Method Selected');
return;
}
if ($scope.isCoinbase == 'sell' && lodash.isEmpty($scope.toAddress)) {
popupService.showAlert(gettextCatalog.getString('Error'), 'No Destination Address');
return;
}
var amountUSD = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount));
if (amountUSD < 1) {
popupService.showAlert(gettextCatalog.getString('Error'), 'Amount must be at least 1.00 USD');
} else if ($scope.coinbase) {
var amountAlternative = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount));
if (amountAlternative < 1) {
popupService.showAlert(gettextCatalog.getString('Error'), 'Amount must be at least 1.00 ' + $scope.alternativeIsoCode);
return;
}
var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount;
$state.transitionTo('tabs.buyandsell.coinbase.confirm', {
toAmount: (amount * unitToSatoshi).toFixed(0),
toAddress: $scope.toAddress,
isCoinbase: $scope.isCoinbase,
coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethodId.value,
coinbaseAmount: amountUSD,
coinbaseAmountCurrency: 'USD'
var goTo = 'tabs.buyandsell.coinbase.' + $scope.coinbase;
$state.transitionTo(goTo, {
amount: amountAlternative,
currency: $scope.alternativeIsoCode
});
} else {
var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount;

View File

@ -0,0 +1,166 @@
'use strict';
angular.module('copayApp.controllers').controller('buyCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService) {
var amount;
var currency;
var initError = function(err) {
$log.error(err);
popupService.showAlert('Error', 'Could not connect to Coinbase', function() {
$ionicHistory.goBack();
});
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
amount = data.stateParams.amount;
currency = data.stateParams.currency;
$scope.network = coinbaseService.getNetwork();
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network
});
$scope.wallet = $scope.wallets[0]; // Default first wallet
ongoingProcess.set('connectingCoinbase', true);
coinbaseService.init(function(err, res) {
if (err) {
ongoingProcess.set('connectingCoinbase', false);
initError(err);
return;
}
var accessToken = res.accessToken;
$scope.paymentMethods = [];
$scope.selectedPaymentMethodId = { value : null };
coinbaseService.getPaymentMethods(accessToken, function(err, p) {
if (err) {
ongoingProcess.set('connectingCoinbase', false);
initError(err);
return;
}
lodash.each(p.data, function(pm) {
if (pm.allow_buy) {
$scope.paymentMethods.push(pm);
}
if (pm.allow_buy && pm.primary_buy) {
$scope.selectedPaymentMethodId.value = pm.id;
$scope.buyRequest();
}
});
});
});
});
$scope.buyRequest = function() {
ongoingProcess.set('connectingCoinbase', true);
coinbaseService.init(function(err, res) {
if (err) {
ongoingProcess.set('connectingCoinbase', false);
initError(err);
return;
}
var accessToken = res.accessToken;
var accountId = res.accountId;
var dataSrc = {
amount: amount,
currency: currency,
payment_method: $scope.selectedPaymentMethodId.value
};
coinbaseService.buyRequest(accessToken, accountId, dataSrc, function(err, data) {
ongoingProcess.set('connectingCoinbase', false);
if (err) {
$log.error(err);
popupService.showAlert('Error', 'Could not create a buy request', function() {
$ionicHistory.goBack();
});
return;
}
$scope.buyRequestInfo = data.data;
});
});
};
$scope.buyConfirm = function() {
var message = 'Buy bitcoin for ' + amount + ' ' + currency;
var okText = 'Confirm';
var cancelText = 'Cancel';
popupService.showConfirm(null, message, okText, cancelText, function(ok) {
if (!ok) return;
ongoingProcess.set('buyingBitcoin', true);
coinbaseService.init(function(err, res) {
if (err) {
ongoingProcess.set('buyingBitcoin', false);
initError(err);
return;
}
var accessToken = res.accessToken;
var accountId = res.accountId;
coinbaseService.buyCommit(accessToken, accountId, $scope.buyRequestInfo.id, function(err, b) {
if (err) {
ongoingProcess.set('buyingBitcoin', false);
popupService.showAlert('Error', 'Could not complete purchase');
return;
}
var tx = b.data.transaction;
if (!tx) {
ongoingProcess.set('buyingBitcoin', false);
popupService.showAlert('Error', 'Transaction not found');
return;
}
$timeout(function() {
coinbaseService.getTransaction(accessToken, accountId, tx.id, function(err, updatedTx) {
if (err) {
ongoingProcess.set('buyingBitcoin', false);
popupService.showAlert('Error', 'Transaction error');
return;
}
walletService.getAddress($scope.wallet, false, function(err, walletAddr) {
if (err) {
ongoingProcess.set('buyingBitcoin', false);
popupService.showAlert('Error', err);
return;
}
updatedTx.data['toAddr'] = walletAddr;
updatedTx.data['status'] = 'pending'; // Forcing "pending" status to process later
$log.debug('Saving transaction to process later...');
coinbaseService.savePendingTransaction(updatedTx.data, {}, function(err) {
if (err) $log.debug(err);
ongoingProcess.set('buyingBitcoin', false);
$scope.buySuccess = updatedTx.data;
$timeout(function() {
$scope.$apply();
});
});
});
});
}, 8000);
});
});
});
};
$scope.showWalletSelector = function() {
$scope.walletSelectorTitle = ($scope.action) == 'buy' ? 'Receive in' : 'Sell From';
$scope.showWallets = true;
};
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
};
$scope.goBackHome = function() {
$ionicHistory.nextViewOptions({
disableAnimate: true,
historyRoot: true
});
$ionicHistory.clearHistory();
$state.go('tabs.home').then(function() {
$state.transitionTo('tabs.buyandsell.coinbase');
});
};
});

View File

@ -6,6 +6,8 @@ angular.module('copayApp.controllers').controller('coinbaseController', function
var isCordova = platformInfo.isCordova;
var init = function() {
var config = configService.getSync().wallet.settings;
$scope.currency = getCurrency(config.alternativeIsoCode);
ongoingProcess.set('connectingCoinbase', true);
coinbaseService.init(function(err, data) {
ongoingProcess.set('connectingCoinbase', false);
@ -15,6 +17,15 @@ angular.module('copayApp.controllers').controller('coinbaseController', function
}
return;
}
// Show rates
coinbaseService.buyPrice(data.accessToken, $scope.currency, function(err, b) {
$scope.buyPrice = b.data || null;
});
coinbaseService.sellPrice(data.accessToken, $scope.currency, function(err, s) {
$scope.sellPrice = s.data || null;
});
// Updating accessToken and accountId
$timeout(function() {
$scope.accessToken = data.accessToken;
@ -25,6 +36,14 @@ angular.module('copayApp.controllers').controller('coinbaseController', function
});
};
var getCurrency = function(code) {
// ONLY "USD" and "EUR"
switch(code) {
case 'EUR' : return 'EUR';
default : return 'USD'
};
};
$scope.updateTransactions = function() {
$log.debug('Getting transactions...');
$scope.pendingTransactions = { data: {} };

View File

@ -942,41 +942,32 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
})
.state('tabs.buyandsell.coinbase.amount', {
url: '/amount/:isCoinbase',
views: {
'tab-home@tabs': {
controller: 'amountController',
templateUrl: 'views/amount.html'
}
url: '/amount/:coinbase/:currency',
views: {
'tab-home@tabs': {
controller: 'amountController',
templateUrl: 'views/amount.html'
}
})
.state('tabs.buyandsell.coinbase.confirm', {
url: '/confirm/:toAmount/:toAddress/:isCoinbase/:coinbasePaymentMethodId/:coinbaseAmount/:coinbaseAmountCurrency',
views: {
'tab-home@tabs': {
controller: 'confirmController',
templateUrl: 'views/confirm.html'
}
}
})
/*
.state('tabs.buyandsell.coinbase.buy', {
url: '/buy',
}
})
.state('tabs.buyandsell.coinbase.buy', {
url: '/buy/:amount/:currency',
views: {
'tab-home@tabs': {
controller: 'buyCoinbaseController',
controllerAs: 'buy',
templateUrl: 'views/buyCoinbase.html'
}
})
.state('tabs.buyandsell.coinbase.sell', {
url: '/sell',
}
})
.state('tabs.buyandsell.coinbase.sell', {
url: '/sell/:amount/:currency',
views: {
'tab-home@tabs': {
controller: 'sellCoinbaseController',
controllerAs: 'sell',
templateUrl: 'views/sellCoinbase.html'
}
})
*/
}
})
/*
*

View File

@ -394,7 +394,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
amount: data.amount,
currency: data.currency,
payment_method: data.payment_method || null,
commit: false
commit: data.commit || false
};
$http(_post('/accounts/' + accountId + '/buys', token, data)).then(function(data) {
$log.info('Coinbase Buy Request: SUCCESS');

View File

@ -1,7 +1,7 @@
<ion-view id="view-amount" hide-tabs>
<ion-nav-bar class="bar-royal">
<ion-nav-title>
{{'Enter Amount' | translate}}
{{coinbase ? (coinbase == 'buy' ? 'Buy bitcoin' : 'Sell bitcoin') : ('Enter Amount' | translate)}}
</ion-nav-title>
<ion-nav-back-button>
</ion-nav-back-button>
@ -14,7 +14,7 @@
<ion-content scroll="false">
<div ng-if="!customAmount && !isGlidera && !isCoinbase">
<div ng-if="!customAmount && !isGlidera && !coinbase">
<div class="item item-no-bottom-border recipient-label" translate>Recipient</div>
<div class="item item-text-wrap item-icon-left bitcoin-address" ng-class="{'item-big-icon-left':cardId}">
@ -39,11 +39,12 @@
</div>
</div>
<div ng-class="{'amount-pane-recipient': !customAmount && !isGlidera && !isCoinbase, 'amount-pane-no-recipient': customAmount || isGlidera || isCoinbase}">
<div ng-class="{'amount-pane-recipient': !customAmount && !isGlidera && !coinbase,
'amount-pane-no-recipient': customAmount || isGlidera || coinbase}">
<div class="amount-bar oh">
<div class="title">
<span ng-show="!isCoinbase" translate>Amount</span>
<span translate>Amount</span>
<div class="size-12" ng-show="isGiftCard">Purchase Amount is limited to USD 1000 per day</div>
<div class="size-12" ng-if="cardId" ng-init="getRates()">{{exchangeRate}}</div>
<div ng-show="isGlidera">
@ -66,26 +67,6 @@
(remaining {{limits.monthlySellRemaining|currency:'':2}} {{limits.currency}})
</div>
</div>
<div ng-show="isCoinbase">
<div class="list select">
<label class="item item-input item-select">
<div class="input-label">
<span ng-show="isCoinbase == 'buy'">Payment Method</span>
<span ng-show="isCoinbase == 'sell'">Deposit into</span>
</div>
<select ng-model="coinbaseSelectedPaymentMethodId.value"
ng-options="item.id as item.name for item in coinbasePaymentMethods">
</select>
</label>
</div>
<div class="limits" ng-show="coinbaseBuyPrice && isCoinbase == 'buy'">
1 BTC ~ {{coinbaseBuyPrice.amount}} {{coinbaseBuyPrice.currency}}
</div>
<div class="limits" ng-show="coinbaseSellPrice && isCoinbase == 'sell'">
1 BTC ~ {{coinbaseSellPrice.amount}} {{coinbaseSellPrice.currency}}
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,96 @@
<ion-view id="coinbase" hide-tabs>
<ion-nav-bar class="bar-royal">
<ion-nav-back-button>
</ion-nav-back-button>
<ion-nav-title>Buy bitcoin</ion-nav-title>
</ion-nav-bar>
<ion-content>
<!-- BUY -->
<div class="list" ng-show="!buySuccess">
<div class="item item-divider">
Purchase Info
</div>
<label class="item item-input item-select">
<div class="input-label">
Payment Method
</div>
<select ng-model="selectedPaymentMethodId.value"
ng-options="item.id as item.name for item in paymentMethods"
ng-change="buyRequest()">
</select>
</label>
<div ng-if="buyRequestInfo">
<div class="item">
Amount
<span class="item-note">
{{buyRequestInfo.amount.amount}} {{buyRequestInfo.amount.currency}}
</span>
</div>
<div class="item">
Payout at
<span class="item-note">
{{buyRequestInfo.payout_at | amCalendar}}
</span>
</div>
<div class="item" ng-click="showWalletSelector()">
<span>Receive in</span>
<span class="item-note">{{wallet ? wallet.name : '...'}}</span>
</div>
<div class="item item-divider">
Fees
</div>
<div class="item" ng-repeat="fee in buyRequestInfo.fees">
{{fee.type}}
<span class="item-note">
{{fee.amount.amount}} {{fee.amount.currency}}
</span>
</div>
<div class="item item-divider">
Total
</div>
<div class="item">
Subtotal
<span class="item-note">
{{buyRequestInfo.subtotal.amount}} {{buyRequestInfo.subtotal.currency}}
</span>
</div>
<div class="item">
Total
<span class="item-note">
{{buyRequestInfo.total.amount}} {{buyRequestInfo.total.currency}}
</span>
</div>
</div>
</div>
<button
ng-show="buyRequestInfo && !buySuccess"
ng-disabled="!selectedPaymentMethodId.value || !buyRequestInfo || !wallet"
class="button button-standard button-primary"
ng-click="buyConfirm()">
Confirm
</button>
<div ng-show="buySuccess">
<div class="p20">
Bitcoin purchase completed. Coinbase has queued the transfer to your selected wallet
</div>
<button
class="button button-standard button-primary"
ng-click="goBackHome()">
Done
</button>
</div>
</ion-content>
<wallet-selector
wallet-selector-title="walletSelectorTitle"
wallet-selector-wallets="wallets"
wallet-selector-selected-wallet="wallet"
wallet-selector-show="showWallets"
wallet-selector-on-select="onWalletSelect">
</wallet-selector>
</ion-view>

View File

@ -59,16 +59,22 @@
<img src="img/coinbase-logo.png" width="200">
</div>
<div class="m10t size-12 text-center text-gray" ng-if="buyPrice && sellPrice">
{{buyPrice.amount}} {{buyPrice.currency}}
|
{{sellPrice.amount}} {{sellPrice.currency}}
</div>
<div class="list card"
ng-show="accountId">
<a class="item item-icon-right"
href ui-sref="tabs.buyandsell.coinbase.amount({isCoinbase: 'buy'})">
href ui-sref="tabs.buyandsell.coinbase.amount({coinbase: 'buy', currency: currency})">
<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.amount({isCoinbase: 'sell'})">
href ui-sref="tabs.buyandsell.coinbase.amount({coinbase: 'sell', currency: currency})">
<img src="img/sell-bitcoin.svg" alt="buy bitcoin" width="35" class="item-img-sell">
Sell Bitcoin
<i class="icon bp-arrow-right"></i>