From 941e7ba70094a1d4a20cb4db27107cc29821b740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ho=C4=8Devar?= Date: Mon, 12 Aug 2019 15:21:03 +0200 Subject: [PATCH 1/4] Implemented GoCrypto integration --- src/js/controllers/review.controller.js | 140 +++++++----- src/js/services/goCryptoService.js | 201 ++++++++++++++++++ src/js/services/incoming-data.service.js | 100 ++++----- .../services/send-flow/send-flow.service.js | 21 +- www/img/icon-gocrypto.svg | 27 +++ www/views/review.html | 5 +- 6 files changed, 395 insertions(+), 99 deletions(-) create mode 100644 src/js/services/goCryptoService.js create mode 100644 www/img/icon-gocrypto.svg diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js index fb1d98b8f..41ffa1184 100644 --- a/src/js/controllers/review.controller.js +++ b/src/js/controllers/review.controller.js @@ -31,6 +31,7 @@ angular , $scope , sendFlowService , sideshiftService + , goCryptoService , soundService , $state , $timeout @@ -56,6 +57,7 @@ angular var toAddress = ''; var tx = {}; var txPayproData = null; + var txGoCryptoData = null; var unitFromSat = 0; var FEE_TOO_HIGH_LIMIT_PERCENTAGE = 15; @@ -83,6 +85,7 @@ angular toAddress = ''; tx = {}; txPayproData = null; + txGoCryptoData = null; unitFromSat = 0; // Public variables @@ -180,6 +183,8 @@ angular break; case 'bip70': initBip70(); + case 'gocrypto': + initGoCrypto(); default: _next(); break; @@ -208,7 +213,76 @@ angular } vm.approve = function() { - + if (vm.thirdParty != null && vm.thirdParty.id === 'gocrypto') { + goCryptoService.validateTx(vm.thirdParty.url).then(function(isValid) { + if (isValid) { + _onApprove(); + } else { + popupService.showAlert(null, gettextCatalog.getString('GoCrypto payment is no longer valid. Please try again.', function onAlert() { + $state.go('tabs.scan').then(function () { + $ionicHistory.clearHistory(); + }); + })); + } + }, function(err) { + popupService.showAlert(null, gettextCatalog.getString('An error occurred while trying to validate GoCrypto payment. Please try again.', function onAlert() { + $state.go('tabs.scan').then(function () { + $ionicHistory.clearHistory(); + }); + })); + }); + } else { + _onApprove(); + } + }; + + vm.chooseFeeLevel = function(tx, wallet) { + + if (wallet.coin == 'bch') return; + if (usingMerchantFee) return; + + var scope = $rootScope.$new(true); + scope.network = tx.network; + scope.feeLevel = tx.feeLevel; + scope.noSave = true; + scope.coin = vm.originWallet.coin; + + if (usingCustomFee) { + scope.customFeePerKB = tx.feeRate; + scope.feePerSatByte = tx.feeRate / 1000; + } + + $ionicModal.fromTemplateUrl('views/modals/chooseFeeLevel.html', { + scope: scope, + backdropClickToClose: false, + hardwareBackButtonClose: false + }).then(function(modal) { + scope.chooseFeeLevelModal = modal; + scope.openModal(); + }); + scope.openModal = function() { + scope.chooseFeeLevelModal.show(); + }; + + scope.hideModal = function(newFeeLevel, customFeePerKB) { + scope.chooseFeeLevelModal.hide(); + $log.debug('New fee level choosen:' + newFeeLevel + ' was:' + tx.feeLevel); + + usingCustomFee = newFeeLevel == 'custom' ? true : false; + + if (tx.feeLevel == newFeeLevel && !usingCustomFee) return; + + tx.feeLevel = newFeeLevel; + if (usingCustomFee) tx.feeRate = parseInt(customFeePerKB); + + updateTx(tx, vm.originWallet, { + clearCache: true, + dryRun: true + }, function() {}); + }; + }; + + function _onApprove() { if (!tx || !vm.originWallet) return; if (vm.paymentExpired) { @@ -269,53 +343,7 @@ angular publishAndSign(); }); }); - }; - - vm.chooseFeeLevel = function(tx, wallet) { - - if (wallet.coin == 'bch') return; - if (usingMerchantFee) return; - - var scope = $rootScope.$new(true); - scope.network = tx.network; - scope.feeLevel = tx.feeLevel; - scope.noSave = true; - scope.coin = vm.originWallet.coin; - - if (usingCustomFee) { - scope.customFeePerKB = tx.feeRate; - scope.feePerSatByte = tx.feeRate / 1000; - } - - $ionicModal.fromTemplateUrl('views/modals/chooseFeeLevel.html', { - scope: scope, - backdropClickToClose: false, - hardwareBackButtonClose: false - }).then(function(modal) { - scope.chooseFeeLevelModal = modal; - scope.openModal(); - }); - scope.openModal = function() { - scope.chooseFeeLevelModal.show(); - }; - - scope.hideModal = function(newFeeLevel, customFeePerKB) { - scope.chooseFeeLevelModal.hide(); - $log.debug('New fee level choosen:' + newFeeLevel + ' was:' + tx.feeLevel); - - usingCustomFee = newFeeLevel == 'custom' ? true : false; - - if (tx.feeLevel == newFeeLevel && !usingCustomFee) return; - - tx.feeLevel = newFeeLevel; - if (usingCustomFee) tx.feeRate = parseInt(customFeePerKB); - - updateTx(tx, vm.originWallet, { - clearCache: true, - dryRun: true - }, function() {}); - }; - }; + } function createVanityTransaction() { var configFeeLevel = config.wallet.settings.feeLevel ? config.wallet.settings.feeLevel : 'normal'; @@ -327,6 +355,7 @@ angular fromWalletId: sendFlowData.fromWalletId, toAddress: sendFlowData.toAddress, paypro: txPayproData, + gocrypto: txGoCryptoData, outs: sendFlowData.outs, feeLevel: configFeeLevel, @@ -632,6 +661,17 @@ angular }; } + function initGoCrypto() { + vm.sendingTitle = gettextCatalog.getString('You are paying'); + vm.memo = vm.thirdParty.memo; + vm.memoExpanded = !!vm.memo; + vm.destination.name = vm.thirdParty.name; + + txGoCryptoData = { + expires: vm.thirdParty.expires + }; + } + function initSideshift(cb) { vm.sendingTitle = gettextCatalog.getString('You are shifting'); if (!vm.thirdParty.data) { @@ -868,6 +908,10 @@ angular if (tx.paypro) startExpirationTimer(tx.paypro.expires); + if (tx.gocrypto) { + startExpirationTimer(tx.gocrypto.expires); + } + updateTx(tx, vm.originWallet, { dryRun: true }, function(err) { diff --git a/src/js/services/goCryptoService.js b/src/js/services/goCryptoService.js new file mode 100644 index 000000000..1ab66e026 --- /dev/null +++ b/src/js/services/goCryptoService.js @@ -0,0 +1,201 @@ +'use strict'; + +(function() { + + angular.module('bitcoincom.services') + .factory('goCryptoService', GoCryptoService); + + function GoCryptoService($log, $http, $timeout, $q, gettextCatalog) { + var GOCRYPTO_PAYMENT_STATUSES = Object.freeze({ + 'OPENED': 0, + 'IN_PAYMENT': 1, + 'PAID': 2, + 'PROCESSING': 3, + 'AUTO_CLOSED': 4, + 'FAILED': 5, + 'NOT_VALID': 6, + 'REFUND': 7 + }); + var GOCRYPTO_CURRENCIES = Object.freeze({ + 'BITCOIN_CORE': 2, + 'BITCOIN_CASH': 7, + }); + var GOCRYPTO_URL_REGEX = /^https?:\/\/gocrypto\.com\/sticker(\/)?/; + var GOCRYPTO_BACKWARD_COMPATIBILITY_URL_REGEX = /^https?:\/\/www\.elipay\.com\/getelipay(\/)?/; + var GOCRYPTO_ENDPOINT_URL = 'https://api.gocrypto.com/publicapi/payment?merchant_id='; + var GOCRYPTO_AUTH_HEADERS = {'Authorization': 'publicauth 6a15c40d-c3fa-41ac-a7ae-b089e95cf8fc'}; + + var req = { + method: 'GET', + url: null, + headers: GOCRYPTO_AUTH_HEADERS, + }; + + var service = { + // Functions + checkAndUpdateTx: checkAndUpdateTx, + validateTx: validateTx, + encodePaymentUrl: encodePaymentUrl, + decodePaymentUrl: decodePaymentUrl, + }; + + return service; + + function checkAndUpdateTx(data) { + $log.debug('Checking if scanned data has GoCrypto payload...'); + var defer = $q.defer(); + + if (typeof data !== 'string') { + defer.resolve(data); + } + + var trimmedUrl = data.trim(); + if (GOCRYPTO_URL_REGEX.test(trimmedUrl) || GOCRYPTO_BACKWARD_COMPATIBILITY_URL_REGEX.test(trimmedUrl)) { + req.url = GOCRYPTO_ENDPOINT_URL + data; + + $http(req).then(function(response) { + if (response.status === 200) { + $log.info('Successfully received GoCrypto payment data'); + + var payment = response.data; + + var paymentOption = null; + for (var i=0; i + + + +Oval 87 + bitpay-logo-grayscale +Created with Sketch. + + + + + + + + + diff --git a/www/views/review.html b/www/views/review.html index 34f6c623e..9e57649c8 100644 --- a/www/views/review.html +++ b/www/views/review.html @@ -49,10 +49,11 @@

