# Create your views here. # billing/views.py from datetime import datetime, timedelta from django.utils import timezone import requests from decouple import config from rest_framework import generics, status from rest_framework.response import Response from api.mixins import StaffEditorPermissionMixin from .models import Device, Payment from .serializers import PaymentSerializer, UpdatePaymentSerializer class InsufficientFundsError(Exception): pass class ListCreatePaymentView(StaffEditorPermissionMixin, generics.ListCreateAPIView): serializer_class = PaymentSerializer queryset = Payment.objects.all().select_related("user") def get_queryset(self): queryset = super().get_queryset() if self.request.user.is_superuser: return queryset return queryset.filter(user=self.request.user) def create(self, request): data = request.data user = request.user amount = data.get("amount") number_of_months = data.get("number_of_months") device_ids = data.get("device_ids", []) print(amount, number_of_months, device_ids) for device_id in device_ids: device = Device.objects.filter(id=device_id, user=user).first() print("DEVICE", device) if not device: return Response( {"message": f"Device with id {device_id} not found."}, status=status.HTTP_400_BAD_REQUEST, ) if not amount or not number_of_months: return Response( {"message": "amount and number_of_months are required."}, status=status.HTTP_400_BAD_REQUEST, ) if not device_ids: return Response( {"message": "device_ids are required."}, status=status.HTTP_400_BAD_REQUEST, ) # Create payment payment = Payment.objects.create( amount=amount, number_of_months=number_of_months, paid=data.get("paid", False), user=user, ) # Connect devices to payment devices = Device.objects.filter(id__in=device_ids, user=user) payment.devices.set(devices) serializer = PaymentSerializer(payment) return Response(serializer.data, status=status.HTTP_201_CREATED) class PaymentDetailAPIView(StaffEditorPermissionMixin, generics.RetrieveAPIView): queryset = Payment.objects.select_related("user").all() serializer_class = PaymentSerializer lookup_field = "pk" class UpdatePaymentAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView): queryset = Payment.objects.select_related("user").all() serializer_class = UpdatePaymentSerializer lookup_field = "pk" def update(self, request, *args, **kwargs): number_of_months = request.data.get("number_of_months") if not number_of_months: return Response( {"message": "number_of_months is required."}, status=status.HTTP_400_BAD_REQUEST, ) if not isinstance(number_of_months, int): return Response( {"message": "number_of_months must be an integer."}, status=status.HTTP_400_BAD_REQUEST, ) device_expire_date = timezone.now() + timedelta(days=30 * number_of_months) instance = self.get_object() devices = instance.devices.all() serializer = self.get_serializer(instance, data=request.data, partial=False) serializer.is_valid(raise_exception=True) self.perform_update(serializer) devices.update(is_active=True, expiry_date=device_expire_date) return Response(serializer.data) class VerifyPaymentView(StaffEditorPermissionMixin, generics.UpdateAPIView): serializer_class = PaymentSerializer queryset = Payment.objects.all() def update(self, request, *args, **kwargs): data = request.data user = request.user print(user) method = data.get("method") payment_id = data.get("payment_id") abs_amount = data.get("abs_amount") if not method: return Response( {"message": "method is required. 'WALLET' or 'TRANSFER'"}, status=status.HTTP_400_BAD_REQUEST, ) if not payment_id: return Response( {"message": "payment_id is required."}, status=status.HTTP_400_BAD_REQUEST, ) if not abs_amount: return Response( {"message": "abs_amount is required."}, status=status.HTTP_400_BAD_REQUEST, ) try: payment = Payment.objects.get(id=payment_id) devices = payment.devices.all() if data["type"] == "WALLET": print("processing WALLET payment") self.process_wallet_payment(user, payment, float(data["abs_amount"])) elif data["type"] == "TRANSFER": self.verify_external_payment(data, payment) # Update devices expiry_date = datetime.now() + timedelta(days=30 * payment.number_of_months) devices.update(is_active=True, expiry_date=expiry_date) return Response({"message": "Payment verified successfully."}) except Payment.DoesNotExist: return Response( {"message": "Payment not found."}, status=status.HTTP_404_NOT_FOUND ) except InsufficientFundsError: return Response( {"message": "Insufficient funds in wallet."}, status=status.HTTP_400_BAD_REQUEST, ) except Exception as e: return Response( {"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR ) def process_wallet_payment(self, user, payment, amount): print("processing wallet payment") print(user, amount) if user.wallet_balance < amount: return Response( {"message": "Insufficient funds in wallet."}, status=status.HTTP_400_BAD_REQUEST, ) payment.paid = True payment.paid_at = datetime.now() payment.method = "WALLET" payment.save() user.wallet_balance -= amount user.save() def verify_external_payment(self, data, payment): response = requests.post( f"{config('PAYMENT_VERIFY_BASE_URL')}/verify-payment", json=data, headers={"Content-Type": "application/json"}, ) response.raise_for_status() print(response.json()) if not response.json().get("success"): raise Exception("Payment verification failed.")