Skip to content
This repository was archived by the owner on Feb 23, 2021. It is now read-only.

Commit dafb56c

Browse files
Merge pull request #667 from lightninglabs/payment-timeout
Payment timeout
2 parents abcadcb + 133c753 commit dafb56c

File tree

8 files changed

+46
-10
lines changed

8 files changed

+46
-10
lines changed

src/action/nav.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ class NavAction {
7373
this._store.route = 'PayLightningDone';
7474
}
7575

76+
goPaymentFailed() {
77+
this._store.route = 'PaymentFailed';
78+
}
79+
7680
goPayBitcoin() {
7781
this._store.route = 'PayBitcoin';
7882
}

src/action/payment.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* call the corresponding GRPC apis for payment management.
44
*/
55

6-
import { PREFIX_URI } from '../config';
6+
import { PREFIX_URI, PAYMENT_TIMEOUT } from '../config';
77
import {
88
toSatoshis,
99
toAmount,
@@ -170,6 +170,11 @@ class PaymentAction {
170170
* @return {Promise<undefined>}
171171
*/
172172
async payLightning() {
173+
let failed = false;
174+
const timeout = setTimeout(() => {
175+
failed = true;
176+
this._nav.goPaymentFailed();
177+
}, PAYMENT_TIMEOUT);
173178
try {
174179
this._nav.goWait();
175180
const invoice = this._store.payment.address.replace(PREFIX_URI, '');
@@ -185,10 +190,14 @@ class PaymentAction {
185190
stream.on('error', reject);
186191
stream.write(JSON.stringify({ payment_request: invoice }), 'utf8');
187192
});
193+
if (failed) return;
188194
this._nav.goPayLightningDone();
189195
} catch (err) {
196+
if (failed) return;
190197
this._nav.goPayLightningConfirm();
191198
this._notification.display({ msg: 'Lightning payment failed!', err });
199+
} finally {
200+
clearTimeout(timeout);
192201
}
193202
}
194203
}

src/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module.exports.RETRY_DELAY = 1000;
66
module.exports.LND_INIT_DELAY = 5000;
77
module.exports.NOTIFICATION_DELAY = 5000;
88
module.exports.RATE_DELAY = 15 * 60 * 1000;
9+
module.exports.PAYMENT_TIMEOUT = 60 * 1000;
910

1011
module.exports.LND_PORT = 10006;
1112
module.exports.LND_PEER_PORT = 10016;

src/view/main.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import Home from './home';
1919
import Payment from './payment';
2020
import PayLightningConfirm from './pay-lightning-confirm';
2121
import PayLightningDone from './pay-lightning-done';
22+
import PaymentFailed from './payment-failed';
2223
import PayBitcoin from './pay-bitcoin';
2324
import PayBitcoinConfirm from './pay-bitcoin-confirm';
2425
import PayBitcoinDone from './pay-bitcoin-done';
@@ -111,6 +112,9 @@ class MainView extends Component {
111112
{route === 'PayLightningDone' && (
112113
<PayLightningDone store={store} payment={payment} nav={nav} />
113114
)}
115+
{route === 'PaymentFailed' && (
116+
<PaymentFailed channel={channel} nav={nav} />
117+
)}
114118
{route === 'PayBitcoin' && (
115119
<PayBitcoin store={store} payment={payment} nav={nav} />
116120
)}

src/view/no-route.js renamed to src/view/payment-failed.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,32 @@ const styles = StyleSheet.create({
2828
},
2929
});
3030

31-
const NoRouteView = ({ channel, payment }) => (
31+
const PaymentFailedView = ({ channel, nav }) => (
3232
<Background color={color.blackDark}>
3333
<MainContent>
3434
<FormStretcher>
3535
<LightningErrorIcon height={115 * 0.8} width={60 * 0.8} />
36-
<H1Text style={styles.h1Txt}>No route found</H1Text>
36+
<H1Text style={styles.h1Txt}>Payment Failed</H1Text>
3737
<CopyText style={styles.copyTxt}>
38-
{"You'll need to manually create a channel"}
38+
{'You may need to manually create a channel.'}
3939
</CopyText>
4040
</FormStretcher>
4141
<PillButton style={styles.createBtn} onPress={() => channel.initCreate()}>
4242
Create channel
4343
</PillButton>
44-
<Button style={styles.retryBtn} onPress={() => payment.init()}>
44+
<Button
45+
style={styles.retryBtn}
46+
onPress={() => nav.goPayLightningConfirm()}
47+
>
4548
<ButtonText>Try again</ButtonText>
4649
</Button>
4750
</MainContent>
4851
</Background>
4952
);
5053

51-
NoRouteView.propTypes = {
54+
PaymentFailedView.propTypes = {
5255
channel: PropTypes.object.isRequired,
53-
payment: PropTypes.object.isRequired,
56+
nav: PropTypes.object.isRequired,
5457
};
5558

56-
export default observer(NoRouteView);
59+
export default observer(PaymentFailedView);

stories/screen-story.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import PayLightningDone from '../src/view/pay-lightning-done';
3838
import PayBitcoin from '../src/view/pay-bitcoin';
3939
import PayBitcoinConfirm from '../src/view/pay-bitcoin-confirm';
4040
import PayBitcoinDone from '../src/view/pay-bitcoin-done';
41-
import NoRoute from '../src/view/no-route';
41+
import PaymentFailed from '../src/view/payment-failed';
4242
import Loader from '../src/view/loader';
4343
import LoaderSyncing from '../src/view/loader-syncing';
4444
import SelectSeed from '../src/view/select-seed';
@@ -149,7 +149,7 @@ storiesOf('Screens', module)
149149
.add('Pay Lightning Done', () => (
150150
<PayLightningDone store={store} payment={payment} nav={nav} />
151151
))
152-
.add('No Route Found', () => <NoRoute channel={channel} payment={payment} />)
152+
.add('Payment Failed', () => <PaymentFailed channel={channel} nav={nav} />)
153153
.add('Pay Bitcoin', () => (
154154
<PayBitcoin store={store} payment={payment} nav={nav} />
155155
))

test/unit/action/nav.spec.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ describe('Action Nav Unit Tests', () => {
130130
});
131131
});
132132

133+
describe('goPaymentFailed()', () => {
134+
it('should set correct route', () => {
135+
nav.goPaymentFailed();
136+
expect(store.route, 'to equal', 'PaymentFailed');
137+
});
138+
});
139+
133140
describe('goPayBitcoin()', () => {
134141
it('should set correct route', () => {
135142
nav.goPayBitcoin();

test/unit/action/payment.spec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ describe('Action Payments Unit Tests', () => {
2222
store = new Store();
2323
store.settings.displayFiat = false;
2424
require('../../../src/config').RETRY_DELAY = 1;
25+
require('../../../src/config').PAYMENT_TIMEOUT = 10;
2526
grpc = sinon.createStubInstance(GrpcAction);
2627
notification = sinon.createStubInstance(NotificationAction);
2728
nav = sinon.createStubInstance(NavAction);
@@ -240,5 +241,12 @@ describe('Action Payments Unit Tests', () => {
240241
expect(nav.goPayLightningConfirm, 'was called once');
241242
expect(notification.display, 'was called once');
242243
});
244+
245+
it('should go to error page on timeout', async () => {
246+
payment.payLightning({ invoice: 'some-invoice' });
247+
await nap(100);
248+
expect(nav.goPaymentFailed, 'was called once');
249+
expect(nav.goPayLightningDone, 'was not called');
250+
});
243251
});
244252
});

0 commit comments

Comments
 (0)