Initial commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from apps.payments.models import Payment
|
||||
|
||||
admin.site.register(Payment)
|
||||
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PaymentsConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "apps.payments"
|
||||
@@ -0,0 +1,36 @@
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
||||
from apps.bookings.models import Booking
|
||||
|
||||
|
||||
class PaymentProvider(models.TextChoices):
|
||||
HYPERPAY = "hyperpay", "HyperPay"
|
||||
PAYTABS = "paytabs", "PayTabs"
|
||||
MOYASAR = "moyasar", "Moyasar"
|
||||
TAP = "tap", "Tap"
|
||||
AMAZON_PAYMENT_SERVICES = "amazon_payment_services", "Amazon Payment Services"
|
||||
CHECKOUT = "checkout", "Checkout.com"
|
||||
OTHER = "other", "Other"
|
||||
|
||||
|
||||
class PaymentStatus(models.TextChoices):
|
||||
CREATED = "created", "Created"
|
||||
AUTHORIZED = "authorized", "Authorized"
|
||||
CAPTURED = "captured", "Captured"
|
||||
FAILED = "failed", "Failed"
|
||||
REFUNDED = "refunded", "Refunded"
|
||||
|
||||
|
||||
class Payment(models.Model):
|
||||
booking = models.ForeignKey(Booking, on_delete=models.CASCADE, related_name="payments")
|
||||
provider = models.CharField(max_length=50, choices=PaymentProvider.choices)
|
||||
status = models.CharField(max_length=20, choices=PaymentStatus.choices, default=PaymentStatus.CREATED)
|
||||
amount = models.DecimalField(max_digits=10, decimal_places=2)
|
||||
currency = models.CharField(max_length=10, default=getattr(settings, "DEFAULT_CURRENCY", "SAR"))
|
||||
external_id = models.CharField(max_length=200, blank=True)
|
||||
metadata = models.JSONField(default=dict, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.provider} {self.amount} {self.currency}"
|
||||
@@ -0,0 +1,48 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from apps.bookings.models import Booking
|
||||
from apps.payments.models import Payment, PaymentProvider, PaymentStatus
|
||||
|
||||
|
||||
class PaymentSerializer(serializers.ModelSerializer):
|
||||
booking_id = serializers.IntegerField(source="booking.id", read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Payment
|
||||
fields = [
|
||||
"id",
|
||||
"booking_id",
|
||||
"provider",
|
||||
"status",
|
||||
"amount",
|
||||
"currency",
|
||||
"external_id",
|
||||
"metadata",
|
||||
"created_at",
|
||||
]
|
||||
read_only_fields = fields
|
||||
|
||||
|
||||
class PaymentCreateSerializer(serializers.ModelSerializer):
|
||||
booking_id = serializers.IntegerField(write_only=True)
|
||||
provider = serializers.ChoiceField(choices=PaymentProvider.choices)
|
||||
|
||||
class Meta:
|
||||
model = Payment
|
||||
fields = ["booking_id", "provider"]
|
||||
|
||||
def validate_booking_id(self, value):
|
||||
if not Booking.objects.filter(id=value).exists():
|
||||
raise serializers.ValidationError("Booking not found")
|
||||
return value
|
||||
|
||||
def create(self, validated_data):
|
||||
booking = Booking.objects.get(id=validated_data["booking_id"])
|
||||
return Payment.objects.create(
|
||||
booking=booking,
|
||||
provider=validated_data["provider"],
|
||||
status=PaymentStatus.CREATED,
|
||||
amount=booking.price_amount,
|
||||
currency=booking.currency,
|
||||
metadata={},
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
from django.urls import include, path
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
from apps.payments.views import PaymentViewSet
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register(r"", PaymentViewSet, basename="payment")
|
||||
|
||||
urlpatterns = [
|
||||
path("", include(router.urls)),
|
||||
]
|
||||
@@ -0,0 +1,53 @@
|
||||
from rest_framework import permissions, status, viewsets
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.bookings.models import Booking
|
||||
from apps.payments.models import Payment
|
||||
from apps.payments.serializers import PaymentCreateSerializer, PaymentSerializer
|
||||
|
||||
|
||||
def user_can_access_booking(user, booking: Booking) -> bool:
|
||||
if getattr(user, "is_superuser", False) or user.role == "admin":
|
||||
return True
|
||||
if user.role == "manager":
|
||||
return booking.salon.owner_id == user.id
|
||||
if user.role == "staff":
|
||||
return booking.staff_id and booking.staff.user_id == user.id
|
||||
return booking.customer_id == user.id
|
||||
|
||||
|
||||
class PaymentViewSet(viewsets.ModelViewSet):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
user = self.request.user
|
||||
if getattr(user, "is_superuser", False) or user.role == "admin":
|
||||
return Payment.objects.all().order_by("-created_at")
|
||||
if user.role == "manager":
|
||||
return Payment.objects.filter(booking__salon__owner=user).order_by("-created_at")
|
||||
if user.role == "staff":
|
||||
return Payment.objects.filter(booking__staff__user=user).order_by("-created_at")
|
||||
return Payment.objects.filter(booking__customer=user).order_by("-created_at")
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == "create":
|
||||
return PaymentCreateSerializer
|
||||
return PaymentSerializer
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
booking = Booking.objects.get(id=serializer.validated_data["booking_id"])
|
||||
if not user_can_access_booking(request.user, booking):
|
||||
return Response({"detail": "Not allowed"}, status=status.HTTP_403_FORBIDDEN)
|
||||
payment = serializer.save()
|
||||
return Response(
|
||||
{
|
||||
"detail": "Payment record created. Provider integration pending.",
|
||||
"payment_id": payment.id,
|
||||
"amount": str(payment.amount),
|
||||
"currency": payment.currency,
|
||||
"status": payment.status,
|
||||
},
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
Reference in New Issue
Block a user