Unverified Commit dafb56ce authored by valentinewallace's avatar valentinewallace Committed by GitHub

Merge pull request #667 from lightninglabs/payment-timeout

Payment timeout
parents abcadcb7 133c753c
...@@ -73,6 +73,10 @@ class NavAction { ...@@ -73,6 +73,10 @@ class NavAction {
this._store.route = 'PayLightningDone'; this._store.route = 'PayLightningDone';
} }
goPaymentFailed() {
this._store.route = 'PaymentFailed';
}
goPayBitcoin() { goPayBitcoin() {
this._store.route = 'PayBitcoin'; this._store.route = 'PayBitcoin';
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* call the corresponding GRPC apis for payment management. * call the corresponding GRPC apis for payment management.
*/ */
import { PREFIX_URI } from '../config'; import { PREFIX_URI, PAYMENT_TIMEOUT } from '../config';
import { import {
toSatoshis, toSatoshis,
toAmount, toAmount,
...@@ -170,6 +170,11 @@ class PaymentAction { ...@@ -170,6 +170,11 @@ class PaymentAction {
* @return {Promise<undefined>} * @return {Promise<undefined>}
*/ */
async payLightning() { async payLightning() {
let failed = false;
const timeout = setTimeout(() => {
failed = true;
this._nav.goPaymentFailed();
}, PAYMENT_TIMEOUT);
try { try {
this._nav.goWait(); this._nav.goWait();
const invoice = this._store.payment.address.replace(PREFIX_URI, ''); const invoice = this._store.payment.address.replace(PREFIX_URI, '');
...@@ -185,10 +190,14 @@ class PaymentAction { ...@@ -185,10 +190,14 @@ class PaymentAction {
stream.on('error', reject); stream.on('error', reject);
stream.write(JSON.stringify({ payment_request: invoice }), 'utf8'); stream.write(JSON.stringify({ payment_request: invoice }), 'utf8');
}); });
if (failed) return;
this._nav.goPayLightningDone(); this._nav.goPayLightningDone();
} catch (err) { } catch (err) {
if (failed) return;
this._nav.goPayLightningConfirm(); this._nav.goPayLightningConfirm();
this._notification.display({ msg: 'Lightning payment failed!', err }); this._notification.display({ msg: 'Lightning payment failed!', err });
} finally {
clearTimeout(timeout);
} }
} }
} }
......
...@@ -6,6 +6,7 @@ module.exports.RETRY_DELAY = 1000; ...@@ -6,6 +6,7 @@ module.exports.RETRY_DELAY = 1000;
module.exports.LND_INIT_DELAY = 5000; module.exports.LND_INIT_DELAY = 5000;
module.exports.NOTIFICATION_DELAY = 5000; module.exports.NOTIFICATION_DELAY = 5000;
module.exports.RATE_DELAY = 15 * 60 * 1000; module.exports.RATE_DELAY = 15 * 60 * 1000;
module.exports.PAYMENT_TIMEOUT = 60 * 1000;
module.exports.LND_PORT = 10006; module.exports.LND_PORT = 10006;
module.exports.LND_PEER_PORT = 10016; module.exports.LND_PEER_PORT = 10016;
......
...@@ -19,6 +19,7 @@ import Home from './home'; ...@@ -19,6 +19,7 @@ import Home from './home';
import Payment from './payment'; import Payment from './payment';
import PayLightningConfirm from './pay-lightning-confirm'; import PayLightningConfirm from './pay-lightning-confirm';
import PayLightningDone from './pay-lightning-done'; import PayLightningDone from './pay-lightning-done';
import PaymentFailed from './payment-failed';
import PayBitcoin from './pay-bitcoin'; import PayBitcoin from './pay-bitcoin';
import PayBitcoinConfirm from './pay-bitcoin-confirm'; import PayBitcoinConfirm from './pay-bitcoin-confirm';
import PayBitcoinDone from './pay-bitcoin-done'; import PayBitcoinDone from './pay-bitcoin-done';
...@@ -111,6 +112,9 @@ class MainView extends Component { ...@@ -111,6 +112,9 @@ class MainView extends Component {
{route === 'PayLightningDone' && ( {route === 'PayLightningDone' && (
<PayLightningDone store={store} payment={payment} nav={nav} /> <PayLightningDone store={store} payment={payment} nav={nav} />
)} )}
{route === 'PaymentFailed' && (
<PaymentFailed channel={channel} nav={nav} />
)}
{route === 'PayBitcoin' && ( {route === 'PayBitcoin' && (
<PayBitcoin store={store} payment={payment} nav={nav} /> <PayBitcoin store={store} payment={payment} nav={nav} />
)} )}
......
...@@ -28,29 +28,32 @@ const styles = StyleSheet.create({ ...@@ -28,29 +28,32 @@ const styles = StyleSheet.create({
}, },
}); });
const NoRouteView = ({ channel, payment }) => ( const PaymentFailedView = ({ channel, nav }) => (
<Background color={color.blackDark}> <Background color={color.blackDark}>
<MainContent> <MainContent>
<FormStretcher> <FormStretcher>
<LightningErrorIcon height={115 * 0.8} width={60 * 0.8} /> <LightningErrorIcon height={115 * 0.8} width={60 * 0.8} />
<H1Text style={styles.h1Txt}>No route found</H1Text> <H1Text style={styles.h1Txt}>Payment Failed</H1Text>
<CopyText style={styles.copyTxt}> <CopyText style={styles.copyTxt}>
{"You'll need to manually create a channel"} {'You may need to manually create a channel.'}
</CopyText> </CopyText>
</FormStretcher> </FormStretcher>
<PillButton style={styles.createBtn} onPress={() => channel.initCreate()}> <PillButton style={styles.createBtn} onPress={() => channel.initCreate()}>
Create channel Create channel
</PillButton> </PillButton>
<Button style={styles.retryBtn} onPress={() => payment.init()}> <Button
style={styles.retryBtn}
onPress={() => nav.goPayLightningConfirm()}
>
<ButtonText>Try again</ButtonText> <ButtonText>Try again</ButtonText>
</Button> </Button>
</MainContent> </MainContent>
</Background> </Background>
); );
NoRouteView.propTypes = { PaymentFailedView.propTypes = {
channel: PropTypes.object.isRequired, channel: PropTypes.object.isRequired,
payment: PropTypes.object.isRequired, nav: PropTypes.object.isRequired,
}; };
export default observer(NoRouteView); export default observer(PaymentFailedView);
...@@ -38,7 +38,7 @@ import PayLightningDone from '../src/view/pay-lightning-done'; ...@@ -38,7 +38,7 @@ import PayLightningDone from '../src/view/pay-lightning-done';
import PayBitcoin from '../src/view/pay-bitcoin'; import PayBitcoin from '../src/view/pay-bitcoin';
import PayBitcoinConfirm from '../src/view/pay-bitcoin-confirm'; import PayBitcoinConfirm from '../src/view/pay-bitcoin-confirm';
import PayBitcoinDone from '../src/view/pay-bitcoin-done'; import PayBitcoinDone from '../src/view/pay-bitcoin-done';
import NoRoute from '../src/view/no-route'; import PaymentFailed from '../src/view/payment-failed';
import Loader from '../src/view/loader'; import Loader from '../src/view/loader';
import LoaderSyncing from '../src/view/loader-syncing'; import LoaderSyncing from '../src/view/loader-syncing';
import SelectSeed from '../src/view/select-seed'; import SelectSeed from '../src/view/select-seed';
...@@ -149,7 +149,7 @@ storiesOf('Screens', module) ...@@ -149,7 +149,7 @@ storiesOf('Screens', module)
.add('Pay Lightning Done', () => ( .add('Pay Lightning Done', () => (
<PayLightningDone store={store} payment={payment} nav={nav} /> <PayLightningDone store={store} payment={payment} nav={nav} />
)) ))
.add('No Route Found', () => <NoRoute channel={channel} payment={payment} />) .add('Payment Failed', () => <PaymentFailed channel={channel} nav={nav} />)
.add('Pay Bitcoin', () => ( .add('Pay Bitcoin', () => (
<PayBitcoin store={store} payment={payment} nav={nav} /> <PayBitcoin store={store} payment={payment} nav={nav} />
)) ))
......
...@@ -130,6 +130,13 @@ describe('Action Nav Unit Tests', () => { ...@@ -130,6 +130,13 @@ describe('Action Nav Unit Tests', () => {
}); });
}); });
describe('goPaymentFailed()', () => {
it('should set correct route', () => {
nav.goPaymentFailed();
expect(store.route, 'to equal', 'PaymentFailed');
});
});
describe('goPayBitcoin()', () => { describe('goPayBitcoin()', () => {
it('should set correct route', () => { it('should set correct route', () => {
nav.goPayBitcoin(); nav.goPayBitcoin();
......
...@@ -22,6 +22,7 @@ describe('Action Payments Unit Tests', () => { ...@@ -22,6 +22,7 @@ describe('Action Payments Unit Tests', () => {
store = new Store(); store = new Store();
store.settings.displayFiat = false; store.settings.displayFiat = false;
require('../../../src/config').RETRY_DELAY = 1; require('../../../src/config').RETRY_DELAY = 1;
require('../../../src/config').PAYMENT_TIMEOUT = 10;
grpc = sinon.createStubInstance(GrpcAction); grpc = sinon.createStubInstance(GrpcAction);
notification = sinon.createStubInstance(NotificationAction); notification = sinon.createStubInstance(NotificationAction);
nav = sinon.createStubInstance(NavAction); nav = sinon.createStubInstance(NavAction);
...@@ -240,5 +241,12 @@ describe('Action Payments Unit Tests', () => { ...@@ -240,5 +241,12 @@ describe('Action Payments Unit Tests', () => {
expect(nav.goPayLightningConfirm, 'was called once'); expect(nav.goPayLightningConfirm, 'was called once');
expect(notification.display, 'was called once'); expect(notification.display, 'was called once');
}); });
it('should go to error page on timeout', async () => {
payment.payLightning({ invoice: 'some-invoice' });
await nap(100);
expect(nav.goPaymentFailed, 'was called once');
expect(nav.goPayLightningDone, 'was not called');
});
}); });
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment