diff --git a/api/logics.py b/api/logics.py index e3ed98c5..340d9f7e 100644 --- a/api/logics.py +++ b/api/logics.py @@ -80,57 +80,81 @@ class Logics(): def update_invoice(cls, order, user, invoice): is_valid_invoice, num_satoshis, description, payment_hash, expires_at = LNNode.validate_ln_invoice(invoice) # only user is the buyer and a valid LN invoice - if cls.is_buyer(order, user) and is_valid_invoice: - order.buyer_invoice, _ = LNPayment.objects.update_or_create( - concept = LNPayment.Concepts.PAYBUYER, - type = LNPayment.Types.NORM, - sender = User.objects.get(username=ESCROW_USERNAME), - receiver= user, - # if there is a LNPayment matching these above, it updates that with defaults below. - defaults={ - 'invoice' : invoice, - 'status' : LNPayment.Status.VALIDI, - 'num_satoshis' : num_satoshis, - 'description' : description, - 'payment_hash' : payment_hash, - 'expires_at' : expires_at} - ) + if not (cls.is_buyer(order, user) or is_valid_invoice): + return False, {'bad_request':'Invalid Lightning Network Invoice. It starts by LNTB...'} - #If the order status was Payment Failed. Move foward to invoice Updated. - if order.status == Order.Status.FAI: - order.status = Order.Status.UPI - order.save() - return True, None + order.buyer_invoice, _ = LNPayment.objects.update_or_create( + concept = LNPayment.Concepts.PAYBUYER, + type = LNPayment.Types.NORM, + sender = User.objects.get(username=ESCROW_USERNAME), + receiver= user, + # if there is a LNPayment matching these above, it updates that one with defaults below. + defaults={ + 'invoice' : invoice, + 'status' : LNPayment.Status.VALIDI, + 'num_satoshis' : num_satoshis, + 'description' : description, + 'payment_hash' : payment_hash, + 'expires_at' : expires_at} + ) - return False, {'bad_request':'Invalid Lightning Network Invoice. It starts by LNTB...'} + # If the order status is 'Waiting for invoice'. Move forward to 'waiting for invoice' + if order.status == Order.Status.WFE: order.status = Order.Status.CHA + + # If the order status is 'Waiting for both'. Move forward to 'waiting for escrow' or to 'chat' + if order.status == Order.Status.WF2: + print(order.trade_escrow) + if order.trade_escrow: + if order.trade_escrow.status == LNPayment.Status.LOCKED: + order.status = Order.Status.CHA + else: + order.status = Order.Status.WFE + + # If the order status was Payment Failed. Move forward to invoice Updated. + if order.status == Order.Status.FAI: + order.status = Order.Status.UPI + + order.save() + return True, None + + @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.''' + '''The order never shows up on the book and order + status becomes "cancelled". That's it.''' + if order.status == Order.Status.WFB and order.maker == user: + order.maker = None + order.status = Order.Status.UCA + order.save() + return True, None - # 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() ''' + # 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''' - # 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.''' + # 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.''' + + else: + return False, {'bad_request':'You cannot cancel this order'} - # 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 @classmethod @@ -168,7 +192,7 @@ class Logics(): return True, {'invoice':invoice,'bond_satoshis':bond_satoshis} @classmethod - def gen_takerbuyer_hodl_invoice(cls, order, user): + def gen_taker_hodl_invoice(cls, order, user): # Do not gen and cancel if a taker invoice is there and older than 2 minutes if order.taker_bond: diff --git a/api/models.py b/api/models.py index 2c2ad4f7..b9a5ae66 100644 --- a/api/models.py +++ b/api/models.py @@ -77,12 +77,12 @@ class Order(models.Model): WFB = 0, 'Waiting for maker bond' PUB = 1, 'Public' DEL = 2, 'Deleted' - TAK = 3, 'Waiting for taker bond' # only needed when taker is a buyer + TAK = 3, 'Waiting for taker bond' UCA = 4, 'Cancelled' WF2 = 5, 'Waiting for trade collateral and buyer invoice' - WTC = 6, 'Waiting only for seller trade collateral' - WBI = 7, 'Waiting only for buyer invoice' - EXF = 8, 'Sending fiat - In chatroom' + WFE = 6, 'Waiting only for seller trade collateral' + WFI = 7, 'Waiting only for buyer invoice' + CHA = 8, 'Sending fiat - In chatroom' CCA = 9, 'Collaboratively cancelled' FSE = 10, 'Fiat sent - In chatroom' FCO = 11, 'Fiat confirmed' diff --git a/api/views.py b/api/views.py index 39ec55d1..ea200022 100644 --- a/api/views.py +++ b/api/views.py @@ -89,6 +89,12 @@ class OrderView(viewsets.ViewSet): if order.status == Order.Status.EXP: return Response({'bad_request':'This order has expired'},status.HTTP_400_BAD_REQUEST) + # 2) If order cancelled + if order.status == Order.Status.UCA: + return Response({'bad_request':'This order has been cancelled by the maker'},status.HTTP_400_BAD_REQUEST) + if order.status == Order.Status.CCA: + return Response({'bad_request':'This order has been cancelled collaborativelly'},status.HTTP_400_BAD_REQUEST) + data = ListOrderSerializer(order).data # Add booleans if user is maker, taker, partipant, buyer or seller @@ -96,11 +102,11 @@ class OrderView(viewsets.ViewSet): data['is_taker'] = order.taker == request.user data['is_participant'] = data['is_maker'] or data['is_taker'] - # 2) If not a participant and order is not public, forbid. + # 3) If not a participant and order is not public, forbid. 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) - # 3) Non participants can view details (but only if PUB) + # 4) Non participants can view details (but only if PUB) elif not data['is_participant'] and order.status != Order.Status.PUB: return Response(data, status=status.HTTP_200_OK) @@ -111,7 +117,7 @@ class OrderView(viewsets.ViewSet): data['taker_nick'] = str(order.taker) data['status_message'] = Order.Status(order.status).label - # 4) If status is 'waiting for maker bond', reply with a MAKER HODL invoice. + # 5) If status is 'waiting for maker bond' and user is MAKER, reply with a MAKER HODL invoice. if order.status == Order.Status.WFB and data['is_maker']: valid, context = Logics.gen_maker_hodl_invoice(order, request.user) if valid: @@ -119,24 +125,16 @@ class OrderView(viewsets.ViewSet): else: return Response(context, status.HTTP_400_BAD_REQUEST) - # 5) If status is 'Taken' and user is taker/buyer, reply with a TAKER HODL invoice. - elif order.status == Order.Status.TAK and data['is_taker'] and data['is_buyer']: - valid, context = Logics.gen_takerbuyer_hodl_invoice(order, request.user) - if valid: - data = {**data, **context} - else: - return 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) + # 6) If status is 'waiting for taker bond' and user is TAKER, reply with a TAKER HODL invoice. + elif order.status == Order.Status.TAK and data['is_taker']: + valid, context = Logics.gen_taker_hodl_invoice(order, request.user) if valid: data = {**data, **context} else: return 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']: + # 7) If status is 'WF2'or'WTC' and user is Seller, reply with an ESCROW HODL invoice. + elif (order.status == Order.Status.WF2 or order.status == Order.Status.WFE) and data['is_seller']: valid, context = Logics.gen_seller_hodl_invoice(order, request.user) if valid: data = {**data, **context} @@ -147,6 +145,10 @@ class OrderView(viewsets.ViewSet): return Response({'Order Not Found':'Invalid Order Id'}, status.HTTP_404_NOT_FOUND) def take_update_confirm_dispute_cancel(self, request, format=None): + ''' + Here take place all of the user updates to the order object. + That is: take, confim, cancel, dispute, update_invoice or rate. + ''' order_id = request.GET.get(self.lookup_url_kwarg) serializer = UpdateOrderSerializer(data=request.data) @@ -169,13 +171,14 @@ class OrderView(viewsets.ViewSet): else: Response({'bad_request':'This order is not public anymore.'}, status.HTTP_400_BAD_REQUEST) # 2) If action is update (invoice) - elif action == 'update' and invoice: - updated, context = Logics.update_invoice(order,request.user,invoice) - if not updated: return Response(context,status.HTTP_400_BAD_REQUEST) + elif action == 'update_invoice' and invoice: + valid, context = Logics.update_invoice(order,request.user,invoice) + if not valid: return Response(context,status.HTTP_400_BAD_REQUEST) # 3) If action is cancel elif action == 'cancel': - pass + valid, context = Logics.cancel_order(order,request.user,invoice) + if not valid: return Response(context,status.HTTP_400_BAD_REQUEST) # 4) If action is confirm elif action == 'confirm':