Merge pull request #1229 from chjj/paypro
WIP Reimplement Payment Protocol
This commit is contained in:
commit
f6e9084548
7 changed files with 44270 additions and 547 deletions
|
|
@ -103,7 +103,7 @@ angular.module('copayApp.controllers').controller('SendController',
|
|||
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
|
||||
$scope.isMobile = isMobile.any();
|
||||
|
||||
if (!window.cordova && !navigator.getUserMedia)
|
||||
if (!window.cordova && !navigator.getUserMedia)
|
||||
$scope.disableScanner =1;
|
||||
|
||||
$scope.submitForm = function(form) {
|
||||
|
|
@ -169,26 +169,32 @@ angular.module('copayApp.controllers').controller('SendController',
|
|||
$rootScope.pendingPayment = null;
|
||||
}
|
||||
|
||||
// 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);
|
||||
// }
|
||||
var uri;
|
||||
if (address.indexOf('bitcoin:') === 0) {
|
||||
uri = new bitcore.BIP21(address).data;
|
||||
} else if (/^https?:\/\//.test(address)) {
|
||||
uri = {
|
||||
merchant: address
|
||||
};
|
||||
}
|
||||
|
||||
w.createTx(address, amount, commentText, done);
|
||||
// If we're setting the domain, ignore the change.
|
||||
if ($rootScope.merchant
|
||||
&& $rootScope.merchant.domain
|
||||
&& address === $rootScope.merchant.domain) {
|
||||
uri = {
|
||||
merchant: $rootScope.merchant.request_url
|
||||
};
|
||||
}
|
||||
|
||||
if (uri && uri.merchant) {
|
||||
w.createPaymentTx({
|
||||
uri: uri.merchant,
|
||||
memo: commentText
|
||||
}, done);
|
||||
} else {
|
||||
w.createTx(address, amount, commentText, done);
|
||||
}
|
||||
|
||||
// reset fields
|
||||
$scope.address = $scope.amount = $scope.commentText = null;
|
||||
|
|
@ -464,6 +470,13 @@ angular.module('copayApp.controllers').controller('SendController',
|
|||
var value = scope.address || '';
|
||||
var uri;
|
||||
|
||||
// If we're setting the domain, ignore the change.
|
||||
if ($rootScope.merchant
|
||||
&& $rootScope.merchant.domain
|
||||
&& value === $rootScope.merchant.domain) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.indexOf('bitcoin:') === 0) {
|
||||
uri = new bitcore.BIP21(value).data;
|
||||
} else if (/^https?:\/\//.test(value)) {
|
||||
|
|
@ -520,12 +533,18 @@ angular.module('copayApp.controllers').controller('SendController',
|
|||
return;
|
||||
}
|
||||
|
||||
var url = merchantData.request_url;
|
||||
var domain = /^(?:https?)?:\/\/([^\/:]+).*$/.exec(url)[1];
|
||||
|
||||
merchantData.unitTotal = (+merchantData.total / config.unitToSatoshi) + '';
|
||||
merchantData.expiration = new Date(
|
||||
merchantData.pr.pd.expires * 1000).toISOString();
|
||||
merchantData.domain = domain;
|
||||
|
||||
$rootScope.merchant = merchantData;
|
||||
|
||||
scope.sendForm.address.$setViewValue(domain);
|
||||
scope.sendForm.address.$render();
|
||||
scope.sendForm.address.$isValid = true;
|
||||
|
||||
scope.sendForm.amount.$setViewValue(merchantData.unitTotal);
|
||||
|
|
@ -537,6 +556,14 @@ angular.module('copayApp.controllers').controller('SendController',
|
|||
var unregister = scope.$watch('address', function() {
|
||||
var val = scope.sendForm.address.$viewValue || '';
|
||||
var uri;
|
||||
// If we're setting the domain, ignore the change.
|
||||
if ($rootScope.merchant
|
||||
&& $rootScope.merchant.domain
|
||||
&& val === $rootScope.merchant.domain) {
|
||||
uri = {
|
||||
merchant: $rootScope.merchant.request_url
|
||||
};
|
||||
}
|
||||
if (val.indexOf('bitcoin:') === 0) {
|
||||
uri = new bitcore.BIP21(val).data;
|
||||
} else if (/^https?:\/\//.test(val)) {
|
||||
|
|
@ -564,9 +591,4 @@ angular.module('copayApp.controllers').controller('SendController',
|
|||
});
|
||||
};
|
||||
|
||||
// XXX Payment Protocol is temporarily disabled.
|
||||
$scope.onChanged = function() {
|
||||
;
|
||||
};
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.directives')
|
||||
.directive('validAddress', function() {
|
||||
.directive('validAddress', ['$rootScope', function($rootScope) {
|
||||
var bitcore = require('bitcore');
|
||||
var Address = bitcore.Address;
|
||||
var bignum = bitcore.Bignum;
|
||||
|
|
@ -11,6 +11,14 @@ angular.module('copayApp.directives')
|
|||
link: function(scope, elem, attrs, ctrl) {
|
||||
var validator = function(value) {
|
||||
|
||||
// If we're setting the domain, ignore the change.
|
||||
if ($rootScope.merchant
|
||||
&& $rootScope.merchant.domain
|
||||
&& value === $rootScope.merchant.domain) {
|
||||
ctrl.$setValidity('validAddress', true);
|
||||
return value;
|
||||
}
|
||||
|
||||
// Regular url
|
||||
if (/^https?:\/\//.test(value)) {
|
||||
ctrl.$setValidity('validAddress', true);
|
||||
|
|
@ -35,7 +43,7 @@ angular.module('copayApp.directives')
|
|||
ctrl.$formatters.unshift(validator);
|
||||
}
|
||||
};
|
||||
})
|
||||
}])
|
||||
.directive('enoughAmount', ['$rootScope',
|
||||
function($rootScope) {
|
||||
var bitcore = require('bitcore');
|
||||
|
|
|
|||
|
|
@ -108,8 +108,8 @@ inherits(Wallet, events.EventEmitter);
|
|||
Wallet.builderOpts = {
|
||||
lockTime: null,
|
||||
signhash: bitcore.Transaction.SIGNHASH_ALL,
|
||||
fee: null,
|
||||
feeSat: null,
|
||||
fee: undefined,
|
||||
feeSat: undefined,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -607,8 +607,8 @@ Wallet.prototype.getMyCopayerIdPriv = function() {
|
|||
*/
|
||||
Wallet.prototype.getSecretNumber = function() {
|
||||
if (this.secretNumber) return this.secretNumber;
|
||||
this.secretNumber = Wallet.getRandomNumber();
|
||||
return this.secretNumber;
|
||||
this.secretNumber = Wallet.getRandomNumber();
|
||||
return this.secretNumber;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1284,21 +1284,13 @@ Wallet.prototype.receivePaymentRequest = function(options, pr, cb) {
|
|||
};
|
||||
}
|
||||
|
||||
var trusted = certs.map(function(cert) {
|
||||
var der = cert.toString('hex');
|
||||
var pem = PayPro.prototype._DERtoPEM(der, 'CERTIFICATE');
|
||||
return PayPro.RootCerts.getTrusted(pem);
|
||||
}).filter(Boolean);
|
||||
|
||||
// Verify Signature
|
||||
var verified = pr.verify();
|
||||
var trust = pr.verify(true);
|
||||
|
||||
if (!verified) {
|
||||
if (!trust.verified) {
|
||||
return cb(new Error('Server sent a bad signature.'));
|
||||
}
|
||||
|
||||
var ca = trusted[0];
|
||||
|
||||
details = PayPro.PaymentDetails.decode(details);
|
||||
var pd = new PayPro();
|
||||
pd = pd.makePaymentDetails(details);
|
||||
|
|
@ -1338,8 +1330,9 @@ Wallet.prototype.receivePaymentRequest = function(options, pr, cb) {
|
|||
merchant_data: merchant_data.toString('hex')
|
||||
},
|
||||
signature: sig.toString('hex'),
|
||||
ca: ca,
|
||||
untrusted: !ca
|
||||
ca: trust.caName,
|
||||
untrusted: !trust.caTrusted,
|
||||
selfSigned: trust.selfSigned
|
||||
},
|
||||
request_url: options.uri,
|
||||
total: bignum('0', 10).toString(10),
|
||||
|
|
@ -1703,7 +1696,8 @@ Wallet.prototype.verifyPaymentRequest = function(ntxid) {
|
|||
pr = pr.makePaymentRequest(data);
|
||||
|
||||
// Verify the signature so we know this is the real request.
|
||||
if (!pr.verify()) {
|
||||
var trust = pr.verify(true);
|
||||
if (!trust.verified) {
|
||||
// Signature does not match cert. It may have
|
||||
// been modified by an untrustworthy person.
|
||||
// We should not sign this transaction proposal!
|
||||
|
|
@ -2005,7 +1999,7 @@ Wallet.prototype.removeTxWithSpentInputs = function(cb) {
|
|||
var proposalsChanged = false;
|
||||
this.blockchain.getUnspent(this.getAddressesStr(), function(err, unspentList) {
|
||||
if (err) return cb(err);
|
||||
|
||||
|
||||
unspentList.forEach(function (unspent) {
|
||||
inputs.forEach(function (input) {
|
||||
input.unspent = input.unspent || (input.txid === unspent.txid && input.vout === unspent.vout);
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
|||
bitcore-0.1.35-paypro.js
|
||||
44166
lib/bitcore.js
Normal file
44166
lib/bitcore.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -42,7 +42,6 @@
|
|||
],
|
||||
"devDependencies": {
|
||||
"async": "0.9.0",
|
||||
"bitcore": "0.1.35",
|
||||
"blanket": "1.1.6",
|
||||
"browser-pack": "2.0.1",
|
||||
"browser-request": "0.3.2",
|
||||
|
|
@ -63,6 +62,7 @@
|
|||
"grunt-contrib-uglify": "^0.5.1",
|
||||
"grunt-contrib-watch": "0.5.3",
|
||||
"grunt-markdown": "0.5.0",
|
||||
"bitcore": "0.1.36",
|
||||
"grunt-mocha-test": "0.8.2",
|
||||
"grunt-shell": "0.6.4",
|
||||
"grunt-angular-gettext": "^0.2.15",
|
||||
|
|
|
|||
|
|
@ -8,29 +8,28 @@
|
|||
<div ng-show="txs.length != 0" class="large-12 line-dashed" style="padding: 0;"></div>
|
||||
|
||||
<h1>{{title|translate}}</h1>
|
||||
<div class="row collapse m0">
|
||||
<div class="large-6 columns">
|
||||
<form name="sendForm" ng-submit="submitForm(sendForm)" novalidate>
|
||||
<div class="row collapse">
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<div class="row collapse">
|
||||
<label for="address"><span translate>To address</span>
|
||||
<small translate ng-hide="!sendForm.address.$pristine || address">required</small>
|
||||
<small translate class="is-valid" ng-show="!sendForm.address.$invalid && address">valid!</small>
|
||||
<small translate class="has-error" ng-show="sendForm.address.$invalid && address">
|
||||
<label for="address">To address
|
||||
<small ng-hide="!sendForm.address.$pristine || address">required</small>
|
||||
<small class="is-valid" ng-show="!sendForm.address.$invalid && address">valid!</small>
|
||||
<small class="has-error" ng-show="sendForm.address.$invalid && address">
|
||||
not valid</small>
|
||||
</label>
|
||||
<div class="small-10 columns">
|
||||
<input type="text" id="address" name="address" ng-disabled="loading"
|
||||
placeholder="{{'Send to'|translate}}" ng-model="address" ng-change="onChanged()" valid-address required>
|
||||
<input type="text" id="address" name="address" ng-disabled="loading || !!$root.merchant"
|
||||
placeholder="Send to" ng-model="address" ng-change="onChanged()" valid-address required>
|
||||
<small class="icon-input" ng-show="!sendForm.address.$invalid && address"><i class="fi-check"></i></small>
|
||||
<small class="icon-input" ng-show="sendForm.address.$invalid && address"><i class="fi-x"></i></small>
|
||||
</div>
|
||||
<div class="small-2 columns" ng-hide="disableScanner" ng-hide="showScanner">
|
||||
<div class="small-2 columns" ng-hide="showScanner">
|
||||
<a class="postfix button black" ng-click="openScanner()"><i class="fi-camera"></i></a>
|
||||
</div>
|
||||
<div class="small-2 columns" ng-show="showScanner">
|
||||
<a translate class="postfix button warning" ng-click="cancelScanner()">Cancel</a>
|
||||
<a class="postfix button warning" ng-click="cancelScanner()">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="scanner" class="row" ng-if="showScanner">
|
||||
|
|
@ -53,30 +52,30 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row collapse">
|
||||
<div class="large-5 medium-5 columns">
|
||||
<div class="row">
|
||||
<div class="large-6 medium-6 columns">
|
||||
<div class="row collapse">
|
||||
<label for="amount"><span translate>Amount</span>
|
||||
<small translate ng-hide="!sendForm.amount.$pristine">required</small>
|
||||
<small translate class="is-valid" ng-show="!sendForm.amount.$invalid && !sendForm.amount.$pristine">Valid</small>
|
||||
<small translate class="has-error" ng-show="sendForm.amount.$invalid && !sendForm.amount.$pristine && !notEnoughAmount">
|
||||
<label for="amount">Amount
|
||||
<small ng-hide="!sendForm.amount.$pristine">required</small>
|
||||
<small class="is-valid" ng-show="!sendForm.amount.$invalid && !sendForm.amount.$pristine">Valid</small>
|
||||
<small class="has-error" ng-show="sendForm.amount.$invalid && !sendForm.amount.$pristine && !notEnoughAmount">
|
||||
Not valid
|
||||
</small>
|
||||
<small translate ng-show="notEnoughAmount" class="has-error">Insufficient funds</small>
|
||||
<small ng-show="notEnoughAmount" class="has-error">Insufficient funds</small>
|
||||
</label>
|
||||
<div class="small-9 columns">
|
||||
<input type="number" id="amount"
|
||||
ng-disabled="loading || ($root.merchant && +$root.merchant.total > 0) || $root.merchantError"
|
||||
name="amount" placeholder="{{'Amount'|translate}}" ng-model="amount"
|
||||
name="amount" placeholder="Amount" ng-model="amount"
|
||||
min="{{minAmount}}" max="10000000000" enough-amount required
|
||||
autocomplete="off"
|
||||
>
|
||||
<small class="icon-input" ng-show="!sendForm.amount.$invalid && amount"><i class="fi-check"></i></small>
|
||||
<small class="icon-input" ng-show="sendForm.amount.$invalid && !sendForm.amount.$pristine && !notEnoughAmount"><i class="fi-x"></i></small>
|
||||
<a class="small input-note" title="{{'Send all funds'|translate}}"
|
||||
<a class="small input-note" title="Send all funds"
|
||||
ng-show="$root.availableBalance > 0 && (!$root.merchant || +$root.merchant.total === 0)"
|
||||
ng-click="topAmount(sendForm)">
|
||||
<span translate>Use all funds</span> ({{getAvailableAmount()}} {{$root.unitName}})
|
||||
Use all funds ({{getAvailableAmount()}} {{$root.unitName}})
|
||||
</a>
|
||||
</div>
|
||||
<div class="small-3 columns">
|
||||
|
|
@ -86,11 +85,11 @@
|
|||
</div>
|
||||
<div class="large-6 medium-6 columns">
|
||||
<div class="row collapse">
|
||||
<label for="alternative"><span translate>Amount in</span> {{ alternativeName }} </label>
|
||||
<label for="alternative">Amount in {{ alternativeName }} </label>
|
||||
<div class="small-9 columns">
|
||||
<input type="number" id="alternative_amount"
|
||||
ng-disabled="loading || !isRateAvailable "
|
||||
name="alternative" placeholder="{{'Amount'|translate}}" ng-model="alternative"
|
||||
ng-disabled="loading || !isRateAvailable || ($root.merchant && +$root.merchant.total > 0) || $root.merchantError"
|
||||
name="alternative" placeholder="Amount" ng-model="alternative"
|
||||
required
|
||||
autocomplete="off"
|
||||
>
|
||||
|
|
@ -102,37 +101,37 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row collapse" ng-show="wallet.isShared()">
|
||||
<div class="row" ng-show="wallet.isShared()">
|
||||
<div class="large-12 columns">
|
||||
<div class="row collapse">
|
||||
<label for="comment"><span translate>Notes</span>
|
||||
<small translate ng-hide="!sendForm.comment.$pristine">optional</small>
|
||||
<small translate class="has-error" ng-show="sendForm.comment.$invalid && !sendForm.comment.$pristine">too long!</small>
|
||||
<label for="comment">Note
|
||||
<small ng-hide="!sendForm.comment.$pristine">optional</small>
|
||||
<small class="has-error" ng-show="sendForm.comment.$invalid && !sendForm.comment.$pristine">too long!</small>
|
||||
</label>
|
||||
<div class="large-12 columns">
|
||||
<textarea id="comment" ng-disabled="loading"
|
||||
name="comment" placeholder="{{'Leave a private message to your copayers'|translate}}" ng-model="commentText" ng-maxlength="100"></textarea>
|
||||
name="comment" placeholder="Leave a private message to your copayers" ng-model="commentText" ng-maxlength="100"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row collapse">
|
||||
<div class="large-5 medium-6 small-12 columns">
|
||||
<button translate type="submit" class="button primary expand text-center" ng-disabled="sendForm.$invalid || loading" loading="Sending">
|
||||
<div class="row">
|
||||
<div class="large-5 medium-3 small-4 columns">
|
||||
<button type="submit" class="button primary expand text-center" ng-disabled="sendForm.$invalid || loading" loading="Sending">
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div><!-- end of row -->
|
||||
|
||||
<div class="large-6 columns show-for-large-up" ng-show="!!$root.merchant">
|
||||
<div class="send-note">
|
||||
<h6>Send to</h6>
|
||||
<p class="text-gray" ng-class="{'hidden': sendForm.address.$invalid || !address}">
|
||||
{{address}}
|
||||
<p class="text-gray" ng-class="{'hidden': sendForm.address.$invalid || !address}"
|
||||
title="{{$root.merchant.request_url}}">
|
||||
{{$root.merchant.domain}}
|
||||
</p>
|
||||
<h6>Total amount for this transaction:</h6>
|
||||
<p class="text-gray" ng-class="{'hidden': sendForm.amount.$invalid || !amount > 0}">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue