Implement cancelling subscriptions

This commit is contained in:
Mark Veidemanis 2022-07-21 13:50:45 +01:00
parent db87a138f2
commit d40557bbba
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
7 changed files with 77 additions and 41 deletions

View File

@ -43,3 +43,4 @@ STRIPE_PUBLIC_API_KEY_PROD = "pk_prod_xxx"
STRIPE_ENDPOINT_SECRET = ""
STATIC_ROOT = ""
SECRET_KEY = "a"

View File

@ -20,7 +20,7 @@ from django.urls import include, path
from django.views.generic import TemplateView
from core.ui.views.drilldown import Drilldown
from core.views import Billing, Home, Order, Portal, Signup
from core.views import Billing, Cancel, Home, Order, Portal, Signup
from core.views.callbacks import Callback
from core.views.charts import SentimentChartJSONView, VolumeChartJSONView
@ -29,6 +29,11 @@ urlpatterns = [
path("callback", Callback.as_view(), name="callback"),
path("billing/", Billing.as_view(), name="billing"),
path("order/<str:plan_name>/", Order.as_view(), name="order"),
path(
"cancel_subscription/<str:plan_name>/",
Cancel.as_view(),
name="cancel_subscription",
),
path(
"success/", TemplateView.as_view(template_name="success.html"), name="success"
),

View File

@ -11,7 +11,6 @@
<div class="columns">
{% if block.column1 is not None %}
<div class="column">
{{ block.column1 }}
</div>
{% endif %}

View File

@ -32,7 +32,7 @@
{% endif %}
{% if plan in user_plans %}
<a class="level-item" href="/cancel/{{ plan.name }}">
<a class="level-item" href="/cancel_subscription/{{ plan.name }}">
<span class="icon is-small has-text-info">
<i class="fas fa-cancel" aria-hidden="true"></i>
</span>

View File

@ -0,0 +1,9 @@
{% extends "base.html" %}
{% block content %}
<section>
<p>Subscription {{ plan }} cancelled!</p>
</section>
{% endblock %}

View File

@ -1,3 +1,4 @@
import logging
import pprint
import stripe
@ -13,6 +14,7 @@ from core.forms import NewUserForm
from core.lib.products import assemble_plan_map
from core.models import ContentBlock, Plan, Session
logger = logging.getLogger(__name__)
pp = pprint.PrettyPrinter(indent=4)
# Create your views here
@ -55,6 +57,25 @@ class Order(LoginRequiredMixin, View):
return JsonResponse({"error": str(e)}, status=500)
class Cancel(LoginRequiredMixin, View):
def get(self, request, plan_name):
plan = Plan.objects.get(name=plan_name)
try:
subscriptions = stripe.Subscription.list(
customer=request.user.stripe_id, price=plan.product_id
)
for subscription in subscriptions["data"]:
items = subscription["items"]["data"]
for item in items:
stripe.Subscription.delete(item["subscription"])
return render(request, "subscriptioncancel.html", {"plan": plan})
# return JsonResponse({'id': session.id})
except Exception as e:
# Raise a server error
logging.error(f"Error cancelling subscription for user: {e}")
return JsonResponse({"error": "True"}, status=500)
class Signup(CreateView):
form_class = NewUserForm
success_url = reverse_lazy("login")

View File

@ -1,3 +1,4 @@
import logging
from datetime import datetime
import stripe
@ -9,6 +10,8 @@ from rest_framework.views import APIView
from core.models import Plan, Session, User
logger = logging.getLogger(__name__)
class Callback(APIView):
parser_classes = [JSONParser]
@ -17,16 +20,17 @@ class Callback(APIView):
def post(self, request):
payload = request.body
sig_header = request.META["HTTP_STRIPE_SIGNATURE"]
try:
stripe.Webhook.construct_event(
payload, sig_header, settings.STRIPE_ENDPOINT_SECRET
)
except ValueError:
# Invalid payload
logger.error("Invalid payload")
return HttpResponse(status=400)
except stripe.error.SignatureVerificationError:
# Invalid signature
logger.error("Invalid signature")
return HttpResponse(status=400)
if request.data is None:
@ -37,7 +41,6 @@ class Callback(APIView):
session = request.data["data"]["object"]["id"]
subscription_id = request.data["data"]["object"]["subscription"]
session_map = Session.objects.get(session=session)
print("querying session", session)
if not session_map:
return JsonResponse({"success": False}, status=500)
user = session_map.user
@ -47,7 +50,7 @@ class Callback(APIView):
if rtype == "customer.subscription.updated":
stripe_id = request.data["data"]["object"]["customer"]
if not stripe_id:
print("No user found for customer:", stripe_id)
logging.error("No stripe id")
return JsonResponse({"success": False}, status=500)
user = User.objects.get(stripe_id=stripe_id)
# ssubscription_active
@ -58,7 +61,9 @@ class Callback(APIView):
if session_iter.subscription_id == subscription_id:
session = session_iter
if not session:
print(f"No session found for subscription id {subscription_id}")
logging.error(
f"No session found for subscription id {subscription_id}"
)
return JsonResponse({"success": False}, status=500)
# query Session objects
# iterate and check against product_id
@ -66,24 +71,20 @@ class Callback(APIView):
product_id = request.data["data"]["object"]["plan"]["id"]
plan = Plan.objects.get(product_id=product_id)
if not plan:
print(f"Plan not found: {product_id}")
logging.error(f"Plan not found: {product_id}")
return JsonResponse({"success": False}, status=500)
session.plan = plan
session.save()
elif rtype == "payment_intent.succeeded":
customer = request.data["data"]["object"]["customer"]
print("customer", customer)
user = User.objects.get(stripe_id=customer)
if not user:
print("No user found for customer:", customer)
logging.error(f"No user found for customer: {customer}")
return JsonResponse({"success": False}, status=500)
print("got", user.email)
session = Session.objects.get(request=request.data["request"]["id"])
print("Got session", session)
user.plans.add(session.plan)
print("ADDING PLAN TO USER PLANS")
user.last_payment = datetime.utcnow()
user.save()
@ -91,7 +92,7 @@ class Callback(APIView):
customer = request.data["data"]["object"]["customer"]
user = User.objects.get(stripe_id=customer)
if not user:
print("No user found for customer:", customer)
logging.error(f"No user found for customer {customer}")
return JsonResponse({"success": False}, status=500)
product_id = request.data["data"]["object"]["plan"]["id"]
plan = Plan.objects.get(product_id=product_id)