From 9abd852f4bdd4f2b5bd6707b165466d57ccb2b9c Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Mon, 8 May 2017 09:23:47 -0300 Subject: [PATCH 1/9] Mercado Libre: First steps --- src/js/controllers/buyMercadoLibre.js | 271 ++ src/js/controllers/mercadoLibre.js | 24 + src/js/controllers/mercadoLibreCards.js | 106 + .../modals/mercadoLibreCardDetails.js | 83 + src/js/routes.js | 51 + src/js/services/mercadoLibreService.js | 196 ++ src/js/services/storageService.js | 12 + src/sass/views/integrations/integrations.scss | 1 + src/sass/views/integrations/mercadolibre.scss | 141 + src/sass/views/tab-home.scss | 4 + www/img/mercado-libre/24px.svg | 24 + www/img/mercado-libre/giftcard-pt.svg | 2311 +++++++++++++++++ www/img/mercado-libre/icon-ml.svg | 1 + www/img/mercado-libre/mlbr.svg | 166 ++ www/views/buyMercadoLibre.html | 85 + www/views/mercadoLibre.html | 56 + www/views/mercadoLibreCards.html | 31 + .../modals/mercadolibre-card-details.html | 85 + 18 files changed, 3648 insertions(+) create mode 100644 src/js/controllers/buyMercadoLibre.js create mode 100644 src/js/controllers/mercadoLibre.js create mode 100644 src/js/controllers/mercadoLibreCards.js create mode 100644 src/js/controllers/modals/mercadoLibreCardDetails.js create mode 100644 src/js/services/mercadoLibreService.js create mode 100644 src/sass/views/integrations/mercadolibre.scss create mode 100644 www/img/mercado-libre/24px.svg create mode 100644 www/img/mercado-libre/giftcard-pt.svg create mode 100644 www/img/mercado-libre/icon-ml.svg create mode 100644 www/img/mercado-libre/mlbr.svg create mode 100644 www/views/buyMercadoLibre.html create mode 100644 www/views/mercadoLibre.html create mode 100644 www/views/mercadoLibreCards.html create mode 100644 www/views/modals/mercadolibre-card-details.html diff --git a/src/js/controllers/buyMercadoLibre.js b/src/js/controllers/buyMercadoLibre.js new file mode 100644 index 000000000..fe8ae653d --- /dev/null +++ b/src/js/controllers/buyMercadoLibre.js @@ -0,0 +1,271 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('buyMercadoLibreController', function($scope, $log, $state, $timeout, $filter, $ionicHistory, lodash, mercadoLibreService, popupService, profileService, ongoingProcess, configService, walletService, payproService, bwcError, externalLinkService, platformInfo) { + + var amount; + var currency; + $scope.isCordova = platformInfo.isCordova; + + $scope.openExternalLink = function(url) { + externalLinkService.open(url); + }; + + var showErrorAndBack = function(msg, err) { + $scope.sendStatus = ''; + err = err && err.errors ? err.errors[0].message : err; + popupService.showAlert(msg, err, function() { + $ionicHistory.goBack(); + }); + }; + + var showError = function(msg, err) { + $scope.sendStatus = ''; + err = err && err.errors ? err.errors[0].message : (err || ''); + popupService.showAlert(msg, err); + }; + + var publishAndSign = function(wallet, txp, onSendStatusChange, cb) { + if (!wallet.canSign() && !wallet.isPrivKeyExternal()) { + var err = 'No signing proposal: No private key'; + $log.info(err); + return cb(err); + } + + walletService.publishAndSign(wallet, txp, function(err, txp) { + if (err) return cb(err); + return cb(null, txp); + }, onSendStatusChange); + }; + + var statusChangeHandler = function(processName, showName, isOn) { + $log.debug('statusChangeHandler: ', processName, showName, isOn); + if (processName == 'buyingGiftCard' && !isOn) { + $scope.sendStatus = 'success'; + $timeout(function() { + $scope.$digest(); + }, 100); + } else if (showName) { + $scope.sendStatus = showName; + } + }; + + var checkTransaction = lodash.throttle(function(count, dataSrc) { + mercadoLibreService.createGiftCard(dataSrc, function(err, giftCard) { +console.log('[buyMercadoLibre.js:53]',giftCard); //TODO + $log.debug("creating gift card " + count); + if (err) { + ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); + giftCard = {}; + giftCard.status = 'FAILURE'; + showError('Error creating gift card', err); + } + + if (giftCard.status == 'PENDING' && count < 3) { + $log.debug("Waiting for payment confirmation"); + checkTransaction(count + 1, dataSrc); + return; + } + + var now = moment().unix() * 1000; + + var newData = giftCard; + newData['invoiceId'] = dataSrc.invoiceId; + newData['accessKey'] = dataSrc.accessKey; + newData['invoiceUrl'] = dataSrc.invoiceUrl; + newData['amount'] = dataSrc.amount; + newData['currency'] = dataSrc.currency; + newData['date'] = dataSrc.invoiceTime || now; + newData['uuid'] = dataSrc.uuid; + + if (newData.status == 'expired') { + mercadoLibreService.savePendingGiftCard(newData, { + remove: true + }, function(err) { + $log.error(err); + ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); + showError('Invoice expired'); + return; + }); + } + + mercadoLibreService.savePendingGiftCard(newData, null, function(err) { + ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); + $log.debug("Saving new gift card with status: " + newData.status); + $scope.mlGiftCard = newData; + }); + }); + }, 8000, { + 'leading': true + }); + + $scope.$on("$ionicView.beforeEnter", function(event, data) { + amount = data.stateParams.amount; + currency = data.stateParams.currency; + + /* TODO + if (amount > 2000 || amount < 50) { + showErrorAndBack('Purchase amount must be a value between 50 and 2000'); + return; + } + */ + + $scope.amountUnitStr = $filter('formatFiatAmount')(amount) + ' ' + currency; + + $scope.network = mercadoLibreService.getNetwork(); + $scope.wallets = profileService.getWallets({ + onlyComplete: true, + network: $scope.network, + hasFunds: true + }); + if (lodash.isEmpty($scope.wallets)) { + showErrorAndBack('No wallets with funds'); + return; + } + $scope.wallet = $scope.wallets[0]; // Default first wallet + }); + + $scope.buyConfirm = function() { + + var message = 'Buy gift card for ' + amount + ' ' + currency; + var okText = 'Confirm'; + var cancelText = 'Cancel'; + popupService.showConfirm(null, message, okText, cancelText, function(ok) { + if (!ok) return; + + var config = configService.getSync(); + var configWallet = config.wallet; + var walletSettings = configWallet.settings; + // Selected WalletID as UUID + var uuid = $scope.wallet.id; + var dataSrc = { + currency: currency, + amount: amount, + uuid: uuid + }; + + ongoingProcess.set('buyingGiftCard', true, statusChangeHandler); + mercadoLibreService.createBitPayInvoice(dataSrc, function(err, dataInvoice) { + if (err) { + ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); + + if (err && err.message && err.message.match(/suspended/i)) { + showError('Service not available', 'Mercadolibre Gift Card Service is not available at this moment. Please try back later.'); + } else { + showError('Could not access Gift Card Service', err); + }; + + return; + } + + var accessKey = dataInvoice ? dataInvoice.accessKey : null; + + if (!accessKey) { + ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); + showError('No access key defined'); + return; + } + + mercadoLibreService.getBitPayInvoice(dataInvoice.invoiceId, function(err, invoice) { + if (err) { + ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); + showError('Error getting BitPay invoice', err); + return; + } + + var payProUrl = (invoice && invoice.paymentUrls) ? invoice.paymentUrls.BIP73 : null; + + if (!payProUrl) { + ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); + showError('Error fetching invoice'); + return; + } + + payproService.getPayProDetails(payProUrl, function(err, payProDetails) { + if (err) { + ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); + showError('Error fetching payment info', bwcError.msg(err)); + return; + } + + var outputs = []; + var toAddress = payProDetails.toAddress; + var amountSat = payProDetails.amount; + var comment = amount + ' ' + currency + ' Mercadolibre Gift Card'; + + outputs.push({ + 'toAddress': toAddress, + 'amount': amountSat, + 'message': comment + }); + + var txp = { + toAddress: toAddress, + amount: amountSat, + outputs: outputs, + message: comment, + payProUrl: payProUrl, + excludeUnconfirmedUtxos: configWallet.spendUnconfirmed ? false : true, + feeLevel: walletSettings.feeLevel || 'normal' + }; + + walletService.createTx($scope.wallet, txp, function(err, ctxp) { + if (err) { + ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); + showError('Could not create transaction', bwcError.msg(err)); + return; + } + publishAndSign($scope.wallet, ctxp, function() {}, function(err, txSent) { + if (err) { + ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); + showError('Could not send transaction', err); + return; + } + $log.debug('Transaction broadcasted. Waiting for confirmation...'); + var invoiceId = JSON.parse(payProDetails.merchant_data).invoiceId; + var dataSrc = { + currency: currency, + amount: amount, + uuid: uuid, + accessKey: accessKey, + invoiceId: invoice.id, + invoiceUrl: payProUrl, + invoiceTime: invoice.invoiceTime + }; + checkTransaction(1, dataSrc); + }); + }); + }, true); // Disable loader + }); + }); + }); + }; + + $scope.showWalletSelector = function() { + $scope.walletSelectorTitle = 'Buy from'; + $scope.showWallets = true; + }; + + $scope.onWalletSelect = function(wallet) { + $scope.wallet = wallet; + }; + + $scope.goBackHome = function() { + $scope.sendStatus = ''; + $ionicHistory.nextViewOptions({ + disableAnimate: true, + historyRoot: true + }); + $ionicHistory.clearHistory(); + var claimCode = $scope.mlGiftCard ? $scope.mlGiftCard.pin : null; + $state.go('tabs.home').then(function() { + $ionicHistory.nextViewOptions({ + disableAnimate: true + }); + $state.transitionTo('tabs.giftcards.mercadoLibre').then(function() { + $state.transitionTo('tabs.giftcards.mercadoLibre.cards', { + cardClaimCode: claimCode + }); + }); + }); + }; +}); diff --git a/src/js/controllers/mercadoLibre.js b/src/js/controllers/mercadoLibre.js new file mode 100644 index 000000000..dfdd81dc1 --- /dev/null +++ b/src/js/controllers/mercadoLibre.js @@ -0,0 +1,24 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('mercadoLibreController', + function($scope, $timeout, $log, mercadoLibreService, externalLinkService, popupService) { + + $scope.openExternalLink = function(url) { + externalLinkService.open(url); + }; + + var init = function() { + mercadoLibreService.getPendingGiftCards(function(err, gcds) { + if (err) $log.error(err); + $scope.giftCards = gcds; + $timeout(function() { + $scope.$digest(); + }); + }); + }; + + $scope.$on("$ionicView.beforeEnter", function(event, data) { + $scope.network = mercadoLibreService.getNetwork(); + init(); + }); + }); diff --git a/src/js/controllers/mercadoLibreCards.js b/src/js/controllers/mercadoLibreCards.js new file mode 100644 index 000000000..7e96a160a --- /dev/null +++ b/src/js/controllers/mercadoLibreCards.js @@ -0,0 +1,106 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('mercadoLibreCardsController', + function($scope, $timeout, $ionicModal, $log, $ionicScrollDelegate, lodash, mercadoLibreService, platformInfo, externalLinkService, popupService, ongoingProcess) { + + $scope.openExternalLink = function(url) { + externalLinkService.open(url); + }; + + var updateGiftCards = function(cb) { + mercadoLibreService.getPendingGiftCards(function(err, gcds) { + if (err) { + popupService.showAlert('Could not get gift cards', err); + if (cb) return cb(); + else return; + } + $scope.giftCards = gcds; + $timeout(function() { + $scope.$digest(); + $ionicScrollDelegate.resize(); + if (cb) return cb(); + }, 100); + }); + }; + + $scope.updatePendingGiftCards = lodash.debounce(function() { + $scope.updatingPending = {}; + updateGiftCards(function() { + var index = 0; + var gcds = $scope.giftCards; + lodash.forEach(gcds, function(dataFromStorage) { + if (dataFromStorage.status == 'PENDING' || dataFromStorage.status == 'invalid') { + $log.debug("Creating / Updating gift card"); + $scope.updatingPending[dataFromStorage.invoiceId] = true; + + mercadoLibreService.createGiftCard(dataFromStorage, function(err, giftCard) { + + $scope.updatingPending[dataFromStorage.invoiceId] = false; + if (err) { + popupService.showAlert('Error creating gift card', err); + return; + } + + if (giftCard.status != 'PENDING') { + var newData = {}; + + lodash.merge(newData, dataFromStorage, giftCard); + + if (newData.status == 'expired') { + mercadoLibreService.savePendingGiftCard(newData, { + remove: true + }, function(err) { + updateGiftCards(); + return; + }); + } + + mercadoLibreService.savePendingGiftCard(newData, null, function(err) { + $log.debug("Saving new gift card"); + updateGiftCards(); + }); + } + }); + } + }); + }); + + }, 1000, { + 'leading': true + }); + + $scope.openCardModal = function(card) { + $scope.card = card; + + $ionicModal.fromTemplateUrl('views/modals/mercadolibre-card-details.html', { + scope: $scope + }).then(function(modal) { + $scope.mercadoLibreCardDetailsModal = modal; + $scope.mercadoLibreCardDetailsModal.show(); + }); + + $scope.$on('modal.hidden', function() { + $scope.updatePendingGiftCards(); + }); + }; + + $scope.$on("$ionicView.beforeEnter", function(event, data) { + $scope.cardClaimCode = data.stateParams.cardClaimCode; + updateGiftCards(function() { + if ($scope.cardClaimCode) { + var card = lodash.find($scope.giftCards, { + claimCode: $scope.cardClaimCode + }); + if (lodash.isEmpty(card)) { + popupService.showAlert(null, 'Card not found'); + return; + } + $scope.openCardModal(card); + } + }); + }); + + $scope.$on("$ionicView.afterEnter", function(event, data) { + $scope.updatePendingGiftCards(); + }); + }); diff --git a/src/js/controllers/modals/mercadoLibreCardDetails.js b/src/js/controllers/modals/mercadoLibreCardDetails.js new file mode 100644 index 000000000..1b1628c9d --- /dev/null +++ b/src/js/controllers/modals/mercadoLibreCardDetails.js @@ -0,0 +1,83 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('mercadoLibreCardDetailsController', function($scope, $log, $timeout, $ionicScrollDelegate, bwcError, mercadoLibreService, lodash, ongoingProcess, popupService, externalLinkService) { + + $scope.cancelGiftCard = function() { + ongoingProcess.set('cancelingGiftCard', true); + mercadoLibreService.cancelGiftCard($scope.card, function(err, data) { + ongoingProcess.set('cancelingGiftCard', false); + if (err) { + popupService.showAlert('Error canceling gift card', bwcError.msg(err)); + return; + } + $scope.card.cardStatus = data.cardStatus; + $timeout(function() { + $ionicScrollDelegate.resize(); + $ionicScrollDelegate.scrollTop(); + }, 10); + mercadoLibreService.savePendingGiftCard($scope.card, null, function(err) { + $scope.refreshGiftCard(); + }); + }); + }; + + $scope.remove = function() { + mercadoLibreService.savePendingGiftCard($scope.card, { + remove: true + }, function(err) { + $scope.cancel(); + }); + }; + + $scope.refreshGiftCard = function() { + ongoingProcess.set('updatingGiftCard', true); + mercadoLibreService.getPendingGiftCards(function(err, gcds) { + if (lodash.isEmpty(gcds)) { + $timeout(function() { + ongoingProcess.set('updatingGiftCard', false); + }, 1000); + } + if (err) { + popupService.showAlert('Error', err); + return; + } + var index = 0; + lodash.forEach(gcds, function(dataFromStorage) { + if (++index == Object.keys(gcds).length) { + $timeout(function() { + ongoingProcess.set('updatingGiftCard', false); + }, 1000); + } + if (dataFromStorage.status == 'PENDING' && dataFromStorage.invoiceId == $scope.card.invoiceId) { + $log.debug("creating gift card"); + mercadoLibreService.createGiftCard(dataFromStorage, function(err, giftCard) { + if (err) { + popupService.showAlert('Error', bwcError.msg(err)); + return; + } + if (!lodash.isEmpty(giftCard)) { + var newData = {}; + lodash.merge(newData, dataFromStorage, giftCard); + mercadoLibreService.savePendingGiftCard(newData, null, function(err) { + $log.debug("Saving new gift card"); + $scope.card = newData; + $timeout(function() { + $scope.$digest(); + }); + }); + } else $log.debug("pending gift card not available yet"); + }); + } + }); + }); + }; + + $scope.cancel = function() { + $scope.mercadoLibreCardDetailsModal.hide(); + }; + + $scope.openExternalLink = function(url) { + externalLinkService.open(url); + }; + +}); diff --git a/src/js/routes.js b/src/js/routes.js index 4904be9f6..e54d11e9a 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -1026,6 +1026,57 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr abstract: true }) + /* + * + * Mercado Libre Gift Card + * + */ + + .state('tabs.giftcards.mercadoLibre', { + url: '/mercadoLibre', + views: { + 'tab-home@tabs': { + controller: 'mercadoLibreController', + templateUrl: 'views/mercadoLibre.html' + } + } + }) + .state('tabs.giftcards.mercadoLibre.cards', { + url: '/cards', + views: { + 'tab-home@tabs': { + controller: 'mercadoLibreCardsController', + templateUrl: 'views/mercadoLibreCards.html' + } + }, + params: { + cardClaimCode: null + } + }) + .state('tabs.giftcards.mercadoLibre.amount', { + url: '/amount', + views: { + 'tab-home@tabs': { + controller: 'amountController', + templateUrl: 'views/amount.html' + } + }, + params: { + nextStep: 'tabs.giftcards.mercadoLibre.buy', + currency: 'BRL', + forceCurrency: true + } + }) + .state('tabs.giftcards.mercadoLibre.buy', { + url: '/buy/:amount/:currency', + views: { + 'tab-home@tabs': { + controller: 'buyMercadoLibreController', + templateUrl: 'views/buyMercadoLibre.html' + } + } + }) + /* * * Amazon.com Gift Card diff --git a/src/js/services/mercadoLibreService.js b/src/js/services/mercadoLibreService.js new file mode 100644 index 000000000..07c86b5ad --- /dev/null +++ b/src/js/services/mercadoLibreService.js @@ -0,0 +1,196 @@ +'use strict'; +angular.module('copayApp.services').factory('mercadoLibreService', function($http, $log, lodash, moment, storageService, configService, platformInfo, nextStepsService, homeIntegrationsService) { + var root = {}; + var credentials = {}; + + // Not used yet + var availableCountries = [{ + 'country': 'Brazil', + 'currency': 'BRL', + 'name': 'Mercado Livre', + 'url': 'https://www.mercadolivre.com.br' + }]; + + /* + * Development: 'testnet' + * Production: 'livenet' + */ + //credentials.NETWORK = 'livenet'; + credentials.NETWORK = 'testnet'; + + if (credentials.NETWORK == 'testnet') { + credentials.BITPAY_API_URL = "https://test.bitpay.com"; + } else { + credentials.BITPAY_API_URL = "https://bitpay.com"; + }; + + var homeItem = { + name: 'mercadoLibre', + title: 'Mercado Libre', + icon: 'icon-ml', + sref: 'tabs.giftcards.mercadoLibre', + }; + + var nextStepItem = { + name: 'mercadoLibre', + title: 'Buy Mercado Libre Gift Cards', + icon: 'icon-ml', + sref: 'tabs.giftcards.mercadoLibre', + }; + + var _getBitPay = function(endpoint) { + return { + method: 'GET', + url: credentials.BITPAY_API_URL + endpoint, + headers: { + 'content-type': 'application/json' + } + }; + }; + + var _postBitPay = function(endpoint, data) { + return { + method: 'POST', + url: credentials.BITPAY_API_URL + endpoint, + headers: { + 'content-type': 'application/json' + }, + data: data + }; + }; + + root.getNetwork = function() { + return credentials.NETWORK; + }; + + root.savePendingGiftCard = function(gc, opts, cb) { + var network = root.getNetwork(); + storageService.getMercadoLibreGiftCards(network, function(err, oldGiftCards) { + if (lodash.isString(oldGiftCards)) { + oldGiftCards = JSON.parse(oldGiftCards); + } + if (lodash.isString(gc)) { + gc = JSON.parse(gc); + } + var inv = oldGiftCards || {}; + inv[gc.invoiceId] = gc; + if (opts && (opts.error || opts.status)) { + inv[gc.invoiceId] = lodash.assign(inv[gc.invoiceId], opts); + } + if (opts && opts.remove) { + delete(inv[gc.invoiceId]); + } + + inv = JSON.stringify(inv); + + + storageService.setMercadoLibreGiftCards(network, inv, function(err) { + + homeIntegrationsService.register(homeItem); + nextStepsService.unregister(nextStepItem.name); + return cb(err); + }); + }); + }; + + root.getPendingGiftCards = function(cb) { + var network = root.getNetwork(); + storageService.getMercadoLibreGiftCards(network, function(err, giftCards) { + var _gcds = giftCards ? JSON.parse(giftCards) : null; + return cb(err, _gcds); + }); + }; + + root.createBitPayInvoice = function(data, cb) { + + // TODO + var dataSrc = { + currency: 'USD' || data.currency, + amount: data.amount, + clientId: data.uuid + }; +console.log('[mercadoLibreService.js:106]',dataSrc); //TODO + + $http(_postBitPay('/amazon-gift/pay', dataSrc)).then(function(data) { + $log.info('BitPay Create Invoice: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('BitPay Create Invoice: ERROR ' + data.data.message); + return cb(data.data); + }); + }; + + root.getBitPayInvoice = function(id, cb) { + $http(_getBitPay('/invoices/' + id)).then(function(data) { + $log.info('BitPay Get Invoice: SUCCESS'); + return cb(null, data.data.data); + }, function(data) { + $log.error('BitPay Get Invoice: ERROR ' + data.data.error); + return cb(data.data.error); + }); + }; + + root.createGiftCard = function(data, cb) { +console.log('[mercadoLibreService.js:132]',data); //TODO + + return cb(null, { + "id": "f2bd6204fc49661a56057d33e37e32f2518ae92eea2f5457c379434712e35537", + "currency_id": data.currency, + "external_reference": "external_id_123456", + "initial_amount": data.amount, + "balance": 59.99, + "status": "active", + "date_creation": "2017­03­14T10:39:14.826­03:00", + "date_activation": "2017­03­14T10:39:15.316­03:00", + "date_expiration": "2018­09­14T10:39:15.316­03:00", + "date_last_updated": "2017­03­14T10:39:15.321­03:00", + "pin": "UYNHSIONUY" + }); + + var dataSrc = { + "clientId": data.uuid, + "invoiceId": data.invoiceId, + "accessKey": data.accessKey + }; + + $http(_postBitPay('/mercado-libre-gift/redeem', dataSrc)).then(function(data) { + var status = data.data.status == 'new' ? 'PENDING' : (data.data.status == 'paid') ? 'PENDING' : data.data.status; + data.data.status = status; + $log.info('Mercado Libre Gift Card Create/Update: ' + status); + return cb(null, data.data); + }, function(data) { + $log.error('Mercado Libre Gift Card Create/Update: ' + data.data.message); + return cb(data.data); + }); + }; + + root.cancelGiftCard = function(data, cb) { + + var dataSrc = { + "clientId": data.uuid, + "invoiceId": data.invoiceId, + "accessKey": data.accessKey + }; + + $http(_postBitPay('/mercado-libre-gift/cancel', dataSrc)).then(function(data) { + $log.info('Mercado Libre Gift Card Cancel: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('Mercado Libre Gift Card Cancel: ' + data.data.message); + return cb(data.data); + }); + }; + + var register = function() { + storageService.getMercadoLibreGiftCards(root.getNetwork(), function(err, giftCards) { + if (giftCards) { + homeIntegrationsService.register(homeItem); + } else { + nextStepsService.register(nextStepItem); + } + }); + }; + + register(); + return root; +}); diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index 5391569fd..4cd2af62e 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -610,5 +610,17 @@ angular.module('copayApp.services') storage.remove('txConfirmNotif-' + txid, cb); }; + root.setMercadoLibreGiftCards = function(network, gcs, cb) { + storage.set('mercadoLibreGiftCards-' + network, gcs, cb); + }; + + root.getMercadoLibreGiftCards = function(network, cb) { + storage.get('mercadoLibreGiftCards-' + network, cb); + }; + + root.removeMercadoLibreGiftCards = function(network, cb) { + storage.remove('MercadoLibreGiftCards-' + network, cb); + }; + return root; }); diff --git a/src/sass/views/integrations/integrations.scss b/src/sass/views/integrations/integrations.scss index 22f9cbc0e..9632da354 100644 --- a/src/sass/views/integrations/integrations.scss +++ b/src/sass/views/integrations/integrations.scss @@ -1,6 +1,7 @@ @import "coinbase"; @import "glidera"; @import "amazon"; +@import "mercadolibre"; #coinbase, #glidera { .button-small { diff --git a/src/sass/views/integrations/mercadolibre.scss b/src/sass/views/integrations/mercadolibre.scss new file mode 100644 index 000000000..ea5ea2f04 --- /dev/null +++ b/src/sass/views/integrations/mercadolibre.scss @@ -0,0 +1,141 @@ +#mercadolibre { + $item-lateral-padding: 20px; + $item-vertical-padding: 10px; + $item-border-color: #EFEFEF; + $item-label-color: #6C6C6E; + @extend .deflash-blue; + .icon-amazon { + background-image: url("../img/mercado-libre/icon-ml.svg"); + } + .spinner svg { + stroke: black; + fill: black; + } + + .add-bottom-for-cta { + bottom: 92px; + } + .head { + padding: 30px $item-lateral-padding 4rem; + border-top: 0; + + .sending-label { + display: flex; + font-size: 18px; + align-items: center; + margin-bottom: 1.8rem; + + img { + margin-right: 1rem; + height: 35px; + width: 35px; + } + + span { + text-transform: capitalize; + } + + .big-icon-svg { + margin-right: 0.6rem; + } + + } + .amount-label{ + line-height: 30px; + .amount{ + font-size: 38px; + margin-bottom: .5rem; + + > .unit { + font-family: "Roboto-Light"; + } + } + .alternative { + font-size: 12px; + font-family: "Roboto-Light"; + color: #9B9B9B; + } + } + } + .item { + border-color: $item-border-color; + } + .info { + .badge { + border-radius: 0; + padding: .5rem; + } + .item { + color: #4A4A4A; + padding-top: $item-vertical-padding; + padding-bottom: $item-vertical-padding; + padding-left: $item-lateral-padding; + + &:not(.item-icon-right) { + padding-right: $item-lateral-padding; + } + + .label { + font-size: 14px; + color: $item-label-color; + margin-bottom: 8px; + } + + .capitalized { + text-transform: capitalize; + } + + .wallet .big-icon-svg > .bg { + height: 24px; + width: 24px; + padding: 2px; + box-shadow: none; + vertical-align: middle; + } + + .total-amount { + font-weight: bold; + } + + &.single-line { + display: flex; + align-items: center; + padding-top: 17px; + padding-bottom: 17px; + + .label { + margin: 0; + flex-grow: 1; + } + } + } + .item-divider { + padding-top: 1.2rem; + color: $item-label-color; + font-size: 15px; + } + .wallet { + display: flex; + align-items: center; + padding: .2rem 0; + margin-bottom: 5px; + + ~ .bp-arrow-right { + top: 14px; + } + + > i { + padding: 0; + position: static; + + > img { + height: 24px; + width: 24px; + padding: 2px; + margin-right: .7rem; + box-shadow: none; + } + } + } + } +} diff --git a/src/sass/views/tab-home.scss b/src/sass/views/tab-home.scss index 3886ba7ee..99dc0f4da 100644 --- a/src/sass/views/tab-home.scss +++ b/src/sass/views/tab-home.scss @@ -17,6 +17,10 @@ .icon-amazon { background-image: url("../img/icon-amazon.svg"); } + .icon-ml { + background-image: url("../img/mercado-libre/icon-ml.svg"); + height: 27px; + } .bg { &.wallet { padding: .25rem diff --git a/www/img/mercado-libre/24px.svg b/www/img/mercado-libre/24px.svg new file mode 100644 index 000000000..7a667609c --- /dev/null +++ b/www/img/mercado-libre/24px.svg @@ -0,0 +1,24 @@ + + + + 24px + Created with Sketch. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/img/mercado-libre/giftcard-pt.svg b/www/img/mercado-libre/giftcard-pt.svg new file mode 100644 index 000000000..f980b61f6 --- /dev/null +++ b/www/img/mercado-libre/giftcard-pt.svg @@ -0,0 +1,2311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/www/img/mercado-libre/icon-ml.svg b/www/img/mercado-libre/icon-ml.svg new file mode 100644 index 000000000..01b1fe7a7 --- /dev/null +++ b/www/img/mercado-libre/icon-ml.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/www/img/mercado-libre/mlbr.svg b/www/img/mercado-libre/mlbr.svg new file mode 100644 index 000000000..031c72393 --- /dev/null +++ b/www/img/mercado-libre/mlbr.svg @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/www/views/buyMercadoLibre.html b/www/views/buyMercadoLibre.html new file mode 100644 index 000000000..711792b13 --- /dev/null +++ b/www/views/buyMercadoLibre.html @@ -0,0 +1,85 @@ + + + + + Buy + + + + +
+ +
+
+ +
+
+ Mercado Libre Gift Card +
+
+
{{amountUnitStr}}
+
+
+ +
+
+
From
+
+ + + + {{wallet ? wallet.name : '...'}} +
+ +
+
+
+ +
+ + + Confirm purchase + + + Slide to buy + + + + Your purchase could not be completed + + + Your purchase was added to the list of pending + + + Bought {{mlGiftCard.amount}} {{mlGiftCard.currency}} + +
+ Gift card generated and ready to use. +
+
+ + + +
diff --git a/www/views/mercadoLibre.html b/www/views/mercadoLibre.html new file mode 100644 index 000000000..bc9d06644 --- /dev/null +++ b/www/views/mercadoLibre.html @@ -0,0 +1,56 @@ + + + + + Mercado Libre Gift Cards + + + +
+ Sandbox version. Only for testing purpose. +
+
+ +
+ Gift Cards are only redeemable on Mercado Livre (Brazil) +
+
+ + +
+
+
+ +
+ Sandbox version. Only for testing purpose. +
+ +
+ Mercado Libre +
+ Only redeemable on Mercado Livre (Brazil) +
+
+ + +
+
diff --git a/www/views/mercadoLibreCards.html b/www/views/mercadoLibreCards.html new file mode 100644 index 000000000..8488e318c --- /dev/null +++ b/www/views/mercadoLibreCards.html @@ -0,0 +1,31 @@ + + + + + Your cards + + + +
+
+ {{id}} + + +

+ {{item.amount | currency : '$ ' : 2}} {{item.currency}} +

+

+ Error + Expired + Still waiting confirmation
(Use higher fees setting to faster delivery)
+ Pending to confirmation + Canceled + {{item.date | amTimeAgo}} +

+
+
+
+
diff --git a/www/views/modals/mercadolibre-card-details.html b/www/views/modals/mercadolibre-card-details.html new file mode 100644 index 000000000..3605c13c8 --- /dev/null +++ b/www/views/modals/mercadolibre-card-details.html @@ -0,0 +1,85 @@ + + + +

Details

+
+ + + +
+ Mercado Libre Gift Card + +
+ Gift Card Amount: + + {{card.amount | currency : '$ ' : 2}} + +
+ + +
+ Created + {{card.date | amTimeAgo}} +
+ + +
+
+ Claim code: {{card.pin}} +
+
+ +
+
+
+ Status: + + CANCELED + +
+
+
+
+
+ Status: + + PENDING + + + STILL PENDING + + + + FAILURE + + + EXPIRED + +
+
+ +
+ +
+ There was a failure to the create gift card. Please, contact BitPay support. +
+ + + +
+
From ef650aff863522755d6d4c11a5d091e1044e4251 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Tue, 4 Jul 2017 10:44:10 -0300 Subject: [PATCH 2/9] Fix styling --- src/js/routes.js | 2 +- src/sass/views/integrations/mercadolibre.scss | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/js/routes.js b/src/js/routes.js index e54d11e9a..63ec93c29 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -1186,7 +1186,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }); }) - .run(function($rootScope, $state, $location, $log, $timeout, startupService, ionicToast, fingerprintService, $ionicHistory, $ionicPlatform, $window, appConfigService, lodash, platformInfo, profileService, uxLanguage, gettextCatalog, openURLService, storageService, scannerService, configService, emailService, /* plugins START HERE => */ coinbaseService, glideraService, amazonService, bitpayCardService, applicationService) { + .run(function($rootScope, $state, $location, $log, $timeout, startupService, ionicToast, fingerprintService, $ionicHistory, $ionicPlatform, $window, appConfigService, lodash, platformInfo, profileService, uxLanguage, gettextCatalog, openURLService, storageService, scannerService, configService, emailService, /* plugins START HERE => */ coinbaseService, glideraService, amazonService, bitpayCardService, applicationService, mercadoLibreService) { uxLanguage.init(); diff --git a/src/sass/views/integrations/mercadolibre.scss b/src/sass/views/integrations/mercadolibre.scss index ea5ea2f04..63b0032f5 100644 --- a/src/sass/views/integrations/mercadolibre.scss +++ b/src/sass/views/integrations/mercadolibre.scss @@ -11,7 +11,7 @@ stroke: black; fill: black; } - + .add-bottom-for-cta { bottom: 92px; } @@ -36,9 +36,14 @@ } .big-icon-svg { + padding: 0 7px 0 0; margin-right: 0.6rem; } + .big-icon-svg > .bg { + height: 27px; + } + } .amount-label{ line-height: 30px; @@ -80,7 +85,7 @@ color: $item-label-color; margin-bottom: 8px; } - + .capitalized { text-transform: capitalize; } From 3e789714464080300d88cd93087fccbc3509237f Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Wed, 5 Jul 2017 18:42:35 -0300 Subject: [PATCH 3/9] Buy gift card. Limits --- src/js/controllers/buyMercadoLibre.js | 311 ++++++++++-------- src/js/controllers/mercadoLibreCards.js | 15 +- src/js/routes.js | 2 +- src/js/services/mercadoLibreService.js | 30 +- src/js/services/profileService.js | 2 + www/views/buyMercadoLibre.html | 35 +- .../modals/mercadolibre-card-details.html | 17 +- 7 files changed, 220 insertions(+), 192 deletions(-) diff --git a/src/js/controllers/buyMercadoLibre.js b/src/js/controllers/buyMercadoLibre.js index fe8ae653d..321d77d8a 100644 --- a/src/js/controllers/buyMercadoLibre.js +++ b/src/js/controllers/buyMercadoLibre.js @@ -1,27 +1,36 @@ 'use strict'; -angular.module('copayApp.controllers').controller('buyMercadoLibreController', function($scope, $log, $state, $timeout, $filter, $ionicHistory, lodash, mercadoLibreService, popupService, profileService, ongoingProcess, configService, walletService, payproService, bwcError, externalLinkService, platformInfo) { +angular.module('copayApp.controllers').controller('buyMercadoLibreController', function($scope, $log, $state, $timeout, $filter, $ionicHistory, $ionicConfig, lodash, mercadoLibreService, popupService, profileService, ongoingProcess, configService, walletService, payproService, bwcError, externalLinkService, platformInfo, txFormatService, gettextCatalog) { var amount; var currency; + var createdTx; + var message; + var invoiceId; + var configWallet = configService.getSync().wallet; $scope.isCordova = platformInfo.isCordova; $scope.openExternalLink = function(url) { externalLinkService.open(url); }; - var showErrorAndBack = function(msg, err) { + var showErrorAndBack = function(title, msg) { + title = title || gettextCatalog.getString('Error'); $scope.sendStatus = ''; - err = err && err.errors ? err.errors[0].message : err; - popupService.showAlert(msg, err, function() { + $log.error(msg); + msg = (msg && msg.errors) ? msg.errors[0].message : msg; + popupService.showAlert(title, msg, function() { $ionicHistory.goBack(); }); }; - var showError = function(msg, err) { + var showError = function(title, msg, cb) { + cb = cb || function() {}; + title = title || gettextCatalog.getString('Error'); $scope.sendStatus = ''; - err = err && err.errors ? err.errors[0].message : (err || ''); - popupService.showAlert(msg, err); + $log.error(msg); + msg = (msg && msg.errors) ? msg.errors[0].message : msg; + popupService.showAlert(title, msg, cb); }; var publishAndSign = function(wallet, txp, onSendStatusChange, cb) { @@ -49,15 +58,95 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f } }; + var createInvoice = function(data, cb) { + mercadoLibreService.createBitPayInvoice(data, function(err, dataInvoice) { + if (err) { + var err_title = gettextCatalog.getString('Error creating the invoice'); + var err_msg; + if (err && err.message && err.message.match(/suspended/i)) { + err_title = gettextCatalog.getString('Service not available'); + err_msg = gettextCatalog.getString('Mercadolibre Gift Card Service is not available at this moment. Please try back later.'); + } else if (err && err.message) { + err_msg = err.message; + } else { + err_msg = gettextCatalog.getString('Could not access Gift Card Service'); + }; + + return cb({ + title: err_title, + message: err_msg + }); + } + + var accessKey = dataInvoice ? dataInvoice.accessKey : null; + + if (!accessKey) { + return cb({ + message: gettextCatalog.getString('No access key defined') + }); + } + + mercadoLibreService.getBitPayInvoice(dataInvoice.invoiceId, function(err, invoice) { + if (err) { + return cb({ + message: gettextCatalog.getString('Could not get the invoice') + }); + } + + return cb(null, invoice, accessKey); + }); + }); + }; + + var createTx = function(wallet, invoice, message, cb) { + var payProUrl = (invoice && invoice.paymentUrls) ? invoice.paymentUrls.BIP73 : null; + + if (!payProUrl) { + return cb({ + title: gettextCatalog.getString('Error in Payment Protocol'), + message: gettextCatalog.getString('Invalid URL') + }); + } + + var outputs = []; + var toAddress = invoice.bitcoinAddress; + var amountSat = parseInt(invoice.btcDue * 100000000); // BTC to Satoshi + + outputs.push({ + 'toAddress': toAddress, + 'amount': amountSat, + 'message': message + }); + + var txp = { + toAddress: toAddress, + amount: amountSat, + outputs: outputs, + message: message, + payProUrl: payProUrl, + excludeUnconfirmedUtxos: configWallet.spendUnconfirmed ? false : true, + feeLevel: configWallet.settings.feeLevel || 'normal' + }; + + walletService.createTx(wallet, txp, function(err, ctxp) { + if (err) { + return cb({ + title: gettextCatalog.getString('Could not create transaction'), + message: bwcError.msg(err) + }); + } + return cb(null, ctxp); + }); + }; + var checkTransaction = lodash.throttle(function(count, dataSrc) { mercadoLibreService.createGiftCard(dataSrc, function(err, giftCard) { -console.log('[buyMercadoLibre.js:53]',giftCard); //TODO $log.debug("creating gift card " + count); - if (err) { + if (err || !giftCard.pin) { + $scope.sendStatus = ''; ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); giftCard = {}; giftCard.status = 'FAILURE'; - showError('Error creating gift card', err); } if (giftCard.status == 'PENDING' && count < 3) { @@ -77,17 +166,6 @@ console.log('[buyMercadoLibre.js:53]',giftCard); //TODO newData['date'] = dataSrc.invoiceTime || now; newData['uuid'] = dataSrc.uuid; - if (newData.status == 'expired') { - mercadoLibreService.savePendingGiftCard(newData, { - remove: true - }, function(err) { - $log.error(err); - ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); - showError('Invoice expired'); - return; - }); - } - mercadoLibreService.savePendingGiftCard(newData, null, function(err) { ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); $log.debug("Saving new gift card with status: " + newData.status); @@ -98,144 +176,106 @@ console.log('[buyMercadoLibre.js:53]',giftCard); //TODO 'leading': true }); + var initialize = function(wallet, parsedAmount) { + $scope.amountUnitStr = parsedAmount.amountUnitStr; + var dataSrc = { + amount: parsedAmount.amount, + currency: parsedAmount.currency, + uuid: wallet.id + }; + ongoingProcess.set('loadingTxInfo', true); + createInvoice(dataSrc, function(err, invoice, accessKey) { + if (err) { + ongoingProcess.set('loadingTxInfo', false); + showErrorAndBack(err.title, err.message); + return; + } + + message = gettextCatalog.getString("Mercado Libre Gift Card {{amountStr}}", { + amountStr: $scope.amountUnitStr + }); + + createTx(wallet, invoice, message, function(err, ctxp) { + ongoingProcess.set('loadingTxInfo', false); + if (err) { + // Clear variables + createdTx = message = $scope.totalFeeStr = $scope.totalAmountStr = $scope.wallet = null; + showError(err.title, err.message); + return; + } + + // Save in memory + createdTx = ctxp; + invoiceId = invoice.id; + + createdTx['giftData'] = { + currency: dataSrc.currency, + amount: dataSrc.amount, + uuid: dataSrc.uuid, + accessKey: accessKey, + invoiceId: invoice.id, + invoiceUrl: invoice.url, + invoiceTime: invoice.invoiceTime + }; + $scope.totalFeeStr = txFormatService.formatAmountStr(ctxp.fee); + $scope.totalAmountStr = txFormatService.formatAmountStr(ctxp.amount + ctxp.fee); + }); + }); + }; + + $scope.$on("$ionicView.beforeLeave", function(event, data) { + $ionicConfig.views.swipeBackEnabled(true); + }); + + $scope.$on("$ionicView.enter", function(event, data) { + $ionicConfig.views.swipeBackEnabled(false); + }); + $scope.$on("$ionicView.beforeEnter", function(event, data) { amount = data.stateParams.amount; currency = data.stateParams.currency; - /* TODO if (amount > 2000 || amount < 50) { - showErrorAndBack('Purchase amount must be a value between 50 and 2000'); + showErrorAndBack(null, gettextCatalog.getString('Purchase amount must be a value between 50 and 2000')); return; } - */ - - $scope.amountUnitStr = $filter('formatFiatAmount')(amount) + ' ' + currency; $scope.network = mercadoLibreService.getNetwork(); $scope.wallets = profileService.getWallets({ onlyComplete: true, - network: $scope.network, - hasFunds: true + network: $scope.network }); if (lodash.isEmpty($scope.wallets)) { - showErrorAndBack('No wallets with funds'); + showErrorAndBack(null, gettextCatalog.getString('No wallets available')); return; } - $scope.wallet = $scope.wallets[0]; // Default first wallet + $scope.onWalletSelect($scope.wallets[0]); // Default first wallet }); $scope.buyConfirm = function() { - var message = 'Buy gift card for ' + amount + ' ' + currency; - var okText = 'Confirm'; - var cancelText = 'Cancel'; - popupService.showConfirm(null, message, okText, cancelText, function(ok) { - if (!ok) return; + if (!createdTx) { + showError(null, gettextCatalog.getString('Transaction has not been created')); + return; + } - var config = configService.getSync(); - var configWallet = config.wallet; - var walletSettings = configWallet.settings; - // Selected WalletID as UUID - var uuid = $scope.wallet.id; - var dataSrc = { - currency: currency, - amount: amount, - uuid: uuid - }; + var title = gettextCatalog.getString('Confirm'); + var okText = gettextCatalog.getString('Ok'); + var cancelText = gettextCatalog.getString('Cancel'); + popupService.showConfirm(title, message, okText, cancelText, function(ok) { + if (!ok) { + $scope.sendStatus = ''; + return; + } ongoingProcess.set('buyingGiftCard', true, statusChangeHandler); - mercadoLibreService.createBitPayInvoice(dataSrc, function(err, dataInvoice) { + publishAndSign($scope.wallet, createdTx, function() {}, function(err, txSent) { if (err) { ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); - - if (err && err.message && err.message.match(/suspended/i)) { - showError('Service not available', 'Mercadolibre Gift Card Service is not available at this moment. Please try back later.'); - } else { - showError('Could not access Gift Card Service', err); - }; - + showError(gettextCatalog.getString('Could not send transaction'), err); return; } - - var accessKey = dataInvoice ? dataInvoice.accessKey : null; - - if (!accessKey) { - ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); - showError('No access key defined'); - return; - } - - mercadoLibreService.getBitPayInvoice(dataInvoice.invoiceId, function(err, invoice) { - if (err) { - ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); - showError('Error getting BitPay invoice', err); - return; - } - - var payProUrl = (invoice && invoice.paymentUrls) ? invoice.paymentUrls.BIP73 : null; - - if (!payProUrl) { - ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); - showError('Error fetching invoice'); - return; - } - - payproService.getPayProDetails(payProUrl, function(err, payProDetails) { - if (err) { - ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); - showError('Error fetching payment info', bwcError.msg(err)); - return; - } - - var outputs = []; - var toAddress = payProDetails.toAddress; - var amountSat = payProDetails.amount; - var comment = amount + ' ' + currency + ' Mercadolibre Gift Card'; - - outputs.push({ - 'toAddress': toAddress, - 'amount': amountSat, - 'message': comment - }); - - var txp = { - toAddress: toAddress, - amount: amountSat, - outputs: outputs, - message: comment, - payProUrl: payProUrl, - excludeUnconfirmedUtxos: configWallet.spendUnconfirmed ? false : true, - feeLevel: walletSettings.feeLevel || 'normal' - }; - - walletService.createTx($scope.wallet, txp, function(err, ctxp) { - if (err) { - ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); - showError('Could not create transaction', bwcError.msg(err)); - return; - } - publishAndSign($scope.wallet, ctxp, function() {}, function(err, txSent) { - if (err) { - ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); - showError('Could not send transaction', err); - return; - } - $log.debug('Transaction broadcasted. Waiting for confirmation...'); - var invoiceId = JSON.parse(payProDetails.merchant_data).invoiceId; - var dataSrc = { - currency: currency, - amount: amount, - uuid: uuid, - accessKey: accessKey, - invoiceId: invoice.id, - invoiceUrl: payProUrl, - invoiceTime: invoice.invoiceTime - }; - checkTransaction(1, dataSrc); - }); - }); - }, true); // Disable loader - }); + checkTransaction(1, createdTx.giftData); }); }); }; @@ -247,6 +287,8 @@ console.log('[buyMercadoLibre.js:53]',giftCard); //TODO $scope.onWalletSelect = function(wallet) { $scope.wallet = wallet; + var parsedAmount = txFormatService.parseAmount(amount, currency); + initialize(wallet, parsedAmount); }; $scope.goBackHome = function() { @@ -256,14 +298,13 @@ console.log('[buyMercadoLibre.js:53]',giftCard); //TODO historyRoot: true }); $ionicHistory.clearHistory(); - var claimCode = $scope.mlGiftCard ? $scope.mlGiftCard.pin : null; $state.go('tabs.home').then(function() { $ionicHistory.nextViewOptions({ disableAnimate: true }); $state.transitionTo('tabs.giftcards.mercadoLibre').then(function() { $state.transitionTo('tabs.giftcards.mercadoLibre.cards', { - cardClaimCode: claimCode + invoiceId: invoiceId }); }); }); diff --git a/src/js/controllers/mercadoLibreCards.js b/src/js/controllers/mercadoLibreCards.js index 7e96a160a..438631c06 100644 --- a/src/js/controllers/mercadoLibreCards.js +++ b/src/js/controllers/mercadoLibreCards.js @@ -46,15 +46,6 @@ angular.module('copayApp.controllers').controller('mercadoLibreCardsController', lodash.merge(newData, dataFromStorage, giftCard); - if (newData.status == 'expired') { - mercadoLibreService.savePendingGiftCard(newData, { - remove: true - }, function(err) { - updateGiftCards(); - return; - }); - } - mercadoLibreService.savePendingGiftCard(newData, null, function(err) { $log.debug("Saving new gift card"); updateGiftCards(); @@ -85,11 +76,11 @@ angular.module('copayApp.controllers').controller('mercadoLibreCardsController', }; $scope.$on("$ionicView.beforeEnter", function(event, data) { - $scope.cardClaimCode = data.stateParams.cardClaimCode; + $scope.invoiceId = data.stateParams.invoiceId; updateGiftCards(function() { - if ($scope.cardClaimCode) { + if ($scope.invoiceId) { var card = lodash.find($scope.giftCards, { - claimCode: $scope.cardClaimCode + invoiceId: $scope.invoiceId }); if (lodash.isEmpty(card)) { popupService.showAlert(null, 'Card not found'); diff --git a/src/js/routes.js b/src/js/routes.js index 63ec93c29..59cfad4fa 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -1050,7 +1050,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }, params: { - cardClaimCode: null + invoiceId: null } }) .state('tabs.giftcards.mercadoLibre.amount', { diff --git a/src/js/services/mercadoLibreService.js b/src/js/services/mercadoLibreService.js index 07c86b5ad..f33991cc7 100644 --- a/src/js/services/mercadoLibreService.js +++ b/src/js/services/mercadoLibreService.js @@ -19,7 +19,7 @@ angular.module('copayApp.services').factory('mercadoLibreService', function($htt credentials.NETWORK = 'testnet'; if (credentials.NETWORK == 'testnet') { - credentials.BITPAY_API_URL = "https://test.bitpay.com"; + credentials.BITPAY_API_URL = "https://gustavo.bp:8088"; } else { credentials.BITPAY_API_URL = "https://bitpay.com"; }; @@ -102,16 +102,13 @@ angular.module('copayApp.services').factory('mercadoLibreService', function($htt }; root.createBitPayInvoice = function(data, cb) { - - // TODO var dataSrc = { - currency: 'USD' || data.currency, + currency: data.currency, amount: data.amount, clientId: data.uuid }; -console.log('[mercadoLibreService.js:106]',dataSrc); //TODO - $http(_postBitPay('/amazon-gift/pay', dataSrc)).then(function(data) { + $http(_postBitPay('/mercado-libre-gift/pay', dataSrc)).then(function(data) { $log.info('BitPay Create Invoice: SUCCESS'); return cb(null, data.data); }, function(data) { @@ -131,22 +128,6 @@ console.log('[mercadoLibreService.js:106]',dataSrc); //TODO }; root.createGiftCard = function(data, cb) { -console.log('[mercadoLibreService.js:132]',data); //TODO - - return cb(null, { - "id": "f2bd6204fc49661a56057d33e37e32f2518ae92eea2f5457c379434712e35537", - "currency_id": data.currency, - "external_reference": "external_id_123456", - "initial_amount": data.amount, - "balance": 59.99, - "status": "active", - "date_creation": "2017­03­14T10:39:14.826­03:00", - "date_activation": "2017­03­14T10:39:15.316­03:00", - "date_expiration": "2018­09­14T10:39:15.316­03:00", - "date_last_updated": "2017­03­14T10:39:15.321­03:00", - "pin": "UYNHSIONUY" - }); - var dataSrc = { "clientId": data.uuid, "invoiceId": data.invoiceId, @@ -164,6 +145,10 @@ console.log('[mercadoLibreService.js:132]',data); //TODO }); }; + /* + * Disabled for now * + */ + /* root.cancelGiftCard = function(data, cb) { var dataSrc = { @@ -180,6 +165,7 @@ console.log('[mercadoLibreService.js:132]',data); //TODO return cb(data.data); }); }; + */ var register = function() { storageService.getMercadoLibreGiftCards(root.getNetwork(), function(err, giftCards) { diff --git a/src/js/services/profileService.js b/src/js/services/profileService.js index d5598a5b8..40c2918ac 100644 --- a/src/js/services/profileService.js +++ b/src/js/services/profileService.js @@ -767,12 +767,14 @@ angular.module('copayApp.services') if (opts.hasFunds) { ret = lodash.filter(ret, function(w) { + if (!w.status) return; return (w.status.availableBalanceSat > 0); }); } if (opts.minAmount) { ret = lodash.filter(ret, function(w) { + if (!w.status) return; return (w.status.availableBalanceSat > opts.minAmount); }); } diff --git a/www/views/buyMercadoLibre.html b/www/views/buyMercadoLibre.html index 711792b13..eb15ae12a 100644 --- a/www/views/buyMercadoLibre.html +++ b/www/views/buyMercadoLibre.html @@ -32,29 +32,40 @@ + +
+
+ Details +
+
+ Network fee + + {{totalFeeStr}} + +
+
+ Total to pay + + {{totalAmountStr}} + +
+
+ click-send-status="sendStatus"> Confirm purchase + slide-send-status="sendStatus"> Slide to buy Bought {{mlGiftCard.amount}} {{mlGiftCard.currency}} -
+
Gift card generated and ready to use.
diff --git a/www/views/modals/mercadolibre-card-details.html b/www/views/modals/mercadolibre-card-details.html index 3605c13c8..12aa92870 100644 --- a/www/views/modals/mercadolibre-card-details.html +++ b/www/views/modals/mercadolibre-card-details.html @@ -20,13 +20,13 @@
- Created - {{card.date | amTimeAgo}} + Created + {{card.date | amTimeAgo}}
-
-
+
+
Claim code: {{card.pin}}
@@ -35,16 +35,16 @@ Redeem Now
-
+
Status: - CANCELED + Inactive
-
+
Status: @@ -72,9 +72,6 @@
- - Cancel - Remove From d0ac2e4fa45b402b6ba821242097254dafe24cea Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Wed, 5 Jul 2017 18:43:47 -0300 Subject: [PATCH 4/9] URL and network --- src/js/services/mercadoLibreService.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/services/mercadoLibreService.js b/src/js/services/mercadoLibreService.js index f33991cc7..86bf74362 100644 --- a/src/js/services/mercadoLibreService.js +++ b/src/js/services/mercadoLibreService.js @@ -15,11 +15,11 @@ angular.module('copayApp.services').factory('mercadoLibreService', function($htt * Development: 'testnet' * Production: 'livenet' */ - //credentials.NETWORK = 'livenet'; - credentials.NETWORK = 'testnet'; + credentials.NETWORK = 'livenet'; + //credentials.NETWORK = 'testnet'; if (credentials.NETWORK == 'testnet') { - credentials.BITPAY_API_URL = "https://gustavo.bp:8088"; + credentials.BITPAY_API_URL = "https://test.bitpay.com"; } else { credentials.BITPAY_API_URL = "https://bitpay.com"; }; From 0b3cb389ae0e39fe883f911b93e5185f3a1f8485 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 6 Jul 2017 18:10:04 -0300 Subject: [PATCH 5/9] UI styling. Improve error handle --- src/js/controllers/buyMercadoLibre.js | 10 +- src/js/controllers/mercadoLibreCards.js | 11 +- src/js/services/mercadoLibreService.js | 2 +- src/sass/views/integrations/mercadolibre.scss | 47 +++++++ www/views/mercadoLibreCards.html | 25 ++-- .../modals/mercadolibre-card-details.html | 121 +++++++++--------- 6 files changed, 134 insertions(+), 82 deletions(-) diff --git a/src/js/controllers/buyMercadoLibre.js b/src/js/controllers/buyMercadoLibre.js index 321d77d8a..79e2c1edf 100644 --- a/src/js/controllers/buyMercadoLibre.js +++ b/src/js/controllers/buyMercadoLibre.js @@ -142,13 +142,21 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f var checkTransaction = lodash.throttle(function(count, dataSrc) { mercadoLibreService.createGiftCard(dataSrc, function(err, giftCard) { $log.debug("creating gift card " + count); - if (err || !giftCard.pin) { + if (err) { $scope.sendStatus = ''; ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); giftCard = {}; giftCard.status = 'FAILURE'; } + if (giftCard && giftCard.cardStatus && (giftCard.cardStatus != 'active' && giftCard.cardStatus != 'inactive' && giftCard.cardStatus != 'expired')) { + $scope.sendStatus = ''; + ongoingProcess.set('buyingGiftCard', false, statusChangeHandler); + giftCard = {}; + giftCard.status = 'FAILURE'; + } + + if (giftCard.status == 'PENDING' && count < 3) { $log.debug("Waiting for payment confirmation"); checkTransaction(count + 1, dataSrc); diff --git a/src/js/controllers/mercadoLibreCards.js b/src/js/controllers/mercadoLibreCards.js index 438631c06..6d67a888a 100644 --- a/src/js/controllers/mercadoLibreCards.js +++ b/src/js/controllers/mercadoLibreCards.js @@ -24,18 +24,15 @@ angular.module('copayApp.controllers').controller('mercadoLibreCardsController', }; $scope.updatePendingGiftCards = lodash.debounce(function() { - $scope.updatingPending = {}; updateGiftCards(function() { var index = 0; var gcds = $scope.giftCards; lodash.forEach(gcds, function(dataFromStorage) { - if (dataFromStorage.status == 'PENDING' || dataFromStorage.status == 'invalid') { + if (dataFromStorage.status == 'PENDING') { $log.debug("Creating / Updating gift card"); - $scope.updatingPending[dataFromStorage.invoiceId] = true; mercadoLibreService.createGiftCard(dataFromStorage, function(err, giftCard) { - $scope.updatingPending[dataFromStorage.invoiceId] = false; if (err) { popupService.showAlert('Error creating gift card', err); return; @@ -44,6 +41,12 @@ angular.module('copayApp.controllers').controller('mercadoLibreCardsController', if (giftCard.status != 'PENDING') { var newData = {}; + if (!giftCard.status) dataFromStorage.status = null; // Fix error from server + + var cardStatus = giftCard.cardStatus; + if (cardStatus && (cardStatus != 'active' && cardStatus != 'inactive' && cardStatus != 'expired')) + giftCard.status = 'FAILURE'; + lodash.merge(newData, dataFromStorage, giftCard); mercadoLibreService.savePendingGiftCard(newData, null, function(err) { diff --git a/src/js/services/mercadoLibreService.js b/src/js/services/mercadoLibreService.js index 86bf74362..eb07ac685 100644 --- a/src/js/services/mercadoLibreService.js +++ b/src/js/services/mercadoLibreService.js @@ -140,7 +140,7 @@ angular.module('copayApp.services').factory('mercadoLibreService', function($htt $log.info('Mercado Libre Gift Card Create/Update: ' + status); return cb(null, data.data); }, function(data) { - $log.error('Mercado Libre Gift Card Create/Update: ' + data.data.message); + $log.error('Mercado Libre Gift Card Create/Update: ', JSON.stringify(data.data)); return cb(data.data); }); }; diff --git a/src/sass/views/integrations/mercadolibre.scss b/src/sass/views/integrations/mercadolibre.scss index 63b0032f5..54b0a7cfb 100644 --- a/src/sass/views/integrations/mercadolibre.scss +++ b/src/sass/views/integrations/mercadolibre.scss @@ -144,3 +144,50 @@ } } } + +#meli-card { + .card-head { + margin: 20px 0; + text-align: center; + .date { + font-size: 12px; + margin: 10px 0; + } + .amount { + font-size: 16px; + font-weight: bold; + } + } + .card-status { + text-align: center; + margin-bottom: 25px; + .card-status-desc { + margin-top: 5px; + font-size: 12px; + color: $v-text-secondary-color; + } + .redeem-pin { + font-weight: bold; + font-size: 22px; + } + .button-redeem { + margin-top: 10px; + background: transparent; + border: none; + font-size: 12px; + color: $v-text-accent-color; + } + } + .card-remove { + text-align: center; + margin-top: 30px; + .button-remove { + margin-top: 10px; + background: transparent; + border: none; + font-size: 12px; + color: red; + } + } + +} diff --git a/www/views/mercadoLibreCards.html b/www/views/mercadoLibreCards.html index 8488e318c..2888651fb 100644 --- a/www/views/mercadoLibreCards.html +++ b/www/views/mercadoLibreCards.html @@ -12,19 +12,20 @@ class="item item-avatar" ng-hide="hideCards"> {{id}} - - + + + Error + Invoice expired + Still pending + Pending + + Inactive + Expired +

- {{item.amount | currency : '$ ' : 2}} {{item.currency}} -

-

- Error - Expired - Still waiting confirmation
(Use higher fees setting to faster delivery)
- Pending to confirmation - Canceled - {{item.date | amTimeAgo}} -

+ {{item.amount | currency : '' : 2}} {{item.currency}} + +

{{item.date | amTimeAgo}}

diff --git a/www/views/modals/mercadolibre-card-details.html b/www/views/modals/mercadolibre-card-details.html index 12aa92870..f4366ad79 100644 --- a/www/views/modals/mercadolibre-card-details.html +++ b/www/views/modals/mercadolibre-card-details.html @@ -1,82 +1,75 @@ - + - -

Details

+

Details

-
- Mercado Libre Gift Card +
+ Mercado Libre Gift Card -
- Gift Card Amount: - - {{card.amount | currency : '$ ' : 2}} - -
+
+ {{card.amount | currency : '' : 2}} {{card.currency}} +
+
+
+
+
{{card.pin}}
+ +
-
- Created +
+ Inactive +
Gift Card is not available to use anymore
+
+ +
+ Expired +
Gift Card is not available to use anymore
+
+ +
+ + Pending + + + Still pending + + + Error + + + Invoice expired + +
+
+ +
-
- There was a failure to the create gift card. Please, contact BitPay support. +
+ + See invoice
+
- +
+ +
From e7f8e2c23523db63c5cadba45f7980dee9e49e7e Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Wed, 26 Jul 2017 16:13:57 -0300 Subject: [PATCH 6/9] Fix wording. Error handler improved --- src/js/controllers/buyMercadoLibre.js | 2 +- src/js/services/mercadoLibreService.js | 12 ++++----- src/sass/views/integrations/mercadolibre.scss | 11 ++++++++- www/img/mercado-libre/meli-card-24px.png | Bin 0 -> 1393 bytes www/views/amount.html | 2 +- www/views/buyMercadoLibre.html | 10 +++++--- www/views/mercadoLibre.html | 23 ++++++++++++------ www/views/mercadoLibreCards.html | 8 +++--- 8 files changed, 44 insertions(+), 24 deletions(-) create mode 100644 www/img/mercado-libre/meli-card-24px.png diff --git a/src/js/controllers/buyMercadoLibre.js b/src/js/controllers/buyMercadoLibre.js index 79e2c1edf..165719a1d 100644 --- a/src/js/controllers/buyMercadoLibre.js +++ b/src/js/controllers/buyMercadoLibre.js @@ -110,7 +110,7 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f var outputs = []; var toAddress = invoice.bitcoinAddress; - var amountSat = parseInt(invoice.btcDue * 100000000); // BTC to Satoshi + var amountSat = parseInt((invoice.btcDue * 100000000).toFixed(0)); // BTC to Satoshi outputs.push({ 'toAddress': toAddress, diff --git a/src/js/services/mercadoLibreService.js b/src/js/services/mercadoLibreService.js index eb07ac685..0db1549ce 100644 --- a/src/js/services/mercadoLibreService.js +++ b/src/js/services/mercadoLibreService.js @@ -26,14 +26,14 @@ angular.module('copayApp.services').factory('mercadoLibreService', function($htt var homeItem = { name: 'mercadoLibre', - title: 'Mercado Libre', + title: 'Mercado Livre Brazil Gift Cards', icon: 'icon-ml', sref: 'tabs.giftcards.mercadoLibre', }; var nextStepItem = { name: 'mercadoLibre', - title: 'Buy Mercado Libre Gift Cards', + title: 'Buy Mercado Livre Brazil Gift Cards', icon: 'icon-ml', sref: 'tabs.giftcards.mercadoLibre', }; @@ -112,7 +112,7 @@ angular.module('copayApp.services').factory('mercadoLibreService', function($htt $log.info('BitPay Create Invoice: SUCCESS'); return cb(null, data.data); }, function(data) { - $log.error('BitPay Create Invoice: ERROR ' + data.data.message); + $log.error('BitPay Create Invoice: ERROR', JSON.stringify(data.data)); return cb(data.data); }); }; @@ -122,8 +122,8 @@ angular.module('copayApp.services').factory('mercadoLibreService', function($htt $log.info('BitPay Get Invoice: SUCCESS'); return cb(null, data.data.data); }, function(data) { - $log.error('BitPay Get Invoice: ERROR ' + data.data.error); - return cb(data.data.error); + $log.error('BitPay Get Invoice: ERROR', JSON.stringify(data.data)); + return cb(data.data); }); }; @@ -140,7 +140,7 @@ angular.module('copayApp.services').factory('mercadoLibreService', function($htt $log.info('Mercado Libre Gift Card Create/Update: ' + status); return cb(null, data.data); }, function(data) { - $log.error('Mercado Libre Gift Card Create/Update: ', JSON.stringify(data.data)); + $log.error('Mercado Libre Gift Card Create/Update: ERROR', JSON.stringify(data.data)); return cb(data.data); }); }; diff --git a/src/sass/views/integrations/mercadolibre.scss b/src/sass/views/integrations/mercadolibre.scss index 54b0a7cfb..db8e6ecfe 100644 --- a/src/sass/views/integrations/mercadolibre.scss +++ b/src/sass/views/integrations/mercadolibre.scss @@ -41,7 +41,8 @@ } .big-icon-svg > .bg { - height: 27px; + height: 28px; + box-shadow: none; } } @@ -145,6 +146,14 @@ } } +#meli-list-cards { + img.item-logo { + width: auto; + height: auto; + border-radius: 0; + } +} + #meli-card { .card-head { margin: 20px 0; diff --git a/www/img/mercado-libre/meli-card-24px.png b/www/img/mercado-libre/meli-card-24px.png new file mode 100644 index 0000000000000000000000000000000000000000..2e42ef103feb6ca9dc4b3b02bc6e031ad3b4b9f4 GIT binary patch literal 1393 zcmeAS@N?(olHy`uVBq!ia0vp^sz5Bk!3HF2t4s=?Nk^)#sNw%$0gl~X?bAC~( zf~lUVo`I4bmx4`EN}5%WiyKrkkY}ruQBqQ1rLSLJUanVete0Puu5V~*X{m2uq;F)T z3sj+7T$xvrSfQI&tPC;&VunjnD>p{1Fbu92Q01B1rI(uwD_T>?dp z+Z#J8yS22Y7-mNB{+xKwO_I|zQ)=;_TW9wCT>9+z>%B8gZff1u^vd}Y^+9|?=ru1v zO-<|Uq~>{_&s_0%cITY!{NH=O&v|DnzUJVPNtaevDNa3?)#$mtxGwQxX8j>QX&Gw;6#Uc8sTamL48x7URJyt6+{yz>=TL{^KtXy|D!f6m`wn^&ETKBViVBqC@h zSe9Mn_)t2SU;O9IClwRzeXlh&7H%-(vVOeZ;!pR~mT5xMra$&lE%th8_sm=7;+H#X zj_`dfD`E0^bo8W&aL?m~{em-XOD_BOuURqS{_n~5&Td7bGebXRtPxOm6x+VSa;4l< z31M3;WzTzW{@>sCYGdQSq|$crRr0)#w+g?R{UOw#r=R)o4=a}2%yX8X{yDQ&qE2{k zz_pm_3mf>Bi>K=D;Z2vmP_@2o%Z2_F;qAwgJ{0kM<-SnEy4CyWys6(TcQrqlRdZGK z%^RobbMhnpv7IW(5T2JklO#{N9;`YMn0y?i@MdBeLe3YQ#d3sFJ{g`xk`#X4h!a6XA;8Rjim>-dZxXe!;{f z$LUd9rzl<0^I4PItG`4gE!ihZee%iEdt+_9)Fz+V^0BOE|JkRO&;RJf?|44vx!cPf z-#-dg*{o^{R$Jt*cfT#|L)nzR=mYC=BX6^_PEWqgnzi5Sqn~ij(mT%DiHQbKXYAjX@GSP1d&Q?xt@{(|r`-wm zkxhU3+sw(8^_2LJ$1=srielx#`%YYVzU1_s%El!^MSJrNEsi~5ytLFO;B57(&jKff zil1Lk_H4axzu>iKn*TcHgYy@wd@hka$8tqV%l^PAkxRWFpU&?rx>9%ZP2^0jecK#E z*SE91*DzK2*VZGI@Hn<)uT)KLbJV;i_m)r5543T(vavLF%HkhJ&eM1Nm)sgXbKxQr z-^;qj!h(g32fGtbEmEJgUv+Kk%01OROT^6T)K+{vwrpkae~zQmXV|*Fb-xr^vLS!o zY+H{*J1u|+x?pb|J;`fKVfsyTRbb?*t2!Fx<>!S-+mE2ch?90o%YgPLgMMq*&%w9^@DH7 zzT5Xp_rLzts$UB47XHq-apmgkkp0o;OXU`QR?xm2vX4u=ylm@?ePLPe=cjkSyzTfm sf8o2gCN9llNi`kEFD>kMG`9H5@LlR?nMt&{5U8Z|boFyt=akR{0MZ$100000 literal 0 HcmV?d00001 diff --git a/www/views/amount.html b/www/views/amount.html index 062722793..5dfee29e5 100644 --- a/www/views/amount.html +++ b/www/views/amount.html @@ -1,7 +1,7 @@ - {{'Enter Amount' | translate}} + {{'Enter amount' | translate}} diff --git a/www/views/buyMercadoLibre.html b/www/views/buyMercadoLibre.html index eb15ae12a..16d5d2944 100644 --- a/www/views/buyMercadoLibre.html +++ b/www/views/buyMercadoLibre.html @@ -2,7 +2,9 @@ - Buy + + {{'Buy'|translate}} + @@ -14,7 +16,7 @@
- Mercado Libre Gift Card + Mercado Livre Brazil Gift Cards
{{amountUnitStr}}
@@ -60,13 +62,13 @@ ng-click="buyConfirm()" ng-if="!isCordova" click-send-status="sendStatus"> - Confirm purchase + {{'Confirm purchase'|translate}} - Slide to buy + {{'Slide to buy'|translate}} - Mercado Libre Gift Cards + + {{'Mercado Livre Brazil Gift Cards'|translate}} + @@ -13,13 +15,18 @@ -
- Gift Cards are only redeemable on Mercado Livre (Brazil) +
+ Only redeemable on Mercado Livre (Brazil)
- + + ng-click="openExternalLink('https://www.mercadolivre.com.br')" translate> + Visit mercadolivre.com.br → +
@@ -30,7 +37,7 @@
Mercado Libre -
+
Only redeemable on Mercado Livre (Brazil)
@@ -40,14 +47,14 @@ - Buy Gift Card + Buy a Gift Card - Your cards + Your Gift Cards
diff --git a/www/views/mercadoLibreCards.html b/www/views/mercadoLibreCards.html index 2888651fb..ba7eba1ec 100644 --- a/www/views/mercadoLibreCards.html +++ b/www/views/mercadoLibreCards.html @@ -1,8 +1,10 @@ - + - Your cards + + {{'Your Gift Cards'|translate}} + @@ -11,7 +13,7 @@ ng-click="openCardModal(item)" class="item item-avatar" ng-hide="hideCards"> - {{id}} + Error From 60f049c0d31fe71410494b68a49bcd5107dde2ed Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Wed, 26 Jul 2017 16:25:12 -0300 Subject: [PATCH 7/9] Clean code. Modal view --- .../modals/mercadoLibreCardDetails.js | 66 +------------------ .../modals/mercadolibre-card-details.html | 8 +-- 2 files changed, 6 insertions(+), 68 deletions(-) diff --git a/src/js/controllers/modals/mercadoLibreCardDetails.js b/src/js/controllers/modals/mercadoLibreCardDetails.js index 1b1628c9d..cbb2a6af6 100644 --- a/src/js/controllers/modals/mercadoLibreCardDetails.js +++ b/src/js/controllers/modals/mercadoLibreCardDetails.js @@ -2,77 +2,15 @@ angular.module('copayApp.controllers').controller('mercadoLibreCardDetailsController', function($scope, $log, $timeout, $ionicScrollDelegate, bwcError, mercadoLibreService, lodash, ongoingProcess, popupService, externalLinkService) { - $scope.cancelGiftCard = function() { - ongoingProcess.set('cancelingGiftCard', true); - mercadoLibreService.cancelGiftCard($scope.card, function(err, data) { - ongoingProcess.set('cancelingGiftCard', false); - if (err) { - popupService.showAlert('Error canceling gift card', bwcError.msg(err)); - return; - } - $scope.card.cardStatus = data.cardStatus; - $timeout(function() { - $ionicScrollDelegate.resize(); - $ionicScrollDelegate.scrollTop(); - }, 10); - mercadoLibreService.savePendingGiftCard($scope.card, null, function(err) { - $scope.refreshGiftCard(); - }); - }); - }; - $scope.remove = function() { mercadoLibreService.savePendingGiftCard($scope.card, { remove: true }, function(err) { - $scope.cancel(); + $scope.close(); }); }; - $scope.refreshGiftCard = function() { - ongoingProcess.set('updatingGiftCard', true); - mercadoLibreService.getPendingGiftCards(function(err, gcds) { - if (lodash.isEmpty(gcds)) { - $timeout(function() { - ongoingProcess.set('updatingGiftCard', false); - }, 1000); - } - if (err) { - popupService.showAlert('Error', err); - return; - } - var index = 0; - lodash.forEach(gcds, function(dataFromStorage) { - if (++index == Object.keys(gcds).length) { - $timeout(function() { - ongoingProcess.set('updatingGiftCard', false); - }, 1000); - } - if (dataFromStorage.status == 'PENDING' && dataFromStorage.invoiceId == $scope.card.invoiceId) { - $log.debug("creating gift card"); - mercadoLibreService.createGiftCard(dataFromStorage, function(err, giftCard) { - if (err) { - popupService.showAlert('Error', bwcError.msg(err)); - return; - } - if (!lodash.isEmpty(giftCard)) { - var newData = {}; - lodash.merge(newData, dataFromStorage, giftCard); - mercadoLibreService.savePendingGiftCard(newData, null, function(err) { - $log.debug("Saving new gift card"); - $scope.card = newData; - $timeout(function() { - $scope.$digest(); - }); - }); - } else $log.debug("pending gift card not available yet"); - }); - } - }); - }); - }; - - $scope.cancel = function() { + $scope.close = function() { $scope.mercadoLibreCardDetailsModal.hide(); }; diff --git a/www/views/modals/mercadolibre-card-details.html b/www/views/modals/mercadolibre-card-details.html index f4366ad79..3d272e3de 100644 --- a/www/views/modals/mercadolibre-card-details.html +++ b/www/views/modals/mercadolibre-card-details.html @@ -1,6 +1,6 @@ -

Details

@@ -9,7 +9,7 @@
- Mercado Libre Gift Card + Mercado Livre Brazil Gift Card
{{card.amount | currency : '' : 2}} {{card.currency}} @@ -59,9 +59,9 @@
-
+
- See invoice + See invoice
From 0f7fc66c368d0b0d0b173c0ce804a530d69dd2f3 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Wed, 26 Jul 2017 16:27:29 -0300 Subject: [PATCH 8/9] Removes unused services --- src/js/controllers/modals/mercadoLibreCardDetails.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/controllers/modals/mercadoLibreCardDetails.js b/src/js/controllers/modals/mercadoLibreCardDetails.js index cbb2a6af6..78bdf544d 100644 --- a/src/js/controllers/modals/mercadoLibreCardDetails.js +++ b/src/js/controllers/modals/mercadoLibreCardDetails.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('mercadoLibreCardDetailsController', function($scope, $log, $timeout, $ionicScrollDelegate, bwcError, mercadoLibreService, lodash, ongoingProcess, popupService, externalLinkService) { +angular.module('copayApp.controllers').controller('mercadoLibreCardDetailsController', function($scope, mercadoLibreService, externalLinkService) { $scope.remove = function() { mercadoLibreService.savePendingGiftCard($scope.card, { From 4a62cf1fba1e0cc1af4c97d43d43cdc988fa60e9 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Wed, 26 Jul 2017 17:46:17 -0300 Subject: [PATCH 9/9] Display more info before purchase --- src/js/controllers/buyMercadoLibre.js | 48 ++++++++++++++++++++++----- www/views/buyMercadoLibre.html | 24 ++++++++++---- 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/js/controllers/buyMercadoLibre.js b/src/js/controllers/buyMercadoLibre.js index 165719a1d..fa01a6143 100644 --- a/src/js/controllers/buyMercadoLibre.js +++ b/src/js/controllers/buyMercadoLibre.js @@ -14,6 +14,11 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f externalLinkService.open(url); }; + var _resetValues = function() { + $scope.totalAmountStr = $scope.amount = $scope.invoiceFee = $scope.networkFee = $scope.totalAmount = $scope.wallet = null; + createdTx = message = invoiceId = null; + }; + var showErrorAndBack = function(title, msg) { title = title || gettextCatalog.getString('Error'); $scope.sendStatus = ''; @@ -58,6 +63,30 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f } }; + var satToFiat = function(sat, cb) { + txFormatService.toFiat(sat, $scope.currencyIsoCode, function(value) { + return cb(value); + }); + }; + + var setTotalAmount = function(amountSat, invoiceFeeSat, networkFeeSat) { + satToFiat(amountSat, function(a) { + $scope.amount = Number(a); + + satToFiat(invoiceFeeSat, function(i) { + $scope.invoiceFee = Number(i); + + satToFiat(networkFeeSat, function(n) { + $scope.networkFee = Number(n); + $scope.totalAmount = $scope.amount + $scope.invoiceFee + $scope.networkFee; + $timeout(function() { + $scope.$digest(); + }); + }); + }); + }); + }; + var createInvoice = function(data, cb) { mercadoLibreService.createBitPayInvoice(data, function(err, dataInvoice) { if (err) { @@ -184,7 +213,9 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f 'leading': true }); - var initialize = function(wallet, parsedAmount) { + var initialize = function(wallet) { + var parsedAmount = txFormatService.parseAmount(amount, currency); + $scope.currencyIsoCode = parsedAmount.currency; $scope.amountUnitStr = parsedAmount.amountUnitStr; var dataSrc = { amount: parsedAmount.amount, @@ -198,16 +229,18 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f showErrorAndBack(err.title, err.message); return; } + // Sometimes API does not return this element; + invoice['buyerPaidBtcMinerFee'] = invoice.buyerPaidBtcMinerFee || 0; + var invoiceFeeSat = (invoice.buyerPaidBtcMinerFee * 100000000).toFixed(); - message = gettextCatalog.getString("Mercado Libre Gift Card {{amountStr}}", { + message = gettextCatalog.getString("{{amountStr}} for Mercado Livre Brazil Gift Card", { amountStr: $scope.amountUnitStr }); createTx(wallet, invoice, message, function(err, ctxp) { ongoingProcess.set('loadingTxInfo', false); if (err) { - // Clear variables - createdTx = message = $scope.totalFeeStr = $scope.totalAmountStr = $scope.wallet = null; + _resetValues(); showError(err.title, err.message); return; } @@ -225,8 +258,8 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f invoiceUrl: invoice.url, invoiceTime: invoice.invoiceTime }; - $scope.totalFeeStr = txFormatService.formatAmountStr(ctxp.fee); - $scope.totalAmountStr = txFormatService.formatAmountStr(ctxp.amount + ctxp.fee); + $scope.totalAmountStr = txFormatService.formatAmountStr(ctxp.amount); + setTotalAmount(parsedAmount.amountSat, invoiceFeeSat, ctxp.fee); }); }); }; @@ -295,8 +328,7 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f $scope.onWalletSelect = function(wallet) { $scope.wallet = wallet; - var parsedAmount = txFormatService.parseAmount(amount, currency); - initialize(wallet, parsedAmount); + initialize(wallet); }; $scope.goBackHome = function() { diff --git a/www/views/buyMercadoLibre.html b/www/views/buyMercadoLibre.html index 16d5d2944..04461ad7a 100644 --- a/www/views/buyMercadoLibre.html +++ b/www/views/buyMercadoLibre.html @@ -25,7 +25,7 @@
-
From
+
From
@@ -34,21 +34,33 @@
-
Details
- Network fee + Gift card - {{totalFeeStr}} + {{amount | currency:'$ ':2}} {{currencyIsoCode}}
- Total to pay + Invoice Fee - {{totalAmountStr}} + {{invoiceFee | currency:'$ ':2}} {{currencyIsoCode}} + +
+
+ Network Fee + + {{networkFee | currency:'$ ':2}} {{currencyIsoCode}} + +
+
+ Total + + {{totalAmount | currency:'$ ':2}} {{currencyIsoCode}} + ({{totalAmountStr}})