Implement Moyasar payments flow with webhooks

This commit is contained in:
2026-02-28 13:01:12 +03:00
parent d9767ff0a7
commit f3c93f500e
12 changed files with 600 additions and 43 deletions
+31 -12
View File
@@ -2,7 +2,7 @@ from rest_framework import serializers
from django.utils.translation import gettext_lazy as _
from apps.bookings.models import Booking
from apps.payments.models import Payment, PaymentProvider, PaymentStatus
from apps.payments.models import Payment, PaymentProvider
class PaymentSerializer(serializers.ModelSerializer):
@@ -18,7 +18,16 @@ class PaymentSerializer(serializers.ModelSerializer):
"amount",
"currency",
"external_id",
"idempotency_key",
"metadata",
"authorized_at",
"captured_at",
"paid_at",
"failed_at",
"refunded_at",
"voided_at",
"verified_at",
"status_updated_at",
"created_at",
]
read_only_fields = fields
@@ -27,23 +36,33 @@ class PaymentSerializer(serializers.ModelSerializer):
class PaymentCreateSerializer(serializers.ModelSerializer):
booking_id = serializers.IntegerField(write_only=True)
provider = serializers.ChoiceField(choices=PaymentProvider.choices)
idempotency_key = serializers.UUIDField(write_only=True)
source = serializers.JSONField(write_only=True, required=False)
callback_url = serializers.URLField(write_only=True, required=False)
class Meta:
model = Payment
fields = ["booking_id", "provider"]
fields = ["booking_id", "provider", "idempotency_key", "source", "callback_url"]
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={},
)
def validate(self, attrs):
provider = attrs.get("provider")
source = attrs.get("source")
if provider != PaymentProvider.MOYASAR:
raise serializers.ValidationError({"provider": _("Provider integration not implemented")})
if source is None:
raise serializers.ValidationError({"source": _("Payment source is required")})
source_type = source.get("type")
if not source_type:
raise serializers.ValidationError({"source": _("Payment source type is required")})
if source_type == "creditcard":
raise serializers.ValidationError(
{"source": _("Card data must not be sent to the backend; use frontend tokenization")}
)
if source_type == "token" and not attrs.get("callback_url"):
raise serializers.ValidationError({"callback_url": _("Callback URL is required for token payments")})
return attrs