# Create your views here. # billing/views.py from datetime import 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) for device in devices: device.has_a_pending_payment = True device.save() 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): instance = self.get_object() number_of_months = instance.number_of_months device_expire_date = timezone.now() + timedelta(days=30 * number_of_months) 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, has_a_pending_payment=False ) return Response(serializer.data) class VerifyPaymentView(StaffEditorPermissionMixin, generics.UpdateAPIView): serializer_class = PaymentSerializer queryset = Payment.objects.all() lookup_field = "pk" def update(self, request, *args, **kwargs): # TODO: Fix check for success payment payment = self.get_object() data = request.data user = request.user print("logged in user", user) print("Payment user", payment.user) if payment.paid: return Response( {"message": "Payment has already been verified."}, status=status.HTTP_400_BAD_REQUEST, ) if payment.user != user and not user.is_superuser: return Response( {"message": "You are not authorized to verify this payment."}, status=status.HTTP_403_FORBIDDEN, ) method = data.get("method") if not method: return Response( {"message": "method is required. 'WALLET' or 'TRANSFER'"}, status=status.HTTP_400_BAD_REQUEST, ) devices = payment.devices.all() payment_status = False if method == "WALLET": if user.wallet_balance < payment.amount: return Response( {"message": "Insufficient funds in wallet."}, status=status.HTTP_400_BAD_REQUEST, ) else: payment_status = self.process_wallet_payment( user, payment, ) if method == "TRANSFER": data = { "benefName": f"{user.first_name} {user.last_name}", "accountNo": user.acc_no, "absAmount": payment.amount, "time": (timezone.now() + timedelta(minutes=5)).strftime( "%Y-%m-%d %H:%M" ), } payment_status = self.verify_transfer_payment(data, payment) if payment_status: # Update devices expiry_date = timezone.now() + timedelta(days=30 * payment.number_of_months) devices.update( is_active=True, expiry_date=expiry_date, has_a_pending_payment=False ) return Response( {"message": f"Payment verified successfully using [{method}]."} ) else: return Response( {"message": f"Payment verification FAILED using [{method}]."} ) def process_wallet_payment(self, user, payment): print("processing wallet payment...") print(user, payment.amount) payment.paid = True payment.paid_at = timezone.now() payment.method = "WALLET" payment.save() user.wallet_balance -= payment.amount user.save() return True def verify_transfer_payment(self, data, payment): print("verifying transfer payment...") response = requests.post( f"{config('PAYMENT_VERIFY_BASE_URL')}/verify-payment", json=data, headers={"Content-Type": "application/json"}, ) response.raise_for_status() mib_resp = response.json() if not response.json().get("success"): return mib_resp["success"] else: return True class DeletePaymentView(StaffEditorPermissionMixin, generics.DestroyAPIView): queryset = Payment.objects.all() serializer_class = PaymentSerializer lookup_field = "pk" def delete(self, request, *args, **kwargs): instance = self.get_object() user = request.user if instance.user != user and not user.is_superuser: return Response( {"message": "You are not authorized to delete this payment."}, status=status.HTTP_403_FORBIDDEN, ) if instance.paid: return Response( {"message": "Paid payments cannot be deleted."}, status=status.HTTP_400_BAD_REQUEST, ) devices = instance.devices.all() devices.update(is_active=False, expiry_date=None, has_a_pending_payment=False) return super().delete(request, *args, **kwargs)