{{vm.destination.name}}[Balance Hidden]

- + ng-if="vm.thirdParty && (vm.thirdParty.id === 'bip70' || vm.thirdParty.id === 'gocrypto')"> + +

{{vm.destination.name}}

Payment expires: {{vm.remainingTimeStr}}

Payment request has expired

From 17c7ccb12ef1360940b8108df74e710fd63b41d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ho=C4=8Devar?= Date: Fri, 30 Aug 2019 15:26:05 +0200 Subject: [PATCH 2/4] Updated GoCrypto sticker URL --- src/js/services/goCryptoService.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/services/goCryptoService.js b/src/js/services/goCryptoService.js index 1ab66e026..cb4798d89 100644 --- a/src/js/services/goCryptoService.js +++ b/src/js/services/goCryptoService.js @@ -20,8 +20,8 @@ 'BITCOIN_CORE': 2, 'BITCOIN_CASH': 7, }); - var GOCRYPTO_URL_REGEX = /^https?:\/\/gocrypto\.com\/sticker(\/)?/; - var GOCRYPTO_BACKWARD_COMPATIBILITY_URL_REGEX = /^https?:\/\/www\.elipay\.com\/getelipay(\/)?/; + var GOCRYPTO_URL_REGEX = /^https?:\/\/(www\.)?gocrypto\.com/; + var GOCRYPTO_BACKWARD_COMPATIBILITY_URL_REGEX = /^https?:\/\/(www\.)?elipay\.com/; var GOCRYPTO_ENDPOINT_URL = 'https://api.gocrypto.com/publicapi/payment?merchant_id='; var GOCRYPTO_AUTH_HEADERS = {'Authorization': 'publicauth 6a15c40d-c3fa-41ac-a7ae-b089e95cf8fc'}; From d13ac944916b2f3fb816a824b6c0337aaadb38cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ho=C4=8Devar?= Date: Mon, 14 Oct 2019 09:30:53 +0200 Subject: [PATCH 3/4] Improve error messages --- src/js/services/goCryptoService.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/js/services/goCryptoService.js b/src/js/services/goCryptoService.js index cb4798d89..0130802ce 100644 --- a/src/js/services/goCryptoService.js +++ b/src/js/services/goCryptoService.js @@ -85,14 +85,14 @@ raiseError( defer, 'Failed to get GoCrypto payment data.', - 'An error occurred while trying to retrieve GoCrypto POS verification data. Please try again.' + 'GoCrypto payment order was not found. Please try again.' ); } }, function(error) { raiseError( defer, 'An error occurred while trying to get GoCrypto payment data.', - 'An error occurred while trying to retrieve GoCrypto POS verification data. Please try again.' + 'GoCrypto payment order was not found. Please try again.' ); }); } else { @@ -128,14 +128,14 @@ raiseError( defer, 'Failed to get GoCrypto payment data.', - 'An error occurred while trying to retrieve GoCrypto POS verification data. Please try again.' + 'GoCrypto payment order was not found. Please try again.' ); } }, function(error) { raiseError( defer, 'An error occurred while trying to get GoCrypto payment data.', - 'An error occurred while trying to retrieve GoCrypto POS verification data. Please try again.' + 'GoCrypto payment order was not found. Please try again.' ); }); } else { @@ -152,8 +152,8 @@ if (expiry_date < Date.now()) { raiseError( defer, - 'GoCrypto payment expired.', - 'An error occurred while trying to retrieve GoCrypto POS verification data. Please try again.' + 'GoCrypto payment order has expired.', + 'GoCrypto payment order has expired.' ); } } @@ -162,8 +162,8 @@ if (paymentStatus !== GOCRYPTO_PAYMENT_STATUSES['IN_PAYMENT']) { raiseError( defer, - 'Incorrect GoCrypto payment.', - 'An error occurred while trying to retrieve GoCrypto POS verification data. Please try again.' + 'GoCrypto payment order was not found.', + 'GoCrypto payment order was not found.' ); } } @@ -172,8 +172,8 @@ if (paymentOption == null) { raiseError( defer, - 'Could not found GoCrypto payment option.', - 'An error occurred while trying to retrieve GoCrypto POS verification data. Please try again.' + 'Incorrect GoCrypto payment order.', + 'Incorrect GoCrypto payment order.' ); } } From cd8fed35f299a524c4f8ff62fc3441702481b734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ho=C4=8Devar?= Date: Mon, 14 Oct 2019 12:11:38 +0200 Subject: [PATCH 4/4] Improve more error messages --- src/js/controllers/review.controller.js | 22 +++++++++++----------- src/js/services/goCryptoService.js | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js index 41ffa1184..8cf94dcb1 100644 --- a/src/js/controllers/review.controller.js +++ b/src/js/controllers/review.controller.js @@ -39,7 +39,7 @@ angular , txFormatService , walletService ) { - + var vm = this; var sendFlowData; @@ -61,7 +61,7 @@ angular var unitFromSat = 0; var FEE_TOO_HIGH_LIMIT_PERCENTAGE = 15; - + // Functions vm.goBack = goBack; vm.onReplay = onReplay; @@ -218,14 +218,14 @@ angular if (isValid) { _onApprove(); } else { - popupService.showAlert(null, gettextCatalog.getString('GoCrypto payment is no longer valid. Please try again.', function onAlert() { + popupService.showAlert(null, gettextCatalog.getString('GoCrypto payment order has expired.', function onAlert() { $state.go('tabs.scan').then(function () { $ionicHistory.clearHistory(); }); })); } }, function(err) { - popupService.showAlert(null, gettextCatalog.getString('An error occurred while trying to validate GoCrypto payment. Please try again.', function onAlert() { + popupService.showAlert(null, gettextCatalog.getString('GoCrypto payment order was not found. Please try again.', function onAlert() { $state.go('tabs.scan').then(function () { $ionicHistory.clearHistory(); }); @@ -375,8 +375,8 @@ angular //tx.toAddress = vm.thirdParty.toAddress; tx.toAddress = bitcoinCashJsService.readAddress(vm.thirdParty.toAddress).legacy; } - - if (sendFlowData.thirdParty && sendFlowData.thirdParty.requiredFeeRate) { + + if (sendFlowData.thirdParty && sendFlowData.thirdParty.requiredFeeRate) { vm.usingMerchantFee = true; tx.feeRate = parseInt(sendFlowData.thirdParty.requiredFeeRate); } @@ -558,7 +558,7 @@ angular } // Check if the recipient is a contact - addressbookService.get(originCoin + address, function onGetContact(err, contact) { + addressbookService.get(originCoin + address, function onGetContact(err, contact) { if (!err && contact) { handleDestinationAsAddressOfContact(contact); } else { @@ -631,7 +631,7 @@ angular if (vm.originWallet.coin !== 'bch') { return; } - + var address = vm.destinationAddress; if (address) { // So the address can be parsed properly @@ -687,11 +687,11 @@ angular walletService.getAddress(vm.originWallet, false, function onReturnWalletAddress(err, returnAddr) { if (err) { return cb(err); - } + } walletService.getAddress(toWallet, false, function onWithdrawalWalletAddress(err, withdrawalAddr) { if (err) { return cb(err); - } + } // Need to use the correct service to do it. var amount = parseFloat(satoshis / 100000000); @@ -728,7 +728,7 @@ angular ionicToast.show(gettextCatalog.getString('Copied to clipboard'), 'bottom', false, 3000); clipboardService.copyToClipboard(explorerTxUrl); } - + } function _onTransactionCompletedSuccessfully() { diff --git a/src/js/services/goCryptoService.js b/src/js/services/goCryptoService.js index 0130802ce..b996de88d 100644 --- a/src/js/services/goCryptoService.js +++ b/src/js/services/goCryptoService.js @@ -172,8 +172,8 @@ if (paymentOption == null) { raiseError( defer, - 'Incorrect GoCrypto payment order.', - 'Incorrect GoCrypto payment order.' + 'Incorrect GoCrypto payment order details.', + 'Incorrect GoCrypto payment order details.' ); } }