Files
goodtimes/manage_subscriptions/views.py
2024-08-13 13:15:42 +05:30

916 lines
35 KiB
Python

from decimal import Decimal
import json
from django.http import HttpResponseBadRequest, HttpResponseRedirect, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
import stripe
from accounts import resource_action
from accounts.models import IAmPrincipal
from django.contrib.auth import login
import jwt
from django.utils import timezone
from django.contrib.auth import get_user_model
from manage_coupons.models import Coupon
from manage_subscriptions.forms import (
StripeProductForm,
SubscriptionForm,
PrincipalSubscriptionForm,
)
from manage_wallets.models import (
PaymentMethod,
Transaction,
TransactionStatus,
TransactionType,
)
from .models import (
Plan,
StripeProduct,
Subscription,
PrincipalSubscription,
SubscriptionStatus,
)
from django.views import generic
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse, reverse_lazy
from django.contrib import messages
from goodtimes import constants
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from django.conf import settings
from django.views.generic.base import TemplateView
from django.db.models import Q
from django.db import transaction
# Create your views here.
class SubscriptionCreateOrUpdateView(LoginRequiredMixin, generic.View):
# Set the page_name and resource
page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
# Initialize the action as ACTION_CREATE (can change based on logic)
action = resource_action.ACTION_CREATE # Default action
template_name = "manage_subscriptions/subscription_add.html"
model = Subscription
form_class = SubscriptionForm
success_url = reverse_lazy("manage_subscriptions:subscription_list")
error_message = "An error occurred while saving the data."
# Determine the success message dynamically based on whether it's an update or create
def get_success_message(self):
self.success_message = (
constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED
)
return self.success_message
# Get the object (if exists) based on URL parameter 'pk'
def get_object(self):
pk = self.kwargs.get("pk")
return get_object_or_404(self.model, pk=pk) if pk else None
# Add page_name and operation to the context
def get_context_data(self, **kwargs):
context = {
"page_name": self.page_name,
"operation": "Add" if not self.object else "Edit",
}
context.update(kwargs) # Include any additional context data passed to the view
return context
def get(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
if self.object is not None:
self.action = resource_action.ACTION_UPDATE
form = self.form_class(instance=self.object)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
if self.object is not None:
self.action = resource_action.ACTION_UPDATE
form = self.form_class(request.POST, instance=self.object)
if not form.is_valid():
print(form.errors)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
# This code ensures that only one free plan can be created by checking for existing free plans before saving a new one.
if form.cleaned_data.get("is_free"):
if self.model.objects.filter(Q(is_free=True) & Q(active=True)).exists():
messages.error(
self.request,
"A free plan is already available. Please deactivate the existing one before creating a new one.",
)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
# Processing Stripe price creation and handling free subscription
success, message = self.handle_stripe_price(form)
if not success:
messages.error(self.request, message)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
form.save()
messages.success(self.request, self.get_success_message())
return redirect(self.success_url)
def handle_stripe_price(self, form):
try:
stripe.api_key = settings.STRIPE_SECRET_KEY
stripe_product_id = (
form.instance.stripe_product.product_id
if form.instance.stripe_product
else None
)
# creating Stripe price only if the subscription is not free
if not form.cleaned_data.get("is_free") and stripe_product_id:
# Getting Stripe Product ID
stripe_product = form.instance.stripe_product
plan = form.instance.plan
# Map the Plan interval to Stripe's recurring interval
# It will only work if the plan title is 'month', 'year' 'week' or 'day'
stripe_interval = plan.title
# Create the Stripe price
stripe_price = stripe.Price.create(
unit_amount=int(
form.cleaned_data["amount"] * 100
), # Amount in cents
currency="gbp", # Adjust the currency as needed
recurring={
"interval": stripe_interval
}, # Use the interval from Plan
product=stripe_product.product_id,
)
# Assign the Stripe price ID to the subscription
form.instance.price_id = stripe_price.id
else:
form.instance.price_id = None # No price ID for free subscriptions
return True, "" # Success
except stripe.error.StripeError as e:
return False, f"Stripe error: {str(e)}"
except Exception as e:
return False, f"An error occurred: {str(e)}"
class SubscriptionView(LoginRequiredMixin, generic.ListView):
page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
action = resource_action.ACTION_READ
model = Subscription
template_name = "manage_subscriptions/subscription_list.html"
context_object_name = "subscription_obj"
def get_queryset(self):
queryset = (
super()
.get_queryset()
.filter(deleted=False, active=True)
.prefetch_related("principal_types")
)
return queryset.order_by("-created_on")
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
class SubscriptionDetailView(LoginRequiredMixin, generic.DetailView):
page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
action = resource_action.ACTION_READ
model = Subscription
template_name = "manage_subscriptions/subscription_details.html"
context_object_name = "subscription"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
class SubscriptionDeleteView(LoginRequiredMixin, generic.View):
page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
action = resource_action.ACTION_DELETE
model = Subscription
success_url = reverse_lazy("manage_subscriptions:subscription_list")
success_message = constants.RECORD_DELETED
error_message = constants.RECORD_NOT_FOUND
def get(self, request, pk):
try:
# Retrieve the subscription object
subscription = self.model.objects.get(id=pk)
# Checking if there is a Stripe Price ID associated with the subscription
stripe_price_id = subscription.price_id
if stripe_price_id:
stripe.api_key = settings.STRIPE_SECRET_KEY
try:
# Updating the Stripe price to mark it as inactive
stripe.Price.modify(stripe_price_id, active=False)
except stripe.error.StripeError as e:
# Handle Stripe errors
messages.error(request, f"Stripe error: {str(e)}")
return redirect(self.success_url)
# Updating the subscription model record
subscription.deleted = True
subscription.active = False
subscription.save()
messages.success(request, self.success_message)
except self.model.DoesNotExist:
messages.error(request, self.error_message)
return redirect(self.success_url)
class StripeProductCreateOrUpdateView(LoginRequiredMixin, generic.View):
# Set the page_name and resource
page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
# Initialize the action as ACTION_CREATE (can change based on logic)
action = resource_action.ACTION_CREATE # Default action
template_name = "manage_subscriptions/product_add.html"
model = StripeProduct
form_class = StripeProductForm
success_url = reverse_lazy("manage_subscriptions:stripe_product_list")
error_message = "An error occurred while saving the data."
# Determine the success message dynamically based on whether it's an update or create
def get_success_message(self):
self.success_message = (
constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED
)
return self.success_message
# Get the object (if exists) based on URL parameter 'pk'
def get_object(self):
pk = self.kwargs.get("pk")
return get_object_or_404(self.model, pk=pk) if pk else None
# Add page_name and operation to the context
def get_context_data(self, **kwargs):
context = {
"page_name": self.page_name,
"operation": "Add" if not self.object else "Edit",
}
context.update(kwargs) # Include any additional context data passed to the view
return context
def get(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
if self.object is not None:
self.action = resource_action.ACTION_UPDATE
form = self.form_class(instance=self.object)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
if self.object is not None:
self.action = resource_action.ACTION_UPDATE
form = self.form_class(request.POST, instance=self.object)
if not form.is_valid():
print(form.errors)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
success, message = self.handle_stripe_product(form)
if not success:
messages.error(self.request, message)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
form.save()
messages.success(self.request, self.get_success_message())
return redirect(self.success_url)
def handle_stripe_product(self, form):
try:
stripe.api_key = settings.STRIPE_SECRET_KEY
stripe_product = stripe.Product.create(
name=form.cleaned_data.get("title"),
description=form.cleaned_data.get("description"),
)
# Save Stripe Product ID to the form instance
form.instance.product_id = stripe_product.id
return True, "" # Success
except stripe.error.StripeError as e:
return False, f"Stripe error: {str(e)}"
except Exception as e:
return False, f"An error occurred: {str(e)}"
class StripeProductView(LoginRequiredMixin, generic.ListView):
page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
action = resource_action.ACTION_READ
model = StripeProduct
template_name = "manage_subscriptions/product_list.html"
context_object_name = "product_obj"
def get_queryset(self):
queryset = super().get_queryset().filter(deleted=False, active=True)
return queryset.order_by("-created_on")
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
""" we are not using product delete functionality because there may be multiple stripe's prices
attached to one product and in case of any error it will mismatch the stripe's price with
our database Subscription objects"""
# class StripeProductDeleteView(LoginRequiredMixin, generic.View):
# page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
# resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
# action = resource_action.ACTION_DELETE
# model = StripeProduct
# success_url = reverse_lazy("manage_subscriptions:stripe_product_list")
# success_message = constants.RECORD_DELETED
# error_message = constants.RECORD_NOT_FOUND
# def get(self, request, pk):
# try:
# # Retrieve the subscription object
# product = self.model.objects.get(id=pk)
# # Fetching the related subscriptions (prices)
# related_subscriptions = Subscription.objects.filter(stripe_product=product)
# # Checking if there is a Stripe Product ID associated with the subscription
# stripe_product_id = product.product_id
# if stripe_product_id:
# stripe.api_key = settings.STRIPE_SECRET_KEY
# # Deactivating related prices on Stripe first
# for subscription in related_subscriptions:
# price_id = subscription.price_id
# if price_id:
# try:
# stripe.Price.modify(price_id, active=False)
# except stripe.error.StripeError as e:
# # Handle Stripe errors
# messages.error(request, f"Stripe error: {str(e)}")
# return redirect(self.success_url)
# try:
# # Updating the Stripe price to mark it as inactive
# stripe.Product.modify(stripe_product_id, active=False)
# except stripe.error.StripeError as e:
# # Handle Stripe errors
# messages.error(request, f"Stripe error: {str(e)}")
# return redirect(self.success_url)
# # Updating the subscription model record
# product.deleted = True
# product.active = False
# product.save()
# messages.success(request, self.success_message)
# except self.model.DoesNotExist:
# messages.error(request, self.error_message)
# return redirect(self.success_url)
# class PlanCreateOrUpdateView(LoginRequiredMixin, generic.View):
# # Set the page_name and resource
# page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
# resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
# # Initialize the action as ACTION_CREATE (can change based on logic)
# action = resource_action.ACTION_CREATE # Default action
# template_name = "manage_subscriptions/plan_add.html"
# model = Plan
# form_class = PlanForm
# success_url = reverse_lazy("manage_subscriptions:plan_list")
# error_message = "An error occurred while saving the data."
# # Determine the success message dynamically based on whether it's an update or create
# def get_success_message(self):
# self.success_message = (
# constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED
# )
# return self.success_message
# # Get the object (if exists) based on URL parameter 'pk'
# def get_object(self):
# pk = self.kwargs.get("pk")
# return get_object_or_404(self.model, pk=pk) if pk else None
# # Add page_name and operation to the context
# def get_context_data(self, **kwargs):
# context = {
# "page_name": self.page_name,
# "operation": "Add" if not self.object else "Edit",
# }
# context.update(kwargs) # Include any additional context data passed to the view
# return context
# def get(self, request, *args, **kwargs):
# self.object = self.get_object()
# # If an object is found, change action to ACTION_UPDATE
# if self.object is not None:
# self.action = resource_action.ACTION_UPDATE
# form = self.form_class(instance=self.object)
# context = self.get_context_data(form=form)
# return render(request, self.template_name, context=context)
# def post(self, request, *args, **kwargs):
# self.object = self.get_object()
# # If an object is found, change action to ACTION_UPDATE
# if self.object is not None:
# self.action = resource_action.ACTION_UPDATE
# form = self.form_class(request.POST, instance=self.object)
# if not form.is_valid():
# print(form.errors)
# context = self.get_context_data(form=form)
# return render(request, self.template_name, context=context)
# form.save()
# messages.success(self.request, self.get_success_message())
# return redirect(self.success_url)
class PlanView(LoginRequiredMixin, generic.ListView):
page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
action = resource_action.ACTION_READ
model = Plan
template_name = "manage_subscriptions/plan_list.html"
context_object_name = "plan_obj"
def get_queryset(self):
return super().get_queryset().filter(deleted=False)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
# class PlanDeleteView(LoginRequiredMixin, generic.View):
# page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
# resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
# action = resource_action.ACTION_DELETE
# model = Plan
# success_url = reverse_lazy("manage_subscriptions:plan_list")
# success_message = constants.RECORD_DELETED
# error_message = constants.RECORD_NOT_FOUND
# def get(self, request, pk):
# try:
# type_obj = self.model.objects.get(id=pk)
# type_obj.deleted = True
# type_obj.active = False
# type_obj.save()
# messages.success(request, self.success_message)
# except self.model.DoesNotExist:
# messages.success(request, self.error_message)
# return redirect(self.success_url)
class PrincipalSubscriptionCreateOrUpdateView(LoginRequiredMixin, generic.View):
# Set the page_name and resource
page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
# Initialize the action as ACTION_CREATE (can change based on logic)
action = resource_action.ACTION_CREATE # Default action
template_name = "manage_subscriptions/principal_subscription_add.html"
model = PrincipalSubscription
form_class = PrincipalSubscriptionForm
success_url = reverse_lazy("manage_subscriptions:principal_subscriptions_list")
error_message = "An error occurred while saving the data."
# Determine the success message dynamically based on whether it's an update or create
def get_success_message(self):
self.success_message = (
constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED
)
return self.success_message
# Get the object (if exists) based on URL parameter 'pk'
def get_object(self):
pk = self.kwargs.get("pk")
return get_object_or_404(self.model, pk=pk) if pk else None
# Add page_name and operation to the context
def get_context_data(self, **kwargs):
context = {
"page_name": self.page_name,
"operation": "Add" if not self.object else "Edit",
}
context.update(kwargs) # Include any additional context data passed to the view
return context
def get(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
if self.object is not None:
self.action = resource_action.ACTION_UPDATE
form = self.form_class(instance=self.object)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
if self.object is not None:
self.action = resource_action.ACTION_UPDATE
form = self.form_class(request.POST, instance=self.object)
if not form.is_valid():
print(form.errors)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
form.save()
messages.success(self.request, self.get_success_message())
return redirect(self.success_url)
class PrincipalSubscriptionView(LoginRequiredMixin, generic.ListView):
page_name = resource_action.RESOURCE_PRINCIPAL_SUBSCRIPTIONS
resource = resource_action.RESOURCE_PRINCIPAL_SUBSCRIPTIONS
action = resource_action.ACTION_READ
model = PrincipalSubscription
template_name = "manage_subscriptions/principal_subscriptions_list.html"
context_object_name = "principal_subscription_obj"
def get_queryset(self):
return super().get_queryset().all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
class PrincipalSubscriptionDetailView(LoginRequiredMixin, generic.DetailView):
page_name = resource_action.RESOURCE_PRINCIPAL_SUBSCRIPTIONS
resource = resource_action.RESOURCE_PRINCIPAL_SUBSCRIPTIONS
action = resource_action.ACTION_READ
model = PrincipalSubscription
template_name = "manage_subscriptions/principal_subscription_details.html"
context_object_name = "principal_subscription_obj"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
class PrincipalSubscriptionDeleteView(LoginRequiredMixin, generic.View):
page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
action = resource_action.ACTION_DELETE
model = PrincipalSubscription
success_url = reverse_lazy("manage_subscriptions:principal_subscriptions_list")
success_message = constants.RECORD_DELETED
error_message = constants.RECORD_NOT_FOUND
def get(self, request, pk):
try:
type_obj = self.model.objects.get(id=pk)
type_obj.deleted = True
type_obj.active = False
type_obj.save()
messages.success(request, self.success_message)
except self.model.DoesNotExist:
messages.success(request, self.error_message)
return redirect(self.success_url)
class SubscriptionPageView(TemplateView):
template_name = "stripe_html/index.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
request = self.request
if request.user.is_authenticated:
print("request.user: ", request.user)
subscriptions = Subscription.objects.filter(
principal_types=request.user.principal_type,
active=True,
deleted=False,
is_free=False,
)
if subscriptions.exists():
context["subscriptions"] = subscriptions
context["stripeCheckoutUrl"] = settings.STRIPE_CHECKOUT_URL
context["stripeFinalUrl"] = settings.STRIPE_FINAL_URL
context["couponValidityCheckUrl"] = settings.COUPON_VALIDITY_CHECK_URL
else:
# Handling the case where no subscriptions are found for the principal type.
context["error"] = "No subscriptions found for your user type."
return context
class ActiveSubscriptionView(TemplateView):
template_name = "stripe_html/active_subscription.html"
def get(self, request, *args, **kwargs):
token = request.GET.get("token") or request.session.get("jwt")
print("token: ", token)
if token:
request.session["jwt"] = token
print("request.session: ", request.session)
try:
# Decode and validate token
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
print("payload: ", payload)
user = get_user_model().objects.get(id=payload["user_id"])
# Manually specify the authentication backend
user.backend = "django.contrib.auth.backends.ModelBackend"
# Log the user in
login(request, user)
print("Logged in user: ", user)
except (
IAmPrincipal.DoesNotExist,
jwt.ExpiredSignatureError,
jwt.InvalidTokenError,
):
return HttpResponseBadRequest("Invalid token or user not found")
today = timezone.now().date()
if request.user.is_authenticated:
latest_subscription = PrincipalSubscription.objects.filter(
principal=request.user,
is_paid=True,
deleted=False,
end_date__gte=today,
).order_by('-end_date').last()
if not latest_subscription:
return HttpResponseRedirect(reverse("manage_subscriptions:stripe"))
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
request = self.request
today = timezone.now().date()
if request.user.is_authenticated:
latest_subscription = PrincipalSubscription.objects.filter(
principal=request.user,
is_paid=True,
deleted=False,
end_date__gte=today,
).order_by('-end_date').last()
context["active_subscription"] = latest_subscription
return context
class CancelSubscriptionView(LoginRequiredMixin, generic.View):
def post(self, request, *args, **kwargs):
subscription_id = request.POST.get("subscription_id")
try:
subscription = PrincipalSubscription.objects.get(
id=subscription_id, principal=request.user
)
except PrincipalSubscription.DoesNotExist:
messages.error(request, "Subscription not found.")
return redirect("manage_subscriptions:cancel")
try:
with transaction.atomic():
if subscription.is_stripe_subscription:
# Cancel Stripe subscription
stripe.Subscription.modify(
subscription.stripe_subscription_id, cancel_at_period_end=True
)
# Updating subscription status in the local database
subscription.status = SubscriptionStatus.INACTIVE
subscription.cancelled = True
subscription.active = False
subscription.cancelled_date_time = timezone.now()
subscription.save()
messages.success(request, "Subscription cancelled successfully.")
return redirect("manage_subscriptions:subscription_cancel_success")
except stripe.error.InvalidRequestError as e:
messages.error(request, f"Stripe error: {str(e)}")
return redirect("manage_subscriptions:subscription_cancel_fails")
@csrf_exempt
def stripe_config(request):
if request.method == "GET":
stripe_config = {"publicKey": settings.STRIPE_PUBLISH_KEY}
return JsonResponse(stripe_config, safe=False)
@csrf_exempt
@require_POST
def validate_coupon(request):
data = json.loads(request.body)
coupon_code = data.get("couponCode", None)
subscription_id = data.get("subscriptionId", None)
final_amount = None
try:
subscription = Subscription.objects.get(id=subscription_id)
except Subscription.DoesNotExist:
return JsonResponse({"error": "Subscription not found."}, status=404)
# If no coupon code is provided, assume no discount and proceed
if not coupon_code:
return JsonResponse({"message": "No coupon code provided."}, status=200)
# Validating Coupon
try:
coupon = Coupon.objects.get(coupon_code=coupon_code)
if not coupon.is_valid():
return JsonResponse({"error": "Coupon is not valid."}, status=400)
# Check discount amount
if coupon.discount_amount and coupon.discount_amount > subscription.amount:
final_amount = subscription.amount - coupon.discount_amount
return JsonResponse(
{"error": "Coupon discount amount exceeds subscription amount."},
status=400,
)
# Check discount percentage
if coupon.discount_percentage:
discount = (
coupon.discount_percentage / Decimal("100")
) * subscription.amount
if discount > subscription.amount:
return JsonResponse(
{
"error": "Coupon discount percentage exceeds subscription amount."
},
status=400,
)
final_amount = subscription.amount - discount
# Retrieving coupon from Stripe if applicable
if coupon.coupon_id:
try:
stripe_coupon = stripe.Coupon.retrieve(coupon.coupon_id)
print("stripe_coupon: ", stripe_coupon)
if (
stripe_coupon.max_redemptions
and stripe_coupon.times_redeemed >= stripe_coupon.max_redemptions
):
return JsonResponse(
{"error": "Coupon max redeems reached."}, status=400
)
return JsonResponse(
{"data": {"coupon": stripe_coupon, "finalAmount": final_amount}},
status=200,
)
except stripe.error.InvalidRequestError:
return JsonResponse(
{"error": f"Invalid coupon code: {coupon_code}"}, status=400
)
except stripe.error.StripeError as e:
return JsonResponse({"error": f"Stripe error: {str(e)}"}, status=400)
else:
return JsonResponse(
{
"error": "Coupon is either invalid, expired, or not associated with Stripe."
},
status=400,
)
except Coupon.DoesNotExist:
return JsonResponse({"error": "Coupon not found."}, status=404)
@csrf_exempt
@require_POST
def create_checkout_session(request):
stripe.api_key = settings.STRIPE_SECRET_KEY
data = json.loads(request.body)
subscription_id = data.get("subscriptionId")
coupon_code = data.get("couponCode")
transaction_amount = data.get("discountAmount")
principal_id = request.user.id
try:
subscription = Subscription.objects.get(id=subscription_id)
except Subscription.DoesNotExist:
return JsonResponse({"error": "Subscription not found."}, status=404)
# Default transaction amount based on subscription amount
print("Before Session Data")
session_data = {
"payment_method_types": ["card"],
"success_url": request.build_absolute_uri("/subscriptions/success/"),
"cancel_url": request.build_absolute_uri("/subscriptions/cancel/"),
"metadata": {
"transaction_amount": str(transaction_amount),
"principal": str(request.user.id),
"subscription_id": str(subscription.id),
"product_id": str(
subscription.stripe_product.product_id
if subscription.stripe_product
else None
),
"couponCode": coupon_code if coupon_code else None,
},
}
print("session_data: ", session_data)
# Coupon Handling
if coupon_code:
try:
stripe_coupon = stripe.Coupon.retrieve(coupon_code)
session_data["discounts"] = [{"coupon": stripe_coupon.id}]
except stripe.error.InvalidRequestError:
return JsonResponse(
{"error": f"Invalid coupon code: {coupon_code}"}, status=400
)
except stripe.error.StripeError as e:
return JsonResponse({"error": f"Stripe error: {str(e)}"}, status=400)
# Creating the Stripe Checkout Session
try:
if subscription.price_id:
session_data["line_items"] = [
{
"price": subscription.price_id,
"quantity": 1,
}
]
session_data["mode"] = "subscription"
session_data["subscription_data"] = {
"metadata": session_data["metadata"],
}
else:
session_data["line_items"] = [
{
"price_data": {
"currency": "gbp",
"product_data": {
"name": subscription.title,
"description": subscription.short_description,
},
"unit_amount": int(subscription.amount * 100),
},
"quantity": 1,
}
]
session_data["mode"] = "payment"
checkout_session = stripe.checkout.Session.create(**session_data)
return JsonResponse({"sessionId": checkout_session["id"]})
except Exception as e:
return JsonResponse({"error": str(e)}, status=500)
class SuccessView(TemplateView):
template_name = "stripe_html/success.html"
class CancelView(TemplateView):
template_name = "stripe_html/cancel.html"
class SubscriptionCancelSuccessView(TemplateView):
template_name = "stripe_html/subscription_cancel_success.html"
class SubscriptionCancelFailsView(TemplateView):
template_name = "stripe_html/subscription_cancel_fails.html"