diff --git a/api/logics.py b/api/logics.py index abb02d11..b5b436d8 100644 --- a/api/logics.py +++ b/api/logics.py @@ -1043,7 +1043,10 @@ class Logics: order.last_satoshis_time = timezone.now() bond_satoshis = int(order.last_satoshis * order.bond_size/100) - description = f"RoboSats - Publishing '{str(order)}' - Maker bond - This payment WILL FREEZE IN YOUR WALLET, check on the website if it was successful. It will automatically return unless you cheat or cancel unilaterally." + if user.profile.wants_stealth: + description = f"This payment WILL FREEZE IN YOUR WALLET, check on the website if it was successful. It will automatically return unless you cheat or cancel unilaterally. Payment reference: {order.reference}" + else: + description = f"RoboSats - Publishing '{str(order)}' - Maker bond - This payment WILL FREEZE IN YOUR WALLET, check on the website if it was successful. It will automatically return unless you cheat or cancel unilaterally." # Gen hold Invoice try: @@ -1155,11 +1158,14 @@ class Logics: order.last_satoshis_time = timezone.now() bond_satoshis = int(order.last_satoshis * order.bond_size/100) pos_text = "Buying" if cls.is_buyer(order, user) else "Selling" - description = ( - f"RoboSats - Taking 'Order {order.id}' {pos_text} BTC for {str(float(order.amount)) + Currency.currency_dict[str(order.currency.currency)]}" - + - " - Taker bond - This payment WILL FREEZE IN YOUR WALLET, check on the website if it was successful. It will automatically return unless you cheat or cancel unilaterally." - ) + if user.profile.wants_stealth: + description = f"This payment WILL FREEZE IN YOUR WALLET, check on the website if it was successful. It will automatically return unless you cheat or cancel unilaterally. Payment reference: {order.reference}" + else: + description = ( + f"RoboSats - Taking 'Order {order.id}' {pos_text} BTC for {str(float(order.amount)) + Currency.currency_dict[str(order.currency.currency)]}" + + + " - Taker bond - This payment WILL FREEZE IN YOUR WALLET, check on the website if it was successful. It will automatically return unless you cheat or cancel unilaterally." + ) # Gen hold Invoice try: @@ -1247,7 +1253,10 @@ class Logics: # If there was no taker_bond object yet, generate one escrow_satoshis = cls.escrow_amount(order, user)[1]["escrow_amount"] # Amount was fixed when taker bond was locked, fee applied here - description = f"RoboSats - Escrow amount for '{str(order)}' - It WILL FREEZE IN YOUR WALLET. It will be released to the buyer once you confirm you received the fiat. It will automatically return if buyer does not confirm the payment." + if user.profile.wants_stealth: + description = f"This payment WILL FREEZE IN YOUR WALLET, check on the website if it was successful. It will automatically return unless you cheat or cancel unilaterally. Payment reference: {order.reference}" + else: + description = f"RoboSats - Escrow amount for '{str(order)}' - It WILL FREEZE IN YOUR WALLET. It will be released to the buyer once you confirm you received the fiat. It will automatically return if buyer does not confirm the payment." # Gen hold Invoice try: diff --git a/api/models.py b/api/models.py index 7c00981d..7f99264e 100644 --- a/api/models.py +++ b/api/models.py @@ -297,6 +297,7 @@ class Order(models.Model): NESINV = 4, "Neither escrow locked or invoice submitted" # order info + reference = models.UUIDField(default = uuid.uuid4, editable = False) status = models.PositiveSmallIntegerField(choices=Status.choices, null=False, default=Status.WFB) @@ -652,6 +653,10 @@ class Profile(models.Model): default=None, blank=True) + # Stealth invoices + wants_stealth = models.BooleanField(default=False, + null=False) + @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): if created: diff --git a/api/serializers.py b/api/serializers.py index 5157e5cf..77740948 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -146,4 +146,7 @@ class TickSerializer(serializers.ModelSerializer): "premium", "fee", ) - depth = 1 \ No newline at end of file + depth = 1 + +class StealthSerializer(serializers.Serializer): + wantsStealth = serializers.BooleanField() diff --git a/api/urls.py b/api/urls.py index 9a8576b2..ec3bb281 100644 --- a/api/urls.py +++ b/api/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from .views import MakerView, OrderView, UserView, BookView, InfoView, RewardView, PriceView, LimitView, HistoricalView, TickView +from .views import MakerView, OrderView, UserView, BookView, InfoView, RewardView, PriceView, LimitView, HistoricalView, TickView, StealthView urlpatterns = [ path("make/", MakerView.as_view()), @@ -16,4 +16,5 @@ urlpatterns = [ path("reward/", RewardView.as_view()), path("historical/", HistoricalView.as_view()), path("ticks/", TickView.as_view()), + path("stealth/", StealthView.as_view()), ] diff --git a/api/views.py b/api/views.py index da4d7d45..c958ae68 100644 --- a/api/views.py +++ b/api/views.py @@ -2,7 +2,7 @@ import os from re import T from django.db.models import Sum, Q from rest_framework import status, viewsets -from rest_framework.generics import CreateAPIView, ListAPIView +from rest_framework.generics import CreateAPIView, ListAPIView, UpdateAPIView from rest_framework.views import APIView from rest_framework.response import Response @@ -11,7 +11,7 @@ from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt from django.contrib.auth.models import User -from api.serializers import ListOrderSerializer, MakeOrderSerializer, UpdateOrderSerializer, ClaimRewardSerializer, PriceSerializer, UserGenSerializer, TickSerializer +from api.serializers import ListOrderSerializer, MakeOrderSerializer, UpdateOrderSerializer, ClaimRewardSerializer, PriceSerializer, UserGenSerializer, TickSerializer, StealthSerializer from api.models import LNPayment, MarketTick, OnchainPayment, Order, Currency, Profile from control.models import AccountingDay, BalanceLog from api.logics import Logics @@ -740,10 +740,13 @@ class UserView(APIView): user.profile.is_referred = True user.profile.referred_by = queryset[0] + user.profile.wants_stealth = False + user.profile.save() context["public_key"] = user.profile.public_key context["encrypted_private_key"] = user.profile.encrypted_private_key + context["wants_stealth"] = user.profile.wants_stealth return Response(context, status=status.HTTP_201_CREATED) # log in user and return pub/priv keys if existing @@ -755,6 +758,7 @@ class UserView(APIView): context["encrypted_private_key"] = user.profile.encrypted_private_key context["earned_rewards"] = user.profile.earned_rewards context["referral_code"] = str(user.profile.referral_code) + context["wants_stealth"] = user.profile.wants_stealth # return active order or last made order if any has_no_active_order, _, order = Logics.validate_already_maker_or_taker(request.user) @@ -1026,3 +1030,29 @@ class HistoricalView(ListAPIView): } return Response(payload, status.HTTP_200_OK) + +class StealthView(UpdateAPIView): + + serializer_class = StealthSerializer + + def put(self, request): + serializer = self.serializer_class(data=request.data) + + if not request.user.is_authenticated: + return Response( + { + "bad_request": + "Woops! It seems you do not have a robot avatar" + }, + status.HTTP_400_BAD_REQUEST, + ) + + if not serializer.is_valid(): + return Response(status=status.HTTP_400_BAD_REQUEST) + + stealth = serializer.data.get("wantsStealth") + + request.user.profile.wants_stealth = stealth + request.user.profile.save() + + return Response({"wantsStealth": stealth}) diff --git a/frontend/src/components/BottomBar.js b/frontend/src/components/BottomBar.js index 11d5923c..00edf222 100644 --- a/frontend/src/components/BottomBar.js +++ b/frontend/src/components/BottomBar.js @@ -124,6 +124,17 @@ class BottomBar extends Component { e.preventDefault(); } + handleSetStealthInvoice = (wantsStealth) => { + const requestOptions = { + method: 'PUT', + headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken')}, + body: JSON.stringify({wantsStealth: wantsStealth}), + }; + fetch('/api/stealth/', requestOptions) + .then((response) => response.json()) + .then((data) => this.props.setAppState({stealthInvoices: data.wantsStealth})); + } + getHost(){ var url = (window.location != window.parent.location) ? this.getHost(document.referrer) : document.location.href; return url.split('/')[2] @@ -488,6 +499,8 @@ bottomBarPhone =()=>{ badInvoice={this.state.badInvoice} earnedRewards={this.props.earnedRewards} setAppState={this.props.setAppState} + stealthInvoices={this.props.stealthInvoices} + handleSetStealthInvoice={this.handleSetStealthInvoice} /> void; setAppState: (state: any) => void; // TODO: move to a ContextProvider } @@ -65,6 +67,8 @@ const ProfileDialog = ({ badInvoice, earnedRewards, setAppState, + stealthInvoices, + handleSetStealthInvoice, }: Props): JSX.Element => { const { t } = useTranslation(); @@ -206,6 +210,24 @@ const ProfileDialog = ({ + + + + handleSetStealthInvoice(!stealthInvoices) + } + /> + } + /> + + + +