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/aigenerator/AI-LG-backend/Ai_logo_generation/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 .utils.stripe import StripeUtils
from .models import Transaction, Product
from rest_framework.response import Response
from authorization.models import CustomUser
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
from image_generation.models import UserCreatedLogo
import json
import random
from utils.email import email_service
from django.http import JsonResponse
from user_notification.tasks import notify_user_payment_successful, notify_user_subscription_expired, notify_user_cancel_subscription

from banner_generation.models import UserBanner

# Function to retrive all product plans
@api_view(['GET'])
def list_products(request):
    try:
        products = Product.objects.all().order_by('amount') 
        plans = []

        for product in products:
            # Base product details
            product_data = {
                "product_Id": product.product_id, 
                "name": product.name,
                "description": product.description,
                "amount": product.amount,  
                "currency": product.currency,
                "resolution" : product.resolution,
                "is_subscription": product.is_subscription,
                "is_active": product.is_active
            }

            # Conditionally add the billing_period if the product is a subscription
            if product.is_subscription and product.billing_period:
                product_data["billing_period"] = product.billing_period

            plans.append(product_data)

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

        return Response(response_data, status=status.HTTP_200_OK)

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

@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',
                # example='prod_12345'
            ),
            'logo_id': openapi.Schema(
                type=openapi.TYPE_STRING,
                description='The ID of the user-generated logo',
                # example='logo_67890'
            ),
        },
        required=['product_id'],
    )
)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def create_payment_intent(request):
    try:
        user = request.user
        if not user:
            return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED)

        customer_id = user.stripe_customer_id
        if not customer_id:
            return Response({"error": "Customer ID is not available for this user"}, status=status.HTTP_400_BAD_REQUEST)

        product_id = request.data.get('product_id')
        if not product_id:
            return Response({"error": "Product ID is required"}, status=status.HTTP_400_BAD_REQUEST)
        
        logo_id = request.data.get('logo_id')  # Get the logo ID from the request
        banner_id = request.data.get("banner_id")  # Get the banner ID from the request
        if not (logo_id or banner_id):
            return Response({"error": "Logo ID or Banner ID is required"}, status=status.HTTP_400_BAD_REQUEST)

        try:
            product = Product.objects.get(product_id=product_id)
        except Product.DoesNotExist:
            return Response({"error": "Product not found"}, status=status.HTTP_404_NOT_FOUND)
        
        if logo_id:
            try:
                UserCreatedLogo.objects.get(id=logo_id, user=user)
            except UserCreatedLogo.DoesNotExist:
                return Response({"error": "Logo not found"}, status=status.HTTP_404_NOT_FOUND)

        if banner_id:
            try:
                UserBanner.objects.get(id=banner_id, user=user)
            except UserBanner.DoesNotExist:
                return Response({"error": "Banner not found"}, status=status.HTTP_404_NOT_FOUND)


        amount_in_cents = int(product.amount * 100)
        # currency = "usd"
        currency = product.currency
        # payment_method_types = ['card', 'paypal']
        payment_method_types = ['card']


        # payment_intent = StripeUtils.create_payment_intent(customer_id, amount_in_cents, currency, payment_method_types)
        payment_metadata = {'logo_id': logo_id} if logo_id else {'banner_id': banner_id}
        payment_intent = StripeUtils.create_payment_intent(
            customer_id, 
            amount_in_cents, 
            currency, 
            payment_method_types, 
            metadata=payment_metadata  
        )
        return Response({
            "client_secret": payment_intent["client_secret"],
            "payment_intent_id": payment_intent["id"],
            "amount": amount_in_cents/100,
            "stripe_customer_id": customer_id,
            "payment_method_types": payment_intent["payment_method_types"],
            "logo_id": logo_id,
            "banner_id": banner_id,
            "success": True,
        }, status=status.HTTP_201_CREATED)

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

