2023-11-02 08:16:02 +01:00
|
|
|
import contextlib
|
2023-12-04 14:03:24 +01:00
|
|
|
import uuid
|
2023-11-02 08:16:02 +01:00
|
|
|
from dataclasses import dataclass
|
2023-11-19 19:45:19 +01:00
|
|
|
from datetime import datetime, timezone
|
2023-12-04 14:03:24 +01:00
|
|
|
from typing import Any, Dict, Optional
|
2023-11-02 08:16:02 +01:00
|
|
|
|
|
|
|
import stripe
|
2023-12-05 12:49:22 +01:00
|
|
|
from django.conf import settings
|
2023-12-04 14:03:24 +01:00
|
|
|
from django.core.management.base import BaseCommand, CommandParser
|
2023-11-02 08:16:02 +01:00
|
|
|
from django.utils.timezone import now as timezone_now
|
|
|
|
from typing_extensions import override
|
|
|
|
|
2023-12-05 07:38:05 +01:00
|
|
|
from corporate.lib.stripe import (
|
|
|
|
RealmBillingSession,
|
2023-12-05 12:49:22 +01:00
|
|
|
RemoteRealmBillingSession,
|
2023-12-05 07:38:05 +01:00
|
|
|
RemoteServerBillingSession,
|
|
|
|
UpgradeRequest,
|
|
|
|
add_months,
|
|
|
|
sign_string,
|
|
|
|
)
|
2023-11-02 08:16:02 +01:00
|
|
|
from corporate.models import Customer, CustomerPlan, LicenseLedger
|
2023-12-04 14:03:24 +01:00
|
|
|
from scripts.lib.zulip_tools import TIMESTAMP_FORMAT
|
2023-11-02 08:16:02 +01:00
|
|
|
from zerver.actions.create_realm import do_create_realm
|
|
|
|
from zerver.actions.create_user import do_create_user
|
|
|
|
from zerver.actions.streams import bulk_add_subscriptions
|
|
|
|
from zerver.apps import flush_cache
|
2023-12-05 12:49:22 +01:00
|
|
|
from zerver.lib.remote_server import get_realms_info_for_push_bouncer
|
2023-11-02 08:16:02 +01:00
|
|
|
from zerver.lib.streams import create_stream_if_needed
|
2023-12-15 02:14:24 +01:00
|
|
|
from zerver.models import Realm, UserProfile
|
|
|
|
from zerver.models.realms import get_realm
|
2023-12-13 02:02:14 +01:00
|
|
|
from zilencer.models import (
|
|
|
|
RemoteRealm,
|
|
|
|
RemoteRealmBillingUser,
|
|
|
|
RemoteServerBillingUser,
|
|
|
|
RemoteZulipServer,
|
|
|
|
)
|
2023-12-05 12:49:22 +01:00
|
|
|
from zilencer.views import update_remote_realm_data_for_server
|
2023-11-02 08:16:02 +01:00
|
|
|
from zproject.config import get_secret
|
|
|
|
|
2023-12-04 14:03:24 +01:00
|
|
|
current_time = timezone_now().strftime(TIMESTAMP_FORMAT)
|
|
|
|
|
2023-11-02 08:16:02 +01:00
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class CustomerProfile:
|
|
|
|
unique_id: str
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule: int = CustomerPlan.BILLING_SCHEDULE_ANNUAL
|
2023-11-02 08:16:02 +01:00
|
|
|
tier: Optional[int] = None
|
2023-12-04 14:03:24 +01:00
|
|
|
new_plan_tier: Optional[int] = None
|
2023-11-02 08:16:02 +01:00
|
|
|
automanage_licenses: bool = False
|
|
|
|
status: int = CustomerPlan.ACTIVE
|
|
|
|
sponsorship_pending: bool = False
|
|
|
|
is_sponsored: bool = False
|
|
|
|
card: str = ""
|
2023-11-08 17:09:33 +01:00
|
|
|
charge_automatically: bool = True
|
2023-12-05 07:38:05 +01:00
|
|
|
is_remote_realm: bool = False
|
|
|
|
is_remote_server: bool = False
|
2023-12-04 14:03:24 +01:00
|
|
|
renewal_date: str = current_time
|
2023-12-06 14:17:28 +01:00
|
|
|
# Use (timezone_now() + timedelta(minutes=1)).strftime(TIMESTAMP_FORMAT) as `end_date` for testing.
|
|
|
|
# `invoice_plan` is not implemented yet for remote servers and realms so no payment is generated in stripe.
|
2023-12-04 14:03:24 +01:00
|
|
|
end_date: str = "2030-10-10-01-10-10"
|
2023-12-05 07:38:05 +01:00
|
|
|
remote_server_plan_start_date: str = "billing_cycle_end_date"
|
2023-11-02 08:16:02 +01:00
|
|
|
|
|
|
|
|
|
|
|
class Command(BaseCommand):
|
|
|
|
help = "Populate database with different types of realms that can exist."
|
|
|
|
|
2023-12-04 14:03:24 +01:00
|
|
|
@override
|
|
|
|
def add_arguments(self, parser: CommandParser) -> None:
|
|
|
|
parser.add_argument(
|
|
|
|
"--only-remote-server",
|
|
|
|
action="store_true",
|
|
|
|
help="Whether to only run for remote servers",
|
|
|
|
)
|
|
|
|
|
2023-12-05 12:49:22 +01:00
|
|
|
parser.add_argument(
|
|
|
|
"--only-remote-realm",
|
|
|
|
action="store_true",
|
|
|
|
help="Whether to only run for remote realms",
|
|
|
|
)
|
|
|
|
|
2023-11-02 08:16:02 +01:00
|
|
|
@override
|
|
|
|
def handle(self, *args: Any, **options: Any) -> None:
|
|
|
|
# Create a realm for each plan type
|
|
|
|
|
|
|
|
customer_profiles = [
|
|
|
|
# NOTE: The unique_id has to be less than 40 characters.
|
|
|
|
CustomerProfile(unique_id="sponsorship-pending", sponsorship_pending=True),
|
2023-11-03 17:21:42 +01:00
|
|
|
CustomerProfile(
|
|
|
|
unique_id="annual-free",
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_ANNUAL,
|
2023-11-03 17:21:42 +01:00
|
|
|
),
|
2023-11-02 08:16:02 +01:00
|
|
|
CustomerProfile(
|
|
|
|
unique_id="annual-standard",
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_ANNUAL,
|
2023-11-30 07:43:06 +01:00
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
2023-11-02 08:16:02 +01:00
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="annual-plus",
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_ANNUAL,
|
2023-11-30 07:43:06 +01:00
|
|
|
tier=CustomerPlan.TIER_CLOUD_PLUS,
|
2023-11-02 08:16:02 +01:00
|
|
|
),
|
2023-11-03 17:21:42 +01:00
|
|
|
CustomerProfile(
|
|
|
|
unique_id="monthly-free",
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_MONTHLY,
|
2023-11-03 17:21:42 +01:00
|
|
|
),
|
2023-11-02 08:16:02 +01:00
|
|
|
CustomerProfile(
|
|
|
|
unique_id="monthly-standard",
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_MONTHLY,
|
2023-11-30 07:43:06 +01:00
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
2023-11-02 08:16:02 +01:00
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="monthly-plus",
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_MONTHLY,
|
2023-11-30 07:43:06 +01:00
|
|
|
tier=CustomerPlan.TIER_CLOUD_PLUS,
|
2023-11-02 08:16:02 +01:00
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="downgrade-end-of-cycle",
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_MONTHLY,
|
2023-11-30 07:43:06 +01:00
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
2023-11-02 08:16:02 +01:00
|
|
|
status=CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE,
|
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="standard-automanage-licenses",
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_MONTHLY,
|
2023-11-30 07:43:06 +01:00
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
2023-11-02 08:16:02 +01:00
|
|
|
automanage_licenses=True,
|
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="standard-automatic-card",
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_MONTHLY,
|
2023-11-30 07:43:06 +01:00
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
2023-11-02 08:16:02 +01:00
|
|
|
card="pm_card_visa",
|
|
|
|
),
|
2023-11-08 17:09:33 +01:00
|
|
|
CustomerProfile(
|
|
|
|
unique_id="standard-invoice-payment",
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_MONTHLY,
|
2023-11-30 07:43:06 +01:00
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
2023-11-08 17:09:33 +01:00
|
|
|
charge_automatically=False,
|
|
|
|
),
|
2023-11-08 17:23:22 +01:00
|
|
|
CustomerProfile(
|
|
|
|
unique_id="standard-switch-to-annual-eoc",
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_MONTHLY,
|
2023-11-30 07:43:06 +01:00
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
2023-11-08 17:23:22 +01:00
|
|
|
status=CustomerPlan.SWITCH_TO_ANNUAL_AT_END_OF_CYCLE,
|
|
|
|
),
|
2023-11-02 08:16:02 +01:00
|
|
|
CustomerProfile(
|
|
|
|
unique_id="sponsored",
|
|
|
|
is_sponsored=True,
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_MONTHLY,
|
2023-12-05 12:49:22 +01:00
|
|
|
# Customer plan might not exist for sponsored realms.
|
2023-12-06 14:37:37 +01:00
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
2023-11-02 08:16:02 +01:00
|
|
|
),
|
2023-11-09 15:05:34 +01:00
|
|
|
CustomerProfile(
|
|
|
|
unique_id="free-trial",
|
2023-11-30 07:43:06 +01:00
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
2023-11-09 15:05:34 +01:00
|
|
|
status=CustomerPlan.FREE_TRIAL,
|
|
|
|
),
|
2023-12-04 14:03:24 +01:00
|
|
|
CustomerProfile(
|
|
|
|
unique_id="legacy-server",
|
|
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_LEGACY,
|
2023-12-05 07:38:05 +01:00
|
|
|
is_remote_server=True,
|
2023-12-04 14:03:24 +01:00
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="legacy-server-upgrade-scheduled",
|
|
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_LEGACY,
|
|
|
|
status=CustomerPlan.SWITCH_PLAN_TIER_AT_PLAN_END,
|
2023-12-18 23:57:32 +01:00
|
|
|
new_plan_tier=CustomerPlan.TIER_SELF_HOSTED_BASIC,
|
2023-12-05 07:38:05 +01:00
|
|
|
is_remote_server=True,
|
2023-12-04 14:03:24 +01:00
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="business-server",
|
|
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
2023-12-05 07:38:05 +01:00
|
|
|
is_remote_server=True,
|
2023-12-04 14:03:24 +01:00
|
|
|
),
|
|
|
|
CustomerProfile(
|
2023-12-05 07:38:05 +01:00
|
|
|
unique_id="business-server-free-trial",
|
2023-12-04 14:03:24 +01:00
|
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
2023-12-05 07:38:05 +01:00
|
|
|
status=CustomerPlan.FREE_TRIAL,
|
|
|
|
is_remote_server=True,
|
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="business-server-sponsorship-pending",
|
|
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
|
|
|
sponsorship_pending=True,
|
|
|
|
is_remote_server=True,
|
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="self-hosted-server-sponsorship-pending",
|
|
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_BASE,
|
|
|
|
sponsorship_pending=True,
|
|
|
|
is_remote_server=True,
|
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="server-sponsored",
|
2023-12-05 12:49:22 +01:00
|
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_COMMUNITY,
|
2023-12-05 07:38:05 +01:00
|
|
|
is_sponsored=True,
|
|
|
|
is_remote_server=True,
|
2023-12-04 14:03:24 +01:00
|
|
|
),
|
2023-12-05 12:49:22 +01:00
|
|
|
CustomerProfile(
|
|
|
|
unique_id="free-tier-remote-realm",
|
|
|
|
is_remote_realm=True,
|
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="business-remote-realm",
|
|
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
|
|
|
is_remote_realm=True,
|
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="business-remote-free-trial",
|
|
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
|
|
|
status=CustomerPlan.FREE_TRIAL,
|
|
|
|
is_remote_realm=True,
|
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="business-remote-sponsorship-pending",
|
|
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
|
|
|
sponsorship_pending=True,
|
|
|
|
is_remote_realm=True,
|
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="remote-sponsorship-pending",
|
|
|
|
sponsorship_pending=True,
|
|
|
|
is_remote_realm=True,
|
|
|
|
),
|
|
|
|
CustomerProfile(
|
|
|
|
unique_id="remote-realm-sponsored",
|
|
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_COMMUNITY,
|
|
|
|
is_sponsored=True,
|
|
|
|
is_remote_realm=True,
|
|
|
|
),
|
2023-11-02 08:16:02 +01:00
|
|
|
]
|
|
|
|
|
2023-12-04 14:03:24 +01:00
|
|
|
servers = []
|
2023-12-05 12:49:22 +01:00
|
|
|
remote_realms = []
|
2023-11-02 08:16:02 +01:00
|
|
|
for customer_profile in customer_profiles:
|
2023-12-05 12:49:22 +01:00
|
|
|
if customer_profile.is_remote_server and not options.get("only_remote_realm"):
|
2023-12-04 14:03:24 +01:00
|
|
|
server_conf = populate_remote_server(customer_profile)
|
|
|
|
servers.append(server_conf)
|
2023-12-05 12:49:22 +01:00
|
|
|
elif customer_profile.is_remote_realm and not options.get("only_remote_server"):
|
|
|
|
remote_realm_conf = populate_remote_realms(customer_profile)
|
|
|
|
remote_realms.append(remote_realm_conf)
|
|
|
|
elif not options.get("only_remote_server") and not options.get("only_remote_realm"):
|
2023-12-04 14:03:24 +01:00
|
|
|
populate_realm(customer_profile)
|
|
|
|
|
|
|
|
print("-" * 40)
|
|
|
|
for server in servers:
|
|
|
|
for key, value in server.items():
|
|
|
|
print(f"{key}: {value}")
|
|
|
|
print("-" * 40)
|
2023-12-03 12:50:08 +01:00
|
|
|
|
2023-12-05 12:49:22 +01:00
|
|
|
for remote_realm_conf in remote_realms:
|
|
|
|
for key, value in remote_realm_conf.items():
|
|
|
|
print(f"{key}: {value}")
|
|
|
|
print("-" * 40)
|
|
|
|
|
2023-12-03 12:50:08 +01:00
|
|
|
|
2023-12-05 07:22:42 +01:00
|
|
|
def add_card_to_customer(customer: Customer) -> None:
|
2023-12-05 07:38:05 +01:00
|
|
|
assert customer.stripe_customer_id is not None
|
2023-12-05 07:22:42 +01:00
|
|
|
# Set the Stripe API key
|
|
|
|
stripe.api_key = get_secret("stripe_secret_key")
|
|
|
|
|
|
|
|
# Create a card payment method and attach it to the customer
|
|
|
|
payment_method = stripe.PaymentMethod.create(
|
|
|
|
type="card",
|
|
|
|
card={"token": "tok_visa"},
|
|
|
|
)
|
|
|
|
|
|
|
|
# Attach the payment method to the customer
|
|
|
|
stripe.PaymentMethod.attach(payment_method.id, customer=customer.stripe_customer_id)
|
|
|
|
|
|
|
|
# Set the default payment method for the customer
|
|
|
|
stripe.Customer.modify(
|
|
|
|
customer.stripe_customer_id,
|
|
|
|
invoice_settings={"default_payment_method": payment_method.id},
|
|
|
|
)
|
|
|
|
|
2023-12-05 07:38:05 +01:00
|
|
|
|
2023-12-05 12:46:57 +01:00
|
|
|
def create_plan_for_customer(customer: Customer, customer_profile: CustomerProfile) -> None:
|
|
|
|
assert customer_profile.tier is not None
|
2023-12-08 02:35:54 +01:00
|
|
|
if customer_profile.status == CustomerPlan.FREE_TRIAL:
|
|
|
|
# 2 months free trial.
|
|
|
|
next_invoice_date = add_months(timezone_now(), 2)
|
|
|
|
else:
|
|
|
|
months = 12
|
|
|
|
if customer_profile.billing_schedule == CustomerPlan.BILLING_SCHEDULE_MONTHLY:
|
|
|
|
months = 1
|
|
|
|
next_invoice_date = add_months(timezone_now(), months)
|
2023-12-05 12:46:57 +01:00
|
|
|
|
|
|
|
customer_plan = CustomerPlan.objects.create(
|
|
|
|
customer=customer,
|
|
|
|
billing_cycle_anchor=timezone_now(),
|
|
|
|
billing_schedule=customer_profile.billing_schedule,
|
|
|
|
tier=customer_profile.tier,
|
|
|
|
price_per_license=1200,
|
|
|
|
automanage_licenses=customer_profile.automanage_licenses,
|
|
|
|
status=customer_profile.status,
|
|
|
|
charge_automatically=customer_profile.charge_automatically,
|
|
|
|
next_invoice_date=next_invoice_date,
|
|
|
|
)
|
|
|
|
|
|
|
|
LicenseLedger.objects.create(
|
|
|
|
licenses=10,
|
|
|
|
licenses_at_next_renewal=10,
|
|
|
|
event_time=timezone_now(),
|
|
|
|
is_renewal=True,
|
|
|
|
plan=customer_plan,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2023-12-05 12:49:22 +01:00
|
|
|
def populate_realm(customer_profile: CustomerProfile) -> Optional[Realm]:
|
2023-12-03 12:50:08 +01:00
|
|
|
unique_id = customer_profile.unique_id
|
2023-12-05 12:49:22 +01:00
|
|
|
if customer_profile.is_remote_realm:
|
|
|
|
plan_type = Realm.PLAN_TYPE_SELF_HOSTED
|
2023-12-06 14:37:37 +01:00
|
|
|
elif customer_profile.is_sponsored:
|
|
|
|
plan_type = Realm.PLAN_TYPE_STANDARD_FREE
|
2023-12-05 12:49:22 +01:00
|
|
|
elif customer_profile.tier is None:
|
2023-12-03 12:50:08 +01:00
|
|
|
plan_type = Realm.PLAN_TYPE_LIMITED
|
|
|
|
elif customer_profile.tier == CustomerPlan.TIER_CLOUD_STANDARD:
|
|
|
|
plan_type = Realm.PLAN_TYPE_STANDARD
|
|
|
|
elif customer_profile.tier == CustomerPlan.TIER_CLOUD_PLUS:
|
|
|
|
plan_type = Realm.PLAN_TYPE_PLUS
|
|
|
|
else:
|
|
|
|
raise AssertionError("Unexpected tier!")
|
|
|
|
plan_name = Realm.ALL_PLAN_TYPES[plan_type]
|
|
|
|
|
|
|
|
# Delete existing realm with this name
|
|
|
|
with contextlib.suppress(Realm.DoesNotExist):
|
|
|
|
get_realm(unique_id).delete()
|
|
|
|
# Because we just deleted a bunch of objects in the database
|
|
|
|
# directly (rather than deleting individual objects in Django,
|
|
|
|
# in which case our post_save hooks would have flushed the
|
|
|
|
# individual objects from memcached for us), we need to flush
|
|
|
|
# memcached in order to ensure deleted objects aren't still
|
|
|
|
# present in the memcached cache.
|
|
|
|
flush_cache(None)
|
|
|
|
|
|
|
|
realm = do_create_realm(
|
|
|
|
string_id=unique_id,
|
|
|
|
name=unique_id,
|
|
|
|
description=unique_id,
|
|
|
|
plan_type=plan_type,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Create a user with billing access
|
|
|
|
full_name = f"{plan_name}-admin"
|
|
|
|
email = f"{full_name}@zulip.com"
|
|
|
|
user = do_create_user(
|
|
|
|
email,
|
|
|
|
full_name,
|
|
|
|
realm,
|
|
|
|
full_name,
|
|
|
|
role=UserProfile.ROLE_REALM_OWNER,
|
|
|
|
acting_user=None,
|
2023-12-05 12:49:22 +01:00
|
|
|
tos_version=settings.TERMS_OF_SERVICE_VERSION,
|
2023-12-03 12:50:08 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
stream, _ = create_stream_if_needed(
|
|
|
|
realm,
|
|
|
|
"all",
|
|
|
|
)
|
|
|
|
|
|
|
|
bulk_add_subscriptions(realm, [stream], [user], acting_user=None)
|
|
|
|
|
2023-12-05 12:49:22 +01:00
|
|
|
if customer_profile.is_remote_realm:
|
|
|
|
# Remote realm billing data on their local server is irrelevant.
|
|
|
|
return realm
|
|
|
|
|
2023-12-13 16:44:54 +01:00
|
|
|
if customer_profile.sponsorship_pending or customer_profile.is_sponsored:
|
|
|
|
# plan_type is already set correctly above for sponsored realms.
|
2023-12-03 12:50:08 +01:00
|
|
|
customer = Customer.objects.create(
|
|
|
|
realm=realm,
|
|
|
|
sponsorship_pending=customer_profile.sponsorship_pending,
|
|
|
|
)
|
2023-12-05 12:49:22 +01:00
|
|
|
return realm
|
2023-12-03 12:50:08 +01:00
|
|
|
|
|
|
|
if customer_profile.tier is None:
|
2023-12-05 12:49:22 +01:00
|
|
|
return realm
|
2023-12-03 12:50:08 +01:00
|
|
|
|
|
|
|
billing_session = RealmBillingSession(user)
|
|
|
|
customer = billing_session.update_or_create_stripe_customer()
|
|
|
|
assert customer.stripe_customer_id is not None
|
|
|
|
if customer_profile.card:
|
2023-12-05 07:22:42 +01:00
|
|
|
add_card_to_customer(customer)
|
2023-12-03 12:50:08 +01:00
|
|
|
|
2023-12-05 12:46:57 +01:00
|
|
|
create_plan_for_customer(customer, customer_profile)
|
|
|
|
return realm
|
2023-12-04 14:03:24 +01:00
|
|
|
|
|
|
|
|
|
|
|
def populate_remote_server(customer_profile: CustomerProfile) -> Dict[str, str]:
|
|
|
|
unique_id = customer_profile.unique_id
|
|
|
|
|
2023-12-06 14:37:37 +01:00
|
|
|
if (
|
|
|
|
customer_profile.is_sponsored
|
|
|
|
and customer_profile.tier == CustomerPlan.TIER_SELF_HOSTED_COMMUNITY
|
|
|
|
):
|
2023-12-05 07:38:05 +01:00
|
|
|
plan_type = RemoteZulipServer.PLAN_TYPE_COMMUNITY
|
|
|
|
elif customer_profile.tier == CustomerPlan.TIER_SELF_HOSTED_LEGACY:
|
2023-12-14 00:17:55 +01:00
|
|
|
plan_type = RemoteZulipServer.PLAN_TYPE_SELF_MANAGED_LEGACY
|
2023-12-04 14:03:24 +01:00
|
|
|
elif customer_profile.tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS:
|
|
|
|
plan_type = RemoteZulipServer.PLAN_TYPE_BUSINESS
|
2023-12-05 07:38:05 +01:00
|
|
|
elif customer_profile.tier is CustomerPlan.TIER_SELF_HOSTED_BASE:
|
2023-12-14 00:17:55 +01:00
|
|
|
plan_type = RemoteZulipServer.PLAN_TYPE_SELF_MANAGED
|
2023-12-04 14:03:24 +01:00
|
|
|
else:
|
|
|
|
raise AssertionError("Unexpected tier!")
|
|
|
|
|
|
|
|
server_uuid = str(uuid.uuid4())
|
|
|
|
api_key = server_uuid
|
2023-12-05 12:49:22 +01:00
|
|
|
hostname = f"{unique_id}.example.com"
|
|
|
|
|
|
|
|
# Delete existing remote server.
|
|
|
|
RemoteZulipServer.objects.filter(hostname=hostname).delete()
|
|
|
|
flush_cache(None)
|
2023-12-04 14:03:24 +01:00
|
|
|
|
|
|
|
remote_server = RemoteZulipServer.objects.create(
|
|
|
|
uuid=server_uuid,
|
|
|
|
api_key=api_key,
|
|
|
|
hostname=f"{unique_id}.example.com",
|
|
|
|
contact_email=f"{unique_id}@example.com",
|
|
|
|
plan_type=plan_type,
|
2023-12-09 16:41:53 +01:00
|
|
|
# TODO: Save property audit log data for server.
|
|
|
|
last_audit_log_update=timezone_now(),
|
2023-12-04 14:03:24 +01:00
|
|
|
)
|
|
|
|
|
2023-12-13 02:02:14 +01:00
|
|
|
billing_user = RemoteServerBillingUser.objects.create(
|
|
|
|
full_name="Server user",
|
|
|
|
remote_server=remote_server,
|
|
|
|
email=f"{unique_id}@example.com",
|
|
|
|
)
|
|
|
|
billing_session = RemoteServerBillingSession(remote_server, billing_user)
|
2023-12-04 14:03:24 +01:00
|
|
|
if customer_profile.tier == CustomerPlan.TIER_SELF_HOSTED_LEGACY:
|
|
|
|
# Create customer plan for these servers for temporary period.
|
2023-11-19 19:45:19 +01:00
|
|
|
renewal_date = datetime.strptime(customer_profile.renewal_date, TIMESTAMP_FORMAT).replace(
|
|
|
|
tzinfo=timezone.utc
|
|
|
|
)
|
|
|
|
end_date = datetime.strptime(customer_profile.end_date, TIMESTAMP_FORMAT).replace(
|
|
|
|
tzinfo=timezone.utc
|
2023-12-04 14:03:24 +01:00
|
|
|
)
|
2023-12-11 09:51:07 +01:00
|
|
|
billing_session.migrate_customer_to_legacy_plan(renewal_date, end_date)
|
2023-12-05 07:38:05 +01:00
|
|
|
# Scheduled server to upgrade to business plan.
|
|
|
|
if customer_profile.status == CustomerPlan.SWITCH_PLAN_TIER_AT_PLAN_END:
|
|
|
|
# This attaches stripe_customer_id to customer.
|
|
|
|
customer = billing_session.update_or_create_stripe_customer()
|
|
|
|
add_card_to_customer(customer)
|
2023-12-18 23:57:32 +01:00
|
|
|
seat_count = 30
|
2023-12-05 07:38:05 +01:00
|
|
|
signed_seat_count, salt = sign_string(str(seat_count))
|
|
|
|
upgrade_request = UpgradeRequest(
|
|
|
|
billing_modality="charge_automatically",
|
|
|
|
schedule="annual",
|
|
|
|
signed_seat_count=signed_seat_count,
|
|
|
|
salt=salt,
|
|
|
|
license_management="automatic",
|
|
|
|
licenses=seat_count,
|
|
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
|
|
|
remote_server_plan_start_date=customer_profile.remote_server_plan_start_date,
|
|
|
|
)
|
|
|
|
billing_session.do_upgrade(upgrade_request)
|
|
|
|
|
2023-12-04 14:03:24 +01:00
|
|
|
elif customer_profile.tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS:
|
2023-12-05 07:38:05 +01:00
|
|
|
customer = billing_session.update_or_create_stripe_customer()
|
|
|
|
assert customer.stripe_customer_id is not None
|
|
|
|
add_card_to_customer(customer)
|
2023-12-05 12:46:57 +01:00
|
|
|
create_plan_for_customer(customer, customer_profile)
|
2023-12-05 07:38:05 +01:00
|
|
|
|
|
|
|
if customer_profile.sponsorship_pending:
|
|
|
|
billing_session.update_customer_sponsorship_status(True)
|
|
|
|
elif customer_profile.is_sponsored:
|
|
|
|
billing_session.do_change_plan_type(tier=None, is_sponsored=True)
|
2023-12-04 14:03:24 +01:00
|
|
|
|
|
|
|
return {
|
|
|
|
"unique_id": unique_id,
|
|
|
|
"server_uuid": server_uuid,
|
|
|
|
"api_key": api_key,
|
|
|
|
}
|
2023-12-05 12:49:22 +01:00
|
|
|
|
|
|
|
|
|
|
|
def populate_remote_realms(customer_profile: CustomerProfile) -> Dict[str, str]:
|
2023-12-08 06:55:51 +01:00
|
|
|
# Delete existing remote realm.
|
|
|
|
RemoteRealm.objects.filter(name=customer_profile.unique_id).delete()
|
|
|
|
flush_cache(None)
|
|
|
|
|
2023-12-05 12:49:22 +01:00
|
|
|
local_realm = populate_realm(customer_profile)
|
|
|
|
assert local_realm is not None
|
|
|
|
|
|
|
|
remote_server_uuid = settings.ZULIP_ORG_ID
|
|
|
|
assert remote_server_uuid is not None
|
|
|
|
remote_server = RemoteZulipServer.objects.filter(
|
|
|
|
uuid=remote_server_uuid,
|
|
|
|
).first()
|
|
|
|
|
|
|
|
if remote_server is None:
|
|
|
|
raise AssertionError("Remote server not found! Please run manage.py register_server")
|
|
|
|
|
|
|
|
update_remote_realm_data_for_server(
|
|
|
|
remote_server, get_realms_info_for_push_bouncer(local_realm.id)
|
|
|
|
)
|
|
|
|
|
|
|
|
remote_realm = RemoteRealm.objects.get(uuid=local_realm.uuid)
|
2023-12-13 02:02:14 +01:00
|
|
|
user = UserProfile.objects.filter(realm=local_realm).first()
|
|
|
|
assert user is not None
|
|
|
|
billing_user = RemoteRealmBillingUser.objects.create(
|
|
|
|
full_name=user.full_name,
|
|
|
|
remote_realm=remote_realm,
|
|
|
|
user_uuid=user.uuid,
|
|
|
|
email=user.email,
|
|
|
|
)
|
|
|
|
billing_session = RemoteRealmBillingSession(remote_realm, billing_user)
|
2023-12-09 16:41:53 +01:00
|
|
|
# TODO: Save property audit log data for server.
|
|
|
|
remote_realm.server.last_audit_log_update = timezone_now()
|
|
|
|
remote_realm.server.save(update_fields=["last_audit_log_update"])
|
2023-12-05 12:49:22 +01:00
|
|
|
customer = billing_session.update_or_create_stripe_customer()
|
|
|
|
assert customer.stripe_customer_id is not None
|
|
|
|
add_card_to_customer(customer)
|
|
|
|
if customer_profile.tier is not None:
|
|
|
|
billing_session.do_change_plan_type(
|
|
|
|
tier=customer_profile.tier, is_sponsored=customer_profile.is_sponsored
|
|
|
|
)
|
2023-12-13 16:44:54 +01:00
|
|
|
if not customer_profile.is_sponsored:
|
|
|
|
create_plan_for_customer(customer, customer_profile)
|
2023-12-05 12:49:22 +01:00
|
|
|
|
|
|
|
if customer_profile.sponsorship_pending:
|
|
|
|
billing_session.update_customer_sponsorship_status(True)
|
|
|
|
|
|
|
|
return {
|
|
|
|
"unique_id": customer_profile.unique_id,
|
|
|
|
"login_url": local_realm.uri + "/self-hosted-billing/",
|
|
|
|
}
|