views.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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().filter(**self.kwargs).order_by("-created").first()
  50. )
  51. # Check organization throttle, in case it changed recently
  52. if subscription:
  53. Organization.objects.filter(
  54. id=subscription.customer.subscriber_id,
  55. is_accepting_events=False,
  56. is_active=True,
  57. djstripe_customers__subscriptions__plan__amount__gt=0,
  58. djstripe_customers__subscriptions__status="active",
  59. ).update(is_accepting_events=True)
  60. return subscription
  61. except Subscription.DoesNotExist as exc:
  62. raise Http404 from exc
  63. @action(detail=True, methods=["get"])
  64. def events_count(self, *args, **kwargs):
  65. """Get event count for current billing period"""
  66. subscription = self.get_object()
  67. if not subscription:
  68. return Response(
  69. {
  70. "eventCount": 0,
  71. "transactionEventCount": 0,
  72. "uptimeCheckEventCount": 0,
  73. "fileSizeMB": 0,
  74. }
  75. )
  76. organization = subscription.customer.subscriber
  77. org = Organization.objects.with_event_counts().get(pk=organization.pk)
  78. data = {
  79. "eventCount": org.issue_event_count,
  80. "transactionEventCount": org.transaction_count,
  81. "uptimeCheckEventCount": org.uptime_check_event_count,
  82. "fileSizeMB": org.file_size,
  83. }
  84. return Response(data)
  85. class ProductViewSet(viewsets.ReadOnlyModelViewSet):
  86. """
  87. Stripe Product + Prices
  88. unit_amount is price in cents
  89. """
  90. queryset = (
  91. Product.objects.filter(
  92. active=True,
  93. livemode=settings.STRIPE_LIVE_MODE,
  94. prices__active=True,
  95. metadata__events__isnull=False,
  96. metadata__is_public="true",
  97. )
  98. .prefetch_related(
  99. Prefetch("prices", queryset=Price.objects.filter(active=True))
  100. )
  101. .distinct()
  102. )
  103. serializer_class = ProductSerializer
  104. class CreateStripeSubscriptionCheckout(views.APIView):
  105. """Create Stripe Checkout, send to client for redirecting to Stripe"""
  106. def get_serializer(self, *args, **kwargs):
  107. return PriceForOrganizationSerializer(
  108. data=self.request.data, context={"request": self.request}
  109. )
  110. @swagger_auto_schema(
  111. responses={200: str(stripe.api_resources.checkout.session.Session)}
  112. )
  113. def post(self, request):
  114. """See https://stripe.com/docs/api/checkout/sessions/create"""
  115. serializer = self.get_serializer()
  116. if serializer.is_valid():
  117. organization = serializer.validated_data["organization"]
  118. customer, _ = Customer.get_or_create(subscriber=organization)
  119. domain = settings.GLITCHTIP_URL.geturl()
  120. session = stripe.checkout.Session.create(
  121. api_key=djstripe_settings.STRIPE_SECRET_KEY,
  122. payment_method_types=["card"],
  123. line_items=[
  124. {
  125. "price": serializer.validated_data["price"].id,
  126. "quantity": 1,
  127. }
  128. ],
  129. mode="subscription",
  130. customer=customer.id,
  131. automatic_tax={
  132. "enabled": settings.STRIPE_AUTOMATIC_TAX,
  133. },
  134. customer_update={"address": "auto", "name": "auto"},
  135. tax_id_collection={
  136. "enabled": True,
  137. },
  138. success_url=domain
  139. + "/"
  140. + organization.slug
  141. + "/settings/subscription?session_id={CHECKOUT_SESSION_ID}",
  142. cancel_url=domain + "",
  143. )
  144. return Response(session)
  145. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  146. class StripeBillingPortal(views.APIView):
  147. def get_serializer(self, *args, **kwargs):
  148. return OrganizationSelectSerializer(
  149. data=self.request.data, context={"request": self.request}
  150. )
  151. @swagger_auto_schema(
  152. responses={200: str(stripe.api_resources.billing_portal.Session)}
  153. )
  154. def post(self, request):
  155. """See https://stripe.com/docs/billing/subscriptions/integrating-self-serve-portal"""
  156. serializer = self.get_serializer()
  157. if serializer.is_valid():
  158. organization = serializer.validated_data["organization"]
  159. customer, _ = Customer.get_or_create(subscriber=organization)
  160. domain = settings.GLITCHTIP_URL.geturl()
  161. session = stripe.billing_portal.Session.create(
  162. api_key=djstripe_settings.STRIPE_SECRET_KEY,
  163. customer=customer.id,
  164. return_url=domain + "/" + organization.slug + "/settings/subscription",
  165. )
  166. return Response(session)
  167. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)