import logging from typing import Optional from django.conf import settings from django.http import HttpRequest, HttpResponse, HttpResponseRedirect from django.shortcuts import render from pydantic import Json from corporate.lib.decorator import ( authenticated_remote_realm_management_endpoint, authenticated_remote_server_management_endpoint, ) from corporate.lib.stripe import ( VALID_BILLING_MODALITY_VALUES, VALID_BILLING_SCHEDULE_VALUES, VALID_LICENSE_MANAGEMENT_VALUES, BillingError, InitialUpgradeRequest, RealmBillingSession, RemoteRealmBillingSession, RemoteServerBillingSession, UpgradeRequest, ) from corporate.models import CustomerPlan from zerver.decorator import require_organization_member, zulip_login_required from zerver.lib.request import REQ, has_request_variables from zerver.lib.response import json_success from zerver.lib.typed_endpoint import typed_endpoint from zerver.lib.validator import check_bool, check_int, check_string_in from zerver.models import UserProfile from zilencer.lib.remote_counts import MissingDataError billing_logger = logging.getLogger("corporate.stripe") @require_organization_member @has_request_variables def upgrade( request: HttpRequest, user: UserProfile, billing_modality: str = REQ(str_validator=check_string_in(VALID_BILLING_MODALITY_VALUES)), schedule: str = REQ(str_validator=check_string_in(VALID_BILLING_SCHEDULE_VALUES)), signed_seat_count: str = REQ(), salt: str = REQ(), license_management: Optional[str] = REQ( default=None, str_validator=check_string_in(VALID_LICENSE_MANAGEMENT_VALUES) ), licenses: Optional[int] = REQ(json_validator=check_int, default=None), tier: int = REQ(default=CustomerPlan.TIER_CLOUD_STANDARD, json_validator=check_int), ) -> HttpResponse: try: upgrade_request = UpgradeRequest( billing_modality=billing_modality, schedule=schedule, signed_seat_count=signed_seat_count, salt=salt, license_management=license_management, licenses=licenses, tier=tier, remote_server_plan_start_date=None, ) billing_session = RealmBillingSession(user) data = billing_session.do_upgrade(upgrade_request) return json_success(request, data) 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 except Exception: 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_realm_management_endpoint @has_request_variables def remote_realm_upgrade( request: HttpRequest, billing_session: RemoteRealmBillingSession, billing_modality: str = REQ(str_validator=check_string_in(VALID_BILLING_MODALITY_VALUES)), schedule: str = REQ(str_validator=check_string_in(VALID_BILLING_SCHEDULE_VALUES)), signed_seat_count: str = REQ(), salt: str = REQ(), license_management: Optional[str] = REQ( default=None, str_validator=check_string_in(VALID_LICENSE_MANAGEMENT_VALUES) ), licenses: Optional[int] = REQ(json_validator=check_int, default=None), remote_server_plan_start_date: Optional[str] = REQ(default=None), tier: int = REQ(default=CustomerPlan.TIER_SELF_HOSTED_BUSINESS, json_validator=check_int), ) -> HttpResponse: try: upgrade_request = UpgradeRequest( billing_modality=billing_modality, schedule=schedule, signed_seat_count=signed_seat_count, salt=salt, license_management=license_management, licenses=licenses, tier=tier, remote_server_plan_start_date=remote_server_plan_start_date, ) data = billing_session.do_upgrade(upgrade_request) return json_success(request, data) except BillingError as e: # nocoverage 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 except Exception: # nocoverage 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 @has_request_variables def remote_server_upgrade( request: HttpRequest, billing_session: RemoteServerBillingSession, billing_modality: str = REQ(str_validator=check_string_in(VALID_BILLING_MODALITY_VALUES)), schedule: str = REQ(str_validator=check_string_in(VALID_BILLING_SCHEDULE_VALUES)), signed_seat_count: str = REQ(), salt: str = REQ(), license_management: Optional[str] = REQ( default=None, str_validator=check_string_in(VALID_LICENSE_MANAGEMENT_VALUES) ), licenses: Optional[int] = REQ(json_validator=check_int, default=None), remote_server_plan_start_date: Optional[str] = REQ(default=None), tier: int = REQ(default=CustomerPlan.TIER_SELF_HOSTED_BUSINESS, json_validator=check_int), ) -> HttpResponse: try: upgrade_request = UpgradeRequest( billing_modality=billing_modality, schedule=schedule, signed_seat_count=signed_seat_count, salt=salt, license_management=license_management, licenses=licenses, tier=tier, remote_server_plan_start_date=remote_server_plan_start_date, ) data = billing_session.do_upgrade(upgrade_request) return json_success(request, data) except BillingError as e: # nocoverage 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 except Exception: # nocoverage 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) @zulip_login_required @has_request_variables def upgrade_page( request: HttpRequest, manual_license_management: bool = REQ(default=False, json_validator=check_bool), tier: int = REQ(default=CustomerPlan.TIER_CLOUD_STANDARD, json_validator=check_int), setup_payment_by_invoice: bool = REQ(default=False, json_validator=check_bool), ) -> HttpResponse: user = request.user assert user.is_authenticated if not settings.BILLING_ENABLED or user.is_guest: return render(request, "404.html", status=404) billing_modality = "charge_automatically" if setup_payment_by_invoice: billing_modality = "send_invoice" initial_upgrade_request = InitialUpgradeRequest( manual_license_management=manual_license_management, tier=tier, billing_modality=billing_modality, ) billing_session = RealmBillingSession(user) redirect_url, context = billing_session.get_initial_upgrade_context(initial_upgrade_request) if redirect_url: return HttpResponseRedirect(redirect_url) response = render(request, "corporate/billing/upgrade.html", context=context) return response @authenticated_remote_realm_management_endpoint @typed_endpoint def remote_realm_upgrade_page( request: HttpRequest, billing_session: RemoteRealmBillingSession, *, manual_license_management: Json[bool] = False, success_message: str = "", tier: str = str(CustomerPlan.TIER_SELF_HOSTED_BUSINESS), setup_payment_by_invoice: Json[bool] = False, ) -> HttpResponse: billing_modality = "charge_automatically" if setup_payment_by_invoice: # nocoverage billing_modality = "send_invoice" initial_upgrade_request = InitialUpgradeRequest( manual_license_management=manual_license_management, tier=int(tier), success_message=success_message, billing_modality=billing_modality, ) try: redirect_url, context = billing_session.get_initial_upgrade_context(initial_upgrade_request) except MissingDataError: # nocoverage return billing_session.missing_data_error_page(request) if redirect_url: # nocoverage return HttpResponseRedirect(redirect_url) response = render(request, "corporate/billing/upgrade.html", context=context) return response @authenticated_remote_server_management_endpoint @typed_endpoint def remote_server_upgrade_page( request: HttpRequest, billing_session: RemoteServerBillingSession, *, manual_license_management: Json[bool] = False, success_message: str = "", tier: str = str(CustomerPlan.TIER_SELF_HOSTED_BUSINESS), setup_payment_by_invoice: Json[bool] = False, ) -> HttpResponse: billing_modality = "charge_automatically" if setup_payment_by_invoice: # nocoverage billing_modality = "send_invoice" initial_upgrade_request = InitialUpgradeRequest( manual_license_management=manual_license_management, tier=int(tier), success_message=success_message, billing_modality=billing_modality, ) try: redirect_url, context = billing_session.get_initial_upgrade_context(initial_upgrade_request) except MissingDataError: # nocoverage return billing_session.missing_data_error_page(request) if redirect_url: # nocoverage return HttpResponseRedirect(redirect_url) response = render(request, "corporate/billing/upgrade.html", context=context) return response