mirror of https://github.com/zulip/zulip.git
corporate: Import corporate.lib.stripe lazily.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
fcafcb24d7
commit
f0f048de69
|
@ -16,11 +16,6 @@ from django.utils.timezone import now as timezone_now
|
||||||
from markupsafe import Markup
|
from markupsafe import Markup
|
||||||
from psycopg2.sql import Composable
|
from psycopg2.sql import Composable
|
||||||
|
|
||||||
from corporate.lib.stripe import (
|
|
||||||
RealmBillingSession,
|
|
||||||
RemoteRealmBillingSession,
|
|
||||||
RemoteServerBillingSession,
|
|
||||||
)
|
|
||||||
from corporate.models import CustomerPlan, LicenseLedger
|
from corporate.models import CustomerPlan, LicenseLedger
|
||||||
from zerver.lib.pysa import mark_sanitized
|
from zerver.lib.pysa import mark_sanitized
|
||||||
from zerver.lib.url_encoding import append_url_query_string
|
from zerver.lib.url_encoding import append_url_query_string
|
||||||
|
@ -196,6 +191,8 @@ def get_remote_activity_plan_data(
|
||||||
remote_realm: RemoteRealm | None = None,
|
remote_realm: RemoteRealm | None = None,
|
||||||
remote_server: RemoteZulipServer | None = None,
|
remote_server: RemoteZulipServer | None = None,
|
||||||
) -> RemoteActivityPlanData:
|
) -> RemoteActivityPlanData:
|
||||||
|
from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession
|
||||||
|
|
||||||
if plan.tier == CustomerPlan.TIER_SELF_HOSTED_LEGACY or plan.status in (
|
if plan.tier == CustomerPlan.TIER_SELF_HOSTED_LEGACY or plan.status in (
|
||||||
CustomerPlan.DOWNGRADE_AT_END_OF_FREE_TRIAL,
|
CustomerPlan.DOWNGRADE_AT_END_OF_FREE_TRIAL,
|
||||||
CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE,
|
CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE,
|
||||||
|
@ -226,6 +223,8 @@ def get_remote_activity_plan_data(
|
||||||
|
|
||||||
|
|
||||||
def get_estimated_arr_and_rate_by_realm() -> tuple[dict[str, int], dict[str, str]]: # nocoverage
|
def get_estimated_arr_and_rate_by_realm() -> tuple[dict[str, int], dict[str, str]]: # nocoverage
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
# NOTE: Customers without a plan might still have a discount attached to them which
|
# NOTE: Customers without a plan might still have a discount attached to them which
|
||||||
# are not included in `plan_rate`.
|
# are not included in `plan_rate`.
|
||||||
annual_revenue = {}
|
annual_revenue = {}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import inspect
|
import inspect
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Concatenate
|
from typing import TYPE_CHECKING, Concatenate
|
||||||
from urllib.parse import urlencode, urljoin
|
from urllib.parse import urlencode, urljoin
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -15,12 +15,14 @@ from corporate.lib.remote_billing_util import (
|
||||||
get_remote_realm_and_user_from_session,
|
get_remote_realm_and_user_from_session,
|
||||||
get_remote_server_and_user_from_session,
|
get_remote_server_and_user_from_session,
|
||||||
)
|
)
|
||||||
from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession
|
|
||||||
from zerver.lib.exceptions import RemoteBillingAuthenticationError
|
from zerver.lib.exceptions import RemoteBillingAuthenticationError
|
||||||
from zerver.lib.subdomains import get_subdomain
|
from zerver.lib.subdomains import get_subdomain
|
||||||
from zerver.lib.url_encoding import append_url_query_string
|
from zerver.lib.url_encoding import append_url_query_string
|
||||||
from zilencer.models import RemoteRealm
|
from zilencer.models import RemoteRealm
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession
|
||||||
|
|
||||||
ParamT = ParamSpec("ParamT")
|
ParamT = ParamSpec("ParamT")
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,7 +56,9 @@ def self_hosting_management_endpoint(
|
||||||
|
|
||||||
|
|
||||||
def authenticated_remote_realm_management_endpoint(
|
def authenticated_remote_realm_management_endpoint(
|
||||||
view_func: Callable[Concatenate[HttpRequest, RemoteRealmBillingSession, ParamT], HttpResponse],
|
view_func: Callable[
|
||||||
|
Concatenate[HttpRequest, "RemoteRealmBillingSession", ParamT], HttpResponse
|
||||||
|
],
|
||||||
) -> Callable[Concatenate[HttpRequest, ParamT], HttpResponse]:
|
) -> Callable[Concatenate[HttpRequest, ParamT], HttpResponse]:
|
||||||
@wraps(view_func)
|
@wraps(view_func)
|
||||||
def _wrapped_view_func(
|
def _wrapped_view_func(
|
||||||
|
@ -63,6 +67,8 @@ def authenticated_remote_realm_management_endpoint(
|
||||||
*args: ParamT.args,
|
*args: ParamT.args,
|
||||||
**kwargs: ParamT.kwargs,
|
**kwargs: ParamT.kwargs,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import RemoteRealmBillingSession
|
||||||
|
|
||||||
if not is_self_hosting_management_subdomain(request): # nocoverage
|
if not is_self_hosting_management_subdomain(request): # nocoverage
|
||||||
return render(request, "404.html", status=404)
|
return render(request, "404.html", status=404)
|
||||||
|
|
||||||
|
@ -160,7 +166,9 @@ def get_next_page_param_from_request_path(request: HttpRequest) -> str | None:
|
||||||
|
|
||||||
|
|
||||||
def authenticated_remote_server_management_endpoint(
|
def authenticated_remote_server_management_endpoint(
|
||||||
view_func: Callable[Concatenate[HttpRequest, RemoteServerBillingSession, ParamT], HttpResponse],
|
view_func: Callable[
|
||||||
|
Concatenate[HttpRequest, "RemoteServerBillingSession", ParamT], HttpResponse
|
||||||
|
],
|
||||||
) -> Callable[Concatenate[HttpRequest, ParamT], HttpResponse]:
|
) -> Callable[Concatenate[HttpRequest, ParamT], HttpResponse]:
|
||||||
@wraps(view_func)
|
@wraps(view_func)
|
||||||
def _wrapped_view_func(
|
def _wrapped_view_func(
|
||||||
|
@ -169,6 +177,8 @@ def authenticated_remote_server_management_endpoint(
|
||||||
*args: ParamT.args,
|
*args: ParamT.args,
|
||||||
**kwargs: ParamT.kwargs,
|
**kwargs: ParamT.kwargs,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import RemoteServerBillingSession
|
||||||
|
|
||||||
if not is_self_hosting_management_subdomain(request): # nocoverage
|
if not is_self_hosting_management_subdomain(request): # nocoverage
|
||||||
return render(request, "404.html", status=404)
|
return render(request, "404.html", status=404)
|
||||||
|
|
||||||
|
|
|
@ -4976,7 +4976,9 @@ class StripeWebhookEndpointTest(ZulipTestCase):
|
||||||
"type": "checkout.session.completed",
|
"type": "checkout.session.completed",
|
||||||
"data": {"object": {"object": "checkout.session", "id": "stripe_session_id"}},
|
"data": {"object": {"object": "checkout.session", "id": "stripe_session_id"}},
|
||||||
}
|
}
|
||||||
with patch("corporate.views.webhook.handle_checkout_session_completed_event") as m:
|
with patch(
|
||||||
|
"corporate.lib.stripe_event_handler.handle_checkout_session_completed_event"
|
||||||
|
) as m:
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/stripe/webhook/",
|
"/stripe/webhook/",
|
||||||
valid_session_event_data,
|
valid_session_event_data,
|
||||||
|
@ -4998,7 +5000,7 @@ class StripeWebhookEndpointTest(ZulipTestCase):
|
||||||
"data": {"object": {"object": "invoice", "id": stripe_invoice_id}},
|
"data": {"object": {"object": "invoice", "id": stripe_invoice_id}},
|
||||||
}
|
}
|
||||||
|
|
||||||
with patch("corporate.views.webhook.handle_invoice_paid_event") as m:
|
with patch("corporate.lib.stripe_event_handler.handle_invoice_paid_event") as m:
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/stripe/webhook/",
|
"/stripe/webhook/",
|
||||||
valid_session_event_data,
|
valid_session_event_data,
|
||||||
|
@ -5015,7 +5017,7 @@ class StripeWebhookEndpointTest(ZulipTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assert_length(Event.objects.filter(stripe_event_id=stripe_event_id), 0)
|
self.assert_length(Event.objects.filter(stripe_event_id=stripe_event_id), 0)
|
||||||
with patch("corporate.views.webhook.handle_invoice_paid_event") as m:
|
with patch("corporate.lib.stripe_event_handler.handle_invoice_paid_event") as m:
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/stripe/webhook/",
|
"/stripe/webhook/",
|
||||||
valid_session_event_data,
|
valid_session_event_data,
|
||||||
|
@ -5026,7 +5028,7 @@ class StripeWebhookEndpointTest(ZulipTestCase):
|
||||||
strip_event = stripe.Event.construct_from(valid_session_event_data, stripe.api_key)
|
strip_event = stripe.Event.construct_from(valid_session_event_data, stripe.api_key)
|
||||||
m.assert_called_once_with(strip_event.data.object, event)
|
m.assert_called_once_with(strip_event.data.object, event)
|
||||||
|
|
||||||
with patch("corporate.views.webhook.handle_invoice_paid_event") as m:
|
with patch("corporate.lib.stripe_event_handler.handle_invoice_paid_event") as m:
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/stripe/webhook/",
|
"/stripe/webhook/",
|
||||||
valid_session_event_data,
|
valid_session_event_data,
|
||||||
|
@ -5048,7 +5050,7 @@ class StripeWebhookEndpointTest(ZulipTestCase):
|
||||||
"data": {"object": {"object": "invoice", "id": stripe_invoice_id}},
|
"data": {"object": {"object": "invoice", "id": stripe_invoice_id}},
|
||||||
}
|
}
|
||||||
|
|
||||||
with patch("corporate.views.webhook.handle_invoice_paid_event") as m:
|
with patch("corporate.lib.stripe_event_handler.handle_invoice_paid_event") as m:
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/stripe/webhook/",
|
"/stripe/webhook/",
|
||||||
valid_invoice_paid_event_data,
|
valid_invoice_paid_event_data,
|
||||||
|
@ -5065,7 +5067,7 @@ class StripeWebhookEndpointTest(ZulipTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assert_length(Event.objects.filter(stripe_event_id=stripe_event_id), 0)
|
self.assert_length(Event.objects.filter(stripe_event_id=stripe_event_id), 0)
|
||||||
with patch("corporate.views.webhook.handle_invoice_paid_event") as m:
|
with patch("corporate.lib.stripe_event_handler.handle_invoice_paid_event") as m:
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/stripe/webhook/",
|
"/stripe/webhook/",
|
||||||
valid_invoice_paid_event_data,
|
valid_invoice_paid_event_data,
|
||||||
|
@ -5076,7 +5078,7 @@ class StripeWebhookEndpointTest(ZulipTestCase):
|
||||||
strip_event = stripe.Event.construct_from(valid_invoice_paid_event_data, stripe.api_key)
|
strip_event = stripe.Event.construct_from(valid_invoice_paid_event_data, stripe.api_key)
|
||||||
m.assert_called_once_with(strip_event.data.object, event)
|
m.assert_called_once_with(strip_event.data.object, event)
|
||||||
|
|
||||||
with patch("corporate.views.webhook.handle_invoice_paid_event") as m:
|
with patch("corporate.lib.stripe_event_handler.handle_invoice_paid_event") as m:
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/stripe/webhook/",
|
"/stripe/webhook/",
|
||||||
valid_invoice_paid_event_data,
|
valid_invoice_paid_event_data,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import Annotated, Any, Literal
|
from typing import TYPE_CHECKING, Annotated, Any, Literal
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse, HttpResponseNotAllowed, HttpResponseRedirect
|
from django.http import HttpRequest, HttpResponse, HttpResponseNotAllowed, HttpResponseRedirect
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
@ -11,14 +11,6 @@ from corporate.lib.decorator import (
|
||||||
authenticated_remote_realm_management_endpoint,
|
authenticated_remote_realm_management_endpoint,
|
||||||
authenticated_remote_server_management_endpoint,
|
authenticated_remote_server_management_endpoint,
|
||||||
)
|
)
|
||||||
from corporate.lib.stripe import (
|
|
||||||
RealmBillingSession,
|
|
||||||
RemoteRealmBillingSession,
|
|
||||||
RemoteServerBillingSession,
|
|
||||||
ServerDeactivateWithExistingPlanError,
|
|
||||||
UpdatePlanRequest,
|
|
||||||
do_deactivate_remote_server,
|
|
||||||
)
|
|
||||||
from corporate.models import CustomerPlan, get_current_plan_by_customer, get_customer_by_realm
|
from corporate.models import CustomerPlan, get_current_plan_by_customer, get_customer_by_realm
|
||||||
from zerver.decorator import process_as_post, require_billing_access, zulip_login_required
|
from zerver.decorator import process_as_post, require_billing_access, zulip_login_required
|
||||||
from zerver.lib.exceptions import JsonableError
|
from zerver.lib.exceptions import JsonableError
|
||||||
|
@ -29,6 +21,10 @@ from zerver.models import UserProfile
|
||||||
from zilencer.lib.remote_counts import MissingDataError
|
from zilencer.lib.remote_counts import MissingDataError
|
||||||
from zilencer.models import RemoteRealm, RemoteZulipServer
|
from zilencer.models import RemoteRealm, RemoteZulipServer
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession
|
||||||
|
|
||||||
|
|
||||||
billing_logger = logging.getLogger("corporate.stripe")
|
billing_logger = logging.getLogger("corporate.stripe")
|
||||||
|
|
||||||
ALLOWED_PLANS_API_STATUS_VALUES = [
|
ALLOWED_PLANS_API_STATUS_VALUES = [
|
||||||
|
@ -49,6 +45,8 @@ def billing_page(
|
||||||
*,
|
*,
|
||||||
success_message: str = "",
|
success_message: str = "",
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
user = request.user
|
user = request.user
|
||||||
assert user.is_authenticated
|
assert user.is_authenticated
|
||||||
|
|
||||||
|
@ -91,11 +89,11 @@ def billing_page(
|
||||||
return render(request, "corporate/billing/billing.html", context=context)
|
return render(request, "corporate/billing/billing.html", context=context)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_realm_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_realm_management_endpoint
|
||||||
def remote_realm_billing_page(
|
def remote_realm_billing_page(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteRealmBillingSession,
|
billing_session: "RemoteRealmBillingSession",
|
||||||
*,
|
*,
|
||||||
success_message: str = "",
|
success_message: str = "",
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
@ -152,11 +150,11 @@ def remote_realm_billing_page(
|
||||||
return render(request, "corporate/billing/billing.html", context=context)
|
return render(request, "corporate/billing/billing.html", context=context)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_server_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_server_management_endpoint
|
||||||
def remote_server_billing_page(
|
def remote_server_billing_page(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteServerBillingSession,
|
billing_session: "RemoteServerBillingSession",
|
||||||
*,
|
*,
|
||||||
success_message: str = "",
|
success_message: str = "",
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
@ -246,6 +244,8 @@ def update_plan(
|
||||||
licenses_at_next_renewal: Json[int] | None = None,
|
licenses_at_next_renewal: Json[int] | None = None,
|
||||||
schedule: Json[int] | None = None,
|
schedule: Json[int] | None = None,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession, UpdatePlanRequest
|
||||||
|
|
||||||
update_plan_request = UpdatePlanRequest(
|
update_plan_request = UpdatePlanRequest(
|
||||||
status=status,
|
status=status,
|
||||||
licenses=licenses,
|
licenses=licenses,
|
||||||
|
@ -257,12 +257,12 @@ def update_plan(
|
||||||
return json_success(request)
|
return json_success(request)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_realm_management_endpoint
|
|
||||||
@process_as_post
|
@process_as_post
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_realm_management_endpoint
|
||||||
def update_plan_for_remote_realm(
|
def update_plan_for_remote_realm(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteRealmBillingSession,
|
billing_session: "RemoteRealmBillingSession",
|
||||||
*,
|
*,
|
||||||
status: Annotated[
|
status: Annotated[
|
||||||
Json[int], AfterValidator(lambda x: check_int_in(x, ALLOWED_PLANS_API_STATUS_VALUES))
|
Json[int], AfterValidator(lambda x: check_int_in(x, ALLOWED_PLANS_API_STATUS_VALUES))
|
||||||
|
@ -272,6 +272,8 @@ def update_plan_for_remote_realm(
|
||||||
licenses_at_next_renewal: Json[int] | None = None,
|
licenses_at_next_renewal: Json[int] | None = None,
|
||||||
schedule: Json[int] | None = None,
|
schedule: Json[int] | None = None,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import UpdatePlanRequest
|
||||||
|
|
||||||
update_plan_request = UpdatePlanRequest(
|
update_plan_request = UpdatePlanRequest(
|
||||||
status=status,
|
status=status,
|
||||||
licenses=licenses,
|
licenses=licenses,
|
||||||
|
@ -282,12 +284,12 @@ def update_plan_for_remote_realm(
|
||||||
return json_success(request)
|
return json_success(request)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_server_management_endpoint
|
|
||||||
@process_as_post
|
@process_as_post
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_server_management_endpoint
|
||||||
def update_plan_for_remote_server(
|
def update_plan_for_remote_server(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteServerBillingSession,
|
billing_session: "RemoteServerBillingSession",
|
||||||
*,
|
*,
|
||||||
status: Annotated[
|
status: Annotated[
|
||||||
Json[int], AfterValidator(lambda x: check_int_in(x, ALLOWED_PLANS_API_STATUS_VALUES))
|
Json[int], AfterValidator(lambda x: check_int_in(x, ALLOWED_PLANS_API_STATUS_VALUES))
|
||||||
|
@ -297,6 +299,8 @@ def update_plan_for_remote_server(
|
||||||
licenses_at_next_renewal: Json[int] | None = None,
|
licenses_at_next_renewal: Json[int] | None = None,
|
||||||
schedule: Json[int] | None = None,
|
schedule: Json[int] | None = None,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import UpdatePlanRequest
|
||||||
|
|
||||||
update_plan_request = UpdatePlanRequest(
|
update_plan_request = UpdatePlanRequest(
|
||||||
status=status,
|
status=status,
|
||||||
licenses=licenses,
|
licenses=licenses,
|
||||||
|
@ -307,14 +311,19 @@ def update_plan_for_remote_server(
|
||||||
return json_success(request)
|
return json_success(request)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_server_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_server_management_endpoint
|
||||||
def remote_server_deactivate_page(
|
def remote_server_deactivate_page(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteServerBillingSession,
|
billing_session: "RemoteServerBillingSession",
|
||||||
*,
|
*,
|
||||||
confirmed: Literal[None, "true"] = None,
|
confirmed: Literal[None, "true"] = None,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import (
|
||||||
|
ServerDeactivateWithExistingPlanError,
|
||||||
|
do_deactivate_remote_server,
|
||||||
|
)
|
||||||
|
|
||||||
if request.method not in ["GET", "POST"]: # nocoverage
|
if request.method not in ["GET", "POST"]: # nocoverage
|
||||||
return HttpResponseNotAllowed(["GET", "POST"])
|
return HttpResponseNotAllowed(["GET", "POST"])
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
@ -8,17 +9,14 @@ from corporate.lib.decorator import (
|
||||||
authenticated_remote_server_management_endpoint,
|
authenticated_remote_server_management_endpoint,
|
||||||
self_hosting_management_endpoint,
|
self_hosting_management_endpoint,
|
||||||
)
|
)
|
||||||
from corporate.lib.stripe import (
|
|
||||||
EventStatusRequest,
|
|
||||||
RealmBillingSession,
|
|
||||||
RemoteRealmBillingSession,
|
|
||||||
RemoteServerBillingSession,
|
|
||||||
)
|
|
||||||
from zerver.decorator import require_organization_member, zulip_login_required
|
from zerver.decorator import require_organization_member, zulip_login_required
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
from zerver.lib.typed_endpoint import typed_endpoint
|
from zerver.lib.typed_endpoint import typed_endpoint
|
||||||
from zerver.models import UserProfile
|
from zerver.models import UserProfile
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession
|
||||||
|
|
||||||
billing_logger = logging.getLogger("corporate.stripe")
|
billing_logger = logging.getLogger("corporate.stripe")
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,6 +29,8 @@ def event_status(
|
||||||
stripe_session_id: str | None = None,
|
stripe_session_id: str | None = None,
|
||||||
stripe_invoice_id: str | None = None,
|
stripe_invoice_id: str | None = None,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import EventStatusRequest, RealmBillingSession
|
||||||
|
|
||||||
event_status_request = EventStatusRequest(
|
event_status_request = EventStatusRequest(
|
||||||
stripe_session_id=stripe_session_id, stripe_invoice_id=stripe_invoice_id
|
stripe_session_id=stripe_session_id, stripe_invoice_id=stripe_invoice_id
|
||||||
)
|
)
|
||||||
|
@ -39,15 +39,17 @@ def event_status(
|
||||||
return json_success(request, data)
|
return json_success(request, data)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_realm_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_realm_management_endpoint
|
||||||
def remote_realm_event_status(
|
def remote_realm_event_status(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteRealmBillingSession,
|
billing_session: "RemoteRealmBillingSession",
|
||||||
*,
|
*,
|
||||||
stripe_session_id: str | None = None,
|
stripe_session_id: str | None = None,
|
||||||
stripe_invoice_id: str | None = None,
|
stripe_invoice_id: str | None = None,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import EventStatusRequest
|
||||||
|
|
||||||
event_status_request = EventStatusRequest(
|
event_status_request = EventStatusRequest(
|
||||||
stripe_session_id=stripe_session_id, stripe_invoice_id=stripe_invoice_id
|
stripe_session_id=stripe_session_id, stripe_invoice_id=stripe_invoice_id
|
||||||
)
|
)
|
||||||
|
@ -55,15 +57,17 @@ def remote_realm_event_status(
|
||||||
return json_success(request, data)
|
return json_success(request, data)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_server_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_server_management_endpoint
|
||||||
def remote_server_event_status(
|
def remote_server_event_status(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteServerBillingSession,
|
billing_session: "RemoteServerBillingSession",
|
||||||
*,
|
*,
|
||||||
stripe_session_id: str | None = None,
|
stripe_session_id: str | None = None,
|
||||||
stripe_invoice_id: str | None = None,
|
stripe_invoice_id: str | None = None,
|
||||||
) -> HttpResponse: # nocoverage
|
) -> HttpResponse: # nocoverage
|
||||||
|
from corporate.lib.stripe import EventStatusRequest
|
||||||
|
|
||||||
event_status_request = EventStatusRequest(
|
event_status_request = EventStatusRequest(
|
||||||
stripe_session_id=stripe_session_id, stripe_invoice_id=stripe_invoice_id
|
stripe_session_id=stripe_session_id, stripe_invoice_id=stripe_invoice_id
|
||||||
)
|
)
|
||||||
|
|
|
@ -25,7 +25,6 @@ from corporate.lib.activity import (
|
||||||
realm_support_link,
|
realm_support_link,
|
||||||
realm_url_link,
|
realm_url_link,
|
||||||
)
|
)
|
||||||
from corporate.lib.stripe import cents_to_dollar_string
|
|
||||||
from corporate.views.support import get_plan_type_string
|
from corporate.views.support import get_plan_type_string
|
||||||
from zerver.decorator import require_server_admin
|
from zerver.decorator import require_server_admin
|
||||||
from zerver.lib.typed_endpoint import typed_endpoint
|
from zerver.lib.typed_endpoint import typed_endpoint
|
||||||
|
@ -92,6 +91,8 @@ def get_realm_day_counts() -> dict[str, dict[str, Markup]]:
|
||||||
|
|
||||||
|
|
||||||
def realm_summary_table(export: bool) -> str:
|
def realm_summary_table(export: bool) -> str:
|
||||||
|
from corporate.lib.stripe import cents_to_dollar_string
|
||||||
|
|
||||||
now = timezone_now()
|
now = timezone_now()
|
||||||
|
|
||||||
query = SQL(
|
query = SQL(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from dataclasses import asdict, dataclass
|
from dataclasses import asdict, dataclass
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
import orjson
|
import orjson
|
||||||
|
@ -13,13 +14,6 @@ from corporate.lib.decorator import (
|
||||||
authenticated_remote_realm_management_endpoint,
|
authenticated_remote_realm_management_endpoint,
|
||||||
authenticated_remote_server_management_endpoint,
|
authenticated_remote_server_management_endpoint,
|
||||||
)
|
)
|
||||||
from corporate.lib.stripe import (
|
|
||||||
RealmBillingSession,
|
|
||||||
RemoteRealmBillingSession,
|
|
||||||
RemoteServerBillingSession,
|
|
||||||
get_configured_fixed_price_plan_offer,
|
|
||||||
get_free_trial_days,
|
|
||||||
)
|
|
||||||
from corporate.models import CustomerPlan, get_current_plan_by_customer, get_customer_by_realm
|
from corporate.models import CustomerPlan, get_current_plan_by_customer, get_customer_by_realm
|
||||||
from zerver.context_processors import get_realm_from_request, latest_info_context
|
from zerver.context_processors import get_realm_from_request, latest_info_context
|
||||||
from zerver.decorator import add_google_analytics, zulip_login_required
|
from zerver.decorator import add_google_analytics, zulip_login_required
|
||||||
|
@ -33,6 +27,9 @@ from zerver.lib.subdomains import is_subdomain_root_or_alias
|
||||||
from zerver.lib.typed_endpoint import typed_endpoint
|
from zerver.lib.typed_endpoint import typed_endpoint
|
||||||
from zerver.models import Realm
|
from zerver.models import Realm
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession
|
||||||
|
|
||||||
|
|
||||||
@add_google_analytics
|
@add_google_analytics
|
||||||
def apps_view(request: HttpRequest, platform: str | None = None) -> HttpResponse:
|
def apps_view(request: HttpRequest, platform: str | None = None) -> HttpResponse:
|
||||||
|
@ -95,6 +92,8 @@ class PlansPageContext:
|
||||||
|
|
||||||
@add_google_analytics
|
@add_google_analytics
|
||||||
def plans_view(request: HttpRequest) -> HttpResponse:
|
def plans_view(request: HttpRequest) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import get_free_trial_days
|
||||||
|
|
||||||
realm = get_realm_from_request(request)
|
realm = get_realm_from_request(request)
|
||||||
context = PlansPageContext(
|
context = PlansPageContext(
|
||||||
is_cloud_realm=True,
|
is_cloud_realm=True,
|
||||||
|
@ -138,8 +137,10 @@ def plans_view(request: HttpRequest) -> HttpResponse:
|
||||||
@add_google_analytics
|
@add_google_analytics
|
||||||
@authenticated_remote_realm_management_endpoint
|
@authenticated_remote_realm_management_endpoint
|
||||||
def remote_realm_plans_page(
|
def remote_realm_plans_page(
|
||||||
request: HttpRequest, billing_session: RemoteRealmBillingSession
|
request: HttpRequest, billing_session: "RemoteRealmBillingSession"
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import get_configured_fixed_price_plan_offer, get_free_trial_days
|
||||||
|
|
||||||
customer = billing_session.get_customer()
|
customer = billing_session.get_customer()
|
||||||
context = PlansPageContext(
|
context = PlansPageContext(
|
||||||
is_self_hosted_realm=True,
|
is_self_hosted_realm=True,
|
||||||
|
@ -205,8 +206,10 @@ def remote_realm_plans_page(
|
||||||
@add_google_analytics
|
@add_google_analytics
|
||||||
@authenticated_remote_server_management_endpoint
|
@authenticated_remote_server_management_endpoint
|
||||||
def remote_server_plans_page(
|
def remote_server_plans_page(
|
||||||
request: HttpRequest, billing_session: RemoteServerBillingSession
|
request: HttpRequest, billing_session: "RemoteServerBillingSession"
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import get_configured_fixed_price_plan_offer, get_free_trial_days
|
||||||
|
|
||||||
customer = billing_session.get_customer()
|
customer = billing_session.get_customer()
|
||||||
context = PlansPageContext(
|
context = PlansPageContext(
|
||||||
is_self_hosted_realm=True,
|
is_self_hosted_realm=True,
|
||||||
|
@ -377,6 +380,8 @@ def communities_view(request: HttpRequest) -> HttpResponse:
|
||||||
|
|
||||||
@zulip_login_required
|
@zulip_login_required
|
||||||
def invoices_page(request: HttpRequest) -> HttpResponseRedirect:
|
def invoices_page(request: HttpRequest) -> HttpResponseRedirect:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
user = request.user
|
user = request.user
|
||||||
assert user.is_authenticated
|
assert user.is_authenticated
|
||||||
|
|
||||||
|
@ -390,7 +395,7 @@ def invoices_page(request: HttpRequest) -> HttpResponseRedirect:
|
||||||
|
|
||||||
@authenticated_remote_realm_management_endpoint
|
@authenticated_remote_realm_management_endpoint
|
||||||
def remote_realm_invoices_page(
|
def remote_realm_invoices_page(
|
||||||
request: HttpRequest, billing_session: RemoteRealmBillingSession
|
request: HttpRequest, billing_session: "RemoteRealmBillingSession"
|
||||||
) -> HttpResponseRedirect:
|
) -> HttpResponseRedirect:
|
||||||
list_invoices_session_url = billing_session.get_past_invoices_session_url()
|
list_invoices_session_url = billing_session.get_past_invoices_session_url()
|
||||||
return HttpResponseRedirect(list_invoices_session_url)
|
return HttpResponseRedirect(list_invoices_session_url)
|
||||||
|
@ -398,7 +403,7 @@ def remote_realm_invoices_page(
|
||||||
|
|
||||||
@authenticated_remote_server_management_endpoint
|
@authenticated_remote_server_management_endpoint
|
||||||
def remote_server_invoices_page(
|
def remote_server_invoices_page(
|
||||||
request: HttpRequest, billing_session: RemoteServerBillingSession
|
request: HttpRequest, billing_session: "RemoteServerBillingSession"
|
||||||
) -> HttpResponseRedirect:
|
) -> HttpResponseRedirect:
|
||||||
list_invoices_session_url = billing_session.get_past_invoices_session_url()
|
list_invoices_session_url = billing_session.get_past_invoices_session_url()
|
||||||
return HttpResponseRedirect(list_invoices_session_url)
|
return HttpResponseRedirect(list_invoices_session_url)
|
||||||
|
@ -414,6 +419,8 @@ def customer_portal(
|
||||||
tier: Json[int] | None = None,
|
tier: Json[int] | None = None,
|
||||||
setup_payment_by_invoice: Json[bool] = False,
|
setup_payment_by_invoice: Json[bool] = False,
|
||||||
) -> HttpResponseRedirect:
|
) -> HttpResponseRedirect:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
user = request.user
|
user = request.user
|
||||||
assert user.is_authenticated
|
assert user.is_authenticated
|
||||||
|
|
||||||
|
@ -427,11 +434,11 @@ def customer_portal(
|
||||||
return HttpResponseRedirect(review_billing_information_url)
|
return HttpResponseRedirect(review_billing_information_url)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_realm_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_realm_management_endpoint
|
||||||
def remote_realm_customer_portal(
|
def remote_realm_customer_portal(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteRealmBillingSession,
|
billing_session: "RemoteRealmBillingSession",
|
||||||
*,
|
*,
|
||||||
return_to_billing_page: Json[bool] = False,
|
return_to_billing_page: Json[bool] = False,
|
||||||
manual_license_management: Json[bool] = False,
|
manual_license_management: Json[bool] = False,
|
||||||
|
@ -444,11 +451,11 @@ def remote_realm_customer_portal(
|
||||||
return HttpResponseRedirect(review_billing_information_url)
|
return HttpResponseRedirect(review_billing_information_url)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_server_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_server_management_endpoint
|
||||||
def remote_server_customer_portal(
|
def remote_server_customer_portal(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteServerBillingSession,
|
billing_session: "RemoteServerBillingSession",
|
||||||
*,
|
*,
|
||||||
return_to_billing_page: Json[bool] = False,
|
return_to_billing_page: Json[bool] = False,
|
||||||
manual_license_management: Json[bool] = False,
|
manual_license_management: Json[bool] = False,
|
||||||
|
|
|
@ -16,7 +16,6 @@ from corporate.lib.activity import (
|
||||||
remote_installation_stats_link,
|
remote_installation_stats_link,
|
||||||
remote_installation_support_link,
|
remote_installation_support_link,
|
||||||
)
|
)
|
||||||
from corporate.lib.stripe import cents_to_dollar_string
|
|
||||||
from zerver.decorator import require_server_admin
|
from zerver.decorator import require_server_admin
|
||||||
from zerver.models.realms import get_org_type_display_name
|
from zerver.models.realms import get_org_type_display_name
|
||||||
from zilencer.models import get_remote_customer_user_count
|
from zilencer.models import get_remote_customer_user_count
|
||||||
|
@ -24,6 +23,8 @@ from zilencer.models import get_remote_customer_user_count
|
||||||
|
|
||||||
@require_server_admin
|
@require_server_admin
|
||||||
def get_remote_server_activity(request: HttpRequest) -> HttpResponse:
|
def get_remote_server_activity(request: HttpRequest) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import cents_to_dollar_string
|
||||||
|
|
||||||
title = "Remote servers"
|
title = "Remote servers"
|
||||||
|
|
||||||
query = SQL(
|
query = SQL(
|
||||||
|
|
|
@ -32,11 +32,6 @@ from corporate.lib.remote_billing_util import (
|
||||||
RemoteBillingUserDict,
|
RemoteBillingUserDict,
|
||||||
get_remote_server_and_user_from_session,
|
get_remote_server_and_user_from_session,
|
||||||
)
|
)
|
||||||
from corporate.lib.stripe import (
|
|
||||||
BILLING_SUPPORT_EMAIL,
|
|
||||||
RemoteRealmBillingSession,
|
|
||||||
RemoteServerBillingSession,
|
|
||||||
)
|
|
||||||
from corporate.models import (
|
from corporate.models import (
|
||||||
CustomerPlan,
|
CustomerPlan,
|
||||||
get_current_plan_by_customer,
|
get_current_plan_by_customer,
|
||||||
|
@ -168,6 +163,8 @@ def remote_realm_billing_finalize_login(
|
||||||
This is the endpoint accessed via the billing_access_url, generated by
|
This is the endpoint accessed via the billing_access_url, generated by
|
||||||
remote_realm_billing_entry entry.
|
remote_realm_billing_entry entry.
|
||||||
"""
|
"""
|
||||||
|
from corporate.lib.stripe import RemoteRealmBillingSession
|
||||||
|
|
||||||
if request.method not in ["GET", "POST"]:
|
if request.method not in ["GET", "POST"]:
|
||||||
return HttpResponseNotAllowed(["GET", "POST"])
|
return HttpResponseNotAllowed(["GET", "POST"])
|
||||||
tos_consent_given = tos_consent == "true"
|
tos_consent_given = tos_consent == "true"
|
||||||
|
@ -375,6 +372,8 @@ def remote_realm_billing_confirm_email(
|
||||||
a fully authenticated session.
|
a fully authenticated session.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from corporate.lib.stripe import BILLING_SUPPORT_EMAIL
|
||||||
|
|
||||||
identity_dict = get_identity_dict_from_signed_access_token(signed_billing_access_token)
|
identity_dict = get_identity_dict_from_signed_access_token(signed_billing_access_token)
|
||||||
try:
|
try:
|
||||||
remote_server = get_remote_server_by_uuid(identity_dict["remote_server_uuid"])
|
remote_server = get_remote_server_by_uuid(identity_dict["remote_server_uuid"])
|
||||||
|
@ -600,6 +599,8 @@ def remote_billing_legacy_server_confirm_login(
|
||||||
a fully authenticated session.
|
a fully authenticated session.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from corporate.lib.stripe import BILLING_SUPPORT_EMAIL
|
||||||
|
|
||||||
try:
|
try:
|
||||||
remote_server, remote_billing_user = get_remote_server_and_user_from_session(
|
remote_server, remote_billing_user = get_remote_server_and_user_from_session(
|
||||||
request, server_uuid=server_uuid
|
request, server_uuid=server_uuid
|
||||||
|
@ -681,6 +682,8 @@ def remote_billing_legacy_server_from_login_confirmation_link(
|
||||||
"""
|
"""
|
||||||
The user comes here via the confirmation link they received via email.
|
The user comes here via the confirmation link they received via email.
|
||||||
"""
|
"""
|
||||||
|
from corporate.lib.stripe import RemoteServerBillingSession
|
||||||
|
|
||||||
if request.method not in ["GET", "POST"]:
|
if request.method not in ["GET", "POST"]:
|
||||||
return HttpResponseNotAllowed(["GET", "POST"])
|
return HttpResponseNotAllowed(["GET", "POST"])
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from pydantic import Json
|
from pydantic import Json
|
||||||
|
@ -7,21 +8,21 @@ from corporate.lib.decorator import (
|
||||||
authenticated_remote_realm_management_endpoint,
|
authenticated_remote_realm_management_endpoint,
|
||||||
authenticated_remote_server_management_endpoint,
|
authenticated_remote_server_management_endpoint,
|
||||||
)
|
)
|
||||||
from corporate.lib.stripe import (
|
|
||||||
RealmBillingSession,
|
|
||||||
RemoteRealmBillingSession,
|
|
||||||
RemoteServerBillingSession,
|
|
||||||
)
|
|
||||||
from zerver.decorator import require_billing_access, require_organization_member
|
from zerver.decorator import require_billing_access, require_organization_member
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
from zerver.lib.typed_endpoint import typed_endpoint
|
from zerver.lib.typed_endpoint import typed_endpoint
|
||||||
from zerver.models import UserProfile
|
from zerver.models import UserProfile
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession
|
||||||
|
|
||||||
billing_logger = logging.getLogger("corporate.stripe")
|
billing_logger = logging.getLogger("corporate.stripe")
|
||||||
|
|
||||||
|
|
||||||
@require_billing_access
|
@require_billing_access
|
||||||
def start_card_update_stripe_session(request: HttpRequest, user: UserProfile) -> HttpResponse:
|
def start_card_update_stripe_session(request: HttpRequest, user: UserProfile) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
billing_session = RealmBillingSession(user)
|
billing_session = RealmBillingSession(user)
|
||||||
session_data = billing_session.create_card_update_session()
|
session_data = billing_session.create_card_update_session()
|
||||||
return json_success(
|
return json_success(
|
||||||
|
@ -32,7 +33,7 @@ def start_card_update_stripe_session(request: HttpRequest, user: UserProfile) ->
|
||||||
|
|
||||||
@authenticated_remote_realm_management_endpoint
|
@authenticated_remote_realm_management_endpoint
|
||||||
def start_card_update_stripe_session_for_remote_realm(
|
def start_card_update_stripe_session_for_remote_realm(
|
||||||
request: HttpRequest, billing_session: RemoteRealmBillingSession
|
request: HttpRequest, billing_session: "RemoteRealmBillingSession"
|
||||||
) -> HttpResponse: # nocoverage
|
) -> HttpResponse: # nocoverage
|
||||||
session_data = billing_session.create_card_update_session()
|
session_data = billing_session.create_card_update_session()
|
||||||
return json_success(
|
return json_success(
|
||||||
|
@ -43,7 +44,7 @@ def start_card_update_stripe_session_for_remote_realm(
|
||||||
|
|
||||||
@authenticated_remote_server_management_endpoint
|
@authenticated_remote_server_management_endpoint
|
||||||
def start_card_update_stripe_session_for_remote_server(
|
def start_card_update_stripe_session_for_remote_server(
|
||||||
request: HttpRequest, billing_session: RemoteServerBillingSession
|
request: HttpRequest, billing_session: "RemoteServerBillingSession"
|
||||||
) -> HttpResponse: # nocoverage
|
) -> HttpResponse: # nocoverage
|
||||||
session_data = billing_session.create_card_update_session()
|
session_data = billing_session.create_card_update_session()
|
||||||
return json_success(
|
return json_success(
|
||||||
|
@ -61,6 +62,8 @@ def start_card_update_stripe_session_for_realm_upgrade(
|
||||||
manual_license_management: Json[bool] = False,
|
manual_license_management: Json[bool] = False,
|
||||||
tier: Json[int],
|
tier: Json[int],
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
billing_session = RealmBillingSession(user)
|
billing_session = RealmBillingSession(user)
|
||||||
session_data = billing_session.create_card_update_session_for_upgrade(
|
session_data = billing_session.create_card_update_session_for_upgrade(
|
||||||
manual_license_management, tier
|
manual_license_management, tier
|
||||||
|
@ -71,11 +74,11 @@ def start_card_update_stripe_session_for_realm_upgrade(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_realm_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_realm_management_endpoint
|
||||||
def start_card_update_stripe_session_for_remote_realm_upgrade(
|
def start_card_update_stripe_session_for_remote_realm_upgrade(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteRealmBillingSession,
|
billing_session: "RemoteRealmBillingSession",
|
||||||
*,
|
*,
|
||||||
manual_license_management: Json[bool] = False,
|
manual_license_management: Json[bool] = False,
|
||||||
tier: Json[int],
|
tier: Json[int],
|
||||||
|
@ -89,11 +92,11 @@ def start_card_update_stripe_session_for_remote_realm_upgrade(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_server_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_server_management_endpoint
|
||||||
def start_card_update_stripe_session_for_remote_server_upgrade(
|
def start_card_update_stripe_session_for_remote_server_upgrade(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteServerBillingSession,
|
billing_session: "RemoteServerBillingSession",
|
||||||
*,
|
*,
|
||||||
manual_license_management: Json[bool] = False,
|
manual_license_management: Json[bool] = False,
|
||||||
tier: Json[int],
|
tier: Json[int],
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
@ -6,19 +8,18 @@ from corporate.lib.decorator import (
|
||||||
authenticated_remote_realm_management_endpoint,
|
authenticated_remote_realm_management_endpoint,
|
||||||
authenticated_remote_server_management_endpoint,
|
authenticated_remote_server_management_endpoint,
|
||||||
)
|
)
|
||||||
from corporate.lib.stripe import (
|
|
||||||
RealmBillingSession,
|
|
||||||
RemoteRealmBillingSession,
|
|
||||||
RemoteServerBillingSession,
|
|
||||||
SponsorshipRequestForm,
|
|
||||||
)
|
|
||||||
from zerver.decorator import require_organization_member, zulip_login_required
|
from zerver.decorator import require_organization_member, zulip_login_required
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
from zerver.models import UserProfile
|
from zerver.models import UserProfile
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession
|
||||||
|
|
||||||
|
|
||||||
@zulip_login_required
|
@zulip_login_required
|
||||||
def sponsorship_page(request: HttpRequest) -> HttpResponse:
|
def sponsorship_page(request: HttpRequest) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
user = request.user
|
user = request.user
|
||||||
assert user.is_authenticated
|
assert user.is_authenticated
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ def sponsorship_page(request: HttpRequest) -> HttpResponse:
|
||||||
@authenticated_remote_realm_management_endpoint
|
@authenticated_remote_realm_management_endpoint
|
||||||
def remote_realm_sponsorship_page(
|
def remote_realm_sponsorship_page(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteRealmBillingSession,
|
billing_session: "RemoteRealmBillingSession",
|
||||||
) -> HttpResponse: # nocoverage
|
) -> HttpResponse: # nocoverage
|
||||||
context = billing_session.get_sponsorship_request_context()
|
context = billing_session.get_sponsorship_request_context()
|
||||||
if context is None:
|
if context is None:
|
||||||
|
@ -47,7 +48,7 @@ def remote_realm_sponsorship_page(
|
||||||
@authenticated_remote_server_management_endpoint
|
@authenticated_remote_server_management_endpoint
|
||||||
def remote_server_sponsorship_page(
|
def remote_server_sponsorship_page(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteServerBillingSession,
|
billing_session: "RemoteServerBillingSession",
|
||||||
) -> HttpResponse: # nocoverage
|
) -> HttpResponse: # nocoverage
|
||||||
context = billing_session.get_sponsorship_request_context()
|
context = billing_session.get_sponsorship_request_context()
|
||||||
if context is None:
|
if context is None:
|
||||||
|
@ -63,6 +64,8 @@ def sponsorship(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
user: UserProfile,
|
user: UserProfile,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession, SponsorshipRequestForm
|
||||||
|
|
||||||
billing_session = RealmBillingSession(user)
|
billing_session = RealmBillingSession(user)
|
||||||
post_data = request.POST.copy()
|
post_data = request.POST.copy()
|
||||||
form = SponsorshipRequestForm(post_data)
|
form = SponsorshipRequestForm(post_data)
|
||||||
|
@ -73,8 +76,10 @@ def sponsorship(
|
||||||
@authenticated_remote_realm_management_endpoint
|
@authenticated_remote_realm_management_endpoint
|
||||||
def remote_realm_sponsorship(
|
def remote_realm_sponsorship(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteRealmBillingSession,
|
billing_session: "RemoteRealmBillingSession",
|
||||||
) -> HttpResponse: # nocoverage
|
) -> HttpResponse: # nocoverage
|
||||||
|
from corporate.lib.stripe import SponsorshipRequestForm
|
||||||
|
|
||||||
post_data = request.POST.copy()
|
post_data = request.POST.copy()
|
||||||
form = SponsorshipRequestForm(post_data)
|
form = SponsorshipRequestForm(post_data)
|
||||||
billing_session.request_sponsorship(form)
|
billing_session.request_sponsorship(form)
|
||||||
|
@ -84,8 +89,10 @@ def remote_realm_sponsorship(
|
||||||
@authenticated_remote_server_management_endpoint
|
@authenticated_remote_server_management_endpoint
|
||||||
def remote_server_sponsorship(
|
def remote_server_sponsorship(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteServerBillingSession,
|
billing_session: "RemoteServerBillingSession",
|
||||||
) -> HttpResponse: # nocoverage
|
) -> HttpResponse: # nocoverage
|
||||||
|
from corporate.lib.stripe import SponsorshipRequestForm
|
||||||
|
|
||||||
post_data = request.POST.copy()
|
post_data = request.POST.copy()
|
||||||
form = SponsorshipRequestForm(post_data)
|
form = SponsorshipRequestForm(post_data)
|
||||||
billing_session.request_sponsorship(form)
|
billing_session.request_sponsorship(form)
|
||||||
|
|
|
@ -24,26 +24,6 @@ from confirmation.models import Confirmation, confirmation_url
|
||||||
from confirmation.settings import STATUS_USED
|
from confirmation.settings import STATUS_USED
|
||||||
from corporate.lib.activity import format_optional_datetime, remote_installation_stats_link
|
from corporate.lib.activity import format_optional_datetime, remote_installation_stats_link
|
||||||
from corporate.lib.billing_types import BillingModality
|
from corporate.lib.billing_types import BillingModality
|
||||||
from corporate.lib.stripe import (
|
|
||||||
BILLING_SUPPORT_EMAIL,
|
|
||||||
RealmBillingSession,
|
|
||||||
RemoteRealmBillingSession,
|
|
||||||
RemoteServerBillingSession,
|
|
||||||
ServerDeactivateWithExistingPlanError,
|
|
||||||
SupportRequestError,
|
|
||||||
SupportType,
|
|
||||||
SupportViewRequest,
|
|
||||||
cents_to_dollar_string,
|
|
||||||
do_deactivate_remote_server,
|
|
||||||
do_reactivate_remote_server,
|
|
||||||
)
|
|
||||||
from corporate.lib.support import (
|
|
||||||
CloudSupportData,
|
|
||||||
RemoteSupportData,
|
|
||||||
get_data_for_cloud_support_view,
|
|
||||||
get_data_for_remote_support_view,
|
|
||||||
get_realm_support_url,
|
|
||||||
)
|
|
||||||
from corporate.models import CustomerPlan
|
from corporate.models import CustomerPlan
|
||||||
from zerver.actions.create_realm import do_change_realm_subdomain
|
from zerver.actions.create_realm import do_change_realm_subdomain
|
||||||
from zerver.actions.realm_settings import (
|
from zerver.actions.realm_settings import (
|
||||||
|
@ -118,6 +98,8 @@ class DemoRequestForm(forms.Form):
|
||||||
@zulip_login_required
|
@zulip_login_required
|
||||||
@typed_endpoint_without_parameters
|
@typed_endpoint_without_parameters
|
||||||
def support_request(request: HttpRequest) -> HttpResponse:
|
def support_request(request: HttpRequest) -> HttpResponse:
|
||||||
|
from corporate.lib.support import get_realm_support_url
|
||||||
|
|
||||||
user = request.user
|
user = request.user
|
||||||
assert user.is_authenticated
|
assert user.is_authenticated
|
||||||
|
|
||||||
|
@ -161,6 +143,8 @@ def support_request(request: HttpRequest) -> HttpResponse:
|
||||||
|
|
||||||
@typed_endpoint_without_parameters
|
@typed_endpoint_without_parameters
|
||||||
def demo_request(request: HttpRequest) -> HttpResponse:
|
def demo_request(request: HttpRequest) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import BILLING_SUPPORT_EMAIL
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"MAX_INPUT_LENGTH": DemoRequestForm.MAX_INPUT_LENGTH,
|
"MAX_INPUT_LENGTH": DemoRequestForm.MAX_INPUT_LENGTH,
|
||||||
"SORTED_ORG_TYPE_NAMES": DemoRequestForm.SORTED_ORG_TYPE_NAMES,
|
"SORTED_ORG_TYPE_NAMES": DemoRequestForm.SORTED_ORG_TYPE_NAMES,
|
||||||
|
@ -340,11 +324,15 @@ ModifyPlan = Literal[
|
||||||
|
|
||||||
RemoteServerStatus = Literal["active", "deactivated"]
|
RemoteServerStatus = Literal["active", "deactivated"]
|
||||||
|
|
||||||
SHARED_SUPPORT_CONTEXT = {
|
|
||||||
"get_org_type_display_name": get_org_type_display_name,
|
def shared_support_context() -> dict[str, object]:
|
||||||
"get_plan_type_name": get_plan_type_string,
|
from corporate.lib.stripe import cents_to_dollar_string
|
||||||
"dollar_amount": cents_to_dollar_string,
|
|
||||||
}
|
return {
|
||||||
|
"get_org_type_display_name": get_org_type_display_name,
|
||||||
|
"get_plan_type_name": get_plan_type_string,
|
||||||
|
"dollar_amount": cents_to_dollar_string,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@require_server_admin
|
@require_server_admin
|
||||||
|
@ -370,7 +358,15 @@ def support(
|
||||||
org_type: Json[NonNegativeInt] | None = None,
|
org_type: Json[NonNegativeInt] | None = None,
|
||||||
max_invites: Json[NonNegativeInt] | None = None,
|
max_invites: Json[NonNegativeInt] | None = None,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
context: dict[str, Any] = {**SHARED_SUPPORT_CONTEXT}
|
from corporate.lib.stripe import (
|
||||||
|
RealmBillingSession,
|
||||||
|
SupportRequestError,
|
||||||
|
SupportType,
|
||||||
|
SupportViewRequest,
|
||||||
|
)
|
||||||
|
from corporate.lib.support import CloudSupportData, get_data_for_cloud_support_view
|
||||||
|
|
||||||
|
context = shared_support_context()
|
||||||
|
|
||||||
if "success_message" in request.session:
|
if "success_message" in request.session:
|
||||||
context["success_message"] = request.session["success_message"]
|
context["success_message"] = request.session["success_message"]
|
||||||
|
@ -692,7 +688,19 @@ def remote_servers_support(
|
||||||
]
|
]
|
||||||
| None = None,
|
| None = None,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
context: dict[str, Any] = {**SHARED_SUPPORT_CONTEXT}
|
from corporate.lib.stripe import (
|
||||||
|
RemoteRealmBillingSession,
|
||||||
|
RemoteServerBillingSession,
|
||||||
|
ServerDeactivateWithExistingPlanError,
|
||||||
|
SupportRequestError,
|
||||||
|
SupportType,
|
||||||
|
SupportViewRequest,
|
||||||
|
do_deactivate_remote_server,
|
||||||
|
do_reactivate_remote_server,
|
||||||
|
)
|
||||||
|
from corporate.lib.support import RemoteSupportData, get_data_for_remote_support_view
|
||||||
|
|
||||||
|
context = shared_support_context()
|
||||||
|
|
||||||
if "success_message" in request.session:
|
if "success_message" in request.session:
|
||||||
context["success_message"] = request.session["success_message"]
|
context["success_message"] = request.session["success_message"]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
||||||
|
@ -10,14 +11,6 @@ from corporate.lib.decorator import (
|
||||||
authenticated_remote_realm_management_endpoint,
|
authenticated_remote_realm_management_endpoint,
|
||||||
authenticated_remote_server_management_endpoint,
|
authenticated_remote_server_management_endpoint,
|
||||||
)
|
)
|
||||||
from corporate.lib.stripe import (
|
|
||||||
BillingError,
|
|
||||||
InitialUpgradeRequest,
|
|
||||||
RealmBillingSession,
|
|
||||||
RemoteRealmBillingSession,
|
|
||||||
RemoteServerBillingSession,
|
|
||||||
UpgradeRequest,
|
|
||||||
)
|
|
||||||
from corporate.models import CustomerPlan
|
from corporate.models import CustomerPlan
|
||||||
from zerver.decorator import require_organization_member, zulip_login_required
|
from zerver.decorator import require_organization_member, zulip_login_required
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
|
@ -25,6 +18,9 @@ from zerver.lib.typed_endpoint import typed_endpoint
|
||||||
from zerver.models import UserProfile
|
from zerver.models import UserProfile
|
||||||
from zilencer.lib.remote_counts import MissingDataError
|
from zilencer.lib.remote_counts import MissingDataError
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession
|
||||||
|
|
||||||
billing_logger = logging.getLogger("corporate.stripe")
|
billing_logger = logging.getLogger("corporate.stripe")
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,6 +38,8 @@ def upgrade(
|
||||||
licenses: Json[int] | None = None,
|
licenses: Json[int] | None = None,
|
||||||
tier: Json[int] = CustomerPlan.TIER_CLOUD_STANDARD,
|
tier: Json[int] = CustomerPlan.TIER_CLOUD_STANDARD,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import BillingError, RealmBillingSession, UpgradeRequest
|
||||||
|
|
||||||
try:
|
try:
|
||||||
upgrade_request = UpgradeRequest(
|
upgrade_request = UpgradeRequest(
|
||||||
billing_modality=billing_modality,
|
billing_modality=billing_modality,
|
||||||
|
@ -77,11 +75,11 @@ def upgrade(
|
||||||
raise BillingError(error_description, error_message)
|
raise BillingError(error_description, error_message)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_realm_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_realm_management_endpoint
|
||||||
def remote_realm_upgrade(
|
def remote_realm_upgrade(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteRealmBillingSession,
|
billing_session: "RemoteRealmBillingSession",
|
||||||
*,
|
*,
|
||||||
billing_modality: BillingModality,
|
billing_modality: BillingModality,
|
||||||
schedule: BillingSchedule,
|
schedule: BillingSchedule,
|
||||||
|
@ -92,6 +90,8 @@ def remote_realm_upgrade(
|
||||||
remote_server_plan_start_date: str | None = None,
|
remote_server_plan_start_date: str | None = None,
|
||||||
tier: Json[int] = CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
tier: Json[int] = CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import BillingError, UpgradeRequest
|
||||||
|
|
||||||
try:
|
try:
|
||||||
upgrade_request = UpgradeRequest(
|
upgrade_request = UpgradeRequest(
|
||||||
billing_modality=billing_modality,
|
billing_modality=billing_modality,
|
||||||
|
@ -125,11 +125,11 @@ def remote_realm_upgrade(
|
||||||
raise BillingError(error_description, error_message)
|
raise BillingError(error_description, error_message)
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_server_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_server_management_endpoint
|
||||||
def remote_server_upgrade(
|
def remote_server_upgrade(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteServerBillingSession,
|
billing_session: "RemoteServerBillingSession",
|
||||||
*,
|
*,
|
||||||
billing_modality: BillingModality,
|
billing_modality: BillingModality,
|
||||||
schedule: BillingSchedule,
|
schedule: BillingSchedule,
|
||||||
|
@ -140,6 +140,8 @@ def remote_server_upgrade(
|
||||||
remote_server_plan_start_date: str | None = None,
|
remote_server_plan_start_date: str | None = None,
|
||||||
tier: Json[int] = CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
tier: Json[int] = CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import BillingError, UpgradeRequest
|
||||||
|
|
||||||
try:
|
try:
|
||||||
upgrade_request = UpgradeRequest(
|
upgrade_request = UpgradeRequest(
|
||||||
billing_modality=billing_modality,
|
billing_modality=billing_modality,
|
||||||
|
@ -182,6 +184,8 @@ def upgrade_page(
|
||||||
tier: Json[int] = CustomerPlan.TIER_CLOUD_STANDARD,
|
tier: Json[int] = CustomerPlan.TIER_CLOUD_STANDARD,
|
||||||
setup_payment_by_invoice: Json[bool] = False,
|
setup_payment_by_invoice: Json[bool] = False,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import InitialUpgradeRequest, RealmBillingSession
|
||||||
|
|
||||||
user = request.user
|
user = request.user
|
||||||
assert user.is_authenticated
|
assert user.is_authenticated
|
||||||
|
|
||||||
|
@ -207,17 +211,19 @@ def upgrade_page(
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_realm_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_realm_management_endpoint
|
||||||
def remote_realm_upgrade_page(
|
def remote_realm_upgrade_page(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteRealmBillingSession,
|
billing_session: "RemoteRealmBillingSession",
|
||||||
*,
|
*,
|
||||||
manual_license_management: Json[bool] = False,
|
manual_license_management: Json[bool] = False,
|
||||||
success_message: str = "",
|
success_message: str = "",
|
||||||
tier: str = str(CustomerPlan.TIER_SELF_HOSTED_BUSINESS),
|
tier: str = str(CustomerPlan.TIER_SELF_HOSTED_BUSINESS),
|
||||||
setup_payment_by_invoice: Json[bool] = False,
|
setup_payment_by_invoice: Json[bool] = False,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import InitialUpgradeRequest
|
||||||
|
|
||||||
billing_modality = "charge_automatically"
|
billing_modality = "charge_automatically"
|
||||||
if setup_payment_by_invoice: # nocoverage
|
if setup_payment_by_invoice: # nocoverage
|
||||||
billing_modality = "send_invoice"
|
billing_modality = "send_invoice"
|
||||||
|
@ -240,17 +246,19 @@ def remote_realm_upgrade_page(
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@authenticated_remote_server_management_endpoint
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
|
@authenticated_remote_server_management_endpoint
|
||||||
def remote_server_upgrade_page(
|
def remote_server_upgrade_page(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
billing_session: RemoteServerBillingSession,
|
billing_session: "RemoteServerBillingSession",
|
||||||
*,
|
*,
|
||||||
manual_license_management: Json[bool] = False,
|
manual_license_management: Json[bool] = False,
|
||||||
success_message: str = "",
|
success_message: str = "",
|
||||||
tier: str = str(CustomerPlan.TIER_SELF_HOSTED_BUSINESS),
|
tier: str = str(CustomerPlan.TIER_SELF_HOSTED_BUSINESS),
|
||||||
setup_payment_by_invoice: Json[bool] = False,
|
setup_payment_by_invoice: Json[bool] = False,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import InitialUpgradeRequest
|
||||||
|
|
||||||
billing_modality = "charge_automatically"
|
billing_modality = "charge_automatically"
|
||||||
if setup_payment_by_invoice: # nocoverage
|
if setup_payment_by_invoice: # nocoverage
|
||||||
billing_modality = "send_invoice"
|
billing_modality = "send_invoice"
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import stripe
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
from corporate.lib.stripe import STRIPE_API_VERSION
|
|
||||||
from corporate.lib.stripe_event_handler import (
|
|
||||||
handle_checkout_session_completed_event,
|
|
||||||
handle_invoice_paid_event,
|
|
||||||
)
|
|
||||||
from corporate.models import Event, Invoice, Session
|
from corporate.models import Event, Invoice, Session
|
||||||
from zproject.config import get_secret
|
from zproject.config import get_secret
|
||||||
|
|
||||||
|
@ -20,6 +14,14 @@ billing_logger = logging.getLogger("corporate.stripe")
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def stripe_webhook(request: HttpRequest) -> HttpResponse:
|
def stripe_webhook(request: HttpRequest) -> HttpResponse:
|
||||||
|
import stripe
|
||||||
|
|
||||||
|
from corporate.lib.stripe import STRIPE_API_VERSION
|
||||||
|
from corporate.lib.stripe_event_handler import (
|
||||||
|
handle_checkout_session_completed_event,
|
||||||
|
handle_invoice_paid_event,
|
||||||
|
)
|
||||||
|
|
||||||
stripe_webhook_endpoint_secret = get_secret("stripe_webhook_endpoint_secret", "")
|
stripe_webhook_endpoint_secret = get_secret("stripe_webhook_endpoint_secret", "")
|
||||||
if (
|
if (
|
||||||
stripe_webhook_endpoint_secret and not settings.TEST_SUITE
|
stripe_webhook_endpoint_secret and not settings.TEST_SUITE
|
||||||
|
|
|
@ -48,9 +48,6 @@ from zerver.models.realms import (
|
||||||
from zerver.models.users import get_system_bot
|
from zerver.models.users import get_system_bot
|
||||||
from zproject.backends import all_default_backend_names
|
from zproject.backends import all_default_backend_names
|
||||||
|
|
||||||
if settings.CORPORATE_ENABLED:
|
|
||||||
from corporate.lib.support import get_realm_support_url
|
|
||||||
|
|
||||||
|
|
||||||
def do_change_realm_subdomain(
|
def do_change_realm_subdomain(
|
||||||
realm: Realm,
|
realm: Realm,
|
||||||
|
@ -372,6 +369,8 @@ def do_create_realm(
|
||||||
|
|
||||||
# Send a notification to the admin realm when a new organization registers.
|
# Send a notification to the admin realm when a new organization registers.
|
||||||
if settings.CORPORATE_ENABLED:
|
if settings.CORPORATE_ENABLED:
|
||||||
|
from corporate.lib.support import get_realm_support_url
|
||||||
|
|
||||||
admin_realm = get_realm(settings.SYSTEM_BOT_REALM)
|
admin_realm = get_realm(settings.SYSTEM_BOT_REALM)
|
||||||
sender = get_system_bot(settings.NOTIFICATION_BOT, admin_realm.id)
|
sender = get_system_bot(settings.NOTIFICATION_BOT, admin_realm.id)
|
||||||
|
|
||||||
|
|
|
@ -62,10 +62,6 @@ from zerver.models.realm_audit_logs import AuditLogEventType
|
||||||
from zerver.models.users import active_user_ids, bot_owner_user_ids, get_system_bot
|
from zerver.models.users import active_user_ids, bot_owner_user_ids, get_system_bot
|
||||||
from zerver.tornado.django_api import send_event_on_commit
|
from zerver.tornado.django_api import send_event_on_commit
|
||||||
|
|
||||||
if settings.BILLING_ENABLED:
|
|
||||||
from corporate.lib.stripe import RealmBillingSession
|
|
||||||
|
|
||||||
|
|
||||||
MAX_NUM_RECENT_MESSAGES = 1000
|
MAX_NUM_RECENT_MESSAGES = 1000
|
||||||
MAX_NUM_RECENT_UNREAD_MESSAGES = 20
|
MAX_NUM_RECENT_UNREAD_MESSAGES = 20
|
||||||
|
|
||||||
|
@ -512,6 +508,9 @@ def do_create_user(
|
||||||
email_address_visibility: int | None = None,
|
email_address_visibility: int | None = None,
|
||||||
add_initial_stream_subscriptions: bool = True,
|
add_initial_stream_subscriptions: bool = True,
|
||||||
) -> UserProfile:
|
) -> UserProfile:
|
||||||
|
if settings.BILLING_ENABLED:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
user_profile = create_user(
|
user_profile = create_user(
|
||||||
email=email,
|
email=email,
|
||||||
|
@ -643,6 +642,9 @@ def do_activate_mirror_dummy_user(
|
||||||
parallel code path to do_create_user; e.g. it likely does not
|
parallel code path to do_create_user; e.g. it likely does not
|
||||||
handle preferences or default streams properly.
|
handle preferences or default streams properly.
|
||||||
"""
|
"""
|
||||||
|
if settings.BILLING_ENABLED:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
change_user_is_active(user_profile, True)
|
change_user_is_active(user_profile, True)
|
||||||
user_profile.is_mirror_dummy = False
|
user_profile.is_mirror_dummy = False
|
||||||
|
@ -714,6 +716,8 @@ def do_reactivate_user(user_profile: UserProfile, *, acting_user: UserProfile |
|
||||||
bot_owner_changed = True
|
bot_owner_changed = True
|
||||||
|
|
||||||
if settings.BILLING_ENABLED:
|
if settings.BILLING_ENABLED:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
billing_session = RealmBillingSession(user=user_profile, realm=user_profile.realm)
|
billing_session = RealmBillingSession(user=user_profile, realm=user_profile.realm)
|
||||||
billing_session.update_license_ledger_if_needed(event_time)
|
billing_session.update_license_ledger_if_needed(event_time)
|
||||||
|
|
||||||
|
|
|
@ -51,9 +51,6 @@ from zerver.models.realms import get_default_max_invites_for_realm_plan_type, ge
|
||||||
from zerver.models.users import active_user_ids
|
from zerver.models.users import active_user_ids
|
||||||
from zerver.tornado.django_api import send_event_on_commit
|
from zerver.tornado.django_api import send_event_on_commit
|
||||||
|
|
||||||
if settings.BILLING_ENABLED:
|
|
||||||
from corporate.lib.stripe import RealmBillingSession
|
|
||||||
|
|
||||||
|
|
||||||
@transaction.atomic(savepoint=False)
|
@transaction.atomic(savepoint=False)
|
||||||
def do_set_realm_property(
|
def do_set_realm_property(
|
||||||
|
@ -516,6 +513,9 @@ def do_deactivate_realm(
|
||||||
if realm.deactivated:
|
if realm.deactivated:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if settings.BILLING_ENABLED:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
realm.deactivated = True
|
realm.deactivated = True
|
||||||
realm.save(update_fields=["deactivated"])
|
realm.save(update_fields=["deactivated"])
|
||||||
|
@ -623,6 +623,8 @@ def do_delete_all_realm_attachments(realm: Realm, *, batch_size: int = 1000) ->
|
||||||
|
|
||||||
def do_scrub_realm(realm: Realm, *, acting_user: UserProfile | None) -> None:
|
def do_scrub_realm(realm: Realm, *, acting_user: UserProfile | None) -> None:
|
||||||
if settings.BILLING_ENABLED:
|
if settings.BILLING_ENABLED:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
billing_session = RealmBillingSession(user=acting_user, realm=realm)
|
billing_session = RealmBillingSession(user=acting_user, realm=realm)
|
||||||
billing_session.downgrade_now_without_creating_additional_invoices()
|
billing_session.downgrade_now_without_creating_additional_invoices()
|
||||||
|
|
||||||
|
|
|
@ -53,9 +53,6 @@ from zerver.models.users import (
|
||||||
)
|
)
|
||||||
from zerver.tornado.django_api import send_event_on_commit
|
from zerver.tornado.django_api import send_event_on_commit
|
||||||
|
|
||||||
if settings.BILLING_ENABLED:
|
|
||||||
from corporate.lib.stripe import RealmBillingSession
|
|
||||||
|
|
||||||
|
|
||||||
def do_delete_user(user_profile: UserProfile, *, acting_user: UserProfile | None) -> None:
|
def do_delete_user(user_profile: UserProfile, *, acting_user: UserProfile | None) -> None:
|
||||||
if user_profile.realm.is_zephyr_mirror_realm:
|
if user_profile.realm.is_zephyr_mirror_realm:
|
||||||
|
@ -324,6 +321,9 @@ def do_deactivate_user(
|
||||||
if not user_profile.is_active:
|
if not user_profile.is_active:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if settings.BILLING_ENABLED:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
if _cascade:
|
if _cascade:
|
||||||
# We need to deactivate bots before the target user, to ensure
|
# We need to deactivate bots before the target user, to ensure
|
||||||
# that a failure partway through this function cannot result
|
# that a failure partway through this function cannot result
|
||||||
|
@ -474,6 +474,8 @@ def do_change_user_role(
|
||||||
)
|
)
|
||||||
maybe_enqueue_audit_log_upload(user_profile.realm)
|
maybe_enqueue_audit_log_upload(user_profile.realm)
|
||||||
if settings.BILLING_ENABLED and UserProfile.ROLE_GUEST in [old_value, value]:
|
if settings.BILLING_ENABLED and UserProfile.ROLE_GUEST in [old_value, value]:
|
||||||
|
from corporate.lib.stripe import RealmBillingSession
|
||||||
|
|
||||||
billing_session = RealmBillingSession(user=user_profile, realm=user_profile.realm)
|
billing_session = RealmBillingSession(user=user_profile, realm=user_profile.realm)
|
||||||
billing_session.update_license_ledger_if_needed(timezone_now())
|
billing_session.update_license_ledger_if_needed(timezone_now())
|
||||||
|
|
||||||
|
|
|
@ -46,10 +46,6 @@ from zerver.models.realms import (
|
||||||
from zerver.models.users import get_user_by_delivery_email, is_cross_realm_bot_email
|
from zerver.models.users import get_user_by_delivery_email, is_cross_realm_bot_email
|
||||||
from zproject.backends import check_password_strength, email_auth_enabled, email_belongs_to_ldap
|
from zproject.backends import check_password_strength, email_auth_enabled, email_belongs_to_ldap
|
||||||
|
|
||||||
if settings.BILLING_ENABLED:
|
|
||||||
from corporate.lib.registration import check_spare_licenses_available_for_registering_new_user
|
|
||||||
from corporate.lib.stripe import LicenseLimitError
|
|
||||||
|
|
||||||
# We don't mark this error for translation, because it's displayed
|
# We don't mark this error for translation, because it's displayed
|
||||||
# only to MIT users.
|
# only to MIT users.
|
||||||
MIT_VALIDATION_ERROR = Markup(
|
MIT_VALIDATION_ERROR = Markup(
|
||||||
|
@ -302,6 +298,11 @@ class HomepageForm(forms.Form):
|
||||||
email_is_not_mit_mailing_list(email)
|
email_is_not_mit_mailing_list(email)
|
||||||
|
|
||||||
if settings.BILLING_ENABLED:
|
if settings.BILLING_ENABLED:
|
||||||
|
from corporate.lib.registration import (
|
||||||
|
check_spare_licenses_available_for_registering_new_user,
|
||||||
|
)
|
||||||
|
from corporate.lib.stripe import LicenseLimitError
|
||||||
|
|
||||||
role = self.invited_as if self.invited_as is not None else UserProfile.ROLE_MEMBER
|
role = self.invited_as if self.invited_as is not None else UserProfile.ROLE_MEMBER
|
||||||
try:
|
try:
|
||||||
check_spare_licenses_available_for_registering_new_user(realm, email, role=role)
|
check_spare_licenses_available_for_registering_new_user(realm, email, role=role)
|
||||||
|
|
|
@ -2372,7 +2372,7 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
||||||
|
|
||||||
with (
|
with (
|
||||||
mock.patch(
|
mock.patch(
|
||||||
"zilencer.views.RemoteRealmBillingSession.get_customer", return_value=None
|
"corporate.lib.stripe.RemoteRealmBillingSession.get_customer", return_value=None
|
||||||
) as m,
|
) as m,
|
||||||
mock.patch(
|
mock.patch(
|
||||||
"corporate.lib.stripe.RemoteRealmBillingSession.current_count_for_billed_licenses",
|
"corporate.lib.stripe.RemoteRealmBillingSession.current_count_for_billed_licenses",
|
||||||
|
@ -2404,7 +2404,8 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
||||||
dummy_customer = mock.MagicMock()
|
dummy_customer = mock.MagicMock()
|
||||||
with (
|
with (
|
||||||
mock.patch(
|
mock.patch(
|
||||||
"zilencer.views.RemoteRealmBillingSession.get_customer", return_value=dummy_customer
|
"corporate.lib.stripe.RemoteRealmBillingSession.get_customer",
|
||||||
|
return_value=dummy_customer,
|
||||||
),
|
),
|
||||||
mock.patch("corporate.lib.stripe.get_current_plan_by_customer", return_value=None) as m,
|
mock.patch("corporate.lib.stripe.get_current_plan_by_customer", return_value=None) as m,
|
||||||
mock.patch(
|
mock.patch(
|
||||||
|
@ -2425,7 +2426,8 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
||||||
|
|
||||||
with (
|
with (
|
||||||
mock.patch(
|
mock.patch(
|
||||||
"zilencer.views.RemoteRealmBillingSession.get_customer", return_value=dummy_customer
|
"corporate.lib.stripe.RemoteRealmBillingSession.get_customer",
|
||||||
|
return_value=dummy_customer,
|
||||||
),
|
),
|
||||||
mock.patch("corporate.lib.stripe.get_current_plan_by_customer", return_value=None),
|
mock.patch("corporate.lib.stripe.get_current_plan_by_customer", return_value=None),
|
||||||
mock.patch(
|
mock.patch(
|
||||||
|
@ -2586,7 +2588,9 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
||||||
"corporate.lib.stripe.RemoteServerBillingSession.get_customer",
|
"corporate.lib.stripe.RemoteServerBillingSession.get_customer",
|
||||||
return_value=dummy_remote_server_customer,
|
return_value=dummy_remote_server_customer,
|
||||||
),
|
),
|
||||||
mock.patch("zilencer.views.RemoteServerBillingSession.sync_license_ledger_if_needed"),
|
mock.patch(
|
||||||
|
"corporate.lib.stripe.RemoteServerBillingSession.sync_license_ledger_if_needed"
|
||||||
|
),
|
||||||
mock.patch(
|
mock.patch(
|
||||||
"corporate.lib.stripe.get_current_plan_by_customer",
|
"corporate.lib.stripe.get_current_plan_by_customer",
|
||||||
side_effect=get_current_plan_by_customer,
|
side_effect=get_current_plan_by_customer,
|
||||||
|
@ -2702,7 +2706,9 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
||||||
# of a deleted realm.
|
# of a deleted realm.
|
||||||
with (
|
with (
|
||||||
self.assertLogs(logger, level="WARNING") as analytics_logger,
|
self.assertLogs(logger, level="WARNING") as analytics_logger,
|
||||||
mock.patch("zilencer.views.RemoteRealmBillingSession.on_paid_plan", return_value=True),
|
mock.patch(
|
||||||
|
"corporate.lib.stripe.RemoteRealmBillingSession.on_paid_plan", return_value=True
|
||||||
|
),
|
||||||
):
|
):
|
||||||
# This time the logger shouldn't get triggered - because the bouncer doesn't
|
# This time the logger shouldn't get triggered - because the bouncer doesn't
|
||||||
# include .realm_locally_deleted realms in its response.
|
# include .realm_locally_deleted realms in its response.
|
||||||
|
|
|
@ -120,10 +120,6 @@ from zproject.backends import (
|
||||||
password_auth_enabled,
|
password_auth_enabled,
|
||||||
)
|
)
|
||||||
|
|
||||||
if settings.BILLING_ENABLED:
|
|
||||||
from corporate.lib.registration import check_spare_licenses_available_for_registering_new_user
|
|
||||||
from corporate.lib.stripe import LicenseLimitError
|
|
||||||
|
|
||||||
|
|
||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
def get_prereg_key_and_redirect(
|
def get_prereg_key_and_redirect(
|
||||||
|
@ -332,6 +328,11 @@ def registration_helper(
|
||||||
return redirect_to_email_login_url(email)
|
return redirect_to_email_login_url(email)
|
||||||
|
|
||||||
if settings.BILLING_ENABLED:
|
if settings.BILLING_ENABLED:
|
||||||
|
from corporate.lib.registration import (
|
||||||
|
check_spare_licenses_available_for_registering_new_user,
|
||||||
|
)
|
||||||
|
from corporate.lib.stripe import LicenseLimitError
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check_spare_licenses_available_for_registering_new_user(realm, email, role=role)
|
check_spare_licenses_available_for_registering_new_user(realm, email, role=role)
|
||||||
except LicenseLimitError:
|
except LicenseLimitError:
|
||||||
|
|
|
@ -30,13 +30,6 @@ from analytics.lib.counts import (
|
||||||
REMOTE_INSTALLATION_COUNT_STATS,
|
REMOTE_INSTALLATION_COUNT_STATS,
|
||||||
do_increment_logging_stat,
|
do_increment_logging_stat,
|
||||||
)
|
)
|
||||||
from corporate.lib.stripe import (
|
|
||||||
BILLING_SUPPORT_EMAIL,
|
|
||||||
RemoteRealmBillingSession,
|
|
||||||
RemoteServerBillingSession,
|
|
||||||
do_deactivate_remote_server,
|
|
||||||
get_push_status_for_remote_request,
|
|
||||||
)
|
|
||||||
from corporate.models import (
|
from corporate.models import (
|
||||||
CustomerPlan,
|
CustomerPlan,
|
||||||
get_current_plan_by_customer,
|
get_current_plan_by_customer,
|
||||||
|
@ -120,6 +113,8 @@ def deactivate_remote_server(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
remote_server: RemoteZulipServer,
|
remote_server: RemoteZulipServer,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import RemoteServerBillingSession, do_deactivate_remote_server
|
||||||
|
|
||||||
billing_session = RemoteServerBillingSession(remote_server)
|
billing_session = RemoteServerBillingSession(remote_server)
|
||||||
do_deactivate_remote_server(remote_server, billing_session)
|
do_deactivate_remote_server(remote_server, billing_session)
|
||||||
return json_success(request)
|
return json_success(request)
|
||||||
|
@ -538,6 +533,8 @@ def remote_server_notify_push(
|
||||||
*,
|
*,
|
||||||
payload: JsonBodyPayload[RemoteServerNotificationPayload],
|
payload: JsonBodyPayload[RemoteServerNotificationPayload],
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import get_push_status_for_remote_request
|
||||||
|
|
||||||
user_id = payload.user_id
|
user_id = payload.user_id
|
||||||
user_uuid = payload.user_uuid
|
user_uuid = payload.user_uuid
|
||||||
user_identity = UserPushIdentityCompat(user_id, user_uuid)
|
user_identity = UserPushIdentityCompat(user_id, user_uuid)
|
||||||
|
@ -844,6 +841,8 @@ def ensure_devices_set_remote_realm(
|
||||||
def update_remote_realm_data_for_server(
|
def update_remote_realm_data_for_server(
|
||||||
server: RemoteZulipServer, server_realms_info: list[RealmDataForAnalytics]
|
server: RemoteZulipServer, server_realms_info: list[RealmDataForAnalytics]
|
||||||
) -> None:
|
) -> None:
|
||||||
|
from corporate.lib.stripe import BILLING_SUPPORT_EMAIL, RemoteRealmBillingSession
|
||||||
|
|
||||||
reported_uuids = [realm.uuid for realm in server_realms_info]
|
reported_uuids = [realm.uuid for realm in server_realms_info]
|
||||||
all_registered_remote_realms_for_server = list(RemoteRealm.objects.filter(server=server))
|
all_registered_remote_realms_for_server = list(RemoteRealm.objects.filter(server=server))
|
||||||
already_registered_remote_realms = [
|
already_registered_remote_realms = [
|
||||||
|
@ -1032,6 +1031,8 @@ def get_human_user_realm_uuids(
|
||||||
def handle_customer_migration_from_server_to_realm(
|
def handle_customer_migration_from_server_to_realm(
|
||||||
server: RemoteZulipServer,
|
server: RemoteZulipServer,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
from corporate.lib.stripe import RemoteServerBillingSession
|
||||||
|
|
||||||
server_billing_session = RemoteServerBillingSession(server)
|
server_billing_session = RemoteServerBillingSession(server)
|
||||||
server_customer = server_billing_session.get_customer()
|
server_customer = server_billing_session.get_customer()
|
||||||
if server_customer is None:
|
if server_customer is None:
|
||||||
|
@ -1160,6 +1161,12 @@ def remote_server_post_analytics(
|
||||||
merge_base: Json[str] | None = None,
|
merge_base: Json[str] | None = None,
|
||||||
api_feature_level: Json[int] | None = None,
|
api_feature_level: Json[int] | None = None,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
from corporate.lib.stripe import (
|
||||||
|
RemoteRealmBillingSession,
|
||||||
|
RemoteServerBillingSession,
|
||||||
|
get_push_status_for_remote_request,
|
||||||
|
)
|
||||||
|
|
||||||
# Lock the server, preventing this from racing with other
|
# Lock the server, preventing this from racing with other
|
||||||
# duplicate submissions of the data
|
# duplicate submissions of the data
|
||||||
server = RemoteZulipServer.objects.select_for_update().get(id=server.id)
|
server = RemoteZulipServer.objects.select_for_update().get(id=server.id)
|
||||||
|
|
Loading…
Reference in New Issue