Files
goodtimes/manage_subscriptions/api/views.py

306 lines
11 KiB
Python
Raw Normal View History

2024-02-29 13:25:50 +05:30
from datetime import timedelta
import datetime
2024-08-05 18:51:49 +05:30
import json
2024-02-29 13:25:50 +05:30
from django.db import transaction
from django.shortcuts import get_object_or_404
from django.utils import timezone
from rest_framework import status
from rest_framework.views import APIView
from django.conf import settings
import stripe
2024-08-05 18:40:34 +05:30
from goodtimes import constants
2024-03-05 22:04:56 +05:30
from manage_subscriptions.models import (
Subscription,
PrincipalSubscription,
SubscriptionStatus,
2024-04-19 13:00:41 +05:30
WebhookEvent,
2024-03-05 22:04:56 +05:30
)
2024-02-29 13:25:50 +05:30
from goodtimes.utils import ApiResponse
from accounts.resource_action import (
PRINCIPAL_TYPE_EVENT_USER,
PRINCIPAL_TYPE_EVENT_MANAGER,
PRINCIPAL_TYPE_FREE_USER,
)
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework_simplejwt.authentication import JWTAuthentication
2024-03-04 21:45:57 +05:30
from manage_wallets.models import (
PaymentMethod,
Transaction,
TransactionStatus,
TransactionType,
)
2024-02-29 13:25:50 +05:30
from .serializers import PrincipalSubscriptionSerializer
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from rest_framework.response import Response
2024-08-05 18:40:34 +05:30
from goodtimes.webhook.payment_processing_service import PaymentProcessingService
import logging
logger = logging.getLogger(__name__)
2024-02-29 13:25:50 +05:30
class CreatePrincipalSubscriptionApi(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
stripe.api_key = settings.STRIPE_SECRET_KEY
def post(self, request):
serializer = PrincipalSubscriptionSerializer(data=request.data)
if serializer.is_valid():
subscription_id = serializer.validated_data.get("subscription")
try:
subscription = Subscription.objects.get(id=subscription_id)
except Subscription.DoesNotExist:
return ApiResponse.error(
status=status.HTTP_404_NOT_FOUND, message="Subscription not found."
)
order_id = (
"order_"
+ str(timezone.localtime().timestamp())
+ str(request.user.email)
)
print("order_id: ", order_id)
2024-03-04 21:45:57 +05:30
# Create a Transaction object with status INITIATE
transaction = Transaction.objects.create(
principal=request.user,
principal_subscription=None, # Since the subscription is not created yet
transaction_type=TransactionType.DEPOSIT, # or PAYMENT, as applicable
payment_method=PaymentMethod.CARD, # Assuming CARD for this example
transaction_status=TransactionStatus.INITIATE,
amount=subscription.amount,
order_id=order_id,
comment="Principal Subscription Initiated",
)
2024-02-29 13:25:50 +05:30
try:
2024-03-01 18:00:42 +05:30
customer = stripe.Customer.create(
email=request.user.email,
shipping={
"name": request.user.first_name,
"address": {
"line1": "Test Address",
"city": "Test City",
2024-03-01 19:21:06 +05:30
"postal_code": "SW1A 2AA",
2024-03-01 20:32:48 +05:30
"country": "GB", # Adjust accordingly
2024-03-01 18:00:42 +05:30
},
},
)
2024-02-29 13:25:50 +05:30
payment_intent = stripe.PaymentIntent.create(
amount=int(subscription.amount * 100),
2024-03-01 18:00:42 +05:30
currency="GBP",
2024-02-29 13:25:50 +05:30
description="Principal Subscription",
metadata={
"principal": request.user.id,
"order_id": order_id,
2024-03-04 21:45:57 +05:30
"subscription": str(subscription.id),
"transaction_id": str(transaction.id),
2024-02-29 13:25:50 +05:30
},
2024-03-01 18:00:42 +05:30
customer=customer.id,
2024-02-29 13:25:50 +05:30
)
return Response(
{
"client_secret": payment_intent.client_secret,
"message": "Payment intent created successfully",
}
)
except stripe.error.StripeError as e:
# Handle Stripe-related errors
return Response({"error": str(e)}, status=400)
else:
fail_response = {
"status": status.HTTP_400_BAD_REQUEST,
"message": "Validation Failed",
"errors": serializer.errors,
}
return ApiResponse.error(**fail_response)
2024-03-07 18:06:57 +05:30
# class CreatePrincipalSubscriptionApi(APIView):
# authentication_classes = [JWTAuthentication]
# permission_classes = [IsAuthenticated]
# def post(self, request):
# serializer = PrincipalSubscriptionSerializer(data=request.data)
# if serializer.is_valid():
# subscription_id = serializer.validated_data.get("subscription").id
# try:
# subscription = Subscription.objects.get(id=subscription_id)
# except Subscription.DoesNotExist:
# return ApiResponse.error(
# status=status.HTTP_404_NOT_FOUND, message="Subscription not found."
# )
# start_date = timezone.localtime().date()
# end_date = start_date + timedelta(days=subscription.plan.days)
# grace_period_end_date = end_date + timedelta(days=15)
# # You can directly pass the additional fields as save method arguments
# instance = serializer.save(
# start_date=start_date,
# end_date=end_date,
# grace_period_end_date=grace_period_end_date,
# created_by=request.user, # Assuming your model has this field and you want to track who created the subscription
# )
# success_response = {
# "status": status.HTTP_201_CREATED, # Use 201 for successful resource creation
# "message": "Success",
# "data": serializer.data,
# }
# return ApiResponse.success(**success_response)
# else:
# fail_response = {
# "status": status.HTTP_400_BAD_REQUEST,
# "message": "Validation Failed",
# "errors": serializer.errors,
# }
# return ApiResponse.error(**fail_response)
2024-02-29 13:25:50 +05:30
@method_decorator(csrf_exempt, name="dispatch")
class StripeWebhookTest(APIView):
authentication_classes = []
permission_classes = [AllowAny]
@transaction.atomic
def post(self, request):
stripe.api_key = settings.STRIPE_SECRET_KEY
payload = request.body
2024-03-05 12:54:29 +05:30
sig_header = request.META["HTTP_STRIPE_SIGNATURE"]
2024-03-04 21:45:57 +05:30
endpoint_secret = "whsec_ccf1f87295603cdd1733995ee2d3c0d6f74c7ceaf28916ea45114a54b7ce1d0f" # Make sure to retrieve this from your settings
2024-03-05 12:54:29 +05:30
event = None
2024-02-29 13:25:50 +05:30
try:
2024-08-05 18:51:49 +05:30
event = stripe.Event.construct_from(json.loads(payload), stripe.api_key)
2024-04-19 13:00:41 +05:30
event_id = event["id"]
2024-04-19 16:05:47 +05:30
event_type = event["type"]
2024-08-02 15:30:19 +05:30
stripe_subscription_id = event["data"]["object"].get("subscription")
2024-04-24 15:54:05 +05:30
2024-08-05 18:40:34 +05:30
stripe_subscription = (
stripe.Subscription.retrieve(stripe_subscription_id)
if stripe_subscription_id
else None
)
current_period_start = (
stripe_subscription["current_period_start"]
if stripe_subscription
else None
)
current_period_end = (
stripe_subscription["current_period_end"]
if stripe_subscription
else None
)
2024-07-31 13:12:17 +05:30
2024-08-02 15:30:19 +05:30
webhook_event, created = WebhookEvent.objects.get_or_create(
event_id=event_id,
defaults={
"event_type": event_type,
2024-08-05 18:40:34 +05:30
"event_payload": event,
2024-08-02 15:30:19 +05:30
},
)
2024-04-24 15:54:05 +05:30
if not created and webhook_event.status == "processed":
2024-04-19 13:00:41 +05:30
return ApiResponse.success(
status=status.HTTP_208_ALREADY_REPORTED,
message="Event already processed",
)
2024-07-31 13:12:17 +05:30
payment_service = PaymentProcessingService(
webhook_data=event,
2024-08-02 15:30:19 +05:30
stripe_subscription=stripe_subscription_id,
2024-07-31 13:12:17 +05:30
current_period_start=current_period_start,
current_period_end=current_period_end,
)
2024-08-05 18:40:34 +05:30
transaction = payment_service.create_transaction()
try:
payment_service.process_event(transaction)
transaction.transaction_status = TransactionStatus.SUCCESS
except Exception as e:
transaction.transaction_status = TransactionStatus.FAIL
transaction.error_message = str(e)
logger.error(f"Transaction Error: {str(e)}")
raise e
finally:
transaction.save()
2024-04-19 13:00:41 +05:30
webhook_event.status = "processed"
2024-08-05 18:40:34 +05:30
webhook_event.processed_at = timezone.now()
2024-04-19 16:44:57 +05:30
webhook_event.save()
2024-08-05 18:40:34 +05:30
2024-04-19 13:00:41 +05:30
return ApiResponse.success(
status=status.HTTP_200_OK, message="Event processed successfully"
)
2024-08-05 18:40:34 +05:30
except stripe.error.SignatureVerificationError as e:
logger.error(f"Invalid Stripe signature: {str(e)}")
2024-03-04 21:45:57 +05:30
return ApiResponse.error(
status=status.HTTP_400_BAD_REQUEST,
2024-08-05 18:40:34 +05:30
message="Invalid signature",
2024-03-04 21:45:57 +05:30
errors=str(e),
)
2024-08-05 18:40:34 +05:30
except ValueError as e:
logger.error(f"Invalid payload: {str(e)}")
2024-03-04 21:45:57 +05:30
return ApiResponse.error(
status=status.HTTP_400_BAD_REQUEST,
2024-08-05 18:40:34 +05:30
message="Invalid payload",
2024-03-04 21:45:57 +05:30
errors=str(e),
)
2024-08-05 18:40:34 +05:30
except Transaction.DoesNotExist as e:
logger.error(f"Transaction does not exist: {str(e)}")
2024-03-04 21:45:57 +05:30
return ApiResponse.error(
2024-08-05 18:40:34 +05:30
status=status.HTTP_404_NOT_FOUND,
message="Transaction not found",
errors=str(e),
2024-02-29 13:25:50 +05:30
)
2024-03-04 21:45:57 +05:30
except Exception as e:
2024-08-05 18:40:34 +05:30
logger.error(f"Error processing webhook event: {str(e)}")
if "webhook_event" in locals():
webhook_event.status = "failed"
webhook_event.error_message = str(e)
webhook_event.save()
2024-03-04 21:45:57 +05:30
return ApiResponse.error(
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
message="Error processing event",
2024-04-24 15:54:05 +05:30
errors=str(e),
2024-02-29 13:25:50 +05:30
)
2024-03-05 22:04:56 +05:30
class LastActiveSubscriptionView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request, *args, **kwargs):
today = timezone.now().date()
try:
last_active_subscription = PrincipalSubscription.objects.filter(
principal=request.user,
2024-03-18 13:22:10 +05:30
is_paid=True,
2024-03-05 22:04:56 +05:30
cancelled=False,
2024-03-15 16:03:24 +05:30
deleted=False,
active=True,
2024-03-05 22:04:56 +05:30
status=SubscriptionStatus.ACTIVE,
end_date__gte=today,
).latest("end_date")
serializer = PrincipalSubscriptionSerializer(last_active_subscription)
return ApiResponse.success(
status=status.HTTP_200_OK,
message=constants.SUCCESS,
data=serializer.data,
)
except PrincipalSubscription.DoesNotExist:
return ApiResponse.error(
status=status.HTTP_404_NOT_FOUND,
message="No Active Subscription Found",
errors="No Active Subscription Found",
)