# 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_customer_id 

        if not customer_id:
            return Response({"error": "Customer ID is required"}, status=status.HTTP_400_BAD_REQUEST)

        # Create the SetupIntent
        setup_intent = stripe.SetupIntent.create(
            # payment_method_types=["card", "paypal"],
            payment_method_types=["card"],
            customer=customer_id,
            usage="off_session"
        )

        return Response({
            "id": setup_intent.id,
            "client_secret": setup_intent.client_secret,
            "payment_method_types": setup_intent["payment_method_types"],
            "success": True,
            "message": "SetupIntent created successfully"
        }, status=status.HTTP_201_CREATED)

    except stripe.error.StripeError as e:
        return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
    except Exception as e:
        return Response({"error": str(e)}, 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_customer_id 
        payment_method_id = request.data.get("payment_method_id")
        product_id = request.data.get("product_id")
        if not customer_id or not payment_method_id:
            return Response({"error": "Customer ID and Payment Method ID are required"}, 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)  # Retrieve from local DB
        except Product.DoesNotExist:
            return Response({"error": "Product not found"}, status=status.HTTP_404_NOT_FOUND)

        # Ensure the product has a valid price_id
        price_id = product.product_id

        # Create the subscription
        # subscription = stripe.Subscription.create(
        #     customer=customer_id,
        #     items=[{"price": price_id}], 
        # )
        # today_date = datetime.today().strftime('%Y-%m-%d')

        try:
            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',
                },
                # metadata={
                #     'description': f'Your subscription for plan {product_name}',
                #     'created_date': today_date,
                # },
                collection_method='charge_automatically',
                off_session=True,
                expand=['latest_invoice.payment_intent'],
            )
            # charge_id = subscription.get('charge')
            charge_id = subscription.latest_invoice.get("charge")
            # random_number_str = str(random.randint(1000, 9999))
            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)

            # amount = subscription.latest_invoice.get("amount_paid") / 100  
            # plan = product.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}")

            

            return Response({
            "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,
            "latest_invoice": subscription,
            "transaction_date" : subscription_start,
            "transaction_id" :transaction_id,
            "success": True,
            "message": "Subscription successful"
            }, status=status.HTTP_201_CREATED)

        except stripe.error.StripeError as error:
            raise Exception(f'Failed to create Subscription: {error.user_message}')

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

# stripe webhook
logger = logging.getLogger('payment')
stripe.api_key = settings.STRIPE_SECRET_KEY
@csrf_exempt
def stripe_webhook(request):
    # logger.debug("Stripe webhook")
    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({"error": "Invalid payload"}, status=400)
    except stripe.error.SignatureVerificationError as e:
        return JsonResponse({"error": "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)

    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 CustomUser instance using customer_id
    try:
        customer = CustomUser.objects.get(stripe_customer_id=customer_id)
    except CustomUser.DoesNotExist:
        # Handle the case where the customer doesn't exist
        return  # or log an error

    amount = payment_intent['amount'] / 100  
    status = payment_intent['status']
    
    try:
        product = Product.objects.get(amount=amount, is_subscription=False)
    except Product.DoesNotExist:
        product = None

    charge_id = payment_intent.get('latest_charge')
    # random_number = random.randint(0, 9999)
    # random_number_str = f"{random_number:04d}"
    receipt_url = None
    plan = product.name 
    
    
    if charge_id:
        charge = stripe.Charge.retrieve(charge_id)
        receipt_url = handle_charge_succeeded(charge)
    
    intent_id = payment_intent.get('id')
    
    logo_id = payment_intent.get('metadata', {}).get('logo_id')    
    banner_id = payment_intent.get('metadata', {}).get('banner_id')    
    transaction_id = f"{intent_id[-12:].upper()}" if intent_id else None
    transaction_date = datetime.now()

    service = email_service.EmailService(customer.id) 
    service.payment_successfull(amount, plan, receipt_url, transaction_id, is_subscription=False)

    if customer:
        notify_user_payment_successful.delay(customer.id, plan)
    
    # Create a transaction record
    transaction = Transaction.objects.create(
        customer=customer,
        amount=amount,
        product=product,
        status=status,
        is_subscription=False,
        receipt_url = receipt_url,
        logo=logo_id,
        transaction_id=transaction_id,
        transaction_date=transaction_date,
        charge_id=charge_id
    )
    
    if logo_id:
        try:
            user_logo = UserCreatedLogo.objects.get(id=logo_id, user=customer)
            user_logo.transaction = transaction
            user_logo.save()
        except UserCreatedLogo.DoesNotExist:
            logger.error(f"Logo with ID {logo_id} not found for customer {customer.email}")

    if banner_id:
        try:
            user_banner = UserBanner.objects.get(id=banner_id, user=customer)
            user_banner.transaction = transaction
            user_banner.save()
        except UserBanner.DoesNotExist:
            logger.error(f"Banner with ID {banner_id} not found for customer {customer.email}")

def handle_invoice_payment_succeeded(invoice):
    logger.debug("hai Invoice")
    logger.debug(invoice)
    customer_id = invoice['customer']
    
    try:
        customer = CustomUser.objects.get(stripe_customer_id=customer_id)
    except CustomUser.DoesNotExist:
        # Handle the case where the customer doesn't exist
        return  # or log an error
    charge_id = invoice.get('charge')
    # random_number = random.randint(0, 9999)
    # random_number_str = f"{random_number:04d}"
    # Retrieve the charge details from Stripe using the 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.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.EmailService(customer.id) 
    service.payment_successfull(amount, plan, receipt_url, transaction_id, is_subscription=True)

    if customer:
        notify_user_payment_successful.delay(customer.id, plan)

    # Create a transaction record for the subscription
    Transaction.objects.create(
        customer=customer,
        amount=amount,
        status=status,
        invoice_id=invoice_id,
        product=product,
        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
    )


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")
    
    customer_id = subscription['customer']
    subscription_id = subscription['id']
    try:
        customer = CustomUser.objects.get(stripe_customer_id=customer_id)
        customer.email_sent_for_3d_cards = True
        customer.save()
    except CustomUser.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)
        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.EmailService(customer.id) 
    service.send_cancel_subscription_mail()
    if customer:
        notify_user_cancel_subscription.delay(customer.id)

def handle_invoice_payment_failed(invoice):
    logger.debug("Invoice payment failed")
    
    customer_id = invoice['customer']
    invoice_id = invoice['id']
    
    try:
        customer = CustomUser.objects.get(stripe_customer_id=customer_id)
    except CustomUser.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.get(subscription_id=invoice['subscription'], customer=customer)
        transaction_record.subscription_status = 'failed'
        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}.")

