Files
goodtimes/manage_subscriptions/views.py

925 lines
36 KiB
Python
Raw Normal View History

from decimal import Decimal
2024-04-07 16:45:22 +05:30
import json
2024-08-12 14:49:48 +05:30
from django.http import HttpResponseBadRequest, HttpResponseRedirect, JsonResponse
2024-02-29 13:25:50 +05:30
from django.shortcuts import get_object_or_404, redirect, render
2024-03-07 18:06:57 +05:30
import stripe
2024-02-29 13:25:50 +05:30
from accounts import resource_action
2024-03-07 18:06:57 +05:30
from accounts.models import IAmPrincipal
from django.contrib.auth import login
2024-03-07 18:06:57 +05:30
import jwt
from django.utils import timezone
from django.contrib.auth import get_user_model
from manage_coupons.models import Coupon
2024-02-29 13:25:50 +05:30
from manage_subscriptions.forms import (
2024-07-31 13:12:17 +05:30
StripeProductForm,
2024-02-29 13:25:50 +05:30
SubscriptionForm,
PrincipalSubscriptionForm,
)
2024-03-07 18:06:57 +05:30
from manage_wallets.models import (
PaymentMethod,
Transaction,
TransactionStatus,
TransactionType,
)
2024-08-12 12:54:29 +05:30
from .models import (
Plan,
StripeProduct,
Subscription,
PrincipalSubscription,
SubscriptionStatus,
)
2024-02-29 13:25:50 +05:30
from django.views import generic
from django.contrib.auth.mixins import LoginRequiredMixin
2024-08-12 14:49:48 +05:30
from django.urls import reverse, reverse_lazy
2024-02-29 13:25:50 +05:30
from django.contrib import messages
from goodtimes import constants
2024-03-07 18:06:57 +05:30
from django.views.decorators.csrf import csrf_exempt
2024-04-07 16:45:22 +05:30
from django.views.decorators.http import require_POST
2024-03-07 18:06:57 +05:30
from django.conf import settings
from django.views.generic.base import TemplateView
from django.db.models import Q
2024-08-12 13:47:12 +05:30
from django.db import transaction
2024-07-26 16:27:41 +05:30
2024-02-29 13:25:50 +05:30
# 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)
2024-07-31 13:12:17 +05:30
# 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)
2024-02-29 13:25:50 +05:30
form.save()
messages.success(self.request, self.get_success_message())
return redirect(self.success_url)
2024-07-31 13:12:17 +05:30
def handle_stripe_price(self, form):
try:
stripe.api_key = settings.STRIPE_SECRET_KEY
2024-08-02 18:13:16 +05:30
stripe_product_id = (
form.instance.stripe_product.product_id
if form.instance.stripe_product
else None
)
2024-07-31 13:12:17 +05:30
# creating Stripe price only if the subscription is not free
2024-08-02 16:20:04 +05:30
if not form.cleaned_data.get("is_free") and stripe_product_id:
2024-07-31 13:12:17 +05:30
# 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)}"
2024-02-29 13:25:50 +05:30
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")
2024-02-29 13:25:50 +05:30
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
2024-08-12 15:31:41 +05:30
class SubscriptionDetailView(LoginRequiredMixin, generic.DetailView):
2024-08-12 12:54:29 +05:30
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
2024-07-31 13:12:17 +05:30
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
2024-03-15 15:57:09 +05:30
2024-07-31 13:12:17 +05:30
def get(self, request, pk):
try:
# Retrieve the subscription object
subscription = self.model.objects.get(id=pk)
2024-03-15 15:57:09 +05:30
2024-07-31 13:12:17 +05:30
# 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
2024-07-31 15:50:35 +05:30
success_url = reverse_lazy("manage_subscriptions:stripe_product_list")
2024-07-31 13:12:17 +05:30
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)
2024-07-31 15:50:35 +05:30
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)
2024-07-31 13:12:17 +05:30
form.save()
messages.success(self.request, self.get_success_message())
return redirect(self.success_url)
2024-07-31 15:50:35 +05:30
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)}"
2024-07-31 13:12:17 +05:30
class StripeProductView(LoginRequiredMixin, generic.ListView):
page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS
action = resource_action.ACTION_READ
2024-07-31 20:18:11 +05:30
model = StripeProduct
2024-07-31 13:12:17 +05:30
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
2024-08-12 12:54:29 +05:30
""" 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
2024-07-31 13:12:17 +05:30
2024-08-12 12:54:29 +05:30
# 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()
2024-07-31 13:12:17 +05:30
2024-08-12 12:54:29 +05:30
# messages.success(request, self.success_message)
2024-07-31 13:12:17 +05:30
2024-08-12 12:54:29 +05:30
# except self.model.DoesNotExist:
# messages.error(request, self.error_message)
2024-07-31 13:12:17 +05:30
2024-08-12 12:54:29 +05:30
# return redirect(self.success_url)
2024-03-15 15:57:09 +05:30
# 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)
2024-02-29 13:25:50 +05:30
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
2024-03-15 15:57:09 +05:30
# 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
2024-02-29 13:25:50 +05:30
2024-03-15 15:57:09 +05:30
# 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)
2024-02-29 13:25:50 +05:30
2024-03-15 15:57:09 +05:30
# return redirect(self.success_url)
2024-02-29 13:25:50 +05:30
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):
2024-03-15 18:51:46 +05:30
page_name = resource_action.RESOURCE_PRINCIPAL_SUBSCRIPTIONS
resource = resource_action.RESOURCE_PRINCIPAL_SUBSCRIPTIONS
2024-02-29 13:25:50 +05:30
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):
2024-03-15 17:12:06 +05:30
return super().get_queryset().all()
2024-02-29 13:25:50 +05:30
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
2024-08-12 15:31:41 +05:30
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
2024-02-29 13:25:50 +05:30
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)
2024-03-07 18:06:57 +05:30
return redirect(self.success_url)
class SubscriptionPageView(TemplateView):
template_name = "stripe_html/index.html"
2024-03-07 18:06:57 +05:30
2024-08-12 12:54:29 +05:30
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"
2024-03-07 18:06:57 +05:30
def get(self, request, *args, **kwargs):
2024-05-24 20:54:35 +05:30
token = request.GET.get("token") or request.session.get("jwt")
2024-03-07 18:06:57 +05:30
print("token: ", token)
if token:
2024-05-24 21:16:30 +05:30
request.session["jwt"] = token
2024-05-24 20:54:35 +05:30
print("request.session: ", request.session)
2024-03-07 18:06:57 +05:30
try:
# Decode and validate token
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
print("payload: ", payload)
2024-08-12 14:49:48 +05:30
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")
2024-08-12 14:58:44 +05:30
today = timezone.now().date()
if request.user.is_authenticated:
active_subscription = (
PrincipalSubscription.objects.filter(
principal=request.user,
is_paid=True,
cancelled=False,
deleted=False,
active=True,
status=SubscriptionStatus.ACTIVE,
)
.select_related("principal") # Optimize query
.order_by("-grace_period_end_date")
.first()
)
if not active_subscription or active_subscription.end_date < today:
return HttpResponseRedirect(reverse("manage_subscriptions:stripe"))
2024-03-07 18:06:57 +05:30
return super().get(request, *args, **kwargs)
2024-03-28 16:55:11 +05:30
def get_context_data(self, **kwargs):
2024-08-12 14:58:44 +05:30
context = super().get_context_data(**kwargs)
2024-03-28 16:55:11 +05:30
request = self.request
2024-08-12 13:47:12 +05:30
today = timezone.now().date()
2024-03-28 16:55:11 +05:30
if request.user.is_authenticated:
2024-08-12 12:54:29 +05:30
active_subscription = (
PrincipalSubscription.objects.filter(
principal=request.user,
is_paid=True,
cancelled=False,
deleted=False,
active=True,
status=SubscriptionStatus.ACTIVE,
)
2024-08-12 14:49:48 +05:30
.select_related("principal") # Optimize query
2024-08-12 12:54:29 +05:30
.order_by("-grace_period_end_date")
.first()
2024-04-07 16:45:22 +05:30
)
2024-08-12 14:58:44 +05:30
context["active_subscription"] = active_subscription
return context
2024-03-28 16:55:11 +05:30
2024-03-07 18:06:57 +05:30
2024-08-12 13:47:12 +05:30
class CancelSubscriptionView(LoginRequiredMixin, generic.View):
def post(self, request, *args, **kwargs):
subscription_id = request.POST.get("subscription_id")
try:
subscription = PrincipalSubscription.objects.get(
2024-08-12 15:33:01 +05:30
stripe_subscription_id=subscription_id, principal=request.user
2024-08-12 13:47:12 +05:30
)
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:success")
except stripe.error.InvalidRequestError as e:
messages.error(request, f"Stripe error: {str(e)}")
return redirect("manage_subscriptions:cancel")
2024-03-07 18:06:57 +05:30
@csrf_exempt
def stripe_config(request):
if request.method == "GET":
2024-07-31 13:12:17 +05:30
stripe_config = {"publicKey": settings.STRIPE_PUBLISH_KEY}
2024-03-07 18:06:57 +05:30
return JsonResponse(stripe_config, safe=False)
2024-07-19 21:18:18 +05:30
@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)
2024-07-19 21:18:18 +05:30
2024-07-26 16:27:41 +05:30
# 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
2024-07-26 16:27:41 +05:30
try:
coupon = Coupon.objects.get(coupon_code=coupon_code)
2024-08-01 14:01:00 +05:30
2024-07-26 16:27:41 +05:30
if not coupon.is_valid():
return JsonResponse({"error": "Coupon is not valid."}, status=400)
2024-08-01 14:01:00 +05:30
# Check discount amount
2024-07-26 16:27:41 +05:30
if coupon.discount_amount and coupon.discount_amount > subscription.amount:
final_amount = subscription.amount - coupon.discount_amount
2024-07-26 16:27:41 +05:30
return JsonResponse(
{"error": "Coupon discount amount exceeds subscription amount."},
status=400,
)
2024-08-01 14:01:00 +05:30
# Check discount percentage
if coupon.discount_percentage:
discount = (
2024-08-01 14:01:00 +05:30
coupon.discount_percentage / Decimal("100")
) * subscription.amount
if discount > subscription.amount:
2024-08-01 14:01:00 +05:30
return JsonResponse(
{
"error": "Coupon discount percentage exceeds subscription amount."
},
status=400,
)
final_amount = subscription.amount - discount
2024-08-01 14:01:00 +05:30
# Retrieving coupon from Stripe if applicable
if coupon.coupon_id:
try:
stripe_coupon = stripe.Coupon.retrieve(coupon.coupon_id)
2024-08-01 15:48:06 +05:30
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
)
2024-08-12 12:54:29 +05:30
return JsonResponse(
{"data": {"coupon": stripe_coupon, "finalAmount": final_amount}},
status=200,
)
2024-08-01 14:01:00 +05:30
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:
2024-07-26 16:27:41 +05:30
return JsonResponse(
2024-08-01 14:01:00 +05:30
{
"error": "Coupon is either invalid, expired, or not associated with Stripe."
},
2024-07-26 16:27:41 +05:30
status=400,
)
except Coupon.DoesNotExist:
return JsonResponse({"error": "Coupon not found."}, status=404)
2024-05-24 15:59:39 +05:30
2024-03-07 18:06:57 +05:30
@csrf_exempt
2024-04-07 16:45:22 +05:30
@require_POST
2024-03-07 18:06:57 +05:30
def create_checkout_session(request):
2024-07-31 13:12:17 +05:30
stripe.api_key = settings.STRIPE_SECRET_KEY
2024-04-07 16:45:22 +05:30
data = json.loads(request.body)
2024-07-31 20:10:21 +05:30
subscription_id = data.get("subscriptionId")
coupon_code = data.get("couponCode")
transaction_amount = data.get("discountAmount")
2024-05-24 15:59:39 +05:30
principal_id = request.user.id
2024-04-07 16:45:22 +05:30
try:
subscription = Subscription.objects.get(id=subscription_id)
except Subscription.DoesNotExist:
2024-05-24 21:16:30 +05:30
return JsonResponse({"error": "Subscription not found."}, status=404)
2024-03-18 13:22:10 +05:30
2024-07-31 20:10:21 +05:30
order_id = f"order_{timezone.localtime().timestamp()}_{request.user.email}"
# Default transaction amount based on subscription amount
2024-08-01 21:03:01 +05:30
print("Before Session Data")
2024-07-31 20:10:21 +05:30
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),
2024-07-31 20:10:21 +05:30
"principal": str(request.user.id),
"order_id": order_id,
"subscription_id": str(subscription.id),
"product_id": str(
subscription.stripe_product.product_id
if subscription.stripe_product
else None
),
2024-07-31 20:10:21 +05:30
"couponCode": coupon_code if coupon_code else None,
},
}
2024-08-01 21:03:01 +05:30
print("session_data: ", session_data)
2024-07-31 20:10:21 +05:30
# Coupon Handling
if coupon_code:
try:
2024-08-01 16:23:49 +05:30
stripe_coupon = stripe.Coupon.retrieve(coupon_code)
2024-08-01 15:48:06 +05:30
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:
2024-08-01 16:23:49 +05:30
return JsonResponse({"error": f"Stripe error: {str(e)}"}, status=400)
2024-07-31 20:10:21 +05:30
# Creating the Stripe Checkout Session
2024-04-07 16:45:22 +05:30
try:
2024-07-31 20:10:21 +05:30
if subscription.price_id:
session_data["line_items"] = [
2024-04-07 16:45:22 +05:30
{
2024-07-31 20:10:21 +05:30
"price": subscription.price_id,
2024-04-07 16:45:22 +05:30
"quantity": 1,
}
2024-07-31 20:10:21 +05:30
]
2024-08-01 13:21:10 +05:30
session_data["mode"] = "subscription"
2024-08-02 19:16:11 +05:30
session_data["subscription_data"] = {
2024-08-02 19:14:51 +05:30
"metadata": session_data["metadata"],
2024-08-02 18:13:16 +05:30
}
2024-07-31 20:10:21 +05:30
else:
session_data["line_items"] = [
{
"price_data": {
2024-08-05 18:40:34 +05:30
"currency": "gbp",
2024-07-31 20:10:21 +05:30
"product_data": {
"name": subscription.title,
"description": subscription.short_description,
},
2024-08-01 16:23:49 +05:30
"unit_amount": int(subscription.amount * 100),
2024-07-31 20:10:21 +05:30
},
"quantity": 1,
}
]
2024-08-01 13:21:10 +05:30
session_data["mode"] = "payment"
2024-07-31 20:10:21 +05:30
checkout_session = stripe.checkout.Session.create(**session_data)
2024-04-07 16:45:22 +05:30
return JsonResponse({"sessionId": checkout_session["id"]})
except Exception as e:
2024-07-31 20:10:21 +05:30
return JsonResponse({"error": str(e)}, status=500)
2024-03-07 18:06:57 +05:30
class SuccessView(TemplateView):
2024-03-07 19:19:26 +05:30
template_name = "stripe_html/success.html"
2024-03-07 18:06:57 +05:30
class CancelView(TemplateView):
2024-03-07 19:19:26 +05:30
template_name = "stripe_html/cancel.html"