views.py 6.9 KB

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