mirror of
https://github.com/RoboSats/robosats.git
synced 2024-12-26 22:06:08 +03:00
Bug fix invoice follow. Copy user token button.
This commit is contained in:
parent
a005b3509d
commit
e31bc1adad
@ -9,9 +9,12 @@ REDIS_URL=''
|
||||
# List of market price public APIs. If the currency is available in more than 1 API, will use median price.
|
||||
MARKET_PRICE_APIS = https://blockchain.info/ticker, https://api.yadio.io/exrates/BTC
|
||||
|
||||
# Host e.g. robotestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion
|
||||
# Host e.g. robosats.com
|
||||
HOST_NAME = ''
|
||||
|
||||
# e.g. robotestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion
|
||||
ONION_LOCATION = ''
|
||||
|
||||
# Trade fee in percentage %
|
||||
FEE = 0.002
|
||||
# Bond size in percentage %
|
||||
|
@ -31,8 +31,8 @@ class OrderAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
||||
|
||||
@admin.register(LNPayment)
|
||||
class LNPaymentAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
||||
list_display = ('id','concept','status','num_satoshis','type','expires_at','sender_link','receiver_link')
|
||||
list_display_links = ('id','concept')
|
||||
list_display = ('id','concept','status','num_satoshis','type','expires_at','sender_link','receiver_link','order_made','order_taken','order_escrow','order_paid')
|
||||
list_display_links = ('id','concept','order_made','order_taken','order_escrow','order_paid')
|
||||
change_links = ('sender','receiver')
|
||||
list_filter = ('type','concept','status')
|
||||
|
||||
|
@ -446,7 +446,9 @@ class Logics():
|
||||
|
||||
@classmethod
|
||||
def is_maker_bond_locked(cls, order):
|
||||
if LNNode.validate_hold_invoice_locked(order.maker_bond.payment_hash):
|
||||
if order.maker_bond.status == LNPayment.Status.LOCKED:
|
||||
return True
|
||||
elif LNNode.validate_hold_invoice_locked(order.maker_bond.payment_hash):
|
||||
order.maker_bond.status = LNPayment.Status.LOCKED
|
||||
order.maker_bond.save()
|
||||
cls.publish_order(order)
|
||||
@ -524,7 +526,7 @@ class Logics():
|
||||
def is_taker_bond_locked(cls, order):
|
||||
if order.taker_bond.status == LNPayment.Status.LOCKED:
|
||||
return True
|
||||
if LNNode.validate_hold_invoice_locked(order.taker_bond.payment_hash):
|
||||
elif LNNode.validate_hold_invoice_locked(order.taker_bond.payment_hash):
|
||||
cls.finalize_contract(order)
|
||||
return True
|
||||
return False
|
||||
@ -574,20 +576,25 @@ class Logics():
|
||||
order.save()
|
||||
return True, {'bond_invoice': hold_payment['invoice'], 'bond_satoshis': bond_satoshis}
|
||||
|
||||
def trade_escrow_received(order):
|
||||
''' Moves the order forward'''
|
||||
# If status is 'Waiting for both' move to Waiting for invoice
|
||||
if order.status == Order.Status.WF2:
|
||||
order.status = Order.Status.WFI
|
||||
# If status is 'Waiting for invoice' move to Chat
|
||||
elif order.status == Order.Status.WFE:
|
||||
order.status = Order.Status.CHA
|
||||
order.expires_at = timezone.now() + timedelta(seconds=Order.t_to_expire[Order.Status.CHA])
|
||||
order.save()
|
||||
|
||||
@classmethod
|
||||
def is_trade_escrow_locked(cls, order):
|
||||
if LNNode.validate_hold_invoice_locked(order.trade_escrow.payment_hash):
|
||||
if order.trade_escrow.status == LNPayment.Status.LOCKED:
|
||||
return True
|
||||
elif LNNode.validate_hold_invoice_locked(order.trade_escrow.payment_hash):
|
||||
order.trade_escrow.status = LNPayment.Status.LOCKED
|
||||
order.trade_escrow.save()
|
||||
# If status is 'Waiting for both' move to Waiting for invoice
|
||||
if order.status == Order.Status.WF2:
|
||||
order.status = Order.Status.WFI
|
||||
# If status is 'Waiting for invoice' move to Chat
|
||||
elif order.status == Order.Status.WFE:
|
||||
order.status = Order.Status.CHA
|
||||
order.expires_at = timezone.now() + timedelta(seconds=Order.t_to_expire[Order.Status.CHA])
|
||||
order.save()
|
||||
cls.trade_escrow_received(order)
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -607,7 +614,7 @@ class Logics():
|
||||
elif order.trade_escrow.status == LNPayment.Status.INVGEN:
|
||||
return True, {'escrow_invoice':order.trade_escrow.invoice, 'escrow_satoshis':order.trade_escrow.num_satoshis}
|
||||
|
||||
# If there was no taker_bond object yet, generates one
|
||||
# If there was no taker_bond object yet, generate one
|
||||
escrow_satoshis = order.last_satoshis # Amount was fixed when taker bond was locked
|
||||
description = f"RoboSats - Escrow amount for '{str(order)}' - The escrow will be released to the buyer once you confirm you received the fiat. It will automatically return if buyer does not confirm the payment."
|
||||
|
||||
|
@ -22,6 +22,7 @@ class Command(BaseCommand):
|
||||
'''
|
||||
|
||||
help = 'Follows all active hold invoices'
|
||||
rest = 5 # seconds between consecutive checks for invoice updates
|
||||
|
||||
# def add_arguments(self, parser):
|
||||
# parser.add_argument('debug', nargs='+', type=boolean)
|
||||
@ -40,7 +41,7 @@ class Command(BaseCommand):
|
||||
stub = LNNode.invoicesstub
|
||||
|
||||
while True:
|
||||
time.sleep(5)
|
||||
time.sleep(self.rest)
|
||||
|
||||
# time it for debugging
|
||||
t0 = time.time()
|
||||
@ -95,21 +96,24 @@ class Command(BaseCommand):
|
||||
|
||||
def update_order_status(self, lnpayment):
|
||||
''' Background process following LND hold invoices
|
||||
might catch LNpayments changing status. If they do,
|
||||
can catch LNpayments changing status. If they do,
|
||||
the order status might have to change status too.'''
|
||||
|
||||
# If the LNPayment goes to LOCKED (ACCEPTED)
|
||||
if lnpayment.status == LNPayment.Status.LOCKED:
|
||||
|
||||
# It is a maker bond => Publish order.
|
||||
order = lnpayment.order_made
|
||||
if not order == None:
|
||||
Logics.publish_order(order)
|
||||
if not lnpayment.order_made == None:
|
||||
Logics.publish_order(lnpayment.order_made)
|
||||
return
|
||||
|
||||
# It is a taker bond => close contract.
|
||||
order = lnpayment.order_taken
|
||||
if not order == None:
|
||||
if order.status == Order.Status.TAK:
|
||||
Logics.finalize_contract(order)
|
||||
return
|
||||
elif not lnpayment.order_taken == None:
|
||||
if lnpayment.order_taken.status == Order.Status.TAK:
|
||||
Logics.finalize_contract(lnpayment.order_taken)
|
||||
return
|
||||
|
||||
# It is a trade escrow => move foward order status.
|
||||
elif not lnpayment.order_escrow == None:
|
||||
Logics.trade_escrow_received(lnpayment.order_escrow)
|
||||
return
|
@ -151,7 +151,7 @@ class Order(models.Model):
|
||||
trade_escrow = models.OneToOneField(LNPayment, related_name='order_escrow', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||
|
||||
# buyer payment LN invoice
|
||||
buyer_invoice = models.ForeignKey(LNPayment, related_name='buyer_invoice', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||
buyer_invoice = models.OneToOneField(LNPayment, related_name='order_paid', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||
|
||||
# ratings
|
||||
maker_rated = models.BooleanField(default=False, null=False)
|
||||
@ -180,9 +180,8 @@ class Order(models.Model):
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
# Make relational back to ORDER
|
||||
return (f'Order {self.id}: {self.Types(self.type).label} BTC for {float(self.amount)} {self.currency}')
|
||||
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=Order)
|
||||
def delete_lnpayment_at_order_deletion(sender, instance, **kwargs):
|
||||
|
16
api/tasks.py
16
api/tasks.py
@ -40,7 +40,7 @@ def follow_send_payment(lnpayment):
|
||||
from base64 import b64decode
|
||||
|
||||
from api.lightning.node import LNNode
|
||||
from api.models import LNPayment
|
||||
from api.models import LNPayment, Order
|
||||
|
||||
MACAROON = b64decode(config('LND_MACAROON_BASE64'))
|
||||
|
||||
@ -48,25 +48,37 @@ def follow_send_payment(lnpayment):
|
||||
request = LNNode.routerrpc.SendPaymentRequest(
|
||||
payment_request=lnpayment.invoice,
|
||||
fee_limit_sat=fee_limit_sat,
|
||||
timeout_seconds=60)
|
||||
timeout_seconds=60) # time out payment in 60 seconds
|
||||
|
||||
order = lnpayment.order_paid
|
||||
for response in LNNode.routerstub.SendPaymentV2(request, metadata=[('macaroon', MACAROON.hex())]):
|
||||
if response.status == 0 : # Status 0 'UNKNOWN'
|
||||
# Not sure when this status happens
|
||||
pass
|
||||
|
||||
if response.status == 1 : # Status 1 'IN_FLIGHT'
|
||||
print('IN_FLIGHT')
|
||||
lnpayment.status = LNPayment.Status.FLIGHT
|
||||
lnpayment.save()
|
||||
order.status = Order.Status.PAY
|
||||
order.save()
|
||||
|
||||
if response.status == 3 : # Status 3 'FAILED'
|
||||
print('FAILED')
|
||||
lnpayment.status = LNPayment.Status.FAILRO
|
||||
lnpayment.save()
|
||||
order.status = Order.Status.FAI
|
||||
order.save()
|
||||
context = LNNode.payment_failure_context[response.failure_reason]
|
||||
# Call for a retry 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.save()
|
||||
return True, None
|
||||
|
||||
@shared_task(name="cache_external_market_prices", ignore_result=True)
|
||||
|
@ -5,7 +5,7 @@ import numpy as np
|
||||
|
||||
market_cache = {}
|
||||
|
||||
@ring.dict(market_cache, expire=5) #keeps in cache for 5 seconds
|
||||
@ring.dict(market_cache, expire=3) #keeps in cache for 3 seconds
|
||||
def get_exchange_rates(currencies):
|
||||
'''
|
||||
Params: list of currency codes.
|
||||
|
@ -63,7 +63,7 @@ export default class OrderPage extends Component {
|
||||
"10": 15000, //'Fiat sent - In chatroom'
|
||||
"11": 60000, //'In dispute'
|
||||
"12": 9999999,//'Collaboratively cancelled'
|
||||
"13": 120000, //'Sending satoshis to buyer'
|
||||
"13": 3000, //'Sending satoshis to buyer'
|
||||
"14": 9999999,//'Sucessful trade'
|
||||
"15": 10000, //'Failed lightning network routing'
|
||||
"16": 9999999,//'Maker lost dispute'
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React, { Component } from "react";
|
||||
import { Button , Dialog, Grid, Typography, TextField, ButtonGroup} from "@mui/material"
|
||||
import { Button , Dialog, Grid, Typography, TextField, ButtonGroup, CircularProgress, IconButton} from "@mui/material"
|
||||
import { Link } from 'react-router-dom'
|
||||
import Image from 'material-ui-image'
|
||||
import InfoDialog from './InfoDialog'
|
||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||
import ContentCopy from "@mui/icons-material/ContentCopy";
|
||||
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
@ -27,6 +29,7 @@ export default class UserGenPage extends Component {
|
||||
this.state = {
|
||||
token: this.genBase62Token(34),
|
||||
openInfo: false,
|
||||
showRobosat: true,
|
||||
};
|
||||
this.getGeneratedUser(this.state.token);
|
||||
}
|
||||
@ -53,6 +56,7 @@ export default class UserGenPage extends Component {
|
||||
shannon_entropy: data.token_shannon_entropy,
|
||||
bad_request: data.bad_request,
|
||||
found: data.found,
|
||||
showRobosat:true,
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -69,10 +73,12 @@ export default class UserGenPage extends Component {
|
||||
|
||||
handleAnotherButtonPressed=(e)=>{
|
||||
this.delGeneratedUser()
|
||||
this.setState({
|
||||
token: this.genBase62Token(34),
|
||||
});
|
||||
this.getGeneratedUser(this.state.token);
|
||||
// this.setState({
|
||||
// showRobosat: false,
|
||||
// token: this.genBase62Token(34),
|
||||
// });
|
||||
// this.getGeneratedUser(this.state.token);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
handleChangeToken=(e)=>{
|
||||
@ -81,6 +87,7 @@ export default class UserGenPage extends Component {
|
||||
token: e.target.value,
|
||||
})
|
||||
this.getGeneratedUser(e.target.value);
|
||||
this.setState({showRobosat: false})
|
||||
}
|
||||
|
||||
handleClickOpenInfo = () => {
|
||||
@ -109,20 +116,26 @@ export default class UserGenPage extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="h5" variant="h5">
|
||||
<b>{this.state.nickname ? "⚡"+this.state.nickname+"⚡" : ""}</b>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<div style={{ maxWidth: 200, maxHeight: 200 }}>
|
||||
<Image className='newAvatar'
|
||||
disableError='true'
|
||||
cover='true'
|
||||
color='null'
|
||||
src={this.state.avatar_url}
|
||||
/>
|
||||
</div><br/>
|
||||
<Grid item xs={12} align="center">
|
||||
{this.state.showRobosat ?
|
||||
<div>
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="h5" variant="h5">
|
||||
<b>{this.state.nickname ? "⚡"+this.state.nickname+"⚡" : ""}</b>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<div style={{ maxWidth: 200, maxHeight: 200 }}>
|
||||
<Image className='newAvatar'
|
||||
disableError='true'
|
||||
cover='true'
|
||||
color='null'
|
||||
src={this.state.avatar_url}
|
||||
/>
|
||||
</div><br/>
|
||||
</Grid>
|
||||
</div>
|
||||
: <CircularProgress />}
|
||||
</Grid>
|
||||
{
|
||||
this.state.found ?
|
||||
@ -134,21 +147,30 @@ export default class UserGenPage extends Component {
|
||||
:
|
||||
""
|
||||
}
|
||||
<Grid item xs={12} align="center">
|
||||
<TextField
|
||||
error={this.state.bad_request}
|
||||
label='Token - Store safely'
|
||||
required='true'
|
||||
value={this.state.token}
|
||||
variant='standard'
|
||||
helperText={this.state.bad_request}
|
||||
size='small'
|
||||
// multiline = {true}
|
||||
onChange={this.handleChangeToken}
|
||||
/>
|
||||
<Grid container align="center">
|
||||
<Grid item xs={12} align="center">
|
||||
<IconButton onClick= {()=>navigator.clipboard.writeText(this.state.token)}>
|
||||
<ContentCopy color='secondary'/>
|
||||
</IconButton>
|
||||
<TextField
|
||||
//sx={{ input: { color: 'purple' } }}
|
||||
InputLabelProps={{
|
||||
style: { color: 'purple' },
|
||||
}}
|
||||
error={this.state.bad_request}
|
||||
label='Token - Store safely'
|
||||
required='true'
|
||||
value={this.state.token}
|
||||
variant='standard'
|
||||
helperText={this.state.bad_request}
|
||||
size='small'
|
||||
// multiline = {true}
|
||||
onChange={this.handleChangeToken}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<Button onClick={this.handleAnotherButtonPressed}>Generate Another Robosat</Button>
|
||||
<Button size='small' onClick={this.handleAnotherButtonPressed}>Generate Another Robosat</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<ButtonGroup variant="contained" aria-label="outlined primary button group">
|
||||
|
Loading…
Reference in New Issue
Block a user