mirror of
https://github.com/i701/sarlink-portal-api.git
synced 2025-07-07 06:06:31 +00:00
feat(billing): Add delete functionality for topups and update creation logic with expiration time ✨
This commit is contained in:
@ -198,3 +198,10 @@ class TopupTests(TestCase):
|
|||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(response.json()["amount"], 50.00)
|
self.assertEqual(response.json()["amount"], 50.00)
|
||||||
self.assertEqual(response.json()["user"]["id"], getattr(self.real_user, "id"))
|
self.assertEqual(response.json()["user"]["id"], getattr(self.real_user, "id"))
|
||||||
|
|
||||||
|
def test_delete_topup(self):
|
||||||
|
topup = Topup.objects.create(amount=50.00, user=self.real_user)
|
||||||
|
url = reverse("delete-topup", kwargs={"pk": topup.pk})
|
||||||
|
response = self.client.delete(url, format="json")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
|
self.assertEqual(Topup.objects.count(), 0)
|
||||||
|
@ -9,6 +9,7 @@ from .views import (
|
|||||||
ListCreateTopupView,
|
ListCreateTopupView,
|
||||||
VerifyTopupPaymentAPIView,
|
VerifyTopupPaymentAPIView,
|
||||||
TopupDetailAPIView,
|
TopupDetailAPIView,
|
||||||
|
DeleteTopupView,
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -35,4 +36,9 @@ urlpatterns = [
|
|||||||
VerifyTopupPaymentAPIView.as_view(),
|
VerifyTopupPaymentAPIView.as_view(),
|
||||||
name="verify-topup-payment",
|
name="verify-topup-payment",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"topup/<str:pk>/delete/",
|
||||||
|
DeleteTopupView.as_view(),
|
||||||
|
name="delete-topup",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -18,6 +18,8 @@ import logging
|
|||||||
from .models import Device, Payment, Topup
|
from .models import Device, Payment, Topup
|
||||||
from .serializers import PaymentSerializer, UpdatePaymentSerializer, TopupSerializer
|
from .serializers import PaymentSerializer, UpdatePaymentSerializer, TopupSerializer
|
||||||
from .filters import PaymentFilter, TopupFilter
|
from .filters import PaymentFilter, TopupFilter
|
||||||
|
from dataclasses import dataclass, asdict
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
env.read_env(os.path.join(BASE_DIR, ".env"))
|
env.read_env(os.path.join(BASE_DIR, ".env"))
|
||||||
|
|
||||||
@ -271,13 +273,15 @@ class ListCreateTopupView(StaffEditorPermissionMixin, generics.ListCreateAPIView
|
|||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
data = request.data
|
data = request.data
|
||||||
user = request.user
|
user = request.user
|
||||||
|
current_time = timezone.now()
|
||||||
|
expires_at = current_time + timedelta(minutes=10) # Topup expires in 10 minutes
|
||||||
amount = data.get("amount")
|
amount = data.get("amount")
|
||||||
if not amount:
|
if not amount:
|
||||||
return Response(
|
return Response(
|
||||||
{"message": "amount is required."},
|
{"message": "amount is required."},
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
topup = Topup.objects.create(amount=amount, user=user)
|
topup = Topup.objects.create(amount=amount, user=user, expires_at=expires_at)
|
||||||
serializer = TopupSerializer(topup)
|
serializer = TopupSerializer(topup)
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
@ -300,12 +304,26 @@ class TopupDetailAPIView(StaffEditorPermissionMixin, generics.RetrieveAPIView):
|
|||||||
return queryset.filter(user=self.request.user)
|
return queryset.filter(user=self.request.user)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Transaction:
|
||||||
|
ref: str
|
||||||
|
sourceBank: str
|
||||||
|
trxDate: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PaymentVerificationResponse:
|
||||||
|
message: str
|
||||||
|
success: bool
|
||||||
|
transaction: Optional[Transaction] = None
|
||||||
|
|
||||||
|
|
||||||
class VerifyTopupPaymentAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView):
|
class VerifyTopupPaymentAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView):
|
||||||
queryset = Topup.objects.all()
|
queryset = Topup.objects.all()
|
||||||
serializer_class = TopupSerializer
|
serializer_class = TopupSerializer
|
||||||
lookup_field = "pk"
|
lookup_field = "pk"
|
||||||
|
|
||||||
def verify_transfer_topup(self, data, topup):
|
def verify_transfer_topup(self, data, topup) -> PaymentVerificationResponse:
|
||||||
if not PAYMENT_BASE_URL:
|
if not PAYMENT_BASE_URL:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"PAYMENT_BASE_URL is not set. Please set it in your environment variables."
|
"PAYMENT_BASE_URL is not set. Please set it in your environment variables."
|
||||||
@ -320,18 +338,32 @@ class VerifyTopupPaymentAPIView(StaffEditorPermissionMixin, generics.UpdateAPIVi
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
logger.error(f"HTTPError: {e}")
|
logger.error(f"HTTPError: {e}")
|
||||||
return False # Or handle the error as appropriate
|
return PaymentVerificationResponse(
|
||||||
|
message="Payment verification failed.", success=False, transaction=None
|
||||||
|
)
|
||||||
mib_resp = response.json()
|
mib_resp = response.json()
|
||||||
print(mib_resp)
|
print(mib_resp)
|
||||||
if not response.json().get("success"):
|
if not response.json().get("success"):
|
||||||
return mib_resp["success"]
|
return PaymentVerificationResponse(
|
||||||
|
message=mib_resp["message"],
|
||||||
|
success=mib_resp["success"],
|
||||||
|
transaction=None,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
topup.paid = True
|
topup.paid = True
|
||||||
# topup.paid_at = timezone.now() # Assuming Topup model has paid_at field
|
topup.paid_at = timezone.now()
|
||||||
topup.mib_reference = mib_resp["transaction"]["ref"] or ""
|
topup.mib_reference = mib_resp["transaction"]["ref"] or ""
|
||||||
topup.paid_at = timezone.now()
|
topup.paid_at = timezone.now()
|
||||||
topup.save()
|
topup.save()
|
||||||
return True
|
return PaymentVerificationResponse(
|
||||||
|
message=mib_resp["message"],
|
||||||
|
success=mib_resp["success"],
|
||||||
|
transaction=Transaction(
|
||||||
|
ref=topup.mib_reference,
|
||||||
|
sourceBank=mib_resp["transaction"]["sourceBank"],
|
||||||
|
trxDate=mib_resp["transaction"]["trxDate"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
topup_instance = self.get_object()
|
topup_instance = self.get_object()
|
||||||
@ -355,16 +387,27 @@ class VerifyTopupPaymentAPIView(StaffEditorPermissionMixin, generics.UpdateAPIVi
|
|||||||
topup_instance.created_at + timedelta(minutes=5)
|
topup_instance.created_at + timedelta(minutes=5)
|
||||||
).strftime("%Y-%m-%d %H:%M"),
|
).strftime("%Y-%m-%d %H:%M"),
|
||||||
}
|
}
|
||||||
print("payment payload in view ->", data)
|
print("DATA", data)
|
||||||
topup_status = self.verify_transfer_topup(data, topup_instance)
|
topup_verification_response = self.verify_transfer_topup(data, topup_instance)
|
||||||
if topup_status:
|
print("TOPUP VERIFICATION RESPONSE", topup_verification_response)
|
||||||
|
if topup_verification_response.success:
|
||||||
return Response(
|
return Response(
|
||||||
{"message": "Topup payment verified successfully."},
|
{
|
||||||
|
"status": topup_verification_response.success,
|
||||||
|
"message": topup_verification_response.message,
|
||||||
|
"transaction": asdict(topup_verification_response.transaction)
|
||||||
|
if topup_verification_response.transaction
|
||||||
|
else None,
|
||||||
|
},
|
||||||
status=status.HTTP_200_OK,
|
status=status.HTTP_200_OK,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return Response(
|
return Response(
|
||||||
{"message": "Topup payment verification failed."},
|
{
|
||||||
|
"status": topup_verification_response.success,
|
||||||
|
"message": topup_verification_response.message
|
||||||
|
or "Topup payment verification failed.",
|
||||||
|
},
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user