HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux spn-python 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 x86_64
User: arjun (1000)
PHP: 8.1.2-1ubuntu2.20
Disabled: NONE
Upload Files
File: //home/arjun/projects/unlimited-leads/Unlimited-Leads-Be/payment/views.py
import stripe
from django.conf import settings
from drf_yasg import openapi
from rest_framework.response import Response
from rest_framework import status
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from services.stripe.stripe import StripeUtils
from payment.models import Transaction, Product
from rest_framework.response import Response
from authorization.models import UnlimitedLeadUser
from django.views.decorators.csrf import csrf_exempt
import logging
import requests
from bs4 import BeautifulSoup
from django.utils import timezone
from datetime import datetime
import json
from django.http import JsonResponse
from rest_framework.generics import ListAPIView
from rest_framework.permissions import IsAdminUser
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter
from payment.serializers import TransactionSerializer
from payment.paginations import TransactionPagination
from payment.filters import TransactionFilter
from datetime import timedelta
from django.utils.timezone import now
from django.db.models.functions import Concat
from django.db.models import F, Value, CharField
from user.models import UserLeadsSearchUsage, EmailOrPhoneVerifyFileUpload
from django.db.models import Q
from services.email import email_service

# Function to retrive all product plans
@api_view(['GET'])
# @permission_classes([IsAuthenticated])
def list_products(request):
    try:
        if not request.user.is_authenticated:
            return Response({
                "success": False,
                "message": "User is not logged in",
                "errors": "Please login to see pricing details.",
                "statusCode": 401
            }, status=status.HTTP_401_UNAUTHORIZED)
        products = Product.objects.all().order_by('amount') 
        plans = []

        for product in products:
            # Base product details
            product_data = {
                "product_Id": product.product_id, 
                "name": product.plan_name,
                "description": product.description,
                "amount": product.amount,  
                "currency": product.currency,
                "is_active": product.is_active,
                "is_free": product.is_free,
                "limit":product.limit
            }
            plans.append(product_data)

        response_data = {
            "data": plans,
            "success": True,
            "message": "Plans listed successfully",
            "statusCode": 200
        }

        return Response(response_data, status=status.HTTP_200_OK)

    except Exception as e:
        return Response({"errors": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


#one time payment
@swagger_auto_schema(
    method='post',
    responses={
        201: openapi.Response(description='PaymentIntent created successfully'),
        400: openapi.Response(description='Invalid input or error'),
    },
    request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
            'record_count': openapi.Schema(
                type=openapi.TYPE_INTEGER,
                description='The total count of records validating',
            ),
            'amount': openapi.Schema(
                type=openapi.TYPE_NUMBER,
                description='The amount to be paid (in the smallest unit of the currency, e.g., cents for USD)',
            ),
            'currency': openapi.Schema(
                type=openapi.TYPE_STRING,
                description='The currency in which the payment will be made (e.g., "usd")',
                default='usd',
            ),
            'validation_id': openapi.Schema(
                type=openapi.TYPE_INTEGER,
                description='The validation id required to update the emailorphonenumverify table'
            ),
        },
        required=['amount', 'record_count'],
    )
)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def create_payment_intent(request):
    try:
        user = request.user
        if not user:
            return Response({
                "success": False,
                "message": "User is not authenticated",
                "errors": "Authentication required",
                "statusCode": status.HTTP_401_UNAUTHORIZED
            }, status=status.HTTP_401_UNAUTHORIZED)
        
        validation_id = request.data.get('validation_id') 
        if not validation_id:
            return Response({
                "success": False,
                "message": "Validation ID is required",
                "errors": "Missing Validation ID",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)

        record_count = request.data.get('record_count')  
        if not record_count:
            return Response({
                "success": False,
                "message": "Total record count is required",
                "errors": "Missing record count in the request",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)
        
        if not isinstance(record_count, int) or record_count <= 0:
            return Response({
                "success": False,
                "message": "Record count must be a positive integer",
                "errors": "Invalid or non-positive record count provided",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)
        
        customer_id = user.stripe_id
        if not customer_id:
            return Response({
                "success": False,
                "message": "Customer ID is not available for this user",
                "errors": "Missing Stripe Customer ID",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)

        amount = request.data.get('amount')
        if not amount:
            return Response({
                "success": False,
                "message": "Amount is required",
                "errors": "Missing amount in the request",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)
        
        # if not isinstance(int(amount), int):
        #     return Response({
        #         "success": False,
        #         "message": "Amount must be an number",
        #         "errors": "Invalid amount type",
        #         "statusCode": status.HTTP_400_BAD_REQUEST
        #     }, status=status.HTTP_400_BAD_REQUEST)

        if amount < 0:
            return Response({
                "success": False,
                "message": "Amount cannot be less than zero",
                "errors": "Negative amount is not allowed",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)

        currency = request.data.get('currency', 'usd')
        amount_in_cents = int(amount * 100)
        payment_method_types = ['card']

        payment_intent = StripeUtils.create_payment_intent(
            customer_id, 
            amount_in_cents, 
            currency, 
            payment_method_types,
            metadata={'record_count': record_count, 'validation_id' : validation_id}
        )

        return Response({
            "data": {
                "client_secret": payment_intent["client_secret"],
                "payment_intent_id": payment_intent["id"],
                "amount": amount_in_cents / 100, 
                "payment_method_types": payment_intent["payment_method_types"],
                "record_count": record_count,
                "validation_id":validation_id
            },
            "success": True,
            "message": "Payment Intent created successfully",            
            "statusCode": status.HTTP_201_CREATED
        }, status=status.HTTP_201_CREATED)

    except stripe.error.StripeError as e:
        return Response({
            "success": False,
            "message": "Stripe error occurred",
            "errors": str(e),
            "statusCode": status.HTTP_400_BAD_REQUEST
        }, status=status.HTTP_400_BAD_REQUEST)

    except Exception as e:
        return Response({
            "success": False,
            "message": "An unexpected error occurred",
            "errors": str(e),
            "statusCode": status.HTTP_400_BAD_REQUEST
        }, status=status.HTTP_400_BAD_REQUEST)


# For free subscription plan
# plan not in stripe product
@swagger_auto_schema(
    method='post',
    responses={
        201: openapi.Response(description='Free plan subscribed successfully'),
        400: openapi.Response(description='Invalid input or error'),
    },
    request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
            'product_id': openapi.Schema(
                type=openapi.TYPE_STRING, 
                description='The ID of the free plan being purchased',
            )
        },
        required=['product_id'],
    )
)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def create_free_plan_transaction(request):
    try:
        user = request.user
        customer_id = user.stripe_id

        if not customer_id:
            return Response({
                "success": False,
                "message": "Customer ID is not available for this user",
                "errors": "Missing Stripe Customer ID",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)

        free_plan_id = request.data.get("product_id")
        if not free_plan_id:
            return Response({
                "success": False,
                "message": "Free plan ID is required",
                "errors": "Missing free plan ID in the request",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)

        # Verify free plan existence
        try:
            product = Product.objects.get(product_id=free_plan_id, is_free=True)
        except Product.DoesNotExist:
            return Response({
                "success": False,
                "message": "Product not found",
                "errors": f"No free product found with ID {free_plan_id}",
                "statusCode": status.HTTP_404_NOT_FOUND
            }, status=status.HTTP_404_NOT_FOUND)
        
        existing_subscription = Transaction.objects.filter(
            customer=user,
            product=product,
            is_subscription=True,
            subscription_status="active"
        ).order_by('-created_at').first()

        if existing_subscription:
            return Response({
                "success": False,
                "message": "You are already subscribed to this free plan",
                "errors": "Active subscription exists for the same free plan",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)

        # Check for an active subscription
        latest_transaction = Transaction.objects.filter(
            customer=user, is_subscription=True
        ).order_by('-created_at').first()
        messageFlag = True
        if latest_transaction and latest_transaction.subscription_status == 'active':
            messageFlag = False
            # Cancel the active subscription
            try:
                user_usage, created = UserLeadsSearchUsage.objects.get_or_create(user=user)
                if not created:
                    user_usage.usage_count = 0
                    user_usage.usage_reset_at = timezone.now()
                    user_usage.save()
                else:
                    # If a new entry is created, it's already initialized with defaults
                    logger.info(f"New UserLeadsSearchUsage entry created for user {user}.")
            except Exception as e:
                logger.error(f"Error while updating UserLeadsSearchUsage: {e}")
                return  # Handle appropriately or log further

            try:
                stripe.Subscription.delete(latest_transaction.subscription_id)
                latest_transaction.subscription_status = 'cancelled'
                latest_transaction.cancelled_at = timezone.now()
                latest_transaction.save()
            except stripe.error.StripeError as e:
                return Response({
                    "success": False,
                    "message": "Failed to cancel the subscription",
                    "errors": str(e),
                    "statusCode": status.HTTP_400_BAD_REQUEST
                }, status=status.HTTP_400_BAD_REQUEST)

        # Create the new free subscription
        billing_period = product.billing_period.lower()
        if billing_period == "day":
            period_days = 1
        elif billing_period == "month":
            period_days = 30
        elif billing_period == "year":
            period_days = 365
        else:
            return Response({
                "success": False,
                "message": "Invalid billing period",
                "errors": f"Unsupported billing period '{billing_period}' for product ID {free_plan_id}",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)

        transaction_date = now()
        subscription_start = transaction_date
        subscription_end = subscription_start + timedelta(days=period_days)

        Transaction.objects.create(
            customer=user,
            purpose="Free plan purchase",
            amount=0,  
            payment_status="succeeded", 
            product=product,  
            is_subscription=True,
            transaction_date=transaction_date,
            subscription_status="active",
            subscription_start=subscription_start,
            subscription_end=subscription_end,
            currency=product.currency
        )

        return Response({
            "success": True,
            "message": "Subscription successful" if messageFlag else "Downgraded to free plan successfully",
            "data": {
                "product_id": free_plan_id,
                "product_name": product.plan_name,
                "transaction_date": transaction_date,
                "subscription_start": subscription_start,
                "subscription_end": subscription_end,
            },
            "statusCode": status.HTTP_200_OK
        }, status=status.HTTP_200_OK)

    except Exception as e:
        return Response({
            "success": False,
            "message": "An unexpected error occurred",
            "errors": str(e),
            "statusCode": status.HTTP_500_INTERNAL_SERVER_ERROR
        }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    

# For subscription method
# create setup intent
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def create_setup_intent(request):
    try:
        user = request.user
        customer_id = user.stripe_id 

        if not customer_id:
            return Response({
                "success": False,
                "message": "Customer ID is not available for this user",
                "errors": "Missing Stripe Customer ID",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)
        
        setup_intent = stripe.SetupIntent.create(
            # payment_method_types=["card", "paypal"],
            payment_method_types=["card"],
            customer=customer_id,
            usage="off_session"
            )
        
        return Response({
            "data": {
                "id": setup_intent.id,
                "client_secret": setup_intent.client_secret,
                "payment_method_types": setup_intent["payment_method_types"]
            },
            "success": True,
            "message": "SetupIntent created successfully",
            "statusCode": status.HTTP_201_CREATED
        }, status=status.HTTP_201_CREATED)

    except stripe.error.StripeError as e:
        return Response({
            "success": False,
            "message": "Stripe error occurred",
            "errors": str(e),
            "statusCode": status.HTTP_400_BAD_REQUEST
        }, status=status.HTTP_400_BAD_REQUEST)
    
    except Exception as e:
        return Response({
            "success": False,
            "message": "An unexpected error occurred",
            "errors": str(e),
            "statusCode": status.HTTP_400_BAD_REQUEST
        }, status=status.HTTP_400_BAD_REQUEST)

# create customer subscription and attach a payment method
@swagger_auto_schema(
    method='post',
    responses={
        201: openapi.Response(description='PaymentIntent created successfully'),
        400: openapi.Response(description='Invalid input or error'),
    },
    request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
            'product_id': openapi.Schema(
                type=openapi.TYPE_STRING, 
                description='The ID of the product being purchased',
            ),
            'payment_method_id': openapi.Schema(
                type=openapi.TYPE_STRING,
                description='The ID of the payment method to be used for the subscription',
            ),
        },
        required=['product_id', 'payment_method_id'],
    )
)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def create_subscription(request):
    try:
        user = request.user
        customer_id = user.stripe_id 

        if not customer_id:
            return Response({
                "success": False,
                "message": "Customer ID is not available for this user",
                "errors": "Missing Stripe Customer ID",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)
        
        payment_method_id = request.data.get("payment_method_id")
        if not payment_method_id:
            return Response({
                "success": False,
                "message": "Payment Method ID is required",
                "errors": "Missing payment method ID in the request",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)
        
        product_id = request.data.get("product_id")
        if not product_id:
            return Response({
                "success": False,
                "message": "Product ID is required",
                "errors": "Missing product ID in the request",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)
        
        # Attach the PaymentMethod to the Customer
        stripe.PaymentMethod.attach(
            payment_method_id,
            customer=customer_id,
        )
        stripe.Customer.modify(
            customer_id,
            invoice_settings={
                'default_payment_method': payment_method_id,
            }
        )

        try:
            product = Product.objects.get(product_id=product_id)
        except Product.DoesNotExist:
            return Response({"errors": "Product not found"}, status=status.HTTP_404_NOT_FOUND)

        price_id = product.product_id

        try:

            latest_transaction = Transaction.objects.filter(
                customer=user,
                subscription_status="active",
                is_cancelled=True
            ).order_by('-created_at').first()
            
            if latest_transaction:
                latest_transaction.subscription_status = "cancelled"
                # latest_transaction.cancelled_at = timezone.now()
                latest_transaction.save()
                
            subscription = stripe.Subscription.create(
                customer=customer_id,
                items=[{"price": price_id}],
                payment_settings={
                    'payment_method_options': {
                        'card': {
                            'request_three_d_secure': 'automatic',
                        },
                    },
                    'save_default_payment_method': 'on_subscription',
                },
                collection_method='charge_automatically',
                off_session=True,
                expand=['latest_invoice.payment_intent'],
            )

            charge_id = subscription.latest_invoice.get("charge")
            transaction_id = f"{charge_id[-12:].upper()}" if charge_id else None
            subscription_start_naive = datetime.fromtimestamp(subscription['current_period_start'])
            subscription_start = timezone.make_aware(subscription_start_naive)
            subscription_end_naive = datetime.fromtimestamp(subscription['current_period_end'])
            subscription_end = timezone.make_aware(subscription_end_naive)

            latest_free_transaction = Transaction.objects.filter(
            customer=user,
            is_subscription=True,
            product__is_free=True,
            subscription_status="active"
            ).order_by('-created_at').first()

            if latest_free_transaction:
                latest_free_transaction.subscription_status = "cancelled"
                latest_free_transaction.cancelled_at = timezone.now()
                # latest_free_transaction.is_subscription = False
                latest_free_transaction.save()

            Transaction.objects.create(
                customer=user,
                purpose="Plan purchase",
                amount=product.amount,
                payment_status="succeeded",
                product=product,
                is_subscription=True,
                transaction_id=transaction_id,
                transaction_date=datetime.now(),
                subscription_id=subscription.id,
                subscription_status=subscription.status,
                subscription_start=subscription_start,
                subscription_end=subscription_end,
                charge_id=charge_id,
                currency=product.currency,
            )

            return Response({
                "data": {
                    "id": subscription.id,
                    "status": subscription.status,
                    "client_secret": subscription.latest_invoice.payment_intent.client_secret,
                    "current_period_start": subscription.current_period_start,
                    "current_period_end": subscription.current_period_end,
                    "transaction_date": subscription_start,
                    "transaction_id": transaction_id,
                    # "latest_invoice": subscription,
                },
                "success": True,
                "message": "Subscription successful",
                "statusCode": status.HTTP_201_CREATED
            }, status=status.HTTP_201_CREATED)

        except stripe.error.StripeError as error:
            return Response({
                "success": False,
                "message": "Failed to create subscription",
                "errors": error.user_message,
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)

    except stripe.error.StripeError as e:
        return Response({
            "success": False,
            "message": "Stripe Error",
            "errors": str(e),
            "statusCode": status.HTTP_400_BAD_REQUEST
        }, status=status.HTTP_400_BAD_REQUEST)
    except Exception as e:
        return Response({
            "success": False,
            "message": "An unexpected error occurred",
            "errors": str(e),
            "statusCode": status.HTTP_400_BAD_REQUEST
        }, status=status.HTTP_400_BAD_REQUEST)

@swagger_auto_schema(
    method='post',
    responses={
        201: openapi.Response(description='Subscription plan updated successfully'),
        400: openapi.Response(description='Invalid input or error'),
    },
    request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
            'new_price_id': openapi.Schema(
                type=openapi.TYPE_STRING, 
                description='The ID of the new product being purchased',
            )
        },
        required=['new_price_id'],
    )
)
@api_view(['POST'])
@permission_classes([IsAuthenticated])    
def update_subscription_plan(request):
    try:
        user = request.user
        customer_id = user.stripe_id

        if not customer_id:
            return Response({
                "success": False,
                "message": "Customer ID is not available for this user",
                "errors": "Missing Stripe Customer ID",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)

        new_price_id = request.data.get("new_price_id")
        if not new_price_id:
            return Response({
                "success": False,
                "message": "New Price ID is required",
                "errors": "Missing new price ID in the request",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)

        # Retrieve the current subscription
        subscriptions = stripe.Subscription.list(customer=customer_id, status="active")
        if not subscriptions.data:
            return Response({
                "success": False,
                "message": "No active subscriptions found for this user",
                "errors": "Active subscription not found",
                "statusCode": status.HTTP_404_NOT_FOUND
            }, status=status.HTTP_404_NOT_FOUND)
        logger.info(f"subscriptionsssssssssssssssssssss , {subscriptions}")

        current_subscription = subscriptions.data[0]
        print("current_subscription.cancel_at_period_endddddddddddddddddd", current_subscription.cancel_at_period_end)

        if current_subscription.cancel_at_period_end:
            return Response({
                "success": False,
                "message": "Cannot upgrade/downgrade the subscription as it is already cancelled.",
                "errors": "Subscription already marked for cancellation.",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)
        
        current_price_id = current_subscription["items"]["data"][0]["price"]["id"]
        current_price = stripe.Price.retrieve(current_price_id).unit_amount / 100 
        new_price = stripe.Price.retrieve(new_price_id).unit_amount / 100 
        # print("current_price")
        # print("new_price", new_price)
        # Determine if it is an upgrade or downgrade
        action_message = (
            "Subscription upgraded successfully" if new_price > current_price
            else "Subscription downgraded successfully"
        )
        # print(action_message)
        # Update subscription to the new plan
        updated_subscription = stripe.Subscription.modify(
            current_subscription.id,
            items=[{
                "id": current_subscription["items"]["data"][0].id,
                "price": new_price_id,
            }],
            # proration_behavior="create_prorations",
            # billing_cycle_anchor="unchanged", 
            proration_behavior="none",
        )
        logger.info(f"updated_subscriptionssssssssssssss, {updated_subscription}")

        # latest_transaction = Transaction.objects.filter(
        #     customer=user,
        #     subscription_status="active",
        #     is_subscription=True
        # ).order_by('-subscription_start').first()

        # if latest_transaction:
        #     latest_transaction.subscription_status = "cancelled"
        #     latest_transaction.save()

        subscription_start_naive = datetime.fromtimestamp(updated_subscription['current_period_start'])
        subscription_start = timezone.make_aware(subscription_start_naive)
        subscription_end_naive = datetime.fromtimestamp(updated_subscription['current_period_end'])
        subscription_end = timezone.make_aware(subscription_end_naive)
        # charge_id = updated_subscription['latest_invoice']['charge']
        # print("charge_id", charge_id)
        # transaction_id = f"{charge_id[-12:].upper()}" if charge_id else None
        product = Product.objects.get(product_id=new_price_id)
        # print(product)
        # if updated_subscription.status == 'succeeded':
        Transaction.objects.create(
            customer=user,
            purpose="Plan purchase",
            amount=product.amount if product else 0,
            payment_status="succeeded",
            product=product,
            is_subscription=True,
            # transaction_id=transaction_id,
            transaction_date=datetime.now(),
            subscription_id=updated_subscription.id,
            subscription_status=updated_subscription.status,
            subscription_start=subscription_start,
            subscription_end=subscription_end,
            # charge_id=charge_id,
            currency=product.currency if product else "USD",
        )

        latest_invoice = stripe.Invoice.retrieve(updated_subscription.latest_invoice)
        payment_intent = latest_invoice.payment_intent

        # If payment_intent exists, retrieve the client secret
        client_secret = None
        if payment_intent:
            payment_intent_obj = stripe.PaymentIntent.retrieve(payment_intent)
            client_secret = payment_intent_obj.client_secret


        return Response({
            "data": {
                "id": updated_subscription.id,
                "status": updated_subscription.status,
                "current_period_start": updated_subscription.current_period_start,
                "current_period_end": updated_subscription.current_period_end,
                "client_secret":client_secret
            },
            "success": True,
            "message": action_message,
            "statusCode": status.HTTP_200_OK
        }, status=status.HTTP_200_OK)

    except stripe.error.StripeError as error:
        return Response({
            "success": False,
            "message": "Failed to update subscription",
            "errors": error.user_message,
            "statusCode": status.HTTP_400_BAD_REQUEST
        }, status=status.HTTP_400_BAD_REQUEST)

    except Exception as e:
        return Response({
            "success": False,
            "message": "An unexpected error occurred",
            "errors": str(e),
            "statusCode": status.HTTP_400_BAD_REQUEST
        }, status=status.HTTP_400_BAD_REQUEST)



# webhook setup
logger = logging.getLogger('payment')
stripe.api_key = settings.STRIPE_SECRET_KEY
@csrf_exempt
def stripe_webhook(request):
    logger.debug(f"Request Headers: {json.dumps(dict(request.headers))}")
    logger.debug(f"Request data: {request}")
    payload = request.body
    sig_header = request.headers.get('Stripe-Signature')  
    endpoint_secret = settings.STRIPE_WEBHOOK_SECRET
    # print(sig_header)

    try:
        event = stripe.Webhook.construct_event(payload, sig_header, endpoint_secret)
    except ValueError as e:
        return JsonResponse({"errors": "Invalid payload"}, status=400)
    except stripe.error.SignatureVerificationError as e:
        return JsonResponse({"errors": "Invalid signature"}, status=400)
    logger.debug(event['type'])
    logger.info(event)
    
    # Handle the event types you care about

    if event['type'] == 'payment_intent.succeeded' and event["data"]['object'].invoice == None:  #one time payment
        payment_intent = event['data']['object']
        handle_payment_intent_succeeded(payment_intent)

    elif event['type'] == "invoice.payment_succeeded": #subscription
        invoice = event['data']['object']
        handle_invoice_payment_succeeded(invoice)

    elif event['type'] == 'invoice.payment_action_required':  # Action required for 3D secure recurring subscription payment
        invoice = event['data']['object']
        # print("Payment Action Required Invoice:", invoice)
        handle_payment_action_required(invoice)
    
    elif event['type'] == 'charge.succeeded'and event["data"]['object'].invoice == None: #to fetch receipt in one time payment
        charge = event['data']['object']
        handle_charge_succeeded(charge)

    elif event['type'] == 'payment_intent.payment_failed'and event["data"]['object'].invoice == None:  # One-time payment failed
        payment_intent = event['data']['object']
        handle_payment_intent_failed(payment_intent)

    elif event['type'] == 'invoice.payment_failed': # in subscription payment failed case flow
        invoice = event['data']['object']
        handle_invoice_payment_failed(invoice)

    elif event['type'] == 'customer.subscription.deleted': #customer cancelling payment
        subscription = event['data']['object']
        handle_subscription_deleted(subscription)

    elif event['type'] == 'invoice.created' and event['data']['object']['billing_reason'] == 'subscription_update': #Update subscription
        updated_subscription = event['data']
        handle_updated_subscription(updated_subscription)
    
    elif event['type'] == 'customer.subscription.updated':  # Detect scheduled cancellations
        subscription = event['data']['object']
        if subscription.get('cancel_at_period_end', False):
            logger.info(f"Subscription {subscription['id']} is set to cancel at the end of the billing period.")

    return JsonResponse({"status": "success"}, status=200)

def handle_payment_intent_succeeded(payment_intent):
    logger.debug("payment intent")
    logger.debug(f"Request data: {payment_intent}")
    customer_id = payment_intent['customer']

    # Retrieve the UnlimitedLeadUser instance using customer_id
    try:
        customer = UnlimitedLeadUser.objects.get(stripe_id=customer_id)
    except UnlimitedLeadUser.DoesNotExist:
        # Handle the case where the customer doesn't exist
        return  # or log an error
    purpose = "Validation service"
    amount = payment_intent['amount'] / 100  
    status = payment_intent['status']
    currency = payment_intent['currency']
    # try:
    #     product = Product.objects.get(amount=amount, is_subscription=False)
    # except Product.DoesNotExist:
    #     product = None

    charge_id = payment_intent.get('latest_charge')
    # print("payment_intent.get('latest_charge')",payment_intent.get('latest_charge'))
    receipt_url = None
    plan = 'Validator Service'
    if charge_id:
        charge = stripe.Charge.retrieve(charge_id)
        receipt_url = handle_charge_succeeded(charge)
    
    intent_id = payment_intent.get('id')
    transaction_id = f"{intent_id[-12:].upper()}" if intent_id else None
    transaction_date = datetime.now()

    record_count = payment_intent.get('metadata', {}).get('record_count')    
    validation_id = payment_intent.get('metadata', {}).get('validation_id')
    filtered_validation_id = EmailOrPhoneVerifyFileUpload.objects.get(id=validation_id)

    if filtered_validation_id:
        filtered_validation_id.transactionID = transaction_id
        filtered_validation_id.payment_status = True
        filtered_validation_id.amount_paid = amount
        filtered_validation_id.save()

    service = email_service.PaymentEmailService(customer.id) 
    service.send_payment_success_email(amount, plan, receipt_url, transaction_id, is_subscription=False)

    # if customer:
    #     notify_user_payment_successful.delay(customer.id, plan)
    
    # Create a transaction record
    try:
        transaction = Transaction.objects.create(
            customer=customer,
            amount=amount,
            # product=product,
            record_count=record_count,
            purpose=purpose,
            payment_status=status,
            is_subscription=False,
            receipt_url=receipt_url,
            transaction_id=transaction_id,
            transaction_date=transaction_date,
            charge_id=charge_id,
            currency=currency
            )
        transaction.save()
        logger.info("Transaction saved successfully")
    except Exception as e:
        logger.error(f"Error saving transaction: {str(e)}")

def handle_updated_subscription(updated_subscription):
    logger.info("haiiiiiiiiiiiiiiiiiiiiiiiiiiii")
    try:
        logger.info("Handling updated subscription")
        logger.info(updated_subscription)

        subscription_id = updated_subscription['object']['subscription']
        invoice_id = updated_subscription['object']['id']
        charge_id = updated_subscription['object']['charge']
        charge_receipt_url = updated_subscription['object']['hosted_invoice_url']
        # print("subscription_id", subscription_id)
        # print("invoice_id", invoice_id)
        # print("charge_id", charge_id)
        # print("receipt_url", receipt_url)

        # print("charge_receipt_url", charge_receipt_url)
        # receipt_url = None
        # if charge_receipt_url:
        #     try:
        #         receipt_url = fetch_pdf_url(charge_receipt_url)
        #         # print(f"PDF URL: {receipt_url}")
        #     except BadRequest as e:
        #         print(f"Error fetching PDF URL: {e}")


        # Fetch the user based on the subscription's customer ID
        customer_id = updated_subscription['object']['customer']
        # print("customer_id", customer_id)

        # Retrieve the UnlimitedLeadUser instance using customer_id
        try:
            user = UnlimitedLeadUser.objects.get(stripe_id=customer_id)
        except UnlimitedLeadUser.DoesNotExist:
            # Handle the case where the customer doesn't exist
            logger.error(f"Customer with stripe_id {customer_id} not found")
            return
        # Find the last active transaction for the user associated with the subscription
        logger.info(f"Filtering transactions for customer: {user}, subscription_status: active, subscription_id: {subscription_id}")
        # transactions = Transaction.objects.filter(
        #     customer=user,
        #     subscription_status="active",
        #     subscription_id=subscription_id
        # )
        # logger.info(f"Found transactions: {transactions}")
        # latest_transaction = transactions.order_by('-transaction_date').first()

        # # print("latest_transaction", latest_transaction)

        # if latest_transaction:
        #     # Update the transaction with the missing details
        #     latest_transaction.invoice_id = invoice_id
        #     latest_transaction.charge_id = charge_id
        #     latest_transaction.transaction_id = charge_id[-12:].upper() if charge_id else None 
        #     latest_transaction.receipt_url = charge_receipt_url
        #     latest_transaction.save()

        #     logger.info(f"Transaction updated: {latest_transaction}")

        # else:
        #     logger.warning(f"No active transaction found for subscription: {subscription_id}")
        # transactions = Transaction.objects.filter(
        #     customer=user,
        #     subscription_status="active",
        #     subscription_id=subscription_id
        # ).order_by('-subscription_start')
        # transactions = Transaction.objects.filter(
        #     customer=user,
        #     subscription_id=subscription_id
        # ).filter(
        #     Q(subscription_status="active") | Q(subscription_status="failed")
        # ).order_by('-subscription_start')
        # print("transactionst", transactions)

        transactions = Transaction.objects.filter(
        customer=user,
        subscription_id=subscription_id
        ).filter(
            Q(subscription_status="active") | Q(subscription_status="failed")
        ).order_by('-subscription_start')
        # print("transactionst", transactions)

        # if transactions.count() >= 2:
        #     # print("step 1")
        #     # Get the second-to-last transaction
        #     # second_last_transaction = transactions[1]  # Zero-based index
        #     # # print("second_last_transaction", second_last_transaction)
        #     # # Update the subscription status to 'cancelled'
        #     # second_last_transaction.subscription_status = "cancelled"
        #     # second_last_transaction.save()
        #     # logger.info(f"Second last transaction updated: {second_last_transaction}")
        #     for transaction in transactions[1:]:  # Skip the first (most recent) transaction
        #         transaction.subscription_status = "cancelled"
        #         transaction.save()
        #         logger.info(f"Transaction {transaction.id} updated to 'cancelled'")
        # else:
        #     logger.warning(f"Not enough transactions found to identify the second last transaction for customer {user.email}")

        # if transactions.count() >= 2:
        #     # print("step 1")
        #     # Get the second-to-last transaction
        #     second_last_transaction = transactions[1]  # Zero-based index
        #     # print("second_last_transaction", second_last_transaction)
        #     # Update the subscription status to 'cancelled'
        #     second_last_transaction.subscription_status = "cancelled"
        #     second_last_transaction.save()
        #     logger.info(f"Second last transaction updated: {second_last_transaction}")
        # else:
        #     logger.warning(f"Not enough transactions found to identify the second last transaction for customer {user.email}")

        # Update the most recent transaction with the new details
        latest_transaction = transactions.first() if transactions.exists() else None

        if latest_transaction:
            latest_transaction.invoice_id = invoice_id
            latest_transaction.charge_id = charge_id
            latest_transaction.transaction_id = charge_id[-12:].upper() if charge_id else None
            latest_transaction.receipt_url = charge_receipt_url
            # latest_transaction.is_cancelled = True
            # latest_transaction.cancelled_at = timezone.now()
            latest_transaction.save()
            logger.info(f"Latest transaction updated: {latest_transaction}")
        else:
            logger.warning(f"No transactions found for customer: {user.email}")


    except Exception as e:
        logger.error(f"Error handling updated subscription: {str(e)}")


def handle_invoice_payment_succeeded(invoice):
    logger.info("jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj")
    logger.debug("Invoice payment")
    logger.debug(invoice)
    customer_id = invoice['customer']
    
    try:
        customer = UnlimitedLeadUser.objects.get(stripe_id=customer_id)
    except UnlimitedLeadUser.DoesNotExist:
        # Handle the case where the customer doesn't exist
        return  # or log an error
    
    try:
        user_usage, created = UserLeadsSearchUsage.objects.get_or_create(user=customer)
        if not created:
            user_usage.usage_count = 0
            user_usage.usage_reset_at = timezone.now()
            user_usage.save()
        else:
            # If a new entry is created, it's already initialized with defaults
            logger.info(f"New UserLeadsSearchUsage entry created for user {customer}.")
    except Exception as e:
        logger.error(f"Error while updating UserLeadsSearchUsage: {e}")
        return  # Handle appropriately or log further
    
    charge_id = invoice['charge']
    # print("charge_id", charge_id)
    if charge_id:
        charge = stripe.Charge.retrieve(charge_id)
    else:
        # Handle case where there is no charge ID
        logger.error("No charge ID associated with the invoice.")
        return
    
    transaction_id = f"{charge_id[-12:].upper()}" if charge_id else None
    transaction_date = datetime.now()
    amount = invoice['amount_paid'] / 100 
    status = 'succeeded'
    charge_payment_method = charge['payment_method_details']['type']
    charge_receipt_url = charge.get('receipt_url', '')
    # plan_name = invoice['lines']['data'][0]['description'] if invoice['lines']['data'] else 'Unknown Plan'
    price_id = None
    if 'lines' in invoice and invoice['lines']['data']:
        line_item = invoice['lines']['data'][0]  # Get the first line item
        if 'price' in line_item:
            price_id = line_item['price']['id']  # Stripe price ID

    # print("Price ID from Stripe:", price_id)
    product = None
    if price_id:
        try:
            product = Product.objects.get(product_id=price_id)  # Assuming product_id in your model matches Stripe's product_id
        except Product.DoesNotExist:
            logger.error(f"Product with price_id {price_id} not found.")
            product = None
    else:
        logger.error("No price_id found in the invoice.")
    invoice_id = invoice['id']
    # invoice_pdf_url = invoice.invoice_pdf
    subscription_id = invoice.get('subscription')
    subscription_status = 'active' if invoice.get('paid') else 'pending'  # Assuming invoice['paid'] determines subscription status
    subscription_start = None
    subscription_end = None

    # If there is a subscription, fetch the subscription details
    if subscription_id:
        subscription = stripe.Subscription.retrieve(subscription_id)
        subscription_start_naive = datetime.fromtimestamp(subscription['current_period_start'])
        subscription_end_naive = datetime.fromtimestamp(subscription['current_period_end'])

        # Convert naive datetime to timezone-aware datetime
        subscription_start = timezone.make_aware(subscription_start_naive)
        subscription_end = timezone.make_aware(subscription_end_naive)     

    # print("charge_payment_method : ",charge_payment_method)
    # print("charge_receipt_url : ",charge_receipt_url)
    if charge_receipt_url:
        try:
            receipt_url = fetch_pdf_url(charge_receipt_url)
            # print(f"PDF URL: {receipt_url}")
        except BadRequest as e:
            print(f"Error fetching PDF URL: {e}")
    
    # amount = subscription.latest_invoice.get("amount_paid") / 100  
    plan = product.plan_name 

    receipt_url = None
    if charge_id:
        charge = stripe.Charge.retrieve(charge_id)
        charge_receipt_url = charge.get('receipt_url', '')
    if charge_receipt_url:
        try:
            receipt_url = fetch_pdf_url(charge_receipt_url)
            # print(f"PDF URL: {receipt_url}")
        except BadRequest as e:
            print(f"Error fetching PDF URL: {e}")

    service = email_service.PaymentEmailService(customer.id) 
    service.send_payment_success_email(amount, plan, receipt_url, transaction_id, is_subscription=True)


    existing_transaction = Transaction.objects.filter(subscription_id=subscription_id).order_by('-created_at').first()

    if existing_transaction:
        # print("step 1",existing_transaction)
        # Update existing transaction (Plan Renew)
        existing_transaction.invoice_id = invoice['id']
        # existing_transaction.receipt_url = invoice.get('hosted_invoice_url', '')
        existing_transaction.is_cancelled = False
        # print("existing_transaction.is_cancelled",existing_transaction.is_cancelled)
        existing_transaction.receipt_url = receipt_url
        existing_transaction.transaction_id = transaction_id
        existing_transaction.charge_id = charge_id
        existing_transaction.subscription_status = "active"
        existing_transaction.save(update_fields=['invoice_id', 'receipt_url', 'subscription_status', 'transaction_id', 'charge_id'])
    else:
        # Create new transaction (Plan Renew)
        # print("step 2", transaction_id)
        Transaction.objects.create(
            customer=customer,
            purpose="Plan renew",
            amount=amount,
            payment_status=status,
            invoice_id=invoice['id'],
            product=product,  # You can fetch the product if required
            is_subscription=True,
            receipt_url=receipt_url,
            transaction_id=transaction_id,
            transaction_date=transaction_date,
            subscription_id=subscription_id,
            subscription_status=subscription_status,
            subscription_start=subscription_start,
            subscription_end=subscription_end,
            charge_id=charge_id,
            currency=product.currency
        )
    transactions = Transaction.objects.filter(
        customer=customer,
        subscription_id=subscription_id
        ).filter(
            Q(subscription_status="active") | Q(subscription_status="failed")
        ).order_by('-subscription_start')
        # print("transactionst", transactions)

    # if transactions.count() >= 2:
    #     transactions.subscription_status = "cancelled"
    #     transactions.save()
    #     logger.info(f"Transaction {transaction.id} updated to 'cancelled'")
    # else:
    #     logger.warning(f"Not enough transactions found for customer {customer.email}")
    if transactions.count() >= 2:
        # print("step 1")
        # Get the second-to-last transaction
        second_last_transaction = transactions[1]  # Zero-based index
        # print("second_last_transaction", second_last_transaction)
        # Update the subscription status to 'cancelled'
        second_last_transaction.subscription_status = "cancelled"
        second_last_transaction.save()
        logger.info(f"Second last transaction updated: {second_last_transaction}")
    else:
        logger.warning(f"Not enough transactions found to identify the second last transaction for customer {customer.email}")



def handle_charge_succeeded(charge):
    logger.debug("Charge succeeded")
    receipt_url = charge.get('receipt_url', '')

    # Log or print the receipt URL
    if receipt_url:
        logger.info(f"Receipt URL: {receipt_url}")
    else:
        logger.error("Receipt URL not found for the charge.")
    
    return receipt_url 


class BadRequest(Exception):
    pass

def fetch_pdf_url(receipt_url):
    try:
        # Make a GET request to the receipt URL
        response = requests.get(receipt_url)
        response.raise_for_status()  # Raise an HTTPError for bad responses (4xx or 5xx)
        
        # Parse the HTML content
        html = response.text
        soup = BeautifulSoup(html, 'html.parser')

        # Find the link with 'dashboard.stripe.com/receipts' in the href attribute
        pdf_relative_url = soup.find('a', href=lambda href: href and 'dashboard.stripe.com/receipts' in href)
        
        if not pdf_relative_url:
            raise BadRequest('PDF link not found on the page.')

        return pdf_relative_url['href']  # Extract the href attribute

    except requests.RequestException as e:
        raise BadRequest(f"Error fetching PDF URL: {str(e)}")
    
def handle_subscription_deleted(subscription):
    logger.debug("Subscription deleted")
    logger.debug(f"Request data: {subscription}")
    
    customer_id = subscription['customer']
    subscription_id = subscription['id']
    try:
        customer = UnlimitedLeadUser.objects.get(stripe_id=customer_id)
        customer.email_sent_for_3d_cards = False
        customer.save()
    except UnlimitedLeadUser.DoesNotExist:
        logger.error(f"Customer with Stripe ID {customer_id} not found.")
        return
    
    # Find the related transaction in your database and update it
    try:
        transaction_record = Transaction.objects.get(subscription_id=subscription_id, customer=customer, subscription_status = 'active')
        transaction_record.subscription_status = 'cancelled'
        # transaction_record.is_subscription = False
        transaction_record.save()
        logger.info(f"Subscription {subscription_id} for customer {customer.email} marked as cancelled.")
    except Transaction.DoesNotExist:
        logger.error(f"Transaction with subscription ID {subscription_id} not found for customer {customer.email}.")
    service = email_service.PaymentEmailService(customer.id) 
    service.send_cancel_subscription_email()

def handle_invoice_payment_failed(invoice):
    logger.debug("Invoice payment failed")
    
    customer_id = invoice['customer']
    invoice_id = invoice['id']
    
    try:
        customer = UnlimitedLeadUser.objects.get(stripe_id=customer_id)
    except UnlimitedLeadUser.DoesNotExist:
        logger.error(f"Customer with Stripe ID {customer_id} not found.")
        return
    
    # Find the related transaction and mark the subscription status as 'failed'
    try:
        # transaction_record = Transaction.objects.filter(subscription_id=invoice['subscription'], customer=customer).first()
        transaction_record = Transaction.objects.filter(
        subscription_id=invoice['subscription'], 
        customer=customer).order_by('-created_at').first()
        if transaction_record:
            transaction_record.subscription_status = 'failed'
            # transaction_record.is_cancelled= True
            transaction_record.save()
            logger.info(f"Subscription {transaction_record.subscription_id} for customer {customer.email} marked as failed.")
    except Transaction.DoesNotExist:
        logger.error(f"Transaction with subscription ID {invoice['subscription']} not found for customer {customer.email}.")
    
    try:
    # Fetch the second last transaction by ordering in descending order and skipping the most recent one
        transaction_records = Transaction.objects.filter(
            subscription_id=invoice['subscription'], 
            customer=customer
        ).order_by('-created_at')

        # Ensure there are at least two records
        if transaction_records.count() >= 2:
            # Get the second last transaction
            second_last_transaction = transaction_records[1]  # index 1 refers to the second last record
            
            # Update the is_cancelled field
            second_last_transaction.is_cancelled = True
            second_last_transaction.save()
            
            logger.info(f"Second last transaction {second_last_transaction.subscription_id} for customer {customer.email} marked as cancelled.")
        else:
            logger.error(f"Less than two transactions found for subscription ID {invoice['subscription']} and customer {customer.email}.")
    except Transaction.DoesNotExist:
        logger.error(f"Transaction with subscription ID {invoice['subscription']} not found for customer {customer.email}.")
    except Exception as e:
        logger.error(f"An unexpected error occurred: {str(e)}")

def handle_payment_action_required(invoice):
    customer_id = invoice['customer']
    
    try:
        customer = UnlimitedLeadUser.objects.get(stripe_id=customer_id)
    except UnlimitedLeadUser.DoesNotExist:
        # Handle the case where the customer doesn't exist
        return  # or log an error
    try:
        payment_link = invoice.get('hosted_invoice_url')

        if not payment_link:
            raise ValueError("Invalid data: Missing user ID or payment link.")

        if customer.email_sent_for_3d_cards:
            service = email_service.PaymentEmailService(customer.id) 
            service.send_renewal_payment_email(payment_link)
        #     notify_user_subscription_expired.delay(customer.id)

        
        customer.email_sent_for_3d_cards = True
        customer.save()
        # print(f"Payment action email sent successfully to user {user_id}.")
    except Exception as e:
        print(f"Error in handle_payment_action_required: {str(e)}")
        raise e

def handle_payment_intent_failed(payment_intent):
    logger.debug("Payment Intent Failed")
    
    customer_id = payment_intent['customer']
    
    # Retrieve the UnlimitedLeadUser instance using customer_id
    try:
        customer = UnlimitedLeadUser.objects.get(stripe_id=customer_id)
    except UnlimitedLeadUser.DoesNotExist:
        # Handle the case where the customer doesn't exist
        logger.error(f"Customer with Stripe ID {customer_id} not found.")
        return  # or log an error

    amount = payment_intent['amount'] / 100  
    status = payment_intent['status']
    failure_message = payment_intent.get('last_payment_error', {}).get('message', 'Unknown error')

    # Attempt to retrieve the product based on the amount (optional)
    try:
        product = Product.objects.get(amount=amount)
    except Product.DoesNotExist:
        product = None
    
    # Log failure details for debugging purposes
    logger.error(f"Payment for customer {customer.email} failed. Reason: {failure_message}")

    # Create a failed transaction record
    Transaction.objects.create(
        customer=customer,
        amount=amount,
        product=product,
        payment_status='failed',  
        is_subscription=False, 
        receipt_url=None  
    )

# @swagger_auto_schema(
#     method='post',
#     responses={
#         200: openapi.Response(description='Subscription cancelled successfully'),
#         400: openapi.Response(description='Invalid input or error'),
#         404: openapi.Response(description='Subscription or Transaction not found'),
#         401: openapi.Response(description='User is not authenticated'),
#     }
# )
# @api_view(['POST'])
# @permission_classes([IsAuthenticated])
# def cancel_subscription(request):
#     try:
#         user = request.user

#         # Fetch the latest transaction with a subscription for the user
#         latest_transaction = Transaction.objects.filter(customer=user, is_subscription=True).order_by('-created_at').first()

#         if not latest_transaction or not latest_transaction.subscription_id or (not latest_transaction.subscription_status == 'active'):
#             return Response({"errors": "No active subscription found for the user."}, status=status.HTTP_404_NOT_FOUND)

#         # Extract the product and subscription IDs from the latest transaction
#         subscription_id = latest_transaction.subscription_id
#         product_id = latest_transaction.product.product_id  # Assuming Transaction model has a foreign key to Product

#         # Cancel the subscription via Stripe API
#         subscription = stripe.Subscription.delete(subscription_id)
        
#         # Update the transaction record to reflect the cancelled subscription
#         latest_transaction.subscription_status = 'cancelled'
#         latest_transaction.save()
#         sub_status = None
#         if subscription.status == "canceled":
#             sub_status = 'cancelled'

#         return Response({
#             "message": f"Subscription cancelled successfully.",
#             "subscription_id": subscription_id,
#             "status": sub_status,
#             "product_id": product_id
#         }, status=status.HTTP_200_OK)

#     except stripe.error.StripeError as e:
#         return Response({"errors": str(e)}, status=status.HTTP_400_BAD_REQUEST)
#     except Exception as e:
#         return Response({"errors": str(e)}, status=status.HTTP_400_BAD_REQUEST)

@swagger_auto_schema(
    method='post',
    responses={
        200: openapi.Response(description='Subscription cancellation scheduled successfully'),
        400: openapi.Response(description='Invalid input or error'),
        404: openapi.Response(description='Subscription or Transaction not found'),
        401: openapi.Response(description='User is not authenticated'),
    }
)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def scheduled_cancel_subscription(request):
    try:
        user = request.user
        latest_transaction = Transaction.objects.filter(customer=user, is_subscription=True).order_by('-created_at').first()

        if not latest_transaction:
            return Response({
                "success": False,
                "message": "No active subscription found for the user.",
                "errors": "Transaction does not exist.",
                "statusCode": status.HTTP_404_NOT_FOUND
            }, status=status.HTTP_404_NOT_FOUND)
        
        if latest_transaction.product.is_free and latest_transaction.subscription_status == "active":
            # latest_transaction.is_subscription = False
            latest_transaction.subscription_status = "cancelled"
            latest_transaction.cancelled_at = timezone.now()
            latest_transaction.save()

            return Response({
                "success": True,
                "message": "Free plan subscription cancelled successfully.",
                "statusCode": status.HTTP_200_OK
            }, status=status.HTTP_200_OK)


        if not latest_transaction.subscription_id or latest_transaction.subscription_status != 'active':
            return Response({
                "success": False,
                "message": "No active subscription found for the user.",
                "errors": "Subscription is not active.",
                "statusCode": status.HTTP_404_NOT_FOUND
            }, status=status.HTTP_404_NOT_FOUND)

        if latest_transaction.is_cancelled:
            return Response({
                "success": False,
                "message": "Subscription is already cancelled.",
                "errors": "This subscription has already been marked for cancellation.",
                "statusCode": status.HTTP_400_BAD_REQUEST
            }, status=status.HTTP_400_BAD_REQUEST)

        subscription_id = latest_transaction.subscription_id

        subscription = stripe.Subscription.modify(
            subscription_id,
            cancel_at_period_end=True
        )
        
        latest_transaction.is_cancelled = True 
        latest_transaction.cancelled_at = timezone.now()
        latest_transaction.save()

        return Response({
            "message": "Subscription cancellation successfully at end of billing period.",
            "subscription_id": subscription_id,
            "cancel_at": subscription.cancel_at,
            "current_period_end": subscription.current_period_end
        }, status=status.HTTP_200_OK)

    except stripe.error.StripeError as error:
        return Response({
            "success": False,
            "message": "Failed to cancel subscription.",
            "errors": error.user_message,
            "statusCode": status.HTTP_400_BAD_REQUEST
        }, status=status.HTTP_400_BAD_REQUEST)

    except Exception as e:
        return Response({
            "success": False,
            "message": "An unexpected error occurred.",
            "errors": str(e),
            "statusCode": status.HTTP_400_BAD_REQUEST
        }, status=status.HTTP_400_BAD_REQUEST)

    
class TransactionListView(ListAPIView):
    permission_classes = [IsAdminUser]
    # queryset = Transaction.objects.all().select_related('customer', 'product')
    queryset = Transaction.objects.filter(transaction_id__isnull=False).select_related('customer', 'product') 
    serializer_class = TransactionSerializer
    filter_backends = [DjangoFilterBackend, OrderingFilter]
    filterset_class = TransactionFilter
    ordering_fields = ['transaction_date', 'amount', 'record_count', 'name']
    ordering = ['-transaction_date']
    pagination_class = TransactionPagination

    def get_queryset(self):
        try:
            return Transaction.objects.filter(
                transaction_id__isnull=False
            ).select_related('customer', 'product').annotate(
                name=Concat(
                    F('customer__first_name'), Value(' '), F('customer__last_name'), output_field=CharField()
                )
            )
        except Exception as e:
            return Response(
                {
                    "success": False,
                    "message": f"An unexpected error occurred: {str(e)}.",
                    "statusCode": status.HTTP_500_INTERNAL_SERVER_ERROR,
                },
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )