Skip to content

Commit

Permalink
check if transaction is replaceable
Browse files Browse the repository at this point in the history
  • Loading branch information
karliatto authored and chill117 committed Oct 3, 2018
1 parent fbbdcf9 commit ecd8327
Show file tree
Hide file tree
Showing 17 changed files with 355 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,7 @@

* TBD:
* Pay by scanning a paper wallet (using camera or NFC)
* Show warning if received payment uses Replace-by-fee (RBF)
* v1.3.0:
* Segwit support (native and backwards compatible addresses)
* Hide testnet payment methods by default
Expand Down
3 changes: 2 additions & 1 deletion css/header.css
Expand Up @@ -85,7 +85,8 @@ html.configured .header-button.pay {
body.view-pay .header-button.pay,
body.view-choose-payment-method .header-button.pay,
body.view-display-payment-address .header-button.pay,
body.view-payment-status .header-button.pay {
body.view-payment-status .header-button.pay,
body.view-payment-replaceable .header-button.pay {
/*
Hide the pay button when viewing the pay screens.
*/
Expand Down
6 changes: 0 additions & 6 deletions css/result-indicator.css
Expand Up @@ -14,12 +14,6 @@
will-change: transform;
margin: 0 auto;
}
.result-indicator.success {
background-color: #7ac142;
}
.result-indicator.failed {
background-color: #d8000c;
}
.result-indicator-inner {
color: #fff;
font-size: 2.2rem;
Expand Down
16 changes: 16 additions & 0 deletions css/views/payment-replaceable.css
@@ -0,0 +1,16 @@
.view.payment-replaceable {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
box-sizing: border-box;
padding: 0;
padding-bottom: 4rem;
}
.view.payment-replaceable .result-indicator {
background-color: #d89e00;
}
.view.payment-replaceable .info {
padding: 2rem;
text-align: center;
}
3 changes: 3 additions & 0 deletions css/views/payment-status.css
Expand Up @@ -12,4 +12,7 @@
}
.view.payment-status.timed-out .result-indicator {
background-color: #d8000c;
}
.view.payment-status.replaceable .result-indicator {
background-color: #d89e00;
}
10 changes: 10 additions & 0 deletions html/templates/payment-replaceable.html
@@ -0,0 +1,10 @@
<div class="result-indicator">
<div class="result-indicator-inner">{{text.message}}</div>
</div>
<div class="info">
<p class="text">{{text.info}}</p>
</div>
<div class="secondary-controls">
<a class="secondary-control button accept">{{text.accept}}</a>
<a class="secondary-control button reject">{{text.reject}}</a>
</div>
6 changes: 6 additions & 0 deletions js/lang/en.js
Expand Up @@ -83,6 +83,12 @@ app.lang['en'] = (function() {
'payment-status.unconfirmed.done': 'Done',
'payment-status.timed-out.message': 'Timed out',
'payment-status.timed-out.done': 'OK',

'payment-replaceable.accept': 'Accept',
'payment-replaceable.reject': 'Reject',
'payment-replaceable.message': 'Replaceable by Fee',
'payment-replaceable.info': 'This transaction can be replaced by higher fee, do you want to accept it?',

'sample-addresses.label': 'Sample Addresses:',
'enter-pin.cancel': 'Cancel',
'enter-pin.submit': 'Enter',
Expand Down
6 changes: 6 additions & 0 deletions js/lang/es.js
Expand Up @@ -81,6 +81,12 @@ app.lang['es'] = (function() {
'payment-status.unconfirmed.done': 'Hecho',
'payment-status.timed-out.message': 'Expirado',
'payment-status.timed-out.done': 'Vale',

'payment-replaceable.accept': 'Aceptar',
'payment-replaceable.reject': 'Rechazar',
'payment-replaceable.message': 'Reemplazable por tasa',
'payment-replaceable.info': 'Esta transacción puede ser reemplazable con una tasa más alta, ¿Quiere aceptarla?',

'sample-addresses.label': 'Direcciones de muestra:',
'enter-pin.cancel': 'Cancelar',
'enter-pin.submit': 'Aceptar',
Expand Down
2 changes: 1 addition & 1 deletion js/payment-methods/bitcoin.js
Expand Up @@ -569,7 +569,7 @@ app.paymentMethods.bitcoin = (function() {

if (tx.amount >= expectedValue) {
// Passing transaction data so it can be stored.
var txData = _.pick(tx, 'txid');
var txData = _.pick(tx, 'txid', 'isReplaceable');
return done(null, txData);
}

Expand Down
26 changes: 26 additions & 0 deletions js/router.js
Expand Up @@ -24,6 +24,7 @@ app.Router = (function() {
'choosePaymentMethod',
'displayPaymentAddress',
'paymentStatus',
'paymentReplaceable',
];

var isAllowedWhenNotConfigured = function(routerMethodName) {
Expand All @@ -49,6 +50,7 @@ app.Router = (function() {
'display-payment-address': 'displayPaymentAddress',
'payment-details/:paymentId': 'paymentDetails',
'payment-status/:status': 'paymentStatus',
'payment-replaceable': 'paymentReplaceable',
'admin': 'admin',
'admin/:page': 'admin',
'about': 'about',
Expand Down Expand Up @@ -238,11 +240,35 @@ app.Router = (function() {

paymentStatus: function(status) {

var paymentRequest = app.paymentRequests.findWhere({ status: 'pending' });

if (!paymentRequest) {
// Start from the beginning of the payment process.
this.navigate('pay', { trigger: true });
return false;
}

app.mainView.renderView('PaymentStatus', {
status: status,
model: paymentRequest,
});
},

paymentReplaceable: function() {

var paymentRequest = app.paymentRequests.findWhere({ status: 'pending' });

if (!paymentRequest) {
// Start from the beginning of the payment process.
this.navigate('pay', { trigger: true });
return false;
}

app.mainView.renderView('PaymentReplaceable', {
model: paymentRequest,
})
}

});

})();
13 changes: 13 additions & 0 deletions js/sound.js
Expand Up @@ -22,6 +22,19 @@ app.sound = (function() {
},
],
},
{
name: 'pay-fail-01',
src: [
{
url: 'sounds/pay-fail-01.ogg',
type: 'audio/ogg',
},
{
url: 'sounds/pay-fail-01.mp3',
type: 'audio/mp3',
},
],
},
],

preloadAll: function(done) {
Expand Down
26 changes: 22 additions & 4 deletions js/views/display-payment-address.js
Expand Up @@ -107,6 +107,12 @@ app.views.DisplayPaymentAddress = (function() {
this.startListeningForStatus();
this.startListeningForPayment();
_.defer(this.queryRate);

// When payment is rejected the model already has rate.
var rate = this.model.get('rate');
if (rate) {
_.defer(this.onChangeRate);
}
},

generatePaymentRequest: function() {
Expand Down Expand Up @@ -199,10 +205,22 @@ app.views.DisplayPaymentAddress = (function() {
return app.mainView.showMessage(error);
}

this.updatePaymentRequest({
status: 'unconfirmed',
data: paymentData,
});
var isReplaceable = paymentData && paymentData.isReplaceable || false;

if (isReplaceable) {
this.updatePaymentRequest({
data: paymentData,
});
// Special case for RBF feature in bitcoin and litecoin.
// Show a warning dialogue where user can accept or reject the payment.
app.router.navigate('payment-replaceable', { trigger: true });
} else {
this.updatePaymentRequest({
status: 'unconfirmed',
data: paymentData,
});
app.router.navigate('payment-status/' + status, { trigger: true });
}

}, this));

Expand Down
80 changes: 80 additions & 0 deletions js/views/payment-replaceable.js
@@ -0,0 +1,80 @@
var app = app || {};

app.views = app.views || {};

app.views.PaymentReplaceable = (function() {

'use strict';

return app.abstracts.BaseView.extend({

className: 'payment-replaceable',
template: '#template-payment-replaceable',

events: {
'click .accept': 'accept',
'click .reject': 'reject',
},

serializeData: function() {

return {
text: {
message: app.i18n.t('payment-replaceable.message'),
info: app.i18n.t('payment-replaceable.info'),
accept: app.i18n.t('payment-replaceable.accept'),
reject: app.i18n.t('payment-replaceable.reject'),
},
};
},

onRender: function() {

this.playSound();
},

playSound: function() {

app.sound.play('pay-fail-01');
},

accept: function(evt) {

var paymentRequest = this.model.toJSON();

if (evt && evt.preventDefault) {
evt.preventDefault();
}

this.model.save(
_.extend(
{},
paymentRequest,
{
status: 'unconfirmed',
}
)
);

// Navigate back to the homescreen
app.router.navigate('pay', { trigger: true });
},

reject: function(evt) {

if (evt && evt.preventDefault) {
evt.preventDefault();
}

// Navigate to the next screen with the amount in the URI.
app.router.navigate('display-payment-address', { trigger: true });
},

onBackButton: function() {

this.reject();
},

});

})();
15 changes: 15 additions & 0 deletions js/views/payment-status.js
Expand Up @@ -15,6 +15,21 @@ app.views.PaymentStatus = (function() {
'click .done': 'done',
},

initialize: function() {
var paymentRequest = this.model.toJSON();
var status = this.options.status;

this.model.save(
_.extend(
{},
paymentRequest,
{
status: status,
}
)
);
},

serializeData: function() {

var status = this.options.status;
Expand Down
Binary file added sounds/pay-fail-01.mp3
Binary file not shown.
Binary file added sounds/pay-fail-01.ogg
Binary file not shown.

0 comments on commit ecd8327

Please sign in to comment.