views.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import stripe
  2. from django.conf import settings
  3. from django.db.models import Prefetch
  4. from django.http import Http404
  5. from djstripe.models import Customer, Price, Product, Subscription, SubscriptionItem
  6. from djstripe.settings import djstripe_settings
  7. from rest_framework import status, views, viewsets
  8. from rest_framework.decorators import action
  9. from rest_framework.response import Response
  10. from apps.organizations_ext.models import Organization
  11. from .serializers import (
  12. CreateSubscriptionSerializer,
  13. OrganizationSelectSerializer,
  14. PriceForOrganizationSerializer,
  15. ProductSerializer,
  16. SubscriptionSerializer,
  17. )
  18. class SubscriptionViewSet(viewsets.ModelViewSet):
  19. """
  20. View subscription status and create new free tier subscriptions
  21. Use organization slug for detail view. Ex: /subscriptions/my-cool-org/
  22. """
  23. queryset = Subscription.objects.all()
  24. serializer_class = SubscriptionSerializer
  25. lookup_field = "customer__subscriber__slug"
  26. def get_serializer_class(self):
  27. if self.action == "create":
  28. return CreateSubscriptionSerializer
  29. return super().get_serializer_class()
  30. def get_queryset(self):
  31. """Any user in an org may view subscription data"""
  32. if self.request.user.is_authenticated:
  33. return self.queryset.filter(
  34. livemode=settings.STRIPE_LIVE_MODE,
  35. customer__subscriber__users=self.request.user,
  36. ).prefetch_related(
  37. Prefetch(
  38. "items",
  39. queryset=SubscriptionItem.objects.select_related("price__product"),
  40. )
  41. )
  42. return self.queryset.none()
  43. def get_object(self):
  44. """Get most recent by slug"""
  45. try:
  46. subscription = (
  47. self.get_queryset()
  48. .filter(**self.kwargs)
  49. .exclude(status="canceled")
  50. .order_by("-created")
  51. .first()
  52. )
  53. # Check organization throttle, in case it changed recently
  54. if subscription:
  55. Organization.objects.filter(
  56. id=subscription.customer.subscriber_id,
  57. is_accepting_events=False,
  58. is_active=True,
  59. djstripe_customers__subscriptions__plan__amount__gt=0,
  60. djstripe_customers__subscriptions__status="active",
  61. ).update(is_accepting_events=True)
  62. return subscription
  63. except Subscription.DoesNotExist as exc:
  64. raise Http404 from exc
  65. @action(detail=True, methods=["get"])
  66. def events_count(self, *args, **kwargs):
  67. """Get event count for current billing period"""
  68. subscription = self.get_object()
  69. if not subscription:
  70. return Response(
  71. {
  72. "eventCount": 0,
  73. "transactionEventCount": 0,
  74. "uptimeCheckEventCount": 0,
  75. "fileSizeMB": 0,
  76. }
  77. )
  78. organization = subscription.customer.subscriber
  79. org = Organization.objects.with_event_counts().get(pk=organization.pk)
  80. data = {
  81. "eventCount": org.issue_event_count,
  82. "transactionEventCount": org.transaction_count,
  83. "uptimeCheckEventCount": org.uptime_check_event_count,
  84. "fileSizeMB": org.file_size,
  85. }
  86. return Response(data)
  87. class ProductViewSet(viewsets.ReadOnlyModelViewSet):
  88. """
  89. Stripe Product + Prices
  90. unit_amount is price in cents
  91. """
  92. queryset = (
  93. Product.objects.filter(
  94. active=True,
  95. livemode=settings.STRIPE_LIVE_MODE,
  96. prices__active=True,
  97. metadata__events__isnull=False,
  98. metadata__is_public="true",
  99. )
  100. .prefetch_related(
  101. Prefetch("prices", queryset=Price.objects.filter(active=True))
  102. )
  103. .distinct()
  104. )
  105. serializer_class = ProductSerializer
  106. class CreateStripeSubscriptionCheckout(views.APIView):
  107. """Create Stripe Checkout, send to client for redirecting to Stripe"""
  108. def get_serializer(self, *args, **kwargs):
  109. return PriceForOrganizationSerializer(
  110. data=self.request.data, context={"request": self.request}
  111. )
  112. def post(self, request):
  113. """See https://stripe.com/docs/api/checkout/sessions/create"""
  114. serializer = self.get_serializer()
  115. if serializer.is_valid():
  116. organization = serializer.validated_data["organization"]
  117. customer, _ = Customer.get_or_create(subscriber=organization)
  118. domain = settings.GLITCHTIP_URL.geturl()
  119. session = stripe.checkout.Session.create(
  120. api_key=djstripe_settings.STRIPE_SECRET_KEY,
  121. payment_method_types=["card"],
  122. line_items=[
  123. {
  124. "price": serializer.validated_data["price"].id,
  125. "quantity": 1,
  126. }
  127. ],
  128. mode="subscription",
  129. customer=customer.id,
  130. automatic_tax={
  131. "enabled": settings.STRIPE_AUTOMATIC_TAX,
  132. },
  133. customer_update={"address": "auto", "name": "auto"},
  134. tax_id_collection={
  135. "enabled": True,
  136. },
  137. success_url=domain
  138. + "/"
  139. + organization.slug
  140. + "/settings/subscription?session_id={CHECKOUT_SESSION_ID}",
  141. cancel_url=domain + "",
  142. )
  143. return Response(session)
  144. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  145. class StripeBillingPortal(views.APIView):
  146. def get_serializer(self, *args, **kwargs):
  147. return OrganizationSelectSerializer(
  148. data=self.request.data, context={"request": self.request}
  149. )
  150. def post(self, request):
  151. """See https://stripe.com/docs/billing/subscriptions/integrating-self-serve-portal"""
  152. serializer = self.get_serializer()
  153. if serializer.is_valid():
  154. organization = serializer.validated_data["organization"]
  155. customer, _ = Customer.get_or_create(subscriber=organization)
  156. domain = settings.GLITCHTIP_URL.geturl()
  157. session = stripe.billing_portal.Session.create(
  158. api_key=djstripe_settings.STRIPE_SECRET_KEY,
  159. customer=customer.id,
  160. return_url=domain + "/" + organization.slug + "/settings/subscription",
  161. )
  162. return Response(session)
  163. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)