refactor(subscription): removed unnecessary field and complexity
This commit is contained in:
@@ -26,15 +26,14 @@ class SubscriptionForm(forms.ModelForm):
|
||||
model = Subscription
|
||||
fields = [
|
||||
"title",
|
||||
"stripe_product",
|
||||
"plan",
|
||||
"short_description",
|
||||
"interval",
|
||||
"interval_count",
|
||||
"high_amount",
|
||||
"amount",
|
||||
"short_description",
|
||||
"principal_types",
|
||||
"referral_percentage",
|
||||
"active",
|
||||
"deleted",
|
||||
"is_free",
|
||||
]
|
||||
|
||||
@@ -46,19 +45,6 @@ class SubscriptionForm(forms.ModelForm):
|
||||
id__in=[event_user.id, event_manager.id]
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
stripe_product = cleaned_data.get("stripe_product")
|
||||
|
||||
if not stripe_product:
|
||||
self.add_error(
|
||||
"stripe_product",
|
||||
"Please select a Stripe product to create a subscription.",
|
||||
)
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class PrincipalSubscriptionForm(forms.ModelForm):
|
||||
class Meta:
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.0.2 on 2024-08-18 18:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('manage_subscriptions', '0010_principalsubscription_comments_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='product_id',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 5.0.2 on 2024-08-19 09:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('manage_subscriptions', '0011_subscription_product_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='interval',
|
||||
field=models.CharField(choices=[('month', 'month'), ('day', 'day'), ('week', 'week'), ('year', 'year')], default='month', max_length=10),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='interval_count',
|
||||
field=models.IntegerField(default=1),
|
||||
),
|
||||
]
|
||||
@@ -35,8 +35,20 @@ class StripeProduct(BaseModel):
|
||||
|
||||
|
||||
class Subscription(BaseModel):
|
||||
MONTH = "month"
|
||||
DAY = "day"
|
||||
WEEK = "week"
|
||||
YEAR = "year"
|
||||
|
||||
INTERVAL_TYPES = [
|
||||
(MONTH, "month"),
|
||||
(DAY, "day"),
|
||||
(WEEK, "week"),
|
||||
(YEAR, "year"),
|
||||
]
|
||||
title = models.CharField(max_length=255)
|
||||
price_id = models.CharField(max_length=255, blank=True, null=True)
|
||||
product_id = models.CharField(max_length=255, blank=True, null=True)
|
||||
stripe_product = models.ForeignKey(
|
||||
StripeProduct,
|
||||
related_name="subscription_product",
|
||||
@@ -50,6 +62,8 @@ class Subscription(BaseModel):
|
||||
plan = models.ForeignKey(
|
||||
Plan, related_name="subscription_plan", on_delete=models.CASCADE
|
||||
)
|
||||
interval = models.CharField(max_length=10, choices=INTERVAL_TYPES)
|
||||
interval_count = models.IntegerField(default=1)
|
||||
high_amount = models.DecimalField(max_digits=14, decimal_places=2, default=0.00)
|
||||
amount = models.DecimalField(max_digits=14, decimal_places=2, default=0.00)
|
||||
principal_types = models.ManyToManyField(
|
||||
@@ -69,25 +83,67 @@ class Subscription(BaseModel):
|
||||
|
||||
def clean(self):
|
||||
# Ensure amount is greater than 1
|
||||
if self.amount <= 1:
|
||||
raise ValidationError({"amount": "Amount must be greater than 1."})
|
||||
if not self.delete:
|
||||
if self.amount <= 1:
|
||||
raise ValidationError({"amount": "Amount must be greater than 1."})
|
||||
|
||||
# Ensure high_amount is greater than amount
|
||||
if self.high_amount <= self.amount:
|
||||
raise ValidationError(
|
||||
{"high_amount": "High amount must be greater than amount."}
|
||||
)
|
||||
|
||||
# Ensure stripe_product is compulsory present
|
||||
# if not self.stripe_product:
|
||||
# raise ValidationError(
|
||||
# {"stripe_product": "Please select stripe product to create subscription."}
|
||||
# )
|
||||
# Ensure high_amount is greater than amount
|
||||
if self.high_amount <= self.amount:
|
||||
raise ValidationError(
|
||||
{"high_amount": "High amount must be greater than amount."}
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.clean() # Call clean before saving to ensure validation
|
||||
from goodtimes.services import StripeService
|
||||
self.clean()
|
||||
if not self.is_free:
|
||||
if self.price_id:
|
||||
# Stipe dont provide to update the price record except active and deactive
|
||||
price = StripeService.retrieve_price(self.price_id)
|
||||
if not price["success"]:
|
||||
raise Exception(price['message'])
|
||||
|
||||
if self.active != price["data"].active:
|
||||
StripeService.update_price(price_id=self.price_id, active=self.active)
|
||||
else:
|
||||
# Create new product and price
|
||||
price = StripeService.create_price(
|
||||
product_data={
|
||||
"name": self.title,
|
||||
"description": self.short_description,
|
||||
},
|
||||
unit_amount=int(self.amount * 100),
|
||||
currency="gbp",
|
||||
recurring={
|
||||
"interval": self.plan.title,
|
||||
"interval_count": self.interval_count,
|
||||
},
|
||||
metadata={
|
||||
"subscription_id": self.id
|
||||
}
|
||||
|
||||
)
|
||||
if not price["success"]:
|
||||
raise Exception(price['message'])
|
||||
|
||||
# add the id in record
|
||||
self.price_id = price["data"].id
|
||||
self.product_id = price["data"].product
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def calculate_date(self):
|
||||
count = {
|
||||
self.DAY: 1,
|
||||
self.MONTH: 30, # assuming a month is 30 days
|
||||
self.YEAR: 365,
|
||||
self.WEEK: 7
|
||||
}
|
||||
|
||||
return count[self.interval] * self.interval_count
|
||||
|
||||
|
||||
|
||||
|
||||
class SubscriptionStatus(models.TextChoices):
|
||||
ACTIVE = "active", _("Active")
|
||||
@@ -139,7 +195,18 @@ class PrincipalSubscription(BaseModel):
|
||||
|
||||
@classmethod
|
||||
def has_principal_subscription(cls, principal):
|
||||
return cls.get_active_princial_subscription(principal).exists()
|
||||
return cls.get_grace_period_princial_subscription(principal).exists()
|
||||
|
||||
@classmethod
|
||||
def get_grace_period_princial_subscription(cls, principal):
|
||||
return cls.objects.filter(
|
||||
principal=principal,
|
||||
is_paid=True,
|
||||
# cancelled=False,
|
||||
active=True,
|
||||
# status=SubscriptionStatus.ACTIVE,
|
||||
grace_period_end_date__gt=timezone.now().date(),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_active_princial_subscription(cls, principal):
|
||||
@@ -147,24 +214,28 @@ class PrincipalSubscription(BaseModel):
|
||||
principal=principal,
|
||||
is_paid=True,
|
||||
# cancelled=False,
|
||||
deleted=False,
|
||||
active=True,
|
||||
# status=SubscriptionStatus.ACTIVE,
|
||||
grace_period_end_date__gt=timezone.now().date(),
|
||||
end_date__gt=timezone.now().date(),
|
||||
)
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_principal_subscription(cls, principal):
|
||||
return cls.objects.filter(
|
||||
principal=principal,
|
||||
is_paid=True,
|
||||
# cancelled=False,
|
||||
deleted=False,
|
||||
active=True,
|
||||
# status=SubscriptionStatus.ACTIVE,
|
||||
).order_by("-grace_period_end_date").first()
|
||||
|
||||
@classmethod
|
||||
def cancel_stipe_auto_renew_subscription(cls, subscription):
|
||||
subscription.status = SubscriptionStatus.INACTIVE
|
||||
subscription.auto_renew = False
|
||||
subscription.cancelled_date_time = timezone.now()
|
||||
subscription.save()
|
||||
|
||||
|
||||
class WebhookEvent(BaseModel):
|
||||
event_id = models.CharField(max_length=255, unique=True, db_index=True)
|
||||
|
||||
@@ -13,11 +13,6 @@ urlpatterns = [
|
||||
name="subscription_add",
|
||||
),
|
||||
path("subscription/<int:pk>/", views.SubscriptionDetailView.as_view(), name="subscription_detail"),
|
||||
# path(
|
||||
# "subscription/edit/<int:pk>/",
|
||||
# views.SubscriptionCreateOrUpdateView.as_view(),
|
||||
# name="subscription_edit",
|
||||
# ),
|
||||
path(
|
||||
"subscription/delete/<int:pk>",
|
||||
views.SubscriptionDeleteView.as_view(),
|
||||
@@ -32,28 +27,9 @@ urlpatterns = [
|
||||
views.StripeProductCreateOrUpdateView.as_view(),
|
||||
name="stripe_product_add",
|
||||
),
|
||||
# path(
|
||||
# "product/delete/<int:pk>",
|
||||
# views.StripeProductDeleteView.as_view(),
|
||||
# name="stripe_product_delete",
|
||||
# ),
|
||||
# PLANS
|
||||
path("plan/list/", views.PlanView.as_view(), name="plan_list"),
|
||||
# path(
|
||||
# "plan/add/",
|
||||
# views.PlanCreateOrUpdateView.as_view(),
|
||||
# name="plan_add",
|
||||
# ),
|
||||
# path(
|
||||
# "plan/edit/<int:pk>/",
|
||||
# views.PlanCreateOrUpdateView.as_view(),
|
||||
# name="plan_edit",
|
||||
# ),
|
||||
# path(
|
||||
# "plan/delete/<int:pk>",
|
||||
# views.PlanDeleteView.as_view(),
|
||||
# name="plan_delete",
|
||||
# ),
|
||||
|
||||
# Principal Subscription
|
||||
path(
|
||||
"principal_subscription/list/",
|
||||
@@ -97,7 +73,8 @@ urlpatterns = [
|
||||
),
|
||||
path("stripe/", views.SubscriptionPageView.as_view(), name="stripe"),
|
||||
path("active/", views.ActiveSubscriptionView.as_view(), name="active"),
|
||||
path("cancel-subscription/", views.CancelSubscriptionView.as_view(), name="cancel_subscription"),
|
||||
path("cancel-subscription/<int:subscription_id>", views.CancelAutoSubscriptionView.as_view(), name="cancel_subscription"),
|
||||
path("404/", views.ErrorView.as_view(), name="error"),
|
||||
path("success/", views.SuccessView.as_view(), name="success"),
|
||||
path("cancel/", views.CancelView.as_view(), name="cancel"),
|
||||
path("subscription-cancel-success/", views.SubscriptionCancelSuccessView.as_view(), name="subscription_cancel_success"),
|
||||
|
||||
@@ -9,6 +9,7 @@ from django.contrib.auth import login
|
||||
import jwt
|
||||
from django.utils import timezone
|
||||
from django.contrib.auth import get_user_model
|
||||
from goodtimes.services import StripeService
|
||||
from manage_coupons.models import Coupon
|
||||
from manage_subscriptions.forms import (
|
||||
StripeProductForm,
|
||||
@@ -112,57 +113,10 @@ class SubscriptionCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
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
|
||||
@@ -213,21 +167,6 @@ class SubscriptionDeleteView(LoginRequiredMixin, generic.View):
|
||||
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()
|
||||
@@ -346,126 +285,6 @@ class StripeProductView(LoginRequiredMixin, generic.ListView):
|
||||
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
|
||||
@@ -483,28 +302,6 @@ class PlanView(LoginRequiredMixin, generic.ListView):
|
||||
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
|
||||
@@ -621,117 +418,135 @@ class PrincipalSubscriptionDeleteView(LoginRequiredMixin, generic.View):
|
||||
return redirect(self.success_url)
|
||||
|
||||
|
||||
class SubscriptionPageView(TemplateView):
|
||||
class SubscriptionPageView(generic.View):
|
||||
template_name = "stripe_html/index.html"
|
||||
model = Subscription
|
||||
error_url = reverse_lazy("manage_subscriptions:error")
|
||||
|
||||
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,
|
||||
)
|
||||
def get(self, request):
|
||||
if not request.user.is_authenticated:
|
||||
return HttpResponseRedirect(self.error_url)
|
||||
|
||||
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
|
||||
print("request user is :", request.user)
|
||||
obj = self.model.objects.filter(
|
||||
principal_types=request.user.principal_type,
|
||||
active=True,
|
||||
is_free=False,
|
||||
)
|
||||
|
||||
if not obj.exists():
|
||||
print(f"No pre-define subscription details found in {self.model} table for user_type {request.user.principal_type}")
|
||||
return HttpResponseRedirect(self.error_url)
|
||||
|
||||
class ActiveSubscriptionView(TemplateView):
|
||||
context = {
|
||||
"subscriptions": obj,
|
||||
# "stripeCheckoutUrl": request.build_absolute_uri(reverse("manage_subscriptions:create_checkout_session")),
|
||||
# "couponValidityCheckUrl": request.build_absolute_uri(reverse("manage_subscriptions:validate_coupon")),
|
||||
"stripeCheckoutUrl": settings.STRIPE_CHECKOUT_URL,
|
||||
"couponValidityCheckUrl": settings.COUPON_VALIDITY_CHECK_URL,
|
||||
"stripe_public_key": settings.STRIPE_PUBLISH_KEY
|
||||
}
|
||||
return render(request, self.template_name, context=context)
|
||||
|
||||
class ActiveSubscriptionView(generic.View):
|
||||
template_name = "stripe_html/active_subscription.html"
|
||||
model = IAmPrincipal
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
token = request.GET.get("token") or request.session.get("jwt")
|
||||
token = request.GET.get("token")
|
||||
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"])
|
||||
user = self.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")
|
||||
return HttpResponseRedirect(reverse("manage_subscriptions:error"))
|
||||
|
||||
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)
|
||||
|
||||
return render(request, self.template_name, context={"subscription": latest_subscription})
|
||||
return HttpResponseRedirect(reverse("manage_subscriptions:error"))
|
||||
|
||||
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 CancelAutoSubscriptionView(LoginRequiredMixin, generic.View):
|
||||
model = PrincipalSubscription
|
||||
error_url = reverse_lazy("manage_subscriptions:error")
|
||||
|
||||
|
||||
class CancelSubscriptionView(LoginRequiredMixin, generic.View):
|
||||
def post(self, request, *args, **kwargs):
|
||||
subscription_id = request.POST.get("subscription_id")
|
||||
def get(self, request, *args, **kwargs):
|
||||
subscription_id = self.kwargs.get("subscription_id")
|
||||
|
||||
try:
|
||||
subscription = PrincipalSubscription.objects.get(
|
||||
subscription = self.model.objects.get(
|
||||
id=subscription_id, principal=request.user
|
||||
)
|
||||
except PrincipalSubscription.DoesNotExist:
|
||||
except self.model.DoesNotExist:
|
||||
messages.error(request, "Subscription not found.")
|
||||
return redirect("manage_subscriptions:cancel")
|
||||
return redirect("manage_subscriptions:error")
|
||||
|
||||
try:
|
||||
with transaction.atomic():
|
||||
if subscription.is_stripe_subscription:
|
||||
# Cancel Stripe subscription
|
||||
stripe.Subscription.modify(
|
||||
subscription.stripe_subscription_id, cancel_at_period_end=True
|
||||
)
|
||||
if subscription.stripe_subscription_id:
|
||||
data = StripeService.cancel_auto_renew_subscription(subscription.stripe_subscription_id)
|
||||
if not data["success"]:
|
||||
return redirect(self.error_url)
|
||||
|
||||
# Updating subscription status in the local database
|
||||
subscription.status = SubscriptionStatus.INACTIVE
|
||||
subscription.cancelled = True
|
||||
subscription.auto_renew = False
|
||||
subscription.cancelled_date_time = timezone.now()
|
||||
subscription.save()
|
||||
self.model.cancel_stipe_auto_renew_subscription(subscription)
|
||||
|
||||
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")
|
||||
except Exception as e:
|
||||
print(f'an error occur {str(e)}')
|
||||
messages.error(request, f"An error occurred while cancelling the subscription {str(e)}")
|
||||
return redirect(self.error_url)
|
||||
|
||||
return redirect(reverse_lazy("manage_subscriptions:active"))
|
||||
|
||||
# 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.auto_renew = 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
|
||||
@@ -826,10 +641,12 @@ def create_checkout_session(request):
|
||||
data = json.loads(request.body)
|
||||
subscription_id = data.get("subscriptionId")
|
||||
coupon_code = data.get("couponCode")
|
||||
transaction_amount = data.get("discountAmount")
|
||||
transaction_amount = data.get("finalAmount")
|
||||
is_recurring = data.get("isRecurring")
|
||||
principal_id = request.user.id
|
||||
|
||||
print(f"subscription data is {subscription_id}, {coupon_code}, { is_recurring}")
|
||||
|
||||
try:
|
||||
subscription = Subscription.objects.get(id=subscription_id)
|
||||
except Subscription.DoesNotExist:
|
||||
@@ -842,14 +659,10 @@ def create_checkout_session(request):
|
||||
"success_url": request.build_absolute_uri("/subscriptions/success/"),
|
||||
"cancel_url": request.build_absolute_uri("/subscriptions/cancel/"),
|
||||
"metadata": {
|
||||
"transaction_amount": str(transaction_amount),
|
||||
"transaction_amount": str(subscription.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
|
||||
),
|
||||
"product_id": subscription.product_id,
|
||||
"couponCode": coupon_code if coupon_code else None,
|
||||
},
|
||||
}
|
||||
@@ -900,6 +713,8 @@ def create_checkout_session(request):
|
||||
return JsonResponse({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
class ErrorView(TemplateView):
|
||||
template_name = "stripe_html/webview_404.html"
|
||||
class SuccessView(TemplateView):
|
||||
template_name = "stripe_html/success.html"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user