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)