2014-03-26 05:18:42 -07:00
|
|
|
'use strict';
|
2014-06-12 13:42:26 -07:00
|
|
|
var bitcore = require('bitcore');
|
2014-03-26 05:18:42 -07:00
|
|
|
|
2014-06-03 13:42:36 -07:00
|
|
|
angular.module('copayApp.controllers').controller('SendController',
|
2014-08-27 12:01:25 -07:00
|
|
|
function($scope, $rootScope, $window, $timeout, $anchorScroll, $modal, isMobile, notification, controllerUtils, rateService) {
|
2014-03-26 05:18:42 -07:00
|
|
|
$scope.title = 'Send';
|
2014-04-24 18:43:19 -07:00
|
|
|
$scope.loading = false;
|
2014-06-16 08:44:18 -07:00
|
|
|
var satToUnit = 1 / config.unitToSatoshi;
|
|
|
|
$scope.defaultFee = bitcore.TransactionBuilder.FEE_PER_1000B_SAT * satToUnit;
|
|
|
|
$scope.unitToBtc = config.unitToSatoshi / bitcore.util.COIN;
|
2014-08-29 09:36:38 -07:00
|
|
|
$scope.unitToSatoshi = config.unitToSatoshi;
|
2014-08-21 12:02:02 -07:00
|
|
|
$scope.minAmount = config.limits.minAmountSatoshi * satToUnit;
|
2014-08-27 13:15:05 -07:00
|
|
|
|
2014-08-29 09:36:38 -07:00
|
|
|
$scope.alternativeName = config.alternativeName;
|
|
|
|
$scope.alternativeIsoCode = config.alternativeIsoCode;
|
2014-08-27 10:20:13 -07:00
|
|
|
|
2014-08-29 09:36:38 -07:00
|
|
|
$scope.isRateAvailable = false;
|
|
|
|
$scope.rateService = rateService;
|
|
|
|
|
|
|
|
rateService.whenAvailable(function() {
|
|
|
|
$scope.isRateAvailable = true;
|
|
|
|
$scope.$digest();
|
|
|
|
});
|
2014-08-27 12:01:25 -07:00
|
|
|
|
2014-08-28 07:37:39 -07:00
|
|
|
/**
|
|
|
|
* Setting the two related amounts as properties prevents an infinite
|
|
|
|
* recursion for watches while preserving the original angular updates
|
|
|
|
*/
|
2014-08-27 10:20:13 -07:00
|
|
|
Object.defineProperty($scope,
|
2014-08-27 12:01:25 -07:00
|
|
|
"alternative", {
|
|
|
|
get: function () {
|
|
|
|
return this._alternative;
|
|
|
|
},
|
|
|
|
set: function (newValue) {
|
|
|
|
this._alternative = newValue;
|
2014-08-29 10:06:20 -07:00
|
|
|
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
|
2014-09-04 07:42:21 -07:00
|
|
|
this._amount = parseFloat(
|
2014-08-27 13:19:28 -07:00
|
|
|
(rateService.fromFiat(newValue, config.alternativeIsoCode) * satToUnit
|
2014-08-28 07:37:39 -07:00
|
|
|
).toFixed(config.unitDecimals), 10);
|
2014-08-29 10:06:20 -07:00
|
|
|
} else {
|
|
|
|
this._amount = 0;
|
|
|
|
}
|
2014-08-27 12:01:25 -07:00
|
|
|
},
|
|
|
|
enumerable: true,
|
|
|
|
configurable: true
|
2014-08-27 10:20:13 -07:00
|
|
|
});
|
|
|
|
Object.defineProperty($scope,
|
2014-08-27 12:01:25 -07:00
|
|
|
"amount", {
|
|
|
|
get: function () {
|
|
|
|
return this._amount;
|
|
|
|
},
|
|
|
|
set: function (newValue) {
|
|
|
|
this._amount = newValue;
|
2014-08-29 10:06:20 -07:00
|
|
|
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
|
2014-09-04 07:42:21 -07:00
|
|
|
this._alternative = parseFloat(
|
2014-08-27 13:19:28 -07:00
|
|
|
(rateService.toFiat(newValue * config.unitToSatoshi, config.alternativeIsoCode)
|
2014-08-28 07:37:39 -07:00
|
|
|
).toFixed(2), 10);
|
2014-08-29 10:06:20 -07:00
|
|
|
} else {
|
|
|
|
this._alternative = 0;
|
|
|
|
}
|
2014-08-27 12:01:25 -07:00
|
|
|
},
|
|
|
|
enumerable: true,
|
|
|
|
configurable: true
|
2014-08-27 10:20:13 -07:00
|
|
|
});
|
2014-03-31 08:48:35 -07:00
|
|
|
|
2014-08-06 09:59:33 -07:00
|
|
|
$scope.loadTxs = function() {
|
|
|
|
var opts = {
|
|
|
|
pending: true,
|
|
|
|
skip: null
|
|
|
|
};
|
|
|
|
controllerUtils.updateTxs(opts);
|
|
|
|
setTimeout(function() {
|
|
|
|
$scope.loading = false;
|
|
|
|
$rootScope.$digest();
|
|
|
|
}, 0);
|
|
|
|
}
|
|
|
|
|
2014-06-18 16:18:13 -07:00
|
|
|
$scope.showAddressBook = function() {
|
|
|
|
var w = $rootScope.wallet;
|
|
|
|
var flag;
|
|
|
|
if (w) {
|
|
|
|
for (var k in w.addressBook) {
|
2014-07-07 13:38:17 -07:00
|
|
|
if (w.addressBook[k]) {
|
2014-06-18 16:18:13 -07:00
|
|
|
flag = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return flag;
|
|
|
|
};
|
|
|
|
|
2014-07-02 13:35:37 -07:00
|
|
|
if ($rootScope.pendingPayment) {
|
|
|
|
var pp = $rootScope.pendingPayment;
|
2014-08-15 19:00:12 -07:00
|
|
|
$scope.address = pp.address + '';
|
2014-08-13 11:10:37 -07:00
|
|
|
var amount = pp.data.amount / config.unitToSatoshi * 100000000;
|
2014-07-02 13:35:37 -07:00
|
|
|
$scope.amount = amount;
|
2014-08-13 11:10:37 -07:00
|
|
|
$scope.commentText = pp.data.message;
|
2014-07-02 13:35:37 -07:00
|
|
|
}
|
|
|
|
|
2014-05-06 13:02:49 -07:00
|
|
|
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
|
|
|
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
|
|
|
|
$scope.isMobile = isMobile.any();
|
2014-04-19 00:55:32 -07:00
|
|
|
|
|
|
|
$scope.submitForm = function(form) {
|
|
|
|
if (form.$invalid) {
|
2014-06-26 13:17:24 -07:00
|
|
|
var message = 'Unable to send transaction proposal.';
|
|
|
|
notification.error('Error', message);
|
2014-04-19 00:55:32 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-24 18:43:19 -07:00
|
|
|
$scope.loading = true;
|
|
|
|
|
2014-04-19 00:55:32 -07:00
|
|
|
var address = form.address.$modelValue;
|
2014-08-22 19:04:59 -07:00
|
|
|
var amount = parseInt((form.amount.$modelValue * config.unitToSatoshi).toFixed(0));
|
2014-06-13 15:45:00 -07:00
|
|
|
var commentText = form.comment.$modelValue;
|
2014-04-19 00:55:32 -07:00
|
|
|
|
|
|
|
var w = $rootScope.wallet;
|
2014-06-13 15:45:00 -07:00
|
|
|
|
2014-08-04 10:47:04 -07:00
|
|
|
function done(ntxid, merchantData) {
|
2014-08-06 23:02:31 -07:00
|
|
|
// If user is granted the privilege of choosing
|
|
|
|
// their own amount, add it to the tx.
|
2014-08-06 10:46:13 -07:00
|
|
|
if (merchantData && +merchantData.total === 0) {
|
2014-08-06 17:17:01 -07:00
|
|
|
var txp = w.txProposals.get(ntxid);
|
2014-08-06 23:02:31 -07:00
|
|
|
var tx = txp.builder.tx = txp.builder.tx || txp.builder.build();
|
|
|
|
tx.outs[0].v = bitcore.Bignum(amount + '', 10).toBuffer({
|
|
|
|
// XXX This may not work in node due
|
|
|
|
// to the bignum only-big endian bug:
|
2014-08-06 10:46:13 -07:00
|
|
|
endian: 'little',
|
|
|
|
size: 1
|
|
|
|
});
|
|
|
|
}
|
2014-08-06 23:02:31 -07:00
|
|
|
|
2014-08-04 12:27:58 -07:00
|
|
|
if (w.isShared()) {
|
2014-06-24 11:46:07 -07:00
|
|
|
$scope.loading = false;
|
2014-06-26 13:17:24 -07:00
|
|
|
var message = 'The transaction proposal has been created';
|
2014-07-31 13:16:03 -07:00
|
|
|
if (merchantData) {
|
2014-08-04 10:47:04 -07:00
|
|
|
if (merchantData.pr.ca) {
|
2014-08-13 15:42:04 -07:00
|
|
|
message += ' This payment protocol transaction' + ' has been verified through ' + merchantData.pr.ca + '.';
|
2014-08-04 10:47:04 -07:00
|
|
|
}
|
2014-08-07 11:38:26 -07:00
|
|
|
message += ' Message from server: ' + merchantData.ack.memo;
|
|
|
|
message += ' For merchant: ' + merchantData.pr.pd.payment_url;
|
2014-07-28 14:46:08 -07:00
|
|
|
}
|
2014-06-26 13:17:24 -07:00
|
|
|
notification.success('Success!', message);
|
2014-08-06 09:59:33 -07:00
|
|
|
$scope.loadTxs();
|
2014-06-24 11:46:07 -07:00
|
|
|
} else {
|
2014-08-04 10:47:04 -07:00
|
|
|
w.sendTx(ntxid, function(txid, merchantData) {
|
2014-06-26 13:17:24 -07:00
|
|
|
if (txid) {
|
2014-07-31 13:16:03 -07:00
|
|
|
var message = 'Transaction id: ' + txid;
|
|
|
|
if (merchantData) {
|
2014-08-04 10:47:04 -07:00
|
|
|
if (merchantData.pr.ca) {
|
2014-08-13 15:42:04 -07:00
|
|
|
message += ' This payment protocol transaction' + ' has been verified through ' + merchantData.pr.ca + '.';
|
2014-08-04 10:47:04 -07:00
|
|
|
}
|
2014-08-07 11:38:26 -07:00
|
|
|
message += ' Message from server: ' + merchantData.ack.memo;
|
|
|
|
message += ' For merchant: ' + merchantData.pr.pd.payment_url;
|
2014-07-31 13:16:03 -07:00
|
|
|
}
|
|
|
|
notification.success('Transaction broadcast', message);
|
2014-06-26 13:17:24 -07:00
|
|
|
} else {
|
|
|
|
notification.error('Error', 'There was an error sending the transaction.');
|
|
|
|
}
|
2014-06-24 11:46:07 -07:00
|
|
|
$scope.loading = false;
|
2014-08-07 05:53:20 -07:00
|
|
|
$scope.loadTxs();
|
2014-06-24 11:46:07 -07:00
|
|
|
});
|
|
|
|
}
|
2014-07-02 13:35:37 -07:00
|
|
|
$rootScope.pendingPayment = null;
|
2014-07-28 14:46:08 -07:00
|
|
|
}
|
|
|
|
|
2014-08-22 11:19:39 -07:00
|
|
|
// XXX Payment Protocol is temporarily disabled.
|
|
|
|
// var uri;
|
|
|
|
// if (address.indexOf('bitcoin:') === 0) {
|
|
|
|
// uri = new bitcore.BIP21(address).data;
|
|
|
|
// } else if (/^https?:\/\//.test(address)) {
|
|
|
|
// uri = {
|
|
|
|
// merchant: address
|
|
|
|
// };
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// if (uri && uri.merchant) {
|
|
|
|
// w.createPaymentTx({
|
|
|
|
// uri: uri.merchant,
|
|
|
|
// memo: commentText
|
|
|
|
// }, done);
|
|
|
|
// } else {
|
|
|
|
// w.createTx(address, amount, commentText, done);
|
|
|
|
// }
|
|
|
|
|
|
|
|
w.createTx(address, amount, commentText, done);
|
2014-04-24 18:43:19 -07:00
|
|
|
|
|
|
|
// reset fields
|
2014-07-25 13:45:06 -07:00
|
|
|
$scope.address = $scope.amount = $scope.commentText = null;
|
|
|
|
form.address.$pristine = form.amount.$pristine = true;
|
2014-05-06 13:02:49 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// QR code Scanner
|
|
|
|
var cameraInput;
|
|
|
|
var video;
|
|
|
|
var canvas;
|
|
|
|
var $video;
|
|
|
|
var context;
|
|
|
|
var localMediaStream;
|
|
|
|
|
|
|
|
var _scan = function(evt) {
|
|
|
|
if ($scope.isMobile) {
|
|
|
|
$scope.scannerLoading = true;
|
|
|
|
var files = evt.target.files;
|
|
|
|
|
|
|
|
if (files.length === 1 && files[0].type.indexOf('image/') === 0) {
|
|
|
|
var file = files[0];
|
|
|
|
|
|
|
|
var reader = new FileReader();
|
|
|
|
reader.onload = (function(theFile) {
|
|
|
|
return function(e) {
|
|
|
|
var mpImg = new MegaPixImage(file);
|
2014-06-12 13:42:26 -07:00
|
|
|
mpImg.render(canvas, {
|
|
|
|
maxWidth: 200,
|
|
|
|
maxHeight: 200,
|
|
|
|
orientation: 6
|
|
|
|
});
|
2014-05-06 13:02:49 -07:00
|
|
|
|
|
|
|
$timeout(function() {
|
|
|
|
qrcode.width = canvas.width;
|
|
|
|
qrcode.height = canvas.height;
|
|
|
|
qrcode.imagedata = context.getImageData(0, 0, qrcode.width, qrcode.height);
|
|
|
|
|
|
|
|
try {
|
|
|
|
qrcode.decode();
|
|
|
|
} catch (e) {
|
2014-06-03 14:38:56 -07:00
|
|
|
// error decoding QR
|
2014-05-06 13:02:49 -07:00
|
|
|
}
|
|
|
|
}, 1500);
|
|
|
|
};
|
|
|
|
})(file);
|
|
|
|
|
|
|
|
// Read in the file as a data URL
|
|
|
|
reader.readAsDataURL(file);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (localMediaStream) {
|
|
|
|
context.drawImage(video, 0, 0, 300, 225);
|
|
|
|
|
|
|
|
try {
|
|
|
|
qrcode.decode();
|
2014-06-12 13:42:26 -07:00
|
|
|
} catch (e) {
|
2014-05-06 13:02:49 -07:00
|
|
|
//qrcodeError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$timeout(_scan, 500);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var _successCallback = function(stream) {
|
|
|
|
video.src = (window.URL && window.URL.createObjectURL(stream)) || stream;
|
|
|
|
localMediaStream = stream;
|
|
|
|
video.play();
|
|
|
|
$timeout(_scan, 1000);
|
|
|
|
};
|
|
|
|
|
|
|
|
var _scanStop = function() {
|
|
|
|
$scope.scannerLoading = false;
|
|
|
|
$scope.showScanner = false;
|
|
|
|
if (!$scope.isMobile) {
|
|
|
|
if (localMediaStream && localMediaStream.stop) localMediaStream.stop();
|
|
|
|
localMediaStream = null;
|
|
|
|
video.src = '';
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var _videoError = function(err) {
|
|
|
|
_scanStop();
|
|
|
|
};
|
|
|
|
|
|
|
|
qrcode.callback = function(data) {
|
|
|
|
_scanStop();
|
|
|
|
|
|
|
|
var str = (data.indexOf('bitcoin:') === 0) ? data.substring(8) : data;
|
2014-05-07 07:21:30 -07:00
|
|
|
$scope.$apply(function() {
|
|
|
|
$scope.address = str;
|
|
|
|
});
|
2014-05-06 13:02:49 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
$scope.cancelScanner = function() {
|
|
|
|
_scanStop();
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.openScanner = function() {
|
2014-07-29 22:55:59 -07:00
|
|
|
if (window.cordova) return $scope.scannerIntent();
|
|
|
|
|
2014-05-06 13:02:49 -07:00
|
|
|
$scope.showScanner = true;
|
|
|
|
|
|
|
|
// Wait a moment until the canvas shows
|
|
|
|
$timeout(function() {
|
|
|
|
canvas = document.getElementById('qr-canvas');
|
|
|
|
context = canvas.getContext('2d');
|
|
|
|
|
|
|
|
if ($scope.isMobile) {
|
|
|
|
cameraInput = document.getElementById('qrcode-camera');
|
|
|
|
cameraInput.addEventListener('change', _scan, false);
|
|
|
|
} else {
|
|
|
|
video = document.getElementById('qrcode-scanner-video');
|
|
|
|
$video = angular.element(video);
|
|
|
|
canvas.width = 300;
|
|
|
|
canvas.height = 225;
|
|
|
|
context.clearRect(0, 0, 300, 225);
|
2014-04-24 18:43:19 -07:00
|
|
|
|
2014-06-12 13:42:26 -07:00
|
|
|
navigator.getUserMedia({
|
|
|
|
video: true
|
|
|
|
}, _successCallback, _videoError);
|
2014-05-06 13:02:49 -07:00
|
|
|
}
|
|
|
|
}, 500);
|
2014-04-24 18:43:19 -07:00
|
|
|
};
|
2014-06-17 21:00:32 -07:00
|
|
|
|
2014-07-25 07:08:29 -07:00
|
|
|
$scope.scannerIntent = function() {
|
|
|
|
cordova.plugins.barcodeScanner.scan(
|
|
|
|
function onSuccess(result) {
|
|
|
|
if (result.cancelled) return;
|
|
|
|
|
2014-08-13 11:10:37 -07:00
|
|
|
var bip21 = new bitcore.BIP21(result.text);
|
|
|
|
$scope.address = bip21.address + '';
|
|
|
|
$scope.commentText = bip21.data.message;
|
2014-07-25 07:08:29 -07:00
|
|
|
|
2014-08-13 11:10:37 -07:00
|
|
|
if (bip21.data.amount) {
|
|
|
|
$scope.amount = bip21.data.amount * bitcore.util.COIN * satToUnit;
|
2014-07-25 07:08:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
$rootScope.$digest();
|
|
|
|
},
|
|
|
|
function onError(error) {
|
|
|
|
alert('Scanning error');
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:33:39 -07:00
|
|
|
$scope.toggleAddressBookEntry = function(key) {
|
2014-07-07 10:53:34 -07:00
|
|
|
var w = $rootScope.wallet;
|
|
|
|
w.toggleAddressBookEntry(key);
|
2014-06-17 21:00:32 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
$scope.copyAddress = function(address) {
|
|
|
|
$scope.address = address;
|
|
|
|
$anchorScroll();
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.openAddressBookModal = function() {
|
|
|
|
var modalInstance = $modal.open({
|
2014-07-18 14:51:55 -07:00
|
|
|
templateUrl: 'views/modals/address-book.html',
|
2014-06-17 21:00:32 -07:00
|
|
|
windowClass: 'tiny',
|
|
|
|
controller: function($scope, $modalInstance) {
|
2014-06-24 08:36:32 -07:00
|
|
|
|
2014-06-17 21:00:32 -07:00
|
|
|
$scope.submitAddressBook = function(form) {
|
|
|
|
if (form.$invalid) {
|
2014-06-26 13:17:24 -07:00
|
|
|
notification.error('Form Error', 'Please complete required fields');
|
2014-06-17 21:00:32 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
var entry = {
|
|
|
|
"address": form.newaddress.$modelValue,
|
|
|
|
"label": form.newlabel.$modelValue
|
|
|
|
};
|
|
|
|
form.newaddress.$pristine = form.newlabel.$pristine = true;
|
|
|
|
$modalInstance.close(entry);
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.cancel = function() {
|
|
|
|
$modalInstance.dismiss('cancel');
|
|
|
|
};
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
modalInstance.result.then(function(entry) {
|
|
|
|
var w = $rootScope.wallet;
|
|
|
|
|
|
|
|
$timeout(function() {
|
|
|
|
$scope.loading = false;
|
|
|
|
var errorMsg;
|
|
|
|
try {
|
2014-06-18 16:18:13 -07:00
|
|
|
w.setAddressBook(entry.address, entry.label);
|
2014-06-17 21:00:32 -07:00
|
|
|
} catch (e) {
|
|
|
|
errorMsg = e.message;
|
|
|
|
}
|
|
|
|
|
2014-06-26 13:17:24 -07:00
|
|
|
if (errorMsg) {
|
|
|
|
notification.error('Error', errorMsg);
|
|
|
|
} else {
|
|
|
|
notification.success('Success', 'New entry has been created');
|
|
|
|
}
|
2014-06-17 21:00:32 -07:00
|
|
|
$rootScope.$digest();
|
|
|
|
}, 500);
|
|
|
|
// reset fields
|
|
|
|
$scope.newaddress = $scope.newlabel = null;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-06-25 14:54:08 -07:00
|
|
|
$scope.getAvailableAmount = function() {
|
|
|
|
return ((($rootScope.availableBalance * config.unitToSatoshi).toFixed(0) - bitcore.TransactionBuilder.FEE_PER_1000B_SAT) / config.unitToSatoshi);
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.topAmount = function(form) {
|
2014-07-02 13:35:37 -07:00
|
|
|
$scope.amount = $scope.getAvailableAmount();
|
2014-06-25 14:54:08 -07:00
|
|
|
form.amount.$pristine = false;
|
2014-06-19 11:55:04 -07:00
|
|
|
};
|
2014-08-06 09:59:33 -07:00
|
|
|
|
|
|
|
|
|
|
|
$scope.send = function(ntxid, cb) {
|
|
|
|
$scope.loading = true;
|
|
|
|
$rootScope.txAlertCount = 0;
|
|
|
|
var w = $rootScope.wallet;
|
2014-08-04 10:47:04 -07:00
|
|
|
w.sendTx(ntxid, function(txid, merchantData) {
|
2014-08-06 09:59:33 -07:00
|
|
|
if (!txid) {
|
|
|
|
notification.error('Error', 'There was an error sending the transaction');
|
|
|
|
} else {
|
2014-08-04 10:47:04 -07:00
|
|
|
if (!merchantData) {
|
2014-08-13 15:42:04 -07:00
|
|
|
notification.success('Transaction broadcast', 'Transaction id: ' + txid);
|
2014-08-04 10:41:35 -07:00
|
|
|
} else {
|
|
|
|
var message = 'Transaction ID: ' + txid;
|
2014-08-04 10:47:04 -07:00
|
|
|
if (merchantData.pr.ca) {
|
2014-08-13 15:42:04 -07:00
|
|
|
message += ' This payment protocol transaction' + ' has been verified through ' + merchantData.pr.ca + '.';
|
2014-08-04 10:41:35 -07:00
|
|
|
}
|
2014-08-07 15:15:02 -07:00
|
|
|
message += ' Message from server: ' + merchantData.ack.memo;
|
|
|
|
message += ' For merchant: ' + merchantData.pr.pd.payment_url;
|
2014-08-04 10:41:35 -07:00
|
|
|
notification.success('Transaction sent', message);
|
|
|
|
}
|
2014-08-06 09:59:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cb) return cb();
|
|
|
|
else $scope.loadTxs();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.sign = function(ntxid) {
|
|
|
|
$scope.loading = true;
|
|
|
|
var w = $rootScope.wallet;
|
|
|
|
w.sign(ntxid, function(ret) {
|
|
|
|
if (!ret) {
|
|
|
|
notification.error('Error', 'There was an error signing the transaction');
|
|
|
|
$scope.loadTxs();
|
|
|
|
} else {
|
|
|
|
var p = w.txProposals.getTxProposal(ntxid);
|
|
|
|
if (p.builder.isFullySigned()) {
|
|
|
|
$scope.send(ntxid, function() {
|
|
|
|
$scope.loadTxs();
|
|
|
|
});
|
|
|
|
} else
|
|
|
|
$scope.loadTxs();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.reject = function(ntxid) {
|
|
|
|
$scope.loading = true;
|
|
|
|
$rootScope.txAlertCount = 0;
|
|
|
|
var w = $rootScope.wallet;
|
|
|
|
w.reject(ntxid);
|
|
|
|
notification.warning('Transaction rejected', 'You rejected the transaction successfully');
|
|
|
|
$scope.loading = false;
|
|
|
|
$scope.loadTxs();
|
|
|
|
};
|
|
|
|
|
2014-08-13 15:42:04 -07:00
|
|
|
|
|
|
|
$scope.onChanged = function() {
|
|
|
|
var scope = $scope;
|
2014-08-19 10:21:40 -07:00
|
|
|
var value = scope.address || '';
|
2014-08-13 15:54:07 -07:00
|
|
|
var uri;
|
|
|
|
|
2014-08-19 10:21:40 -07:00
|
|
|
if (value.indexOf('bitcoin:') === 0) {
|
|
|
|
uri = new bitcore.BIP21(value).data;
|
|
|
|
} else if (/^https?:\/\//.test(value)) {
|
2014-08-13 15:54:07 -07:00
|
|
|
uri = {
|
|
|
|
merchant: value
|
|
|
|
};
|
|
|
|
}
|
2014-08-19 10:21:40 -07:00
|
|
|
|
2014-08-13 15:54:07 -07:00
|
|
|
if (!uri || !uri.merchant) {
|
|
|
|
return;
|
|
|
|
}
|
2014-08-19 10:21:40 -07:00
|
|
|
|
2014-08-13 15:42:04 -07:00
|
|
|
notification.info('Fetching Payment',
|
|
|
|
'Retrieving Payment Request from ' + uri.merchant);
|
|
|
|
|
|
|
|
// Payment Protocol URI (BIP-72)
|
|
|
|
scope.wallet.fetchPaymentTx(uri.merchant, function(err, merchantData) {
|
|
|
|
var balance = $rootScope.availableBalance;
|
|
|
|
var available = +(balance * config.unitToSatoshi).toFixed(0);
|
|
|
|
|
|
|
|
if (merchantData && available < +merchantData.total) {
|
|
|
|
err = new Error('No unspent outputs available.');
|
2014-08-13 17:51:33 -07:00
|
|
|
err.amount = merchantData.total;
|
2014-08-13 15:42:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
scope.sendForm.address.$isValid = false;
|
2014-08-13 18:06:57 -07:00
|
|
|
|
2014-08-13 17:51:33 -07:00
|
|
|
if (err.amount) {
|
|
|
|
scope.sendForm.amount.$setViewValue(+err.amount / config.unitToSatoshi);
|
|
|
|
scope.sendForm.amount.$render();
|
|
|
|
scope.sendForm.amount.$isValid = false;
|
|
|
|
scope.notEnoughAmount = true;
|
2014-08-13 18:06:57 -07:00
|
|
|
$rootScope.merchantError = true;
|
|
|
|
var lastAddr = scope.sendForm.address.$viewValue;
|
|
|
|
var unregister = scope.$watch('address', function() {
|
|
|
|
if (scope.sendForm.address.$viewValue !== lastAddr) {
|
|
|
|
delete $rootScope.merchantError;
|
|
|
|
scope.sendForm.amount.$setViewValue('');
|
|
|
|
scope.sendForm.amount.$render();
|
|
|
|
unregister();
|
|
|
|
if ($rootScope.$$phase !== '$apply' && $rootScope.$$phase !== '$digest') {
|
|
|
|
$rootScope.$apply();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2014-08-13 17:51:33 -07:00
|
|
|
}
|
2014-08-13 18:06:57 -07:00
|
|
|
|
2014-08-13 15:42:04 -07:00
|
|
|
notification.error('Error', err.message || 'Bad payment server.');
|
2014-08-13 18:06:57 -07:00
|
|
|
|
2014-08-13 17:29:02 -07:00
|
|
|
if ($rootScope.$$phase !== '$apply' && $rootScope.$$phase !== '$digest') {
|
|
|
|
$rootScope.$apply();
|
|
|
|
}
|
2014-08-13 15:42:04 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
merchantData.unitTotal = (+merchantData.total / config.unitToSatoshi) + '';
|
|
|
|
merchantData.expiration = new Date(
|
|
|
|
merchantData.pr.pd.expires * 1000).toISOString();
|
|
|
|
|
|
|
|
$rootScope.merchant = merchantData;
|
|
|
|
|
|
|
|
scope.sendForm.address.$isValid = true;
|
|
|
|
|
|
|
|
scope.sendForm.amount.$setViewValue(merchantData.unitTotal);
|
|
|
|
scope.sendForm.amount.$render();
|
|
|
|
scope.sendForm.amount.$isValid = true;
|
|
|
|
|
|
|
|
// If the address changes to a non-payment-protocol one,
|
|
|
|
// delete the `merchant` property from the scope.
|
|
|
|
var unregister = scope.$watch('address', function() {
|
|
|
|
var val = scope.sendForm.address.$viewValue || '';
|
2014-08-19 10:21:40 -07:00
|
|
|
var uri;
|
|
|
|
if (val.indexOf('bitcoin:') === 0) {
|
|
|
|
uri = new bitcore.BIP21(val).data;
|
|
|
|
} else if (/^https?:\/\//.test(val)) {
|
|
|
|
uri = {
|
|
|
|
merchant: val
|
|
|
|
};
|
|
|
|
}
|
2014-08-13 15:42:04 -07:00
|
|
|
if (!uri || !uri.merchant) {
|
|
|
|
delete $rootScope.merchant;
|
|
|
|
scope.sendForm.amount.$setViewValue('');
|
|
|
|
scope.sendForm.amount.$render();
|
|
|
|
unregister();
|
|
|
|
if ($rootScope.$$phase !== '$apply' && $rootScope.$$phase !== '$digest') {
|
|
|
|
$rootScope.$apply();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if ($rootScope.$$phase !== '$apply' && $rootScope.$$phase !== '$digest') {
|
|
|
|
$rootScope.$apply();
|
|
|
|
}
|
|
|
|
|
|
|
|
notification.info('Payment Request',
|
|
|
|
'Server is requesting ' + merchantData.unitTotal + ' ' + config.unitName + '.' + ' Message: ' + merchantData.pr.pd.memo);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-08-22 11:19:39 -07:00
|
|
|
// XXX Payment Protocol is temporarily disabled.
|
|
|
|
$scope.onChanged = function() {
|
|
|
|
;
|
|
|
|
};
|
|
|
|
|
2014-03-26 05:18:42 -07:00
|
|
|
});
|