paypro working 1-1

This commit is contained in:
Matias Alejo Garcia 2014-12-09 01:29:06 -03:00
commit 5f6c9482b4
6 changed files with 125 additions and 112 deletions

2
TODO
View file

@ -1,2 +1,4 @@
- join. on walletComplete! - join. on walletComplete!
- paypro fetch-> return TIMEOUT - paypro fetch-> return TIMEOUT
- yellow lock on paypro
- attack: change paypro ->no! in is cached

View file

@ -99,15 +99,14 @@ header .alt-currency {
font-weight: 700; font-weight: 700;
} }
.green { .color-greeni {
color: #1abc9c; color: #1abc9c !important;
} }
.red { .color-yellowi {
color: #A02F23; color: yellow !important;
} }
.alt-currency.green { .alt-currency.green {
background: #1abc9c; background: #1abc9c;
} }

View file

@ -119,38 +119,44 @@ angular.module('copayApp.controllers').controller('SendController',
if (msg.match('totalNeededAmount') || msg.match('unspent not set')) if (msg.match('totalNeededAmount') || msg.match('unspent not set'))
msg = 'Insufficient funds' msg = 'Insufficient funds'
if (msg.match('expired'))
msg = 'The payment request has expired';
var message = 'The transaction' + (w.isShared() ? ' proposal' : '') + var message = 'The transaction' + (w.isShared() ? ' proposal' : '') +
' could not be created: ' + msg; ' could not be created: ' + msg;
$scope.error = message; $scope.error = message;
$timeout(function(){ $timeout(function() {
$scope.$digest(); $scope.$digest();
},1); }, 1);
}; };
$scope.submitForm = function(form) { $scope.submitForm = function(form) {
if (form.$invalid) { if (form.$invalid) {
$scope.error = 'Unable to send transaction proposal'; $scope.error = 'Unable to send transaction proposal';
return; return;
} }
$scope.loading = true; $scope.loading = true;
var url = $scope._url;
var address = form.address.$modelValue;
var amount = parseInt((form.amount.$modelValue * unitToSat).toFixed(0));
var comment = form.comment.$modelValue; var comment = form.comment.$modelValue;
var merchantData = $scope._merchantData;
var address, amount;
if (!merchantData) {
address = form.address.$modelValue;
amount = parseInt((form.amount.$modelValue * unitToSat).toFixed(0));
}
w.spend({ w.spend({
merchantData: merchantData,
toAddress: address, toAddress: address,
amountSat: amount, amountSat: amount,
comment: comment, comment: comment,
url: url,
}, function(err, txid, status) { }, function(err, txid, status) {
$scope.loading = false; $scope.loading = false;
if (err) if (err)
return $scope.setError(err); return $scope.setError(err);
$scope.resetForm(status); $scope.resetForm(status);
@ -331,13 +337,15 @@ angular.module('copayApp.controllers').controller('SendController',
$scope.setForm = function(to, amount, comment) { $scope.setForm = function(to, amount, comment) {
var form = $scope.sendForm; var form = $scope.sendForm;
form.address.$setViewValue(to); if (to) {
form.address.$isValid = true; form.address.$setViewValue(to);
form.address.$render(); form.address.$isValid = true;
$scope.lockAddress = true; form.address.$render();
$scope.lockAddress = true;
}
if (amount) { if (amount) {
form.amount.$setViewValue(""+amount); form.amount.$setViewValue("" + amount);
form.amount.$isValid = true; form.amount.$isValid = true;
form.amount.$render(); form.amount.$render();
$scope.lockAmount = true; $scope.lockAmount = true;
@ -353,28 +361,42 @@ angular.module('copayApp.controllers').controller('SendController',
$scope.resetForm = function(status) { $scope.resetForm = function(status) {
var form = $scope.sendForm; var form = $scope.sendForm;
form.address.$pristine = form.amount.$pristine = true;
$scope.fetchingURL = null; $scope.fetchingURL = null;
$scope._merchantData = $scope._domain = null;
$scope.lockAddress = false; $scope.lockAddress = false;
$scope.lockAmount = false; $scope.lockAmount = false;
form.address.$setViewValue('');
form.address.$render(); $scope._amount = $scope._address = null;
form.amount.$pristine = true;
form.amount.$setViewValue(''); form.amount.$setViewValue('');
form.amount.$render(); form.amount.$render();
form.comment.$setViewValue(''); form.comment.$setViewValue('');
form.comment.$render(); form.comment.$render();
form.$setPristine(); form.$setPristine();
$scope.notifyStatus(status); $scope.notifyStatus(status);
$timeout(function(){ $timeout(function() {
if (form.address) {
form.address.$pristine = true;
form.address.$setViewValue('');
form.address.$render();
}
$rootScope.$digest(); $rootScope.$digest();
},1); }, 1);
}; };
var $oscope = $scope;
$scope.openPPModal = function(pp) { $scope.openPPModal = function(merchantData) {
var ModalInstanceCtrl = function($scope, $modalInstance) { var ModalInstanceCtrl = function($scope, $modalInstance) {
$scope.pp = pp; $scope.md = merchantData;
$scope.alternative = $oscope._alternative;
$scope.alternativeIsoCode = $oscope.alternativeIsoCode;
$scope.isRateAvailable = $oscope.isRateAvailable;
$scope.cancel = function() { $scope.cancel = function() {
$modalInstance.dismiss('cancel'); $modalInstance.dismiss('cancel');
}; };
@ -393,9 +415,6 @@ angular.module('copayApp.controllers').controller('SendController',
$scope.fetchingURL = uri; $scope.fetchingURL = uri;
$scope.loading = true; $scope.loading = true;
var balance = w.balanceInfo.availableBalance;
var available = +(balance * unitToSat).toFixed(0);
// Payment Protocol URI (BIP-72) // Payment Protocol URI (BIP-72)
w.fetchPaymentRequest({ w.fetchPaymentRequest({
url: uri url: uri
@ -404,15 +423,16 @@ angular.module('copayApp.controllers').controller('SendController',
$scope.fetchingURL = null; $scope.fetchingURL = null;
if (err) { if (err) {
if (err.match('TIMEOUT')) { copay.logger.warn(err);
if (err.toString().match('TIMEOUT')) {
$scope.resetForm('Payment server timed out'); $scope.resetForm('Payment server timed out');
} else { } else {
$scope.resetForm(err.toString()); $scope.resetForm(err.toString());
} }
} else if (merchantData && available < +merchantData.total) {
$scope.resetForm('Insufficient funds');
} else { } else {
$scope.setForm(merchantData.domain, merchantData.unitTotal) $scope._merchantData = merchantData;
$scope._domain = merchantData.domain;
$scope.setForm(null, merchantData.unitTotal);
} }
}); });
}; };
@ -428,9 +448,6 @@ angular.module('copayApp.controllers').controller('SendController',
}; };
var addr = parsed.address.toString(); var addr = parsed.address.toString();
console.log('[send.js.430:parsed:]',addr,parsed.data); //TODO
if (parsed.data.merchant) if (parsed.data.merchant)
return $scope.setFromPayPro(parsed.data.merchant); return $scope.setFromPayPro(parsed.data.merchant);

View file

@ -1747,9 +1747,11 @@ Wallet.prototype._addOutputsToMerchantData = function(merchantData) {
// If user is granted the privilege of choosing // If user is granted the privilege of choosing
// their own amount, add it to the tx. // their own amount, add it to the tx.
if (merchantData.total === 0 && options.amount) { if (merchantData.total == "0" && options.amount) {
merchant.outs[0].amountSatStr = merchantData.total = outions.amount; merchant.outs[0].amountSatStr = merchantData.total = options.amount;
} }
merchantData.unitTotal = merchantData.total ? (+merchantData.total / this.settings.unitToSatoshi) + '' : 0;
}; };
/** /**
@ -1829,11 +1831,11 @@ Wallet.prototype.parsePaymentRequest = function(options, rawData) {
request_url: options.url, request_url: options.url,
domain: /^(?:https?)?:\/\/([^\/:]+).*$/.exec(options.url)[1], domain: /^(?:https?)?:\/\/([^\/:]+).*$/.exec(options.url)[1],
total: total, total: total,
unitTotal: total ? (+total / w.settings.unitToSatoshi) + '' : null,
expirationDate: expires ? new Date(expires * 1000) : null, expirationDate: expires ? new Date(expires * 1000) : null,
}; };
this._addOutputsToMerchantData(merchantData, options.amount); this._addOutputsToMerchantData(merchantData, options.amount);
return merchantData; return merchantData;
}; };
@ -2178,9 +2180,21 @@ Wallet.prototype.spend = function(opts, cb) {
var toAddress = opts.toAddress; var toAddress = opts.toAddress;
var amountSat = opts.amountSat; var amountSat = opts.amountSat;
var comment = opts.comment; var comment = opts.comment;
var url = opts.url; var merchantData = opts.merchantData;
// PayPro? With given merchant data
if (opts.merchantData && !opts.toAddress) {
if (!merchantData.outs[0].address)
return cb(new Error('BADPAYPRO'));
opts.toAddress = merchantData.outs[0].address;
opts.amountSat = parseInt(merchantData.outs[0].amountSatStr);
return self.spend(opts, cb);
}
// PayPro? Fetch payment data and recurse // PayPro? Fetch payment data and recurse
var url = opts.url;
if (url && !opts.merchantData) { if (url && !opts.merchantData) {
return self.fetchPaymentRequest({ return self.fetchPaymentRequest({
url: url, url: url,
@ -2189,8 +2203,6 @@ Wallet.prototype.spend = function(opts, cb) {
}, function(err, merchantData) { }, function(err, merchantData) {
if (err) return cb(err); if (err) return cb(err);
opts.merchantData = merchantData; opts.merchantData = merchantData;
opts.toAddress = merchantData.outs[0].address;
opts.amountSat = parseInt(merchantData.outs[0].amountSatStr);
return self.spend(opts, cb); return self.spend(opts, cb);
}); });
} }
@ -2208,13 +2220,13 @@ Wallet.prototype.spend = function(opts, cb) {
try { try {
txp = self._createTxProposal(toAddress, txp = self._createTxProposal(toAddress,
amountSat, comment, safeUnspent, opts.builderOpts); amountSat, comment, safeUnspent, opts.builderOpts);
} catch (e) {
log.error(e);
return cb(e);
}
if (opts.merchantData) { if (opts.merchantData) {
txp.addMerchantData(opts.merchantData); txp.addMerchantData(opts.merchantData);
}
} catch (e) {
log.warn(e);
return cb(e);
} }
var ntxid = self.txProposals.add(txp); var ntxid = self.txProposals.add(txp);

View file

@ -1,25 +1,26 @@
<a class="close-reveal-modal" ng-click="cancel()">&#215;</a> <a class="close-reveal-modal" ng-click="cancel()">&#215;</a>
<div class="size-14"> <div class="size-14">
<h1 class="m30v">Payment Protocol Request</h1> <h1 class="m30v">Payment Request</h1>
<p> <p>
<b translate>Signature:</b> <b translate>Signature:</b> {{md.domain}}
<span ng-show="!!$root.merchant.pr.ca"><i class="fi-lock green"></i> {{$root.merchant.pr.ca}}</span> <span ng-show="md.pr.ca"><i class="fi-lock color-greeni"></i> {{md.pr.ca}}</span>
<span ng-show="!$root.merchant.pr.ca"><i class="fi-unlock red"></i> Untrusted</span> <span ng-show="!md.pr.ca"><i class="fi-unlock color-yellowi"></i> Untrusted</span>
<p> <p>
<b translate>Merchant Message:</b> <b translate>Merchant Message:</b>
{{$root.merchant.pr.pd.memo || address}} <br>
{{md.pr.pd.memo || address}}
</p> </p>
<p> <p>
<b translate>Merchant Message:</b> <b translate>Amount:</b>
<i>{{amount}} {{$root.wallet.settings.unitName}}</i> {{md.unitTotal}} {{$root.wallet.settings.unitName}}
<span class="text-gray" ng-if="isRateAvailable"> <span class="text-gray" ng-if="isRateAvailable">
{{ alternative }} {{ alternativeIsoCode }} {{ alternative }} {{ alternativeIsoCode }}
</span> </span>
<p ng-show="!!$root.merchant"> <p>
Expires {{$root.merchant.expiration | amTimeAgo }} [{{$root.merchant.domain}}] <b>Expires</b> {{md.expiration | amTimeAgo }}
</p> </p>
</div> </div>

View file

@ -29,47 +29,52 @@
</span> </span>
</div> </div>
<div ng-show="!_url"> <div ng-if="!_merchantData">
<div class="row collapse"> <div class="row collapse">
<label for="address" class="left"> <label for="address" class="left">
<span translate>To (*)</span> <span translate>To (*)</span>
</label> </label>
<span ng-hide="sendForm.address.$pristine"> <span ng-hide="sendForm.address.$pristine">
<span translate class="has-error right size-12" ng-show="sendForm.address.$invalid"> <span translate class="has-error right size-12" ng-show="sendForm.address.$invalid">
<span class="icon-input"> <span class="icon-input">
<i class="fi-x"></i> <i class="fi-x"></i>
</span> </span>
Not valid Not valid
</span> </span>
<small class="icon-input right" ng-show="!sendForm.address.$invalid"> <small class="icon-input right" ng-show="!sendForm.address.$invalid">
<i class="fi-check"></i> <i class="fi-check"></i>
</small> </small>
</span> </span>
</div>
<div class="input">
<input type="text" id="address" name="address" ng-disabled="loading || lockAddress" placeholder="{{'Bitcoin address'|translate}}" ng-model="_address" valid-address required>
<i class="fi-address-book"></i>
<div ng-hide="showScanner || disableScanner">
<a class="postfix button black" ng-click="openScanner()"><i class="fi-camera size-24"></i></a>
</div> </div>
<div ng-show="showScanner">
<a translate class="postfix button warning" ng-click="cancelScanner()"><i class="fi-x size-18"></i></a> <div class="input">
<input type="text" id="address" name="address" ng-disabled="loading || lockAddress" placeholder="{{'Bitcoin address'|translate}}" ng-model="_address" valid-address required>
<i class="fi-address-book"></i>
<div ng-hide="showScanner || disableScanner">
<a class="postfix button black" ng-click="openScanner()"><i class="fi-camera size-24"></i></a>
</div>
<div ng-show="showScanner">
<a translate class="postfix button warning" ng-click="cancelScanner()"><i class="fi-x size-18"></i></a>
</div>
</div>
<div id="scanner" class="row" ng-if="showScanner" ng-include="'views/includes/scanner.html'">
</div> </div>
</div> </div>
<div ng-show="_url"> <div ng-show="_merchantData">
<div class="row collapse"> <div class="row collapse" ng-click="openPPModal(_merchantData)">
{{_url}}
</div>
</div>
<div id="scanner" class="row" ng-if="showScanner" ng-include="'views/includes/scanner.html'"> <label for="domain">
</div> <span translate>Payment to</span>
</div> </label>
<div class="input">
<input type="text" id="domain" name="domain" ng-model="_domain" ng-disabled="1">
<i class="fi-lock color-greeni"></i>
</div>
</div>
</div>
<div class="row" ng-init="showAlternative = false"> <div class="row" ng-init="showAlternative = false">
<div class="large-12 medium-12 columns"> <div class="large-12 medium-12 columns">
@ -118,34 +123,11 @@
</div> </div>
</div> </div>
<div class="row"> <div class="row" ng-show="fetchingURL">
<div class="large-12 columns" ng-show="fetchingURL"> <div class="large-12 columns">
<h3> <p>>>
<i class="fi-bitcoin-circle icon-rotate spinner"></i> <i class="fi-bitcoin-circle icon-rotate spinner"></i>
Fetching payment Fetching payment request
</h3>
<p>From {{fetchingURL}}
</div>
<div class="large-12 columns" ng-show="!!$root.merchant">
<h3>Payment Protocol Request</h3>
<div class="send-note" ng-click="openPPModal(btx)">
<p>
<span ng-show="!!$root.merchant.pr.ca"><i class="fi-lock green"></i> {{$root.merchant.pr.ca}}</span>
<span ng-show="!$root.merchant.pr.ca"><i class="fi-unlock red"></i> Untrusted</span>
{{$root.merchant.pr.pd.memo || address}} TODO ADDRESS
</p>
<p>TODO AMOUNT
<i>{{amount}} {{$root.wallet.settings.unitName}}</i>
<span class="text-gray" ng-if="isRateAvailable">
TODO ALT
{{ alternative }} {{ alternativeIsoCode }}
</span>
<p ng-show="!!$root.merchant">
Expires {{$root.merchant.expiration | amTimeAgo }} [{{$root.merchant.domain}}]
</p>
</div>
</div> </div>
</div> </div>
@ -164,10 +146,10 @@
<div class="row"> <div class="row">
<div class="large-6 medium-6 small-6 columns text-left"> <div class="large-6 medium-6 small-6 columns text-left">
<a class="button tiny secondary m0" title="Address book" ng-hide="!!$root.merchant || lockAddress" ng-click="openAddressBook()"> <a class="button tiny secondary m0" title="Address book" ng-hide="_merchantData || lockAddress" ng-click="openAddressBook()">
<i class="fi-address-book"></i> Address book <i class="fi-address-book"></i> Address book
</a> </a>
<a ng-click="resetForm()" class="button warning m0" ng-show="!!$root.merchant || lockAddress" ng-disabled="loading"> <a ng-click="resetForm()" class="button warning m0" ng-show="_merchantData || lockAddress" ng-disabled="loading">
Cancel Cancel
</a> </a>
</div> </div>