mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 20:21:35 +00:00
Add more logics bareframes
This commit is contained in:
parent
a1771ae5ea
commit
34e05465c2
@ -30,7 +30,7 @@ class OrderAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(LNPayment)
|
@admin.register(LNPayment)
|
||||||
class LNPaymentAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
class LNPaymentAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
||||||
list_display = ('id','concept','status','num_satoshis','type','invoice','preimage','expires_at','sender_link','receiver_link')
|
list_display = ('id','concept','status','num_satoshis','type','invoice','expires_at','sender_link','receiver_link')
|
||||||
list_display_links = ('id','concept')
|
list_display_links = ('id','concept')
|
||||||
change_links = ('sender','receiver')
|
change_links = ('sender','receiver')
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ class LNNode():
|
|||||||
Place holder functions to interact with Lightning Node
|
Place holder functions to interact with Lightning Node
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def gen_hodl_invoice(num_satoshis, description):
|
def gen_hodl_invoice(num_satoshis, description, expiry):
|
||||||
'''Generates hodl invoice to publish an order'''
|
'''Generates hodl invoice to publish an order'''
|
||||||
# TODO
|
# TODO
|
||||||
invoice = ''.join(random.choices(string.ascii_uppercase + string.digits, k=80)) #FIX
|
invoice = ''.join(random.choices(string.ascii_uppercase + string.digits, k=80)) #FIX
|
||||||
|
@ -11,6 +11,12 @@ BOND_SIZE = float(config('BOND_SIZE'))
|
|||||||
MARKET_PRICE_API = config('MARKET_PRICE_API')
|
MARKET_PRICE_API = config('MARKET_PRICE_API')
|
||||||
ESCROW_USERNAME = config('ESCROW_USERNAME')
|
ESCROW_USERNAME = config('ESCROW_USERNAME')
|
||||||
|
|
||||||
|
EXP_MAKER_BOND_INVOICE = int(config('EXP_MAKER_BOND_INVOICE'))
|
||||||
|
EXP_TAKER_BOND_INVOICE = int(config('EXP_TAKER_BOND_INVOICE'))
|
||||||
|
EXP_TRADE_ESCR_INVOICE = int(config('EXP_TRADE_ESCR_INVOICE'))
|
||||||
|
|
||||||
|
BOND_EXPIRY = int(config('BOND_EXPIRY'))
|
||||||
|
ESCROW_EXPIRY = int(config('ESCROW_EXPIRY'))
|
||||||
|
|
||||||
class Logics():
|
class Logics():
|
||||||
|
|
||||||
@ -46,12 +52,10 @@ class Logics():
|
|||||||
if order.is_explicit:
|
if order.is_explicit:
|
||||||
satoshis_now = order.satoshis
|
satoshis_now = order.satoshis
|
||||||
else:
|
else:
|
||||||
|
# TODO Add fallback Public APIs and error handling
|
||||||
market_prices = requests.get(MARKET_PRICE_API).json()
|
market_prices = requests.get(MARKET_PRICE_API).json()
|
||||||
print(market_prices)
|
|
||||||
exchange_rate = float(market_prices[Order.Currencies(order.currency).label]['last'])
|
exchange_rate = float(market_prices[Order.Currencies(order.currency).label]['last'])
|
||||||
print(exchange_rate)
|
|
||||||
satoshis_now = ((float(order.amount) * 1+float(order.premium)) / exchange_rate) * 100*1000*1000
|
satoshis_now = ((float(order.amount) * 1+float(order.premium)) / exchange_rate) * 100*1000*1000
|
||||||
print(satoshis_now)
|
|
||||||
|
|
||||||
return satoshis_now
|
return satoshis_now
|
||||||
|
|
||||||
@ -85,25 +89,57 @@ class Logics():
|
|||||||
if order.status == Order.Status.FAI:
|
if order.status == Order.Status.FAI:
|
||||||
order.status = Order.Status.UPI
|
order.status = Order.Status.UPI
|
||||||
order.save()
|
order.save()
|
||||||
return True
|
return True, None
|
||||||
|
|
||||||
|
return False, {'bad_request':'Invalid Lightning Network Invoice. It starts by LNTB...'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def cancel_order(cls, order, user, state):
|
||||||
|
|
||||||
|
# 1) When maker cancels before bond
|
||||||
|
'''The order never shows up on the book and status
|
||||||
|
changes to cancelled. That's it.'''
|
||||||
|
|
||||||
|
# 2) When maker cancels after bond
|
||||||
|
'''The order dissapears from book and goes to cancelled.
|
||||||
|
Maker is charged a small amount of sats, to prevent DDOS
|
||||||
|
on the LN node and order book'''
|
||||||
|
|
||||||
|
# 3) When taker cancels before bond
|
||||||
|
''' The order goes back to the book as public.
|
||||||
|
LNPayment "order.taker_bond" is deleted() '''
|
||||||
|
|
||||||
|
# 4) When taker or maker cancel after bond
|
||||||
|
'''The order goes into cancelled status if maker cancels.
|
||||||
|
The order goes into the public book if taker cancels.
|
||||||
|
In both cases there is a small fee.'''
|
||||||
|
|
||||||
|
# 5) When trade collateral has been posted
|
||||||
|
'''Always goes to cancelled status. Collaboration is needed.
|
||||||
|
When a user asks for cancel, 'order.is_pending_cancel' goes True.
|
||||||
|
When the second user asks for cancel. Order is totally cancelled.
|
||||||
|
Has a small cost for both parties to prevent node DDOS.'''
|
||||||
|
pass
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def gen_maker_hodl_invoice(cls, order, user):
|
def gen_maker_hodl_invoice(cls, order, user):
|
||||||
|
|
||||||
# Do not and delete if order is more than 5 minutes old
|
# Do not gen and delete if order is more than 5 minutes old
|
||||||
if order.expires_at < timezone.now():
|
if order.expires_at < timezone.now():
|
||||||
cls.order_expires(order)
|
cls.order_expires(order)
|
||||||
return False, {'Order expired':'cannot generate a bond invoice for an expired order. Make a new one.'}
|
return False, {'Order expired':'cannot generate a bond invoice for an expired order. Make a new one.'}
|
||||||
|
|
||||||
|
# Return the previous invoice if there was one
|
||||||
if order.maker_bond:
|
if order.maker_bond:
|
||||||
return True, {'invoice':order.maker_bond.invoice,'bond_satoshis':order.maker_bond.num_satoshis}
|
return True, {'invoice':order.maker_bond.invoice,'bond_satoshis':order.maker_bond.num_satoshis}
|
||||||
|
|
||||||
order.satoshis_now = cls.satoshis_now(order)
|
order.satoshis_now = cls.satoshis_now(order)
|
||||||
bond_satoshis = order.satoshis_now * BOND_SIZE
|
bond_satoshis = order.satoshis_now * BOND_SIZE
|
||||||
description = f'Robosats maker bond for order ID {order.id}. Will return to you if you do not cheat!'
|
description = f'RoboSats - Maker bond for order ID {order.id}. These sats will return to you if you do not cheat!'
|
||||||
invoice, payment_hash, expires_at = LNNode.gen_hodl_invoice(num_satoshis = bond_satoshis, description=description)
|
|
||||||
|
# Gen HODL Invoice
|
||||||
|
invoice, payment_hash, expires_at = LNNode.gen_hodl_invoice(bond_satoshis, description, BOND_EXPIRY*3600)
|
||||||
|
|
||||||
order.maker_bond = LNPayment.objects.create(
|
order.maker_bond = LNPayment.objects.create(
|
||||||
concept = LNPayment.Concepts.MAKEBOND,
|
concept = LNPayment.Concepts.MAKEBOND,
|
||||||
@ -115,30 +151,32 @@ class Logics():
|
|||||||
num_satoshis = bond_satoshis,
|
num_satoshis = bond_satoshis,
|
||||||
description = description,
|
description = description,
|
||||||
payment_hash = payment_hash,
|
payment_hash = payment_hash,
|
||||||
expires_at = expires_at,
|
expires_at = expires_at)
|
||||||
)
|
|
||||||
|
|
||||||
order.save()
|
order.save()
|
||||||
return True, {'invoice':invoice,'bond_satoshis':bond_satoshis}
|
return True, {'invoice':invoice,'bond_satoshis':bond_satoshis}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def gen_taker_buyer_hodl_invoice(cls, order, user):
|
def gen_takerbuyer_hodl_invoice(cls, order, user):
|
||||||
|
|
||||||
# Do not and delete if order is more than 5 minutes old
|
# Do not gen and cancel if a taker invoice is there and older than 2 minutes
|
||||||
if order.expires_at < timezone.now():
|
if order.taker_bond.created_at < (timezone.now()+timedelta(minutes=EXP_TAKER_BOND_INVOICE)):
|
||||||
cls.order_expires(order)
|
cls.cancel_order(order, user, 3) # State 3, cancel order before taker bond
|
||||||
return False, {'Order expired':'cannot generate a bond invoice for an expired order. Make a new one.'}
|
return False, {'Invoice expired':'You did not confirm taking the order in time.'}
|
||||||
|
|
||||||
if order.maker_bond:
|
# Return the previous invoice if there was one
|
||||||
return True, {'invoice':order.maker_bond.invoice,'bond_satoshis':order.maker_bond.num_satoshis}
|
if order.taker_bond:
|
||||||
|
return True, {'invoice':order.taker_bond.invoice,'bond_satoshis':order.taker_bond.num_satoshis}
|
||||||
|
|
||||||
order.satoshis_now = cls.satoshis_now(order)
|
order.satoshis_now = cls.satoshis_now(order)
|
||||||
bond_satoshis = order.satoshis_now * BOND_SIZE
|
bond_satoshis = order.satoshis_now * BOND_SIZE
|
||||||
description = f'Robosats maker bond for order ID {order.id}. Will return to you if you do not cheat!'
|
description = f'RoboSats - Taker bond for order ID {order.id}. These sats will return to you if you do not cheat!'
|
||||||
invoice, payment_hash, expires_at = LNNode.gen_hodl_invoice(num_satoshis = bond_satoshis, description=description)
|
|
||||||
|
|
||||||
order.maker_bond = LNPayment.objects.create(
|
# Gen HODL Invoice
|
||||||
concept = LNPayment.Concepts.MAKEBOND,
|
invoice, payment_hash, expires_at = LNNode.gen_hodl_invoice(bond_satoshis, description, BOND_EXPIRY*3600)
|
||||||
|
|
||||||
|
order.taker_bond = LNPayment.objects.create(
|
||||||
|
concept = LNPayment.Concepts.TAKEBOND,
|
||||||
type = LNPayment.Types.HODL,
|
type = LNPayment.Types.HODL,
|
||||||
sender = user,
|
sender = user,
|
||||||
receiver = User.objects.get(username=ESCROW_USERNAME),
|
receiver = User.objects.get(username=ESCROW_USERNAME),
|
||||||
@ -147,8 +185,7 @@ class Logics():
|
|||||||
num_satoshis = bond_satoshis,
|
num_satoshis = bond_satoshis,
|
||||||
description = description,
|
description = description,
|
||||||
payment_hash = payment_hash,
|
payment_hash = payment_hash,
|
||||||
expires_at = expires_at,
|
expires_at = expires_at)
|
||||||
)
|
|
||||||
|
|
||||||
order.save()
|
order.save()
|
||||||
return True, {'invoice':invoice,'bond_satoshis':bond_satoshis}
|
return True, {'invoice':invoice,'bond_satoshis':bond_satoshis}
|
@ -49,7 +49,6 @@ class LNPayment(models.Model):
|
|||||||
# payment info
|
# payment info
|
||||||
invoice = models.CharField(max_length=300, unique=False, null=True, default=None, blank=True)
|
invoice = models.CharField(max_length=300, unique=False, null=True, default=None, blank=True)
|
||||||
payment_hash = models.CharField(max_length=300, unique=False, null=True, default=None, blank=True)
|
payment_hash = models.CharField(max_length=300, unique=False, null=True, default=None, blank=True)
|
||||||
preimage = models.CharField(max_length=300, unique=False, null=True, default=None, blank=True)
|
|
||||||
description = models.CharField(max_length=300, unique=False, null=True, default=None, blank=True)
|
description = models.CharField(max_length=300, unique=False, null=True, default=None, blank=True)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
expires_at = models.DateTimeField()
|
expires_at = models.DateTimeField()
|
||||||
@ -79,22 +78,21 @@ class Order(models.Model):
|
|||||||
PUB = 1, 'Public'
|
PUB = 1, 'Public'
|
||||||
DEL = 2, 'Deleted'
|
DEL = 2, 'Deleted'
|
||||||
TAK = 3, 'Waiting for taker bond' # only needed when taker is a buyer
|
TAK = 3, 'Waiting for taker bond' # only needed when taker is a buyer
|
||||||
UCA = 4, 'Unilaterally cancelled'
|
UCA = 4, 'Cancelled'
|
||||||
RET = 5, 'Returned to order book' # Probably same as 1 in most cases.
|
WF2 = 5, 'Waiting for trade collateral and buyer invoice'
|
||||||
WF2 = 6, 'Waiting for trade collateral and buyer invoice'
|
WTC = 6, 'Waiting only for seller trade collateral'
|
||||||
WTC = 7, 'Waiting only for trade collateral'
|
WBI = 7, 'Waiting only for buyer invoice'
|
||||||
WBI = 8, 'Waiting only for buyer invoice'
|
EXF = 8, 'Sending fiat - In chatroom'
|
||||||
EXF = 9, 'Exchanging fiat / In chat'
|
CCA = 9, 'Collaboratively cancelled'
|
||||||
CCA = 10, 'Collaboratively cancelled'
|
FSE = 10, 'Fiat sent - In chatroom'
|
||||||
FSE = 11, 'Fiat sent'
|
FCO = 11, 'Fiat confirmed'
|
||||||
FCO = 12, 'Fiat confirmed'
|
SUC = 12, 'Sucessfully settled'
|
||||||
SUC = 13, 'Sucessfully settled'
|
FAI = 13, 'Failed lightning network routing'
|
||||||
FAI = 14, 'Failed lightning network routing'
|
UPI = 14, 'Updated invoice'
|
||||||
UPI = 15, 'Updated invoice'
|
DIS = 15, 'In dispute'
|
||||||
DIS = 16, 'In dispute'
|
MLD = 16, 'Maker lost dispute'
|
||||||
MLD = 17, 'Maker lost dispute'
|
TLD = 17, 'Taker lost dispute'
|
||||||
TLD = 18, 'Taker lost dispute'
|
EXP = 18, 'Expired'
|
||||||
EXP = 19, 'Expired'
|
|
||||||
|
|
||||||
# order info
|
# order info
|
||||||
status = models.PositiveSmallIntegerField(choices=Status.choices, null=False, default=Status.WFB)
|
status = models.PositiveSmallIntegerField(choices=Status.choices, null=False, default=Status.WFB)
|
||||||
@ -117,10 +115,10 @@ class Order(models.Model):
|
|||||||
t0_satoshis = models.PositiveBigIntegerField(null=True, validators=[MinValueValidator(MIN_TRADE), MaxValueValidator(MAX_TRADE)], blank=True) # sats at creation
|
t0_satoshis = models.PositiveBigIntegerField(null=True, validators=[MinValueValidator(MIN_TRADE), MaxValueValidator(MAX_TRADE)], blank=True) # sats at creation
|
||||||
last_satoshis = models.PositiveBigIntegerField(null=True, validators=[MinValueValidator(0), MaxValueValidator(MAX_TRADE*2)], blank=True) # sats last time checked. Weird if 2* trade max...
|
last_satoshis = models.PositiveBigIntegerField(null=True, validators=[MinValueValidator(0), MaxValueValidator(MAX_TRADE*2)], blank=True) # sats last time checked. Weird if 2* trade max...
|
||||||
|
|
||||||
|
|
||||||
# order participants
|
# order participants
|
||||||
maker = models.ForeignKey(User, related_name='maker', on_delete=models.CASCADE, null=True, default=None) # unique = True, a maker can only make one order
|
maker = models.ForeignKey(User, related_name='maker', on_delete=models.CASCADE, null=True, default=None) # unique = True, a maker can only make one order
|
||||||
taker = models.ForeignKey(User, related_name='taker', on_delete=models.SET_NULL, null=True, default=None, blank=True) # unique = True, a taker can only take one order
|
taker = models.ForeignKey(User, related_name='taker', on_delete=models.SET_NULL, null=True, default=None, blank=True) # unique = True, a taker can only take one order
|
||||||
|
is_pending_cancel = models.BooleanField(default=False, null=False) # When collaborative cancel is needed and one partner has cancelled.
|
||||||
|
|
||||||
# order collateral
|
# order collateral
|
||||||
maker_bond = models.ForeignKey(LNPayment, related_name='maker_bond', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
maker_bond = models.ForeignKey(LNPayment, related_name='maker_bond', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||||
|
@ -11,12 +11,7 @@ class MakeOrderSerializer(serializers.ModelSerializer):
|
|||||||
model = Order
|
model = Order
|
||||||
fields = ('type','currency','amount','payment_method','is_explicit','premium','satoshis')
|
fields = ('type','currency','amount','payment_method','is_explicit','premium','satoshis')
|
||||||
|
|
||||||
class UpdateOrderSerializer(serializers.ModelSerializer):
|
class UpdateOrderSerializer(serializers.Serializer):
|
||||||
class Meta:
|
invoice = serializers.CharField(max_length=300, allow_null=True, allow_blank=True, default=None)
|
||||||
model = Order
|
action = serializers.ChoiceField(choices=('take','dispute','cancel','confirm','rate'), allow_null=False)
|
||||||
fields = ('id','buyer_invoice')
|
rating = serializers.ChoiceField(choices=('1','2','3','4','5'), allow_null=True, allow_blank=True, default=None)
|
||||||
|
|
||||||
class UpdateInvoiceSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = LNPayment
|
|
||||||
fields = ['invoice']
|
|
@ -3,7 +3,7 @@ from .views import OrderMakerView, OrderView, UserView, BookView
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('make/', OrderMakerView.as_view()),
|
path('make/', OrderMakerView.as_view()),
|
||||||
path('order/', OrderView.as_view({'get':'get','post':'take_or_update'})),
|
path('order/', OrderView.as_view({'get':'get','post':'take_update_confirm_dispute_cancel'})),
|
||||||
path('usergen/', UserView.as_view()),
|
path('usergen/', UserView.as_view()),
|
||||||
path('book/', BookView.as_view()),
|
path('book/', BookView.as_view()),
|
||||||
]
|
]
|
108
api/views.py
108
api/views.py
@ -7,9 +7,9 @@ from rest_framework.response import Response
|
|||||||
from django.contrib.auth import authenticate, login, logout
|
from django.contrib.auth import authenticate, login, logout
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from .serializers import ListOrderSerializer, MakeOrderSerializer, UpdateInvoiceSerializer
|
from .serializers import ListOrderSerializer, MakeOrderSerializer, UpdateOrderSerializer
|
||||||
from .models import Order
|
from .models import Order
|
||||||
from .logics import Logics
|
from .logics import EXP_MAKER_BOND_INVOICE, Logics
|
||||||
|
|
||||||
from .nick_generator.nick_generator import NickGenerator
|
from .nick_generator.nick_generator import NickGenerator
|
||||||
from robohash import Robohash
|
from robohash import Robohash
|
||||||
@ -23,7 +23,7 @@ from django.utils import timezone
|
|||||||
|
|
||||||
from decouple import config
|
from decouple import config
|
||||||
|
|
||||||
EXPIRATION_MAKE = config('EXPIRATION_MAKE')
|
EXP_MAKER_BOND_INVOICE = int(config('EXP_MAKER_BOND_INVOICE'))
|
||||||
|
|
||||||
avatar_path = Path('frontend/static/assets/avatars')
|
avatar_path = Path('frontend/static/assets/avatars')
|
||||||
avatar_path.mkdir(parents=True, exist_ok=True)
|
avatar_path.mkdir(parents=True, exist_ok=True)
|
||||||
@ -36,8 +36,9 @@ class OrderMakerView(CreateAPIView):
|
|||||||
def post(self,request):
|
def post(self,request):
|
||||||
serializer = self.serializer_class(data=request.data)
|
serializer = self.serializer_class(data=request.data)
|
||||||
|
|
||||||
if serializer.is_valid():
|
if not serializer.is_valid(): return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||||
otype = serializer.data.get('type')
|
|
||||||
|
type = serializer.data.get('type')
|
||||||
currency = serializer.data.get('currency')
|
currency = serializer.data.get('currency')
|
||||||
amount = serializer.data.get('amount')
|
amount = serializer.data.get('amount')
|
||||||
payment_method = serializer.data.get('payment_method')
|
payment_method = serializer.data.get('payment_method')
|
||||||
@ -46,34 +47,28 @@ class OrderMakerView(CreateAPIView):
|
|||||||
is_explicit = serializer.data.get('is_explicit')
|
is_explicit = serializer.data.get('is_explicit')
|
||||||
|
|
||||||
valid, context = Logics.validate_already_maker_or_taker(request.user)
|
valid, context = Logics.validate_already_maker_or_taker(request.user)
|
||||||
if not valid:
|
if not valid: return Response(context, status=status.HTTP_409_CONFLICT)
|
||||||
return Response(context, status=status.HTTP_409_CONFLICT)
|
|
||||||
|
|
||||||
# Creates a new order in db
|
# Creates a new order
|
||||||
order = Order(
|
order = Order(
|
||||||
type=otype,
|
type=type,
|
||||||
status=Order.Status.WFB,
|
|
||||||
currency=currency,
|
currency=currency,
|
||||||
amount=amount,
|
amount=amount,
|
||||||
payment_method=payment_method,
|
payment_method=payment_method,
|
||||||
premium=premium,
|
premium=premium,
|
||||||
satoshis=satoshis,
|
satoshis=satoshis,
|
||||||
is_explicit=is_explicit,
|
is_explicit=is_explicit,
|
||||||
expires_at=timezone.now()+timedelta(minutes=EXPIRATION_MAKE),
|
expires_at=timezone.now()+timedelta(minutes=EXP_MAKER_BOND_INVOICE),
|
||||||
maker=request.user)
|
maker=request.user)
|
||||||
|
|
||||||
order.t0_satoshis=Logics.satoshis_now(order) # TODO reate Order class method when new instance is created!
|
order.last_satoshis = order.t0_satoshis = Logics.satoshis_now(order) # TODO move to Order class method when new instance is created!
|
||||||
order.last_satoshis=Logics.satoshis_now(order)
|
|
||||||
order.save()
|
order.save()
|
||||||
|
|
||||||
if not serializer.is_valid():
|
|
||||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
return Response(ListOrderSerializer(order).data, status=status.HTTP_201_CREATED)
|
return Response(ListOrderSerializer(order).data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
|
||||||
class OrderView(viewsets.ViewSet):
|
class OrderView(viewsets.ViewSet):
|
||||||
serializer_class = UpdateInvoiceSerializer
|
serializer_class = UpdateOrderSerializer
|
||||||
lookup_url_kwarg = 'order_id'
|
lookup_url_kwarg = 'order_id'
|
||||||
|
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
@ -88,7 +83,7 @@ class OrderView(viewsets.ViewSet):
|
|||||||
if len(order) == 1 :
|
if len(order) == 1 :
|
||||||
order = order[0]
|
order = order[0]
|
||||||
|
|
||||||
# If order expired
|
# 1) If order expired
|
||||||
if order.status == Order.Status.EXP:
|
if order.status == Order.Status.EXP:
|
||||||
return Response({'bad_request':'This order has expired'},status.HTTP_400_BAD_REQUEST)
|
return Response({'bad_request':'This order has expired'},status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@ -99,11 +94,11 @@ class OrderView(viewsets.ViewSet):
|
|||||||
data['is_taker'] = order.taker == request.user
|
data['is_taker'] = order.taker == request.user
|
||||||
data['is_participant'] = data['is_maker'] or data['is_taker']
|
data['is_participant'] = data['is_maker'] or data['is_taker']
|
||||||
|
|
||||||
# If not a participant and order is not public, forbid.
|
# 2) If not a participant and order is not public, forbid.
|
||||||
if not data['is_participant'] and order.status != Order.Status.PUB:
|
if not data['is_participant'] and order.status != Order.Status.PUB:
|
||||||
return Response({'bad_request':'Not allowed to see this order'},status.HTTP_403_FORBIDDEN)
|
return Response({'bad_request':'Not allowed to see this order'},status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
# non participants can view some details, but only if PUB
|
# 3) Non participants can view details (but only if PUB)
|
||||||
elif not data['is_participant'] and order.status != Order.Status.PUB:
|
elif not data['is_participant'] and order.status != Order.Status.PUB:
|
||||||
return Response(data, status=status.HTTP_200_OK)
|
return Response(data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@ -114,45 +109,73 @@ class OrderView(viewsets.ViewSet):
|
|||||||
data['taker_nick'] = str(order.taker)
|
data['taker_nick'] = str(order.taker)
|
||||||
data['status_message'] = Order.Status(order.status).label
|
data['status_message'] = Order.Status(order.status).label
|
||||||
|
|
||||||
# If status is 'waiting for maker bond', reply with a hodl invoice too.
|
# 4) If status is 'waiting for maker bond', reply with a MAKER HODL invoice.
|
||||||
if order.status == Order.Status.WFB and data['is_maker']:
|
if order.status == Order.Status.WFB and data['is_maker']:
|
||||||
valid, context = Logics.gen_maker_hodl_invoice(order, request.user)
|
valid, context = Logics.gen_maker_hodl_invoice(order, request.user)
|
||||||
if valid:
|
data = {**data, **context} if valid else Response(context, status.HTTP_400_BAD_REQUEST)
|
||||||
data = {**data, **context}
|
|
||||||
else:
|
# 5) If status is 'Public' and user is taker/buyer, reply with a TAKER HODL invoice.
|
||||||
Response(context, status=status.HTTP_400_BAD_REQUEST)
|
elif order.status == Order.Status.PUB and data['is_taker'] and data['is_buyer']:
|
||||||
|
valid, context = Logics.gen_takerbuyer_hodl_invoice(order, request.user)
|
||||||
|
data = {**data, **context} if valid else Response(context, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
# 6) If status is 'Public' and user is taker/seller, reply with a ESCROW HODL invoice.
|
||||||
|
elif order.status == Order.Status.PUB and data['is_taker'] and data['is_seller']:
|
||||||
|
valid, context = Logics.gen_seller_hodl_invoice(order, request.user)
|
||||||
|
data = {**data, **context} if valid else Response(context, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
# 7) If status is 'WF2/WTC' and user is maker/seller, reply with an ESCROW HODL invoice.
|
||||||
|
elif (order.status == Order.Status.WF2 or order.status == Order.Status.WF2) and data['is_maker'] and data['is_seller']:
|
||||||
|
valid, context = Logics.gen_seller_hodl_invoice(order, request.user)
|
||||||
|
data = {**data, **context} if valid else Response(context, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
return Response(data, status=status.HTTP_200_OK)
|
return Response(data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
return Response({'Order Not Found':'Invalid Order Id'},status=status.HTTP_404_NOT_FOUND)
|
return Response({'Order Not Found':'Invalid Order Id'},status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
def take_update_confirm_dispute_cancel(self, request, format=None):
|
||||||
|
|
||||||
def take_or_update(self, request, format=None):
|
|
||||||
order_id = request.GET.get(self.lookup_url_kwarg)
|
order_id = request.GET.get(self.lookup_url_kwarg)
|
||||||
|
|
||||||
serializer = UpdateInvoiceSerializer(data=request.data)
|
serializer = UpdateOrderSerializer(data=request.data)
|
||||||
|
if not serializer.is_valid(): return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
order = Order.objects.get(id=order_id)
|
order = Order.objects.get(id=order_id)
|
||||||
|
|
||||||
if serializer.is_valid():
|
# action is either 1)'take', 2)'confirm', 3)'cancel', 4)'dispute' , 5)'update' (invoice) 6)'rate' (counterparty)
|
||||||
|
action = serializer.data.get('action')
|
||||||
invoice = serializer.data.get('invoice')
|
invoice = serializer.data.get('invoice')
|
||||||
|
rating = serializer.data.get('rating')
|
||||||
|
|
||||||
|
# 1) If action is take, it is be taker request!
|
||||||
# If this is an empty POST request (no invoice), it must be taker request!
|
if action == 'take':
|
||||||
if not invoice and order.status == Order.Status.PUB:
|
if order.status == Order.Status.PUB:
|
||||||
valid, context = Logics.validate_already_maker_or_taker(request.user)
|
valid, context = Logics.validate_already_maker_or_taker(request.user)
|
||||||
if not valid: return Response(context, status=status.HTTP_409_CONFLICT)
|
if not valid: return Response(context, status=status.HTTP_409_CONFLICT)
|
||||||
|
|
||||||
Logics.take(order, request.user)
|
Logics.take(order, request.user)
|
||||||
|
else: Response({'bad_request':'This order is not public anymore.'}, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# An invoice came in! update it
|
# 2) If action is update (invoice)
|
||||||
elif invoice:
|
elif action == 'update' and invoice:
|
||||||
print(invoice)
|
updated, context = Logics.update_invoice(order,request.user,invoice)
|
||||||
updated = Logics.update_invoice(order=order,user=request.user,invoice=invoice)
|
if not updated: return Response(context,status.HTTP_400_BAD_REQUEST)
|
||||||
if not updated:
|
|
||||||
return Response({'bad_request':'Invalid Lightning Network Invoice. It starts by LNTB...'})
|
|
||||||
|
|
||||||
# Something else is going on. Probably not allowed.
|
# 3) If action is cancel
|
||||||
|
elif action == 'cancel':
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 4) If action is confirm
|
||||||
|
elif action == 'confirm':
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 5) If action is dispute
|
||||||
|
elif action == 'dispute':
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 6) If action is dispute
|
||||||
|
elif action == 'rate' and rating:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# If nothing... something else is going on. Probably not allowed!
|
||||||
else:
|
else:
|
||||||
return Response({'bad_request':'Not allowed'})
|
return Response({'bad_request':'Not allowed'})
|
||||||
|
|
||||||
@ -264,6 +287,7 @@ class BookView(ListAPIView):
|
|||||||
user = User.objects.filter(id=data['maker'])
|
user = User.objects.filter(id=data['maker'])
|
||||||
if len(user) == 1:
|
if len(user) == 1:
|
||||||
data['maker_nick'] = user[0].username
|
data['maker_nick'] = user[0].username
|
||||||
|
|
||||||
# Non participants should not see the status or who is the taker
|
# Non participants should not see the status or who is the taker
|
||||||
for key in ('status','taker'):
|
for key in ('status','taker'):
|
||||||
del data[key]
|
del data[key]
|
||||||
|
@ -87,8 +87,10 @@ export default class OrderPage extends Component {
|
|||||||
console.log(this.state)
|
console.log(this.state)
|
||||||
const requestOptions = {
|
const requestOptions = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken')},
|
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
|
||||||
body: JSON.stringify({}),
|
body: JSON.stringify({
|
||||||
|
'action':'take',
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
fetch('/api/order/' + '?order_id=' + this.orderId, requestOptions)
|
fetch('/api/order/' + '?order_id=' + this.orderId, requestOptions)
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
|
Loading…
Reference in New Issue
Block a user