mirror of
https://github.com/i701/sarlink-portal-api.git
synced 2025-07-07 06:06:31 +00:00
Merge pull request #9 from i701/feat/topups
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 5m37s
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 5m37s
feat/topups
This commit is contained in:
@ -25,6 +25,7 @@ class TopupAdmin(admin.ModelAdmin):
|
||||
"amount",
|
||||
"paid",
|
||||
"paid_at",
|
||||
"status",
|
||||
"created_at",
|
||||
"is_expired",
|
||||
"expires_at",
|
||||
|
@ -51,6 +51,7 @@ class Command(BaseCommand):
|
||||
min_value=100.00,
|
||||
max_value=5000.00,
|
||||
),
|
||||
status=random.choice(["PENDING", "PAID", "CANCELLED"]),
|
||||
user=random_user,
|
||||
updated_at=timezone.now(),
|
||||
expires_at=expires_at_date,
|
||||
|
25
billing/migrations/0011_topup_status.py
Normal file
25
billing/migrations/0011_topup_status.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 5.2 on 2025-07-05 12:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("billing", "0010_add_expiry_notification_sent_to_topup"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="topup",
|
||||
name="status",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("PENDING", "Pending"),
|
||||
("PAID", "Paid"),
|
||||
("CANCELLED", "Cancelled"),
|
||||
],
|
||||
default="PENDING",
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
]
|
@ -52,6 +52,15 @@ class Topup(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="topups")
|
||||
paid = models.BooleanField(default=False)
|
||||
paid_at = models.DateTimeField(null=True, blank=True)
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
("PENDING", "Pending"),
|
||||
("PAID", "Paid"),
|
||||
("CANCELLED", "Cancelled"),
|
||||
],
|
||||
default="PENDING",
|
||||
)
|
||||
mib_reference = models.CharField(default="", null=True, blank=True)
|
||||
expires_at = models.DateTimeField(null=True, blank=True)
|
||||
expiry_notification_sent = models.BooleanField(default=False)
|
||||
|
@ -44,6 +44,7 @@ class TopupSerializer(serializers.ModelSerializer):
|
||||
"amount",
|
||||
"user",
|
||||
"paid",
|
||||
"status",
|
||||
"mib_reference",
|
||||
"is_expired",
|
||||
"expires_at",
|
||||
|
@ -9,7 +9,7 @@ from .views import (
|
||||
ListCreateTopupView,
|
||||
VerifyTopupPaymentAPIView,
|
||||
TopupDetailAPIView,
|
||||
DeleteTopupView,
|
||||
CancelTopupView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
@ -37,8 +37,8 @@ urlpatterns = [
|
||||
name="verify-topup-payment",
|
||||
),
|
||||
path(
|
||||
"topup/<str:pk>/delete/",
|
||||
DeleteTopupView.as_view(),
|
||||
name="delete-topup",
|
||||
"topup/<str:pk>/cancel/",
|
||||
CancelTopupView.as_view(),
|
||||
name="cancel-topup",
|
||||
),
|
||||
]
|
||||
|
@ -355,9 +355,8 @@ class VerifyTopupPaymentAPIView(StaffEditorPermissionMixin, generics.UpdateAPIVi
|
||||
)
|
||||
else:
|
||||
topup.paid = True
|
||||
topup.paid_at = timezone.now()
|
||||
topup.mib_reference = mib_resp["transaction"]["ref"] or ""
|
||||
topup.paid_at = timezone.now()
|
||||
topup.paid_at = mib_resp["transaction"]["trxDate"]
|
||||
topup.save()
|
||||
return PaymentVerificationResponse(
|
||||
message=mib_resp["message"],
|
||||
@ -397,6 +396,8 @@ class VerifyTopupPaymentAPIView(StaffEditorPermissionMixin, generics.UpdateAPIVi
|
||||
if topup_verification_response.success:
|
||||
user.wallet_balance += topup_instance.amount # type: ignore
|
||||
user.save()
|
||||
topup_instance.status = "PAID"
|
||||
topup_instance.save()
|
||||
return Response(
|
||||
{
|
||||
"status": topup_verification_response.success,
|
||||
@ -418,17 +419,22 @@ class VerifyTopupPaymentAPIView(StaffEditorPermissionMixin, generics.UpdateAPIVi
|
||||
)
|
||||
|
||||
|
||||
class DeleteTopupView(StaffEditorPermissionMixin, generics.DestroyAPIView):
|
||||
queryset = Topup.objects.all()
|
||||
class CancelTopupView(StaffEditorPermissionMixin, generics.UpdateAPIView):
|
||||
queryset = Topup.objects.all().select_related("user")
|
||||
serializer_class = TopupSerializer
|
||||
lookup_field = "pk"
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
def update(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
user = request.user
|
||||
if instance.status == "CANCELLED":
|
||||
return Response(
|
||||
{"message": "Topup has already been cancelled."},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
if instance.is_expired:
|
||||
return Response(
|
||||
{"message": "Expired topups cannot be deleted."},
|
||||
{"message": "Expired topups cannot be cancelled."},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
if (
|
||||
@ -445,4 +451,6 @@ class DeleteTopupView(StaffEditorPermissionMixin, generics.DestroyAPIView):
|
||||
{"message": "Paid topups cannot be deleted."},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
return super().delete(request, *args, **kwargs)
|
||||
instance.status = "CANCELLED"
|
||||
instance.save()
|
||||
return super().update(request, *args, **kwargs)
|
||||
|
Reference in New Issue
Block a user