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": "20170314T10:39:14.82603:00", + "date_activation": "20170314T10:39:15.31603:00", + "date_expiration": "20180914T10:39:15.31603:00", + "date_last_updated": "20170314T10:39:15.32103: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) + + + Buy a Gift Card + Visit mercadolivre.com.br → + + + + + + Sandbox version. Only for testing purpose. + + + + + + Only redeemable on Mercado Livre (Brazil) + + + + + + + + Buy Gift Card + + + + + + Your cards + + + + + + 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 + + + + + + + + + + {{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 @@ + + + + Close + + Details + + + + + + + + + Gift Card Amount: + + {{card.amount | currency : '$ ' : 2}} + + + + + + Created + {{card.date | amTimeAgo}} + + + + + + Claim code: {{card.pin}} + + + + Redeem Now + + + + + Status: + + CANCELED + + + + + + + Status: + + PENDING + + + STILL PENDING + + + + FAILURE + + + EXPIRED + + + + + See invoice + + + + + There was a failure to the create gift card. Please, contact BitPay support. + + + + + Cancel + + + Remove + + + + +
+ Error + Expired + Still waiting confirmation (Use higher fees setting to faster delivery) + Pending to confirmation + Canceled + {{item.date | amTimeAgo}} +