2018-09-25 12:24:11 +02:00
|
|
|
import logging
|
2024-07-12 02:30:25 +02:00
|
|
|
from typing import Annotated
|
2018-09-25 12:24:11 +02:00
|
|
|
|
2020-06-11 00:54:34 +02:00
|
|
|
from django.conf import settings
|
2018-09-25 12:24:11 +02:00
|
|
|
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
2019-02-02 23:53:22 +01:00
|
|
|
from django.shortcuts import render
|
2024-07-08 07:39:03 +02:00
|
|
|
from pydantic import AfterValidator, Json
|
2018-09-25 12:24:11 +02:00
|
|
|
|
2023-12-01 06:44:59 +01:00
|
|
|
from corporate.lib.decorator import (
|
|
|
|
authenticated_remote_realm_management_endpoint,
|
|
|
|
authenticated_remote_server_management_endpoint,
|
|
|
|
)
|
2020-06-11 00:54:34 +02:00
|
|
|
from corporate.lib.stripe import (
|
2023-11-14 11:59:48 +01:00
|
|
|
VALID_BILLING_MODALITY_VALUES,
|
|
|
|
VALID_BILLING_SCHEDULE_VALUES,
|
|
|
|
VALID_LICENSE_MANAGEMENT_VALUES,
|
2020-06-11 00:54:34 +02:00
|
|
|
BillingError,
|
2023-11-20 08:40:09 +01:00
|
|
|
InitialUpgradeRequest,
|
2023-10-26 14:11:43 +02:00
|
|
|
RealmBillingSession,
|
2023-11-24 09:12:17 +01:00
|
|
|
RemoteRealmBillingSession,
|
2023-12-01 06:44:59 +01:00
|
|
|
RemoteServerBillingSession,
|
2023-11-14 11:59:48 +01:00
|
|
|
UpgradeRequest,
|
2020-06-11 00:54:34 +02:00
|
|
|
)
|
2023-11-30 01:48:46 +01:00
|
|
|
from corporate.models import CustomerPlan
|
2023-11-26 18:29:51 +01:00
|
|
|
from zerver.decorator import require_organization_member, zulip_login_required
|
2021-07-04 08:19:18 +02:00
|
|
|
from zerver.lib.response import json_success
|
2023-11-30 07:03:25 +01:00
|
|
|
from zerver.lib.typed_endpoint import typed_endpoint
|
2024-07-08 07:39:03 +02:00
|
|
|
from zerver.lib.typed_endpoint_validators import check_string_in
|
2023-11-30 01:48:46 +01:00
|
|
|
from zerver.models import UserProfile
|
2023-12-07 15:27:39 +01:00
|
|
|
from zilencer.lib.remote_counts import MissingDataError
|
2018-09-25 12:24:11 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
billing_logger = logging.getLogger("corporate.stripe")
|
2018-09-25 12:24:11 +02:00
|
|
|
|
2018-12-22 05:29:25 +01:00
|
|
|
|
2020-07-15 22:18:32 +02:00
|
|
|
@require_organization_member
|
2024-07-08 07:39:03 +02:00
|
|
|
@typed_endpoint
|
2021-02-12 08:19:30 +01:00
|
|
|
def upgrade(
|
|
|
|
request: HttpRequest,
|
|
|
|
user: UserProfile,
|
2024-07-08 07:39:03 +02:00
|
|
|
*,
|
|
|
|
billing_modality: Annotated[
|
|
|
|
str, AfterValidator(lambda val: check_string_in(val, VALID_BILLING_MODALITY_VALUES))
|
|
|
|
],
|
|
|
|
schedule: Annotated[
|
|
|
|
str, AfterValidator(lambda val: check_string_in(val, VALID_BILLING_SCHEDULE_VALUES))
|
|
|
|
],
|
|
|
|
signed_seat_count: str,
|
|
|
|
salt: str,
|
2024-07-12 02:30:23 +02:00
|
|
|
license_management: Annotated[
|
|
|
|
str, AfterValidator(lambda val: check_string_in(val, VALID_LICENSE_MANAGEMENT_VALUES))
|
|
|
|
]
|
|
|
|
| None = None,
|
|
|
|
licenses: Json[int] | None = None,
|
2024-07-08 07:39:03 +02:00
|
|
|
tier: Json[int] = CustomerPlan.TIER_CLOUD_STANDARD,
|
2021-02-12 08:19:30 +01:00
|
|
|
) -> HttpResponse:
|
2018-12-07 18:43:22 +01:00
|
|
|
try:
|
2023-11-14 11:59:48 +01:00
|
|
|
upgrade_request = UpgradeRequest(
|
|
|
|
billing_modality=billing_modality,
|
|
|
|
schedule=schedule,
|
|
|
|
signed_seat_count=signed_seat_count,
|
|
|
|
salt=salt,
|
|
|
|
license_management=license_management,
|
|
|
|
licenses=licenses,
|
2023-12-18 13:02:36 +01:00
|
|
|
tier=tier,
|
2023-12-04 14:20:08 +01:00
|
|
|
remote_server_plan_start_date=None,
|
2023-04-10 21:48:52 +02:00
|
|
|
)
|
2023-11-13 07:55:57 +01:00
|
|
|
billing_session = RealmBillingSession(user)
|
2023-11-14 11:59:48 +01:00
|
|
|
data = billing_session.do_upgrade(upgrade_request)
|
|
|
|
return json_success(request, data)
|
2021-08-29 15:33:29 +02:00
|
|
|
except BillingError as e:
|
|
|
|
billing_logger.warning(
|
|
|
|
"BillingError during upgrade: %s. user=%s, realm=%s (%s), billing_modality=%s, "
|
|
|
|
"schedule=%s, license_management=%s, licenses=%s",
|
|
|
|
e.error_description,
|
|
|
|
user.id,
|
|
|
|
user.realm.id,
|
|
|
|
user.realm.string_id,
|
|
|
|
billing_modality,
|
|
|
|
schedule,
|
|
|
|
license_management,
|
|
|
|
licenses,
|
|
|
|
)
|
|
|
|
raise e
|
2020-06-12 01:35:37 +02:00
|
|
|
except Exception:
|
2020-08-11 03:19:00 +02:00
|
|
|
billing_logger.exception("Uncaught exception in billing:", stack_info=True)
|
2020-10-17 03:42:50 +02:00
|
|
|
error_message = BillingError.CONTACT_SUPPORT.format(email=settings.ZULIP_ADMINISTRATOR)
|
2018-12-07 18:43:22 +01:00
|
|
|
error_description = "uncaught exception during upgrade"
|
2021-07-04 08:19:18 +02:00
|
|
|
raise BillingError(error_description, error_message)
|
2018-12-07 18:43:22 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2023-12-01 07:14:12 +01:00
|
|
|
@authenticated_remote_realm_management_endpoint
|
2024-07-08 07:39:03 +02:00
|
|
|
@typed_endpoint
|
2023-12-01 07:14:12 +01:00
|
|
|
def remote_realm_upgrade(
|
|
|
|
request: HttpRequest,
|
|
|
|
billing_session: RemoteRealmBillingSession,
|
2024-07-08 07:39:03 +02:00
|
|
|
*,
|
|
|
|
billing_modality: Annotated[
|
|
|
|
str, AfterValidator(lambda val: check_string_in(val, VALID_BILLING_MODALITY_VALUES))
|
|
|
|
],
|
|
|
|
schedule: Annotated[
|
|
|
|
str, AfterValidator(lambda val: check_string_in(val, VALID_BILLING_SCHEDULE_VALUES))
|
|
|
|
],
|
|
|
|
signed_seat_count: str,
|
|
|
|
salt: str,
|
2024-07-12 02:30:23 +02:00
|
|
|
license_management: Annotated[
|
|
|
|
str, AfterValidator(lambda val: check_string_in(val, VALID_LICENSE_MANAGEMENT_VALUES))
|
|
|
|
]
|
|
|
|
| None = None,
|
|
|
|
licenses: Json[int] | None = None,
|
|
|
|
remote_server_plan_start_date: str | None = None,
|
2024-07-08 07:39:03 +02:00
|
|
|
tier: Json[int] = CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
2023-12-13 12:25:23 +01:00
|
|
|
) -> HttpResponse:
|
2023-12-01 07:14:12 +01:00
|
|
|
try:
|
|
|
|
upgrade_request = UpgradeRequest(
|
|
|
|
billing_modality=billing_modality,
|
|
|
|
schedule=schedule,
|
|
|
|
signed_seat_count=signed_seat_count,
|
|
|
|
salt=salt,
|
|
|
|
license_management=license_management,
|
|
|
|
licenses=licenses,
|
2023-12-18 13:02:36 +01:00
|
|
|
tier=tier,
|
2023-12-11 18:00:42 +01:00
|
|
|
remote_server_plan_start_date=remote_server_plan_start_date,
|
2023-12-01 07:14:12 +01:00
|
|
|
)
|
|
|
|
data = billing_session.do_upgrade(upgrade_request)
|
|
|
|
return json_success(request, data)
|
2023-12-13 12:25:23 +01:00
|
|
|
except BillingError as e: # nocoverage
|
2023-12-01 07:14:12 +01:00
|
|
|
billing_logger.warning(
|
|
|
|
"BillingError during upgrade: %s. remote_realm=%s (%s), billing_modality=%s, "
|
|
|
|
"schedule=%s, license_management=%s, licenses=%s",
|
|
|
|
e.error_description,
|
|
|
|
billing_session.remote_realm.id,
|
|
|
|
billing_session.remote_realm.host,
|
|
|
|
billing_modality,
|
|
|
|
schedule,
|
|
|
|
license_management,
|
|
|
|
licenses,
|
|
|
|
)
|
|
|
|
raise e
|
2023-12-13 12:25:23 +01:00
|
|
|
except Exception: # nocoverage
|
2023-12-01 07:14:12 +01:00
|
|
|
billing_logger.exception("Uncaught exception in billing:", stack_info=True)
|
|
|
|
error_message = BillingError.CONTACT_SUPPORT.format(email=settings.ZULIP_ADMINISTRATOR)
|
|
|
|
error_description = "uncaught exception during upgrade"
|
|
|
|
raise BillingError(error_description, error_message)
|
|
|
|
|
|
|
|
|
|
|
|
@authenticated_remote_server_management_endpoint
|
2024-07-08 07:39:03 +02:00
|
|
|
@typed_endpoint
|
2023-12-01 07:14:12 +01:00
|
|
|
def remote_server_upgrade(
|
|
|
|
request: HttpRequest,
|
|
|
|
billing_session: RemoteServerBillingSession,
|
2024-07-08 07:39:03 +02:00
|
|
|
*,
|
|
|
|
billing_modality: Annotated[
|
|
|
|
str, AfterValidator(lambda val: check_string_in(val, VALID_BILLING_MODALITY_VALUES))
|
|
|
|
],
|
|
|
|
schedule: Annotated[
|
|
|
|
str, AfterValidator(lambda val: check_string_in(val, VALID_BILLING_SCHEDULE_VALUES))
|
|
|
|
],
|
|
|
|
signed_seat_count: str,
|
|
|
|
salt: str,
|
2024-07-12 02:30:23 +02:00
|
|
|
license_management: Annotated[
|
|
|
|
str, AfterValidator(lambda val: check_string_in(val, VALID_LICENSE_MANAGEMENT_VALUES))
|
|
|
|
]
|
|
|
|
| None = None,
|
|
|
|
licenses: Json[int] | None = None,
|
|
|
|
remote_server_plan_start_date: str | None = None,
|
2024-07-08 07:39:03 +02:00
|
|
|
tier: Json[int] = CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
2023-12-19 12:24:15 +01:00
|
|
|
) -> HttpResponse:
|
2023-12-01 07:14:12 +01:00
|
|
|
try:
|
|
|
|
upgrade_request = UpgradeRequest(
|
|
|
|
billing_modality=billing_modality,
|
|
|
|
schedule=schedule,
|
|
|
|
signed_seat_count=signed_seat_count,
|
|
|
|
salt=salt,
|
|
|
|
license_management=license_management,
|
|
|
|
licenses=licenses,
|
2023-12-18 13:02:36 +01:00
|
|
|
tier=tier,
|
2023-12-04 14:20:08 +01:00
|
|
|
remote_server_plan_start_date=remote_server_plan_start_date,
|
2023-12-01 07:14:12 +01:00
|
|
|
)
|
|
|
|
data = billing_session.do_upgrade(upgrade_request)
|
|
|
|
return json_success(request, data)
|
2023-12-19 12:24:15 +01:00
|
|
|
except BillingError as e: # nocoverage
|
2023-12-01 07:14:12 +01:00
|
|
|
billing_logger.warning(
|
|
|
|
"BillingError during upgrade: %s. remote_server=%s (%s), billing_modality=%s, "
|
|
|
|
"schedule=%s, license_management=%s, licenses=%s",
|
|
|
|
e.error_description,
|
|
|
|
billing_session.remote_server.id,
|
|
|
|
billing_session.remote_server.hostname,
|
|
|
|
billing_modality,
|
|
|
|
schedule,
|
|
|
|
license_management,
|
|
|
|
licenses,
|
|
|
|
)
|
|
|
|
raise e
|
2023-12-19 12:24:15 +01:00
|
|
|
except Exception: # nocoverage
|
2023-12-01 07:14:12 +01:00
|
|
|
billing_logger.exception("Uncaught exception in billing:", stack_info=True)
|
|
|
|
error_message = BillingError.CONTACT_SUPPORT.format(email=settings.ZULIP_ADMINISTRATOR)
|
|
|
|
error_description = "uncaught exception during upgrade"
|
|
|
|
raise BillingError(error_description, error_message)
|
|
|
|
|
|
|
|
|
2018-09-25 12:24:11 +02:00
|
|
|
@zulip_login_required
|
2024-07-08 07:39:03 +02:00
|
|
|
@typed_endpoint
|
2023-11-23 09:31:46 +01:00
|
|
|
def upgrade_page(
|
2023-11-10 10:59:28 +01:00
|
|
|
request: HttpRequest,
|
2024-07-08 07:39:03 +02:00
|
|
|
*,
|
|
|
|
manual_license_management: Json[bool] = False,
|
|
|
|
tier: Json[int] = CustomerPlan.TIER_CLOUD_STANDARD,
|
|
|
|
setup_payment_by_invoice: Json[bool] = False,
|
2021-07-29 19:01:59 +02:00
|
|
|
) -> HttpResponse:
|
2018-09-25 12:24:11 +02:00
|
|
|
user = request.user
|
2021-07-24 20:37:35 +02:00
|
|
|
assert user.is_authenticated
|
2020-06-09 12:24:32 +02:00
|
|
|
|
2020-07-15 22:18:32 +02:00
|
|
|
if not settings.BILLING_ENABLED or user.is_guest:
|
|
|
|
return render(request, "404.html", status=404)
|
|
|
|
|
2024-03-04 00:44:59 +01:00
|
|
|
billing_modality = "charge_automatically"
|
|
|
|
if setup_payment_by_invoice:
|
|
|
|
billing_modality = "send_invoice"
|
|
|
|
|
2023-11-20 08:40:09 +01:00
|
|
|
initial_upgrade_request = InitialUpgradeRequest(
|
|
|
|
manual_license_management=manual_license_management,
|
2023-12-18 13:02:36 +01:00
|
|
|
tier=tier,
|
2024-03-04 00:44:59 +01:00
|
|
|
billing_modality=billing_modality,
|
2023-04-10 21:48:52 +02:00
|
|
|
)
|
2023-11-20 08:40:09 +01:00
|
|
|
billing_session = RealmBillingSession(user)
|
2023-11-20 20:32:29 +01:00
|
|
|
redirect_url, context = billing_session.get_initial_upgrade_context(initial_upgrade_request)
|
2023-04-10 21:48:52 +02:00
|
|
|
|
2023-11-20 08:40:09 +01:00
|
|
|
if redirect_url:
|
|
|
|
return HttpResponseRedirect(redirect_url)
|
2023-02-13 20:40:51 +01:00
|
|
|
|
2024-01-29 15:46:18 +01:00
|
|
|
response = render(request, "corporate/billing/upgrade.html", context=context)
|
2018-09-25 12:24:11 +02:00
|
|
|
return response
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2023-11-26 18:34:33 +01:00
|
|
|
@authenticated_remote_realm_management_endpoint
|
2023-11-24 09:12:17 +01:00
|
|
|
@typed_endpoint
|
|
|
|
def remote_realm_upgrade_page(
|
|
|
|
request: HttpRequest,
|
2023-11-30 11:36:04 +01:00
|
|
|
billing_session: RemoteRealmBillingSession,
|
2023-11-24 09:12:17 +01:00
|
|
|
*,
|
|
|
|
manual_license_management: Json[bool] = False,
|
2023-12-11 18:00:42 +01:00
|
|
|
success_message: str = "",
|
2023-12-18 13:02:36 +01:00
|
|
|
tier: str = str(CustomerPlan.TIER_SELF_HOSTED_BUSINESS),
|
2024-03-04 00:44:59 +01:00
|
|
|
setup_payment_by_invoice: Json[bool] = False,
|
2023-12-13 12:25:23 +01:00
|
|
|
) -> HttpResponse:
|
2024-03-04 00:44:59 +01:00
|
|
|
billing_modality = "charge_automatically"
|
|
|
|
if setup_payment_by_invoice: # nocoverage
|
|
|
|
billing_modality = "send_invoice"
|
|
|
|
|
2023-11-24 09:12:17 +01:00
|
|
|
initial_upgrade_request = InitialUpgradeRequest(
|
|
|
|
manual_license_management=manual_license_management,
|
2023-12-18 13:02:36 +01:00
|
|
|
tier=int(tier),
|
2023-12-11 18:00:42 +01:00
|
|
|
success_message=success_message,
|
2024-03-04 00:44:59 +01:00
|
|
|
billing_modality=billing_modality,
|
2023-11-24 09:12:17 +01:00
|
|
|
)
|
2023-12-07 15:27:39 +01:00
|
|
|
try:
|
|
|
|
redirect_url, context = billing_session.get_initial_upgrade_context(initial_upgrade_request)
|
2023-12-13 12:25:23 +01:00
|
|
|
except MissingDataError: # nocoverage
|
2023-12-07 15:27:39 +01:00
|
|
|
return billing_session.missing_data_error_page(request)
|
2023-11-24 09:12:17 +01:00
|
|
|
|
2023-12-13 12:25:23 +01:00
|
|
|
if redirect_url: # nocoverage
|
2023-11-24 09:12:17 +01:00
|
|
|
return HttpResponseRedirect(redirect_url)
|
|
|
|
|
2024-01-29 15:46:18 +01:00
|
|
|
response = render(request, "corporate/billing/upgrade.html", context=context)
|
2023-11-24 09:12:17 +01:00
|
|
|
return response
|
2023-12-01 06:44:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
@authenticated_remote_server_management_endpoint
|
|
|
|
@typed_endpoint
|
|
|
|
def remote_server_upgrade_page(
|
|
|
|
request: HttpRequest,
|
|
|
|
billing_session: RemoteServerBillingSession,
|
|
|
|
*,
|
|
|
|
manual_license_management: Json[bool] = False,
|
2023-12-06 14:17:13 +01:00
|
|
|
success_message: str = "",
|
2023-12-18 13:02:36 +01:00
|
|
|
tier: str = str(CustomerPlan.TIER_SELF_HOSTED_BUSINESS),
|
2024-03-04 00:44:59 +01:00
|
|
|
setup_payment_by_invoice: Json[bool] = False,
|
2023-12-19 12:24:15 +01:00
|
|
|
) -> HttpResponse:
|
2024-03-04 00:44:59 +01:00
|
|
|
billing_modality = "charge_automatically"
|
|
|
|
if setup_payment_by_invoice: # nocoverage
|
|
|
|
billing_modality = "send_invoice"
|
|
|
|
|
2023-12-01 06:44:59 +01:00
|
|
|
initial_upgrade_request = InitialUpgradeRequest(
|
|
|
|
manual_license_management=manual_license_management,
|
2023-12-18 13:02:36 +01:00
|
|
|
tier=int(tier),
|
2023-12-06 14:17:13 +01:00
|
|
|
success_message=success_message,
|
2024-03-04 00:44:59 +01:00
|
|
|
billing_modality=billing_modality,
|
2023-12-01 06:44:59 +01:00
|
|
|
)
|
2023-12-07 15:27:39 +01:00
|
|
|
try:
|
|
|
|
redirect_url, context = billing_session.get_initial_upgrade_context(initial_upgrade_request)
|
2023-12-19 12:24:15 +01:00
|
|
|
except MissingDataError: # nocoverage
|
2023-12-07 15:27:39 +01:00
|
|
|
return billing_session.missing_data_error_page(request)
|
2023-12-01 06:44:59 +01:00
|
|
|
|
2023-12-19 12:24:15 +01:00
|
|
|
if redirect_url: # nocoverage
|
2023-12-01 06:44:59 +01:00
|
|
|
return HttpResponseRedirect(redirect_url)
|
|
|
|
|
2024-01-29 15:46:18 +01:00
|
|
|
response = render(request, "corporate/billing/upgrade.html", context=context)
|
2023-12-01 06:44:59 +01:00
|
|
|
return response
|