From 97f8209207ae2d7f157ea4796402c33b193359dd Mon Sep 17 00:00:00 2001 From: rizwanisready Date: Fri, 19 Apr 2024 13:00:41 +0530 Subject: [PATCH] getting webhook events into database --- manage_events/api/serializers.py | 2 +- manage_subscriptions/api/views.py | 41 ++++++++--- .../migrations/0004_webhookevent.py | 69 +++++++++++++++++++ manage_subscriptions/models.py | 19 +++++ 4 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 manage_subscriptions/migrations/0004_webhookevent.py diff --git a/manage_events/api/serializers.py b/manage_events/api/serializers.py index a9d3200..a01747d 100644 --- a/manage_events/api/serializers.py +++ b/manage_events/api/serializers.py @@ -166,7 +166,7 @@ class CreateEventSerializer(serializers.ModelSerializer): # Update fields if there is any change. if tags is not None: # Clear existing tags and add the new ones - # instance.tags.clear() + instance.tags.clear() instance.tags.add(*tags) if images_data is not None: diff --git a/manage_subscriptions/api/views.py b/manage_subscriptions/api/views.py index 399fe11..9d3fadf 100644 --- a/manage_subscriptions/api/views.py +++ b/manage_subscriptions/api/views.py @@ -14,6 +14,7 @@ from manage_subscriptions.models import ( Subscription, PrincipalSubscription, SubscriptionStatus, + WebhookEvent, ) from goodtimes.utils import ApiResponse from accounts.resource_action import ( @@ -174,6 +175,28 @@ class StripeWebhookTest(APIView): event = None try: event = stripe.Event.construct_from(json.loads(payload), stripe.api_key) + event_id = event["id"] + # Check if the event has been processed already + if WebhookEvent.objects.filter(event_id=event_id, status="processed").exists(): + return ApiResponse.success( + status=status.HTTP_208_ALREADY_REPORTED, + message="Event already processed", + ) + + # Log event processing + WebhookEvent.objects.create( + event_id=event_id, + event_payload=json.loads(payload), # optional + ) + + payment_service = services.PaymentProcessingService(webhook_data=event) + payment_service.process_event() + webhook_event = WebhookEvent.objects.get(event_id=event_id) + webhook_event.status = "processed" + return ApiResponse.success( + status=status.HTTP_200_OK, message="Event processed successfully" + ) + except ValueError as e: # Invalid payload return ApiResponse.error( @@ -188,16 +211,6 @@ class StripeWebhookTest(APIView): message="Invalid signature", errors=str(e), ) - - # Initialize the PaymentProcessingService with the webhook data - payment_service = services.PaymentProcessingService(webhook_data=event) - - try: - # Process the event (handle both successful and failed charges) - payment_service.process_event() - return ApiResponse.success( - status=status.HTTP_200_OK, message="Event processed successfully" - ) except Transaction.DoesNotExist: # Handle case where the transaction does not exist return ApiResponse.error( @@ -205,10 +218,16 @@ class StripeWebhookTest(APIView): ) except Exception as e: # Handle any other exceptions + WebhookEvent.objects.create( + event_id=event_id, + status="failed", + error_message=str(e), + event_payload=json.loads(payload) # Optionally store payload for audit + ) return ApiResponse.error( status=status.HTTP_500_INTERNAL_SERVER_ERROR, message="Error processing event", - errors=str(e), + errors=str(e) ) diff --git a/manage_subscriptions/migrations/0004_webhookevent.py b/manage_subscriptions/migrations/0004_webhookevent.py new file mode 100644 index 0000000..75e7f53 --- /dev/null +++ b/manage_subscriptions/migrations/0004_webhookevent.py @@ -0,0 +1,69 @@ +# Generated by Django 5.0.2 on 2024-04-19 07:29 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ( + "manage_subscriptions", + "0003_subscription_high_amount_subscription_image_and_more", + ), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="WebhookEvent", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("active", models.BooleanField(default=True)), + ("deleted", models.BooleanField(default=False)), + ("created_on", models.DateTimeField(auto_now_add=True)), + ("modified_on", models.DateTimeField(auto_now=True)), + ( + "event_id", + models.CharField(db_index=True, max_length=255, unique=True), + ), + ("received_at", models.DateTimeField(auto_now_add=True)), + ("processed_at", models.DateTimeField(blank=True, null=True)), + ("status", models.CharField(default="received", max_length=20)), + ("error_message", models.TextField(blank=True, null=True)), + ("event_payload", models.JSONField(blank=True, null=True)), + ( + "created_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_created", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_modified", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "db_table": "webhook_event", + }, + ), + ] diff --git a/manage_subscriptions/models.py b/manage_subscriptions/models.py index 5125750..51e2a4b 100644 --- a/manage_subscriptions/models.py +++ b/manage_subscriptions/models.py @@ -74,3 +74,22 @@ class PrincipalSubscription(BaseModel): def __str__(self): return f"{self.subscription} - {self.principal.first_name}" + + +class WebhookEvent(BaseModel): + event_id = models.CharField(max_length=255, unique=True, db_index=True) + received_at = models.DateTimeField(auto_now_add=True) + processed_at = models.DateTimeField(null=True, blank=True) + status = models.CharField( + max_length=20, default="received" + ) # e.g., 'received', 'processed', 'failed' + error_message = models.TextField(null=True, blank=True) + event_payload = models.JSONField( + null=True, blank=True + ) # Optional: Store the payload for debugging. + + def __str__(self): + return f"Webhook Event {self.event_id} - Status: {self.status}" + + class Meta: + db_table = "webhook_event"