views.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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 rest_framework import status, views, viewsets
  9. from rest_framework.decorators import action
  10. from rest_framework.response import Response
  11. from organizations_ext.models import Organization
  12. from .serializers import (
  13. CreateSubscriptionSerializer,
  14. OrganizationSelectSerializer,
  15. PriceForOrganizationSerializer,
  16. ProductSerializer,
  17. SubscriptionSerializer,
  18. )
  19. class SubscriptionViewSet(viewsets.ModelViewSet):
  20. """
  21. View subscription status and create new free tier subscriptions
  22. Use organization slug for detail view. Ex: /subscriptions/my-cool-org/
  23. """
  24. queryset = Subscription.objects.all()
  25. serializer_class = SubscriptionSerializer
  26. lookup_field = "customer__subscriber__slug"
  27. def get_serializer_class(self):
  28. if self.action == "create":
  29. return CreateSubscriptionSerializer
  30. return super().get_serializer_class()
  31. def get_queryset(self):
  32. """Any user in an org may view subscription data"""
  33. if self.request.user.is_authenticated:
  34. return self.queryset.filter(
  35. livemode=settings.STRIPE_LIVE_MODE,
  36. customer__subscriber__users=self.request.user,
  37. ).prefetch_related(
  38. Prefetch(
  39. "items",
  40. queryset=SubscriptionItem.objects.select_related("price__product"),
  41. )
  42. )
  43. return self.queryset.none()
  44. def get_object(self):
  45. """Get most recent by slug"""
  46. try:
  47. subscription = (
  48. self.get_queryset()
  49. .filter(**self.kwargs)
  50. .exclude(status="canceled")
  51. .order_by("-created")
  52. .first()
  53. )
  54. # Check organization throttle, in case it changed recently
  55. if subscription:
  56. Organization.objects.filter(
  57. id=subscription.customer.subscriber_id,
  58. is_accepting_events=False,
  59. is_active=True,
  60. djstripe_customers__subscriptions__plan__amount__gt=0,
  61. djstripe_customers__subscriptions__status="active",
  62. ).update(is_accepting_events=True)
  63. return subscription
  64. except Subscription.DoesNotExist as exc:
  65. raise Http404 from exc
  66. @action(detail=True, methods=["get"])
  67. def events_count(self, *args, **kwargs):
  68. """Get event count for current billing period"""
  69. subscription = self.get_object()
  70. if not subscription:
  71. return Response(
  72. {
  73. "eventCount": 0,
  74. "transactionEventCount": 0,
  75. "uptimeCheckEventCount": 0,
  76. "fileSizeMB": 0,
  77. }
  78. )
  79. organization = subscription.customer.subscriber
  80. org = Organization.objects.with_event_counts().get(pk=organization.pk)
  81. data = {
  82. "eventCount": org.issue_event_count,
  83. "transactionEventCount": org.transaction_count,
  84. "uptimeCheckEventCount": org.uptime_check_event_count,
  85. "fileSizeMB": org.file_size,
  86. }
  87. return Response(data)
  88. class ProductViewSet(viewsets.ReadOnlyModelViewSet):
  89. """
  90. Stripe Product + Prices
  91. unit_amount is price in cents
  92. """
  93. queryset = (
  94. Product.objects.filter(
  95. active=True,
  96. livemode=settings.STRIPE_LIVE_MODE,
  97. prices__active=True,
  98. metadata__events__isnull=False,
  99. metadata__is_public="true",
  100. )
  101. .prefetch_related(
  102. Prefetch("prices", queryset=Price.objects.filter(active=True))
  103. )
  104. .distinct()
  105. )
  106. serializer_class = ProductSerializer
  107. class CreateStripeSubscriptionCheckout(views.APIView):
  108. """Create Stripe Checkout, send to client for redirecting to Stripe"""
  109. def get_serializer(self, *args, **kwargs):
  110. return PriceForOrganizationSerializer(
  111. data=self.request.data, context={"request": self.request}
  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. def post(self, request):
  152. """See https://stripe.com/docs/billing/subscriptions/integrating-self-serve-portal"""
  153. serializer = self.get_serializer()
  154. if serializer.is_valid():
  155. organization = serializer.validated_data["organization"]
  156. customer, _ = Customer.get_or_create(subscriber=organization)
  157. domain = settings.GLITCHTIP_URL.geturl()
  158. session = stripe.billing_portal.Session.create(
  159. api_key=djstripe_settings.STRIPE_SECRET_KEY,
  160. customer=customer.id,
  161. return_url=domain + "/" + organization.slug + "/settings/subscription",
  162. )
  163. return Response(session)
  164. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)