mirror of
https://github.com/RoboSats/robosats.git
synced 2024-12-27 14:22:58 +03:00
Add logics for payment retry, first iteration.
This commit is contained in:
parent
25ab5fdf2e
commit
2d1a2e4c5c
@ -295,6 +295,7 @@ class Logics():
|
|||||||
concept = LNPayment.Concepts.PAYBUYER,
|
concept = LNPayment.Concepts.PAYBUYER,
|
||||||
type = LNPayment.Types.NORM,
|
type = LNPayment.Types.NORM,
|
||||||
sender = User.objects.get(username=ESCROW_USERNAME),
|
sender = User.objects.get(username=ESCROW_USERNAME),
|
||||||
|
order_paid = order, # In case this user has other buyer_invoices, update the one related to this order.
|
||||||
receiver= user,
|
receiver= user,
|
||||||
# if there is a LNPayment matching these above, it updates that one with defaults below.
|
# if there is a LNPayment matching these above, it updates that one with defaults below.
|
||||||
defaults={
|
defaults={
|
||||||
@ -320,6 +321,10 @@ class Logics():
|
|||||||
order.expires_at = timezone.now() + timedelta(seconds=Order.t_to_expire[Order.Status.CHA])
|
order.expires_at = timezone.now() + timedelta(seconds=Order.t_to_expire[Order.Status.CHA])
|
||||||
else:
|
else:
|
||||||
order.status = Order.Status.WFE
|
order.status = Order.Status.WFE
|
||||||
|
|
||||||
|
# If the order status is 'Failed Routing'. Retry payment.
|
||||||
|
if order.status == Order.Status.FAI:
|
||||||
|
follow_send_payment(order.buyer_invoice)
|
||||||
|
|
||||||
order.save()
|
order.save()
|
||||||
return True, None
|
return True, None
|
||||||
@ -766,10 +771,7 @@ class Logics():
|
|||||||
if LNNode.double_check_htlc_is_settled(order.trade_escrow.payment_hash):
|
if LNNode.double_check_htlc_is_settled(order.trade_escrow.payment_hash):
|
||||||
is_payed, context = follow_send_payment(order.buyer_invoice) ##### !!! KEY LINE - PAYS THE BUYER INVOICE !!!
|
is_payed, context = follow_send_payment(order.buyer_invoice) ##### !!! KEY LINE - PAYS THE BUYER INVOICE !!!
|
||||||
if is_payed:
|
if is_payed:
|
||||||
order.status = Order.Status.SUC
|
# RETURN THE BONDS // Probably best also do it even if payment failed
|
||||||
order.buyer_invoice.status = LNPayment.Status.SUCCED
|
|
||||||
order.expires_at = timezone.now() + timedelta(seconds=Order.t_to_expire[Order.Status.SUC])
|
|
||||||
# RETURN THE BONDS
|
|
||||||
cls.return_bond(order.taker_bond)
|
cls.return_bond(order.taker_bond)
|
||||||
cls.return_bond(order.maker_bond)
|
cls.return_bond(order.maker_bond)
|
||||||
order.save()
|
order.save()
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
|
||||||
from api.lightning.node import LNNode
|
from api.lightning.node import LNNode
|
||||||
|
from api.tasks import follow_send_payment
|
||||||
from api.models import LNPayment, Order
|
from api.models import LNPayment, Order
|
||||||
from api.logics import Logics
|
from api.logics import Logics
|
||||||
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from datetime import timedelta
|
||||||
from decouple import config
|
from decouple import config
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
import time
|
import time
|
||||||
@ -12,25 +14,36 @@ import time
|
|||||||
MACAROON = b64decode(config('LND_MACAROON_BASE64'))
|
MACAROON = b64decode(config('LND_MACAROON_BASE64'))
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
'''
|
|
||||||
Background: SubscribeInvoices stub iterator would be great to use here.
|
|
||||||
However, it only sends updates when the invoice is OPEN (new) or SETTLED.
|
|
||||||
We are very interested on the other two states (CANCELLED and ACCEPTED).
|
|
||||||
Therefore, this thread (follow_invoices) will iterate over all LNpayment
|
|
||||||
objects and do InvoiceLookupV2 every X seconds to update their state 'live'
|
|
||||||
'''
|
|
||||||
|
|
||||||
help = 'Follows all active hold invoices'
|
help = 'Follows all active hold invoices'
|
||||||
rest = 5 # seconds between consecutive checks for invoice updates
|
rest = 5 # seconds between consecutive checks for invoice updates
|
||||||
|
|
||||||
# def add_arguments(self, parser):
|
def handle(self, *args, **options):
|
||||||
# parser.add_argument('debug', nargs='+', type=boolean)
|
''' Infinite loop to check invoices and retry payments.
|
||||||
|
ever mind database locked error, keep going, print out'''
|
||||||
|
|
||||||
|
while True:
|
||||||
|
time.sleep(self.rest)
|
||||||
|
|
||||||
def follow_invoices(self, *args, **options):
|
try:
|
||||||
|
self.follow_hold_invoices()
|
||||||
|
self.retry_payments()
|
||||||
|
except Exception as e:
|
||||||
|
if 'database is locked' in str(e):
|
||||||
|
self.stdout.write('database is locked')
|
||||||
|
|
||||||
|
self.stdout.write(str(e))
|
||||||
|
|
||||||
|
def follow_hold_invoices(self):
|
||||||
''' Follows and updates LNpayment objects
|
''' Follows and updates LNpayment objects
|
||||||
until settled or canceled'''
|
until settled or canceled
|
||||||
|
|
||||||
# TODO handle 'database is locked'
|
Background: SubscribeInvoices stub iterator would be great to use here.
|
||||||
|
However, it only sends updates when the invoice is OPEN (new) or SETTLED.
|
||||||
|
We are very interested on the other two states (CANCELLED and ACCEPTED).
|
||||||
|
Therefore, this thread (follow_invoices) will iterate over all LNpayment
|
||||||
|
objects and do InvoiceLookupV2 every X seconds to update their state 'live'
|
||||||
|
'''
|
||||||
|
|
||||||
lnd_state_to_lnpayment_status = {
|
lnd_state_to_lnpayment_status = {
|
||||||
0: LNPayment.Status.INVGEN, # OPEN
|
0: LNPayment.Status.INVGEN, # OPEN
|
||||||
@ -41,64 +54,76 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
stub = LNNode.invoicesstub
|
stub = LNNode.invoicesstub
|
||||||
|
|
||||||
while True:
|
# time it for debugging
|
||||||
time.sleep(self.rest)
|
t0 = time.time()
|
||||||
|
queryset = LNPayment.objects.filter(type=LNPayment.Types.HOLD, status__in=[LNPayment.Status.INVGEN, LNPayment.Status.LOCKED])
|
||||||
|
|
||||||
# time it for debugging
|
debug = {}
|
||||||
t0 = time.time()
|
debug['num_active_invoices'] = len(queryset)
|
||||||
queryset = LNPayment.objects.filter(type=LNPayment.Types.HOLD, status__in=[LNPayment.Status.INVGEN, LNPayment.Status.LOCKED])
|
debug['invoices'] = []
|
||||||
|
at_least_one_changed = False
|
||||||
|
|
||||||
debug = {}
|
for idx, hold_lnpayment in enumerate(queryset):
|
||||||
debug['num_active_invoices'] = len(queryset)
|
old_status = LNPayment.Status(hold_lnpayment.status).label
|
||||||
debug['invoices'] = []
|
|
||||||
at_least_one_changed = False
|
|
||||||
|
|
||||||
for idx, hold_lnpayment in enumerate(queryset):
|
|
||||||
old_status = LNPayment.Status(hold_lnpayment.status).label
|
|
||||||
|
|
||||||
try:
|
|
||||||
request = LNNode.invoicesrpc.LookupInvoiceMsg(payment_hash=bytes.fromhex(hold_lnpayment.payment_hash))
|
|
||||||
response = stub.LookupInvoiceV2(request, metadata=[('macaroon', MACAROON.hex())])
|
|
||||||
hold_lnpayment.status = lnd_state_to_lnpayment_status[response.state]
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
# If it fails at finding the invoice: it has been canceled.
|
|
||||||
# In RoboSats DB we make a distinction between cancelled and returned (LND does not)
|
|
||||||
if 'unable to locate invoice' in str(e):
|
|
||||||
self.stdout.write(str(e))
|
|
||||||
hold_lnpayment.status = LNPayment.Status.CANCEL
|
|
||||||
# LND restarted.
|
|
||||||
if 'wallet locked, unlock it' in str(e):
|
|
||||||
self.stdout.write(str(timezone.now())+':: Wallet Locked')
|
|
||||||
# Other write to logs
|
|
||||||
else:
|
|
||||||
self.stdout.write(str(e))
|
|
||||||
|
|
||||||
new_status = LNPayment.Status(hold_lnpayment.status).label
|
|
||||||
|
|
||||||
# Only save the hold_payments that change (otherwise this function does not scale)
|
|
||||||
changed = not old_status==new_status
|
|
||||||
if changed:
|
|
||||||
# self.handle_status_change(hold_lnpayment, old_status)
|
|
||||||
hold_lnpayment.save()
|
|
||||||
self.update_order_status(hold_lnpayment)
|
|
||||||
|
|
||||||
# Report for debugging
|
|
||||||
new_status = LNPayment.Status(hold_lnpayment.status).label
|
|
||||||
debug['invoices'].append({idx:{
|
|
||||||
'payment_hash': str(hold_lnpayment.payment_hash),
|
|
||||||
'old_status': old_status,
|
|
||||||
'new_status': new_status,
|
|
||||||
}})
|
|
||||||
|
|
||||||
at_least_one_changed = at_least_one_changed or changed
|
|
||||||
|
|
||||||
debug['time']=time.time()-t0
|
try:
|
||||||
|
request = LNNode.invoicesrpc.LookupInvoiceMsg(payment_hash=bytes.fromhex(hold_lnpayment.payment_hash))
|
||||||
|
response = stub.LookupInvoiceV2(request, metadata=[('macaroon', MACAROON.hex())])
|
||||||
|
hold_lnpayment.status = lnd_state_to_lnpayment_status[response.state]
|
||||||
|
|
||||||
if at_least_one_changed:
|
except Exception as e:
|
||||||
self.stdout.write(str(timezone.now()))
|
# If it fails at finding the invoice: it has been canceled.
|
||||||
self.stdout.write(str(debug))
|
# In RoboSats DB we make a distinction between cancelled and returned (LND does not)
|
||||||
|
if 'unable to locate invoice' in str(e):
|
||||||
|
self.stdout.write(str(e))
|
||||||
|
hold_lnpayment.status = LNPayment.Status.CANCEL
|
||||||
|
# LND restarted.
|
||||||
|
if 'wallet locked, unlock it' in str(e):
|
||||||
|
self.stdout.write(str(timezone.now())+':: Wallet Locked')
|
||||||
|
# Other write to logs
|
||||||
|
else:
|
||||||
|
self.stdout.write(str(e))
|
||||||
|
|
||||||
|
new_status = LNPayment.Status(hold_lnpayment.status).label
|
||||||
|
|
||||||
|
# Only save the hold_payments that change (otherwise this function does not scale)
|
||||||
|
changed = not old_status==new_status
|
||||||
|
if changed:
|
||||||
|
# self.handle_status_change(hold_lnpayment, old_status)
|
||||||
|
self.update_order_status(hold_lnpayment)
|
||||||
|
hold_lnpayment.save()
|
||||||
|
|
||||||
|
# Report for debugging
|
||||||
|
new_status = LNPayment.Status(hold_lnpayment.status).label
|
||||||
|
debug['invoices'].append({idx:{
|
||||||
|
'payment_hash': str(hold_lnpayment.payment_hash),
|
||||||
|
'old_status': old_status,
|
||||||
|
'new_status': new_status,
|
||||||
|
}})
|
||||||
|
|
||||||
|
at_least_one_changed = at_least_one_changed or changed
|
||||||
|
|
||||||
|
debug['time']=time.time()-t0
|
||||||
|
|
||||||
|
if at_least_one_changed:
|
||||||
|
self.stdout.write(str(timezone.now()))
|
||||||
|
self.stdout.write(str(debug))
|
||||||
|
|
||||||
|
def retry_payments(self):
|
||||||
|
''' Checks if any payment is due for retry, and tries to pay it'''
|
||||||
|
|
||||||
|
queryset = LNPayment.objects.filter(type=LNPayment.Types.NORM,
|
||||||
|
status__in=[LNPayment.Status.VALIDI, LNPayment.Status.FAILRO],
|
||||||
|
routing_attempts__lt=4,
|
||||||
|
last_routing_time__lt=(timezone.now()-timedelta(minutes=int(config('RETRY_TIME')))))
|
||||||
|
for lnpayment in queryset:
|
||||||
|
success, _ = follow_send_payment(lnpayment)
|
||||||
|
|
||||||
|
# If already 3 attempts and last failed. Make it expire (ask for a new invoice) an reset attempts.
|
||||||
|
if not success and lnpayment.routing_attempts == 3:
|
||||||
|
lnpayment.status = LNPayment.Status.EXPIRE
|
||||||
|
lnpayment.routing_attempts = 0
|
||||||
|
lnpayment.save()
|
||||||
|
|
||||||
def update_order_status(self, lnpayment):
|
def update_order_status(self, lnpayment):
|
||||||
''' Background process following LND hold invoices
|
''' Background process following LND hold invoices
|
||||||
@ -123,21 +148,27 @@ class Command(BaseCommand):
|
|||||||
elif hasattr(lnpayment, 'order_escrow' ):
|
elif hasattr(lnpayment, 'order_escrow' ):
|
||||||
Logics.trade_escrow_received(lnpayment.order_escrow)
|
Logics.trade_escrow_received(lnpayment.order_escrow)
|
||||||
return
|
return
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.stdout.write(str(e))
|
self.stdout.write(str(e))
|
||||||
|
|
||||||
# TODO If a lnpayment goes from LOCKED to INVGED. Totally weird
|
# If the LNPayment goes to CANCEL from INVGEN, the invoice had expired
|
||||||
# halt the order
|
# If it goes to CANCEL from LOCKED the bond was relased. Order had expired in both cases.
|
||||||
if lnpayment.status == LNPayment.Status.LOCKED:
|
# Testing needed for end of time trades!
|
||||||
pass
|
if lnpayment.status == LNPayment.Status.CANCEL :
|
||||||
|
if hasattr(lnpayment, 'order_made' ):
|
||||||
|
Logics.order_expires(lnpayment.order_made)
|
||||||
|
return
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
elif hasattr(lnpayment, 'order_taken' ):
|
||||||
''' Never mind database locked error, keep going, print them out'''
|
Logics.order_expires(lnpayment.order_taken)
|
||||||
|
return
|
||||||
try:
|
|
||||||
self.follow_invoices()
|
elif hasattr(lnpayment, 'order_escrow' ):
|
||||||
except Exception as e:
|
Logics.order_expires(lnpayment.order_escrow)
|
||||||
if 'database is locked' in str(e):
|
return
|
||||||
self.stdout.write('database is locked')
|
|
||||||
|
# TODO If a lnpayment goes from LOCKED to INVGEN. Totally weird
|
||||||
self.stdout.write(str(e))
|
# halt the order
|
||||||
|
if lnpayment.status == LNPayment.Status.INVGEN:
|
||||||
|
pass
|
66
api/tasks.py
66
api/tasks.py
@ -38,6 +38,8 @@ def follow_send_payment(lnpayment):
|
|||||||
|
|
||||||
from decouple import config
|
from decouple import config
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
|
from django.utils import timezone
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from api.lightning.node import LNNode
|
from api.lightning.node import LNNode
|
||||||
from api.models import LNPayment, Order
|
from api.models import LNPayment, Order
|
||||||
@ -51,36 +53,54 @@ def follow_send_payment(lnpayment):
|
|||||||
timeout_seconds=60) # time out payment in 60 seconds
|
timeout_seconds=60) # time out payment in 60 seconds
|
||||||
|
|
||||||
order = lnpayment.order_paid
|
order = lnpayment.order_paid
|
||||||
for response in LNNode.routerstub.SendPaymentV2(request, metadata=[('macaroon', MACAROON.hex())]):
|
try:
|
||||||
if response.status == 0 : # Status 0 'UNKNOWN'
|
for response in LNNode.routerstub.SendPaymentV2(request, metadata=[('macaroon', MACAROON.hex())]):
|
||||||
# Not sure when this status happens
|
if response.status == 0 : # Status 0 'UNKNOWN'
|
||||||
pass
|
# Not sure when this status happens
|
||||||
|
pass
|
||||||
|
|
||||||
if response.status == 1 : # Status 1 'IN_FLIGHT'
|
if response.status == 1 : # Status 1 'IN_FLIGHT'
|
||||||
print('IN_FLIGHT')
|
print('IN_FLIGHT')
|
||||||
lnpayment.status = LNPayment.Status.FLIGHT
|
lnpayment.status = LNPayment.Status.FLIGHT
|
||||||
lnpayment.save()
|
lnpayment.save()
|
||||||
order.status = Order.Status.PAY
|
order.status = Order.Status.PAY
|
||||||
order.save()
|
order.save()
|
||||||
|
|
||||||
if response.status == 3 : # Status 3 'FAILED'
|
if response.status == 3 : # Status 3 'FAILED'
|
||||||
print('FAILED')
|
print('FAILED')
|
||||||
lnpayment.status = LNPayment.Status.FAILRO
|
lnpayment.status = LNPayment.Status.FAILRO
|
||||||
|
lnpayment.last_routing_time = timezone.now()
|
||||||
|
lnpayment.routing_attempts += 1
|
||||||
|
lnpayment.save()
|
||||||
|
order.status = Order.Status.FAI
|
||||||
|
order.expires_at = timezone.now() + timedelta(seconds=Order.t_to_expire[Order.Status.FAI])
|
||||||
|
order.save()
|
||||||
|
context = {'routing_failed': LNNode.payment_failure_context[response.failure_reason]}
|
||||||
|
print(context)
|
||||||
|
# Call a retry in 5 mins here?
|
||||||
|
return False, context
|
||||||
|
|
||||||
|
if response.status == 2 : # Status 2 'SUCCEEDED'
|
||||||
|
print('SUCCEEDED')
|
||||||
|
lnpayment.status = LNPayment.Status.SUCCED
|
||||||
|
lnpayment.save()
|
||||||
|
order.status = Order.Status.SUC
|
||||||
|
order.expires_at = timezone.now() + timedelta(seconds=Order.t_to_expire[Order.Status.SUC])
|
||||||
|
order.save()
|
||||||
|
return True, None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
if "invoice expired" in str(e):
|
||||||
|
print('INVOICE EXPIRED')
|
||||||
|
lnpayment.status = LNPayment.Status.EXPIRE
|
||||||
|
lnpayment.last_routing_time = timezone.now()
|
||||||
lnpayment.save()
|
lnpayment.save()
|
||||||
order.status = Order.Status.FAI
|
order.status = Order.Status.FAI
|
||||||
|
order.expires_at = timezone.now() + timedelta(seconds=Order.t_to_expire[Order.Status.FAI])
|
||||||
order.save()
|
order.save()
|
||||||
context = LNNode.payment_failure_context[response.failure_reason]
|
context = {'routing_failed':'The payout invoice has expired'}
|
||||||
# Call a retry here?
|
|
||||||
return False, context
|
return False, context
|
||||||
|
|
||||||
if response.status == 2 : # Status 2 'SUCCEEDED'
|
|
||||||
print('SUCCEEDED')
|
|
||||||
lnpayment.status = LNPayment.Status.SUCCED
|
|
||||||
lnpayment.save()
|
|
||||||
order.status = Order.Status.SUC
|
|
||||||
order.save()
|
|
||||||
return True, None
|
|
||||||
|
|
||||||
@shared_task(name="cache_external_market_prices", ignore_result=True)
|
@shared_task(name="cache_external_market_prices", ignore_result=True)
|
||||||
def cache_market():
|
def cache_market():
|
||||||
|
|
||||||
|
@ -236,6 +236,11 @@ class OrderView(viewsets.ViewSet):
|
|||||||
elif order.status == Order.Status.FAI:
|
elif order.status == Order.Status.FAI:
|
||||||
data['retries'] = order.buyer_invoice.routing_attempts
|
data['retries'] = order.buyer_invoice.routing_attempts
|
||||||
data['next_retry_time'] = order.buyer_invoice.last_routing_time + timedelta(minutes=RETRY_TIME)
|
data['next_retry_time'] = order.buyer_invoice.last_routing_time + timedelta(minutes=RETRY_TIME)
|
||||||
|
|
||||||
|
if order.buyer_invoice.status == LNPayment.Status.EXPIRE:
|
||||||
|
data['invoice_expired'] = True
|
||||||
|
# Add invoice amount once again if invoice was expired.
|
||||||
|
data['invoice_amount'] = int(order.last_satoshis * (1-FEE))
|
||||||
|
|
||||||
return Response(data, status.HTTP_200_OK)
|
return Response(data, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
@ -87,6 +87,7 @@ export default class OrderPage extends Component {
|
|||||||
delay: this.setDelay(newStateVars.status),
|
delay: this.setDelay(newStateVars.status),
|
||||||
currencyCode: this.getCurrencyCode(newStateVars.currency),
|
currencyCode: this.getCurrencyCode(newStateVars.currency),
|
||||||
penalty: newStateVars.penalty, // in case penalty time has finished, it goes back to null
|
penalty: newStateVars.penalty, // in case penalty time has finished, it goes back to null
|
||||||
|
invoice_expired: newStateVars.invoice_expired // in case invoice had expired, it goes back to null when it is valid again
|
||||||
};
|
};
|
||||||
|
|
||||||
var completeStateVars = Object.assign({}, newStateVars, otherStateVars);
|
var completeStateVars = Object.assign({}, newStateVars, otherStateVars);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import { Link, Paper, Rating, Button, Grid, Typography, TextField, List, ListItem, ListItemText, Divider, ListItemIcon, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "@mui/material"
|
import { Paper, Rating, Button, Grid, Typography, TextField, List, ListItem, ListItemText, Divider, ListItemIcon, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "@mui/material"
|
||||||
import QRCode from "react-qr-code";
|
import QRCode from "react-qr-code";
|
||||||
|
import Countdown from 'react-countdown';
|
||||||
import Chat from "./Chat"
|
import Chat from "./Chat"
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
@ -349,7 +349,7 @@ export default class TradeBox extends Component {
|
|||||||
showInputInvoice(){
|
showInputInvoice(){
|
||||||
return (
|
return (
|
||||||
|
|
||||||
// TODO Option to upload files and images
|
// TODO Option to upload using QR from camera
|
||||||
|
|
||||||
<Grid container spacing={1}>
|
<Grid container spacing={1}>
|
||||||
{/* In case the taker was very fast to scan the bond, make the taker found alarm sound again */}
|
{/* In case the taker was very fast to scan the bond, make the taker found alarm sound again */}
|
||||||
@ -662,10 +662,45 @@ handleRatingChange=(e)=>{
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
showRoutingFailed(){
|
showRoutingFailed=()=>{
|
||||||
|
|
||||||
// TODO If it has failed 3 times, ask for a new invoice.
|
// TODO If it has failed 3 times, ask for a new invoice.
|
||||||
|
if(this.props.data.invoice_expired){
|
||||||
|
return(
|
||||||
|
<Grid container spacing={1}>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<Typography component="h6" variant="h6">
|
||||||
|
Lightning Routing Failed
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<Typography component="body2" variant="body2" align="center">
|
||||||
|
Your invoice has expires or more than 3 payments have been attempted.
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<Typography color="primary" component="subtitle1" variant="subtitle1">
|
||||||
|
<b> Submit a LN invoice for {pn(this.props.data.invoice_amount)} Sats </b>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<TextField
|
||||||
|
error={this.state.badInvoice}
|
||||||
|
helperText={this.state.badInvoice ? this.state.badInvoice : "" }
|
||||||
|
label={"Payout Lightning Invoice"}
|
||||||
|
required
|
||||||
|
inputProps={{
|
||||||
|
style: {textAlign:"center"}
|
||||||
|
}}
|
||||||
|
multiline
|
||||||
|
onChange={this.handleInputInvoiceChanged}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<Button onClick={this.handleClickSubmitInvoiceButton} variant='contained' color='primary'>Submit</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
}else{
|
||||||
return(
|
return(
|
||||||
<Grid container spacing={1}>
|
<Grid container spacing={1}>
|
||||||
<Grid item xs={12} align="center">
|
<Grid item xs={12} align="center">
|
||||||
@ -675,18 +710,19 @@ handleRatingChange=(e)=>{
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} align="center">
|
<Grid item xs={12} align="center">
|
||||||
<Typography component="body2" variant="body2" align="center">
|
<Typography component="body2" variant="body2" align="center">
|
||||||
RoboSats will retry pay your invoice 3 times every 5 minutes. If it keeps failing, you
|
RoboSats will try to pay your invoice 3 times every 5 minutes. If it keeps failing, you
|
||||||
will be able to submit a new invoice. Check whether you have enough inboud liquidity.
|
will be able to submit a new invoice. Check whether you have enough inboud liquidity.
|
||||||
Remember that lightning nodes must be online in order to receive payments.
|
Remember that lightning nodes must be online in order to receive payments.
|
||||||
</Typography>
|
</Typography>
|
||||||
<List>
|
<List>
|
||||||
|
<Divider/>
|
||||||
<ListItemText secondary="Next attempt in">
|
<ListItemText secondary="Next attempt in">
|
||||||
<Countdown date={new Date(this.props.data.next_retry_time)} renderer={this.countdownRenderer} />
|
<Countdown date={new Date(this.props.data.next_retry_time)} renderer={this.countdownRenderer} />
|
||||||
</ListItemText>
|
</ListItemText>
|
||||||
</List>
|
</List>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
)
|
)}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
Loading…
Reference in New Issue
Block a user