def handle_payment_action_required(invoice):
    customer_id = invoice['customer']
    
    try:
        customer = CustomUser.objects.get(stripe_customer_id=customer_id)
    except CustomUser.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 not customer.email_sent_for_3d_cards:
            service = email_service.EmailService(customer.id) 
            service.send_3D_secure_card_confirmation_mail(payment_link)
            notify_user_subscription_expired.delay(customer.id)

        
        customer.email_sent_for_3d_cards = False
        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 CustomUser instance using customer_id
    try:
        customer = CustomUser.objects.get(stripe_customer_id=customer_id)
    except CustomUser.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,
        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({"error": "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({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
    except Exception as e:
        return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)


# @swagger_auto_schema(
#     method='post',
#     request_body=openapi.Schema(
#         type=openapi.TYPE_OBJECT,
#         properties={
#             'transaction_id': openapi.Schema(
#                 type=openapi.TYPE_STRING,
#                 description='The ID of the transaction',
#                 # example='txn_12345'
#             ),
#             'transaction_date': openapi.Schema(
#                 type=openapi.TYPE_STRING,
#                 description='The date of the transaction in ISO 8601 format',
#                 # example='2024-10-23T13:31:56'
#             ),
#         },
#         required=['transaction_id', 'transaction_date'],
#     ),
#     responses={
#         200: openapi.Response(
#             description='Latest transaction details retrieved and updated successfully.',
#             schema=openapi.Schema(
#                 type=openapi.TYPE_OBJECT,
#                 properties={
#                     'transaction_id': openapi.Schema(type=openapi.TYPE_STRING, description='Transaction ID'),
#                     'transaction_date': openapi.Schema(type=openapi.TYPE_STRING, description='Transaction date and time'),
#                 }
#             ),
#         ),
#         400: openapi.Response(description='Invalid input or error'),
#         401: openapi.Response(description='Unauthorized - User not authenticated'),
#         404: openapi.Response(description='Transaction not found'),
#     }
# )
# @api_view(['POST'])
# @permission_classes([IsAuthenticated])
# def update_latest_transaction(request):
    # transaction_id = request.data.get('transaction_id')
    # transaction_date = request.data.get('transaction_date')

    # user = request.user
    # if not user.is_authenticated:
    #     return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED)

    # # Validate inputs
    # if not transaction_id or not transaction_date:
    #     return Response({"error": "Both transaction_id and transaction_date are required."}, status=status.HTTP_400_BAD_REQUEST)

    # try:
    #     # Parse the transaction date to a datetime object
    #     parsed_transaction_date = datetime.fromisoformat(transaction_date)

    #     # Find the latest transaction for the logged-in user that is not a subscription
    #     transaction = Transaction.objects.filter(customer=user, is_subscription=False).order_by('-created_at').first()
        
    #     if not transaction:
    #         return Response({"error": "No transactions found for this user."}, status=status.HTTP_404_NOT_FOUND)

    #     # Update the transaction with the fetched values
    #     transaction.transaction_id = transaction_id
    #     transaction.transaction_date = parsed_transaction_date
    #     transaction.save()

    #     # Prepare response data
    #     transaction_details = {
    #         "transaction_id": transaction.transaction_id,
    #         "transaction_date": transaction.transaction_date.isoformat() + 'Z' if transaction.transaction_date else None,  # Add 'Z' for UTC
    #     }

    #     return Response(transaction_details, status=status.HTTP_200_OK)

    # except ValueError:
    #     return Response({"error": "Invalid date format. Please provide a valid ISO 8601 date."}, status=status.HTTP_400_BAD_REQUEST)
    # except Exception as e:
    #     return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)