paypro working 1-1
This commit is contained in:
parent
97c93e7909
commit
5f6c9482b4
6 changed files with 125 additions and 112 deletions
2
TODO
2
TODO
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,34 +119,40 @@ 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;
|
||||||
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,26 @@
|
||||||
<a class="close-reveal-modal" ng-click="cancel()">×</a>
|
<a class="close-reveal-modal" ng-click="cancel()">×</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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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'">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<label for="domain">
|
||||||
|
<span translate>Payment to</span>
|
||||||
|
</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>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue