stripe: Render upgrade page for remote servers on legacy plan.

This commit is contained in:
Aman Agrawal 2023-12-04 13:11:35 +00:00 committed by Tim Abbott
parent 7d83508235
commit f22ccd3125
4 changed files with 131 additions and 22 deletions

View File

@ -608,6 +608,7 @@ class UpgradePageContext(TypedDict):
page_params: UpgradePageParams
payment_method: Optional[str]
plan: str
remote_server_legacy_plan_end_date: Optional[str]
salt: str
seat_count: int
signed_seat_count: str
@ -767,6 +768,31 @@ class BillingSession(ABC):
return True
return False
def get_remote_server_legacy_plan(
self, customer: Optional[Customer], status: int = CustomerPlan.ACTIVE
) -> Optional[CustomerPlan]:
# status = CustomerPlan.ACTIVE means that the legacy plan is not scheduled for an upgrade.
# status = CustomerPlan.SWITCH_PLAN_TIER_AT_PLAN_END means that the legacy plan is scheduled for an upgrade.
if customer is None:
return None
return CustomerPlan.objects.filter(
customer=customer,
tier=CustomerPlan.TIER_SELF_HOSTED_LEGACY,
status=status,
next_invoice_date=None,
).first()
def get_formatted_remote_server_legacy_plan_end_date(
self, customer: Optional[Customer], status: int = CustomerPlan.ACTIVE
) -> Optional[str]: # nocoverage
plan = self.get_remote_server_legacy_plan(customer, status)
if plan is None:
return None
assert plan.end_date is not None
return plan.end_date.strftime("%B %d, %Y")
@catch_stripe_errors
def create_stripe_customer(self) -> Customer:
stripe_customer_data = self.get_data_for_stripe_customer()
@ -1514,9 +1540,14 @@ class BillingSession(ABC):
if self.is_sponsored_or_pending(customer):
return f"{self.billing_session_url}/sponsorship", None
billing_page_url = reverse("billing_page")
if customer is not None and (get_current_plan_by_customer(customer) is not None):
return billing_page_url, None
remote_server_legacy_plan_end_date = self.get_formatted_remote_server_legacy_plan_end_date(
customer
)
# Show upgrade page for remote servers on legacy plan.
if customer is not None and remote_server_legacy_plan_end_date is None:
customer_plan = get_current_plan_by_customer(customer)
if customer_plan is not None:
return f"{self.billing_session_url}/billing", None
percent_off = Decimal(0)
if customer is not None and customer.default_discount is not None:
@ -1540,13 +1571,18 @@ class BillingSession(ABC):
signed_seat_count, salt = sign_string(str(seat_count))
tier = initial_upgrade_request.tier
free_trial_days = settings.FREE_TRIAL_DAYS
free_trial_days = None
free_trial_end_date = None
if free_trial_days is not None:
_, _, free_trial_end, _ = compute_plan_parameters(
tier, False, CustomerPlan.BILLING_SCHEDULE_ANNUAL, None, True
)
free_trial_end_date = f"{free_trial_end:%B} {free_trial_end.day}, {free_trial_end.year}"
# Don't show free trial for remote servers on legacy plan.
if remote_server_legacy_plan_end_date is None:
free_trial_days = settings.FREE_TRIAL_DAYS
if free_trial_days is not None:
_, _, free_trial_end, _ = compute_plan_parameters(
tier, False, CustomerPlan.BILLING_SCHEDULE_ANNUAL, None, True
)
free_trial_end_date = (
f"{free_trial_end:%B} {free_trial_end.day}, {free_trial_end.year}"
)
context: UpgradePageContext = {
"customer_name": customer_specific_context["customer_name"],
@ -1557,6 +1593,7 @@ class BillingSession(ABC):
"free_trial_days": free_trial_days,
"free_trial_end_date": free_trial_end_date,
"is_demo_organization": customer_specific_context["is_demo_organization"],
"remote_server_legacy_plan_end_date": remote_server_legacy_plan_end_date,
"manual_license_management": initial_upgrade_request.manual_license_management,
"min_invoiced_licenses": max(seat_count, MIN_INVOICED_LICENSES),
"page_params": {

View File

@ -859,7 +859,7 @@ class StripeTest(StripeTestCase):
# Check that we can no longer access /upgrade
response = self.client_get("/upgrade/")
self.assertEqual(response.status_code, 302)
self.assertEqual("/billing/", response["Location"])
self.assertEqual("http://zulip.testserver/billing", response["Location"])
# Check /billing/ has the correct information
with time_machine.travel(self.now, tick=False):
@ -1007,7 +1007,7 @@ class StripeTest(StripeTestCase):
# Check that we can no longer access /upgrade
response = self.client_get("/upgrade/")
self.assertEqual(response.status_code, 302)
self.assertEqual("/billing/", response["Location"])
self.assertEqual("http://zulip.testserver/billing", response["Location"])
# Check /billing/ has the correct information
with time_machine.travel(self.now, tick=False):
@ -1983,12 +1983,12 @@ class StripeTest(StripeTestCase):
)
response = self.client_get("/upgrade/")
self.assertEqual(response.status_code, 302)
self.assertEqual(response["Location"], "/billing/")
self.assertEqual(response["Location"], "http://zulip.testserver/billing")
with self.settings(FREE_TRIAL_DAYS=30):
response = self.client_get("/upgrade/")
self.assertEqual(response.status_code, 302)
self.assertEqual(response["Location"], "/billing/")
self.assertEqual(response["Location"], "http://zulip.testserver/billing")
def test_get_latest_seat_count(self) -> None:
realm = get_realm("zulip")

View File

@ -58,6 +58,16 @@
</div>
{% endif %}
{% if remote_server_legacy_plan_end_date %}
<div class="input-box upgrade-page-field no-validation">
<select name="remote_server_plan_start_date" id="remote-server-plan-start-date-select">
<option value="billing_cycle_end_date">{{ remote_server_legacy_plan_end_date }}</option>
<option value="today">Today</option>
</select>
<label for="remote-server-plan-start-date-select">Plan start date</label>
</div>
{% endif %}
{% if manual_license_management %}
<div class="input-box upgrade-page-field" id="upgrade-manual-license-count-wrapper">
<label for="licenses" class="inline-block label-title">Number of licenses {% if not exempt_from_license_number_check %}(minimum {{ seat_count }}){% endif %}</label>
@ -69,13 +79,32 @@
<input type="hidden" name="license_management" value="automatic" />
{% endif %}
{% if remote_server_legacy_plan_end_date %}
<div class="input-box upgrade-page-field no-validation" id="due-today-for-future-update-wrapper">
<label for="due-today-for-future-update" class="inline-block label-title">
Due today
</label>
<div id="due-today-for-future-update" class="not-editable-realm-field">
<h1>$0</h1>
</div>
</div>
{% endif %}
<div class="input-box upgrade-page-field no-validation">
<label for="due-today" class="inline-block label-title">Due
{% if free_trial_days %}
on {{ free_trial_end_date }}
{% else %}
today
<label for="due-today" class="inline-block label-title">
{% if remote_server_legacy_plan_end_date %}
<span id="due-today-remote-server-title">
Due {{ remote_server_legacy_plan_end_date }}
</span>
{% endif %}
<span id="due-today-title">
Due
{% if free_trial_days %}
on {{ free_trial_end_date }}
{% else %}
today
{% endif %}
</span>
</label>
<div id="due-today" class="not-editable-realm-field">
$<span class="due-today-unit-price"></span> x
@ -149,11 +178,18 @@
<div class="upgrade-button-container input-box upgrade-page-field" {% if is_demo_organization %}data-tippy-content="{% trans %}Convert demo organization before upgrading.{% endtrans %}"{% endif %}>
<button id="org-upgrade-button{% if is_demo_organization %} permanent-disabled{% endif %}" {% if not payment_method %}disabled{% endif %}>
<span id="org-upgrade-button-text">
{% if free_trial_days %}
Start {{ free_trial_days }}-day free trial
{% else %}
Purchase {{ plan }}
{% if remote_server_legacy_plan_end_date %}
<span id="org-future-upgrade-button-text-remote-server">
Schedule upgrade to {{ plan }}
</span>
{% endif %}
<span id="org-today-upgrade-button-text">
{% if free_trial_days %}
Start {{ free_trial_days }}-day free trial
{% else %}
Purchase {{ plan }}
{% endif %}
</span>
</span>
<object class="loader upgrade-button-loader" type="image/svg+xml" data="{{ static('images/loading/loader-white.svg') }}"></object>
</button>

View File

@ -14,6 +14,13 @@ const prices: Prices = {
const ls = localstorage();
const ls_selected_schedule = ls.get("selected_schedule");
const ls_remote_server_plan_start_date = ls.get("remote_server_plan_start_date");
// The special value "billing_cycle_end_date" is used internally; it
// should not appear in the UI.
let remote_server_plan_start_date: string =
typeof ls_remote_server_plan_start_date === "string"
? ls_remote_server_plan_start_date
: "billing_cycle_end_date";
let selected_schedule: string =
typeof ls_selected_schedule === "string" ? ls_selected_schedule : "monthly";
let current_license_count = page_params.seat_count;
@ -39,6 +46,27 @@ function update_due_today(schedule: string): void {
$("#due-today .due-today-unit-price").text(helpers.format_money(unit_price));
}
function update_due_today_for_remote_server(start_date: string): void {
const $due_today_for_future_update_wrapper = $("#due-today-for-future-update-wrapper");
if ($due_today_for_future_update_wrapper.length === 0) {
// Only present legacy remote server page.
return;
}
if (start_date === "billing_cycle_end_date") {
$due_today_for_future_update_wrapper.show();
$("#due-today-title").hide();
$("#due-today-remote-server-title").show();
$("#org-future-upgrade-button-text-remote-server").show();
$("#org-today-upgrade-button-text").hide();
} else {
$due_today_for_future_update_wrapper.hide();
$("#due-today-title").show();
$("#due-today-remote-server-title").hide();
$("#org-future-upgrade-button-text-remote-server").hide();
$("#org-today-upgrade-button-text").show();
}
}
function update_license_count(license_count: number): void {
$("#upgrade-licenses-change-error").text("");
if (!license_count || license_count < page_params.seat_count) {
@ -122,6 +150,14 @@ export const initialize = (): void => {
update_due_today(selected_schedule);
});
update_due_today_for_remote_server(remote_server_plan_start_date);
$("#remote-server-plan-start-date-select").val(remote_server_plan_start_date);
$<HTMLInputElement>("#remote-server-plan-start-date-select").on("change", function () {
remote_server_plan_start_date = this.value;
ls.set("remote_server_plan_start_date", remote_server_plan_start_date);
update_due_today_for_remote_server(remote_server_plan_start_date);
});
$("#autopay_annual_price_per_month").text(
`Pay annually ($${helpers.format_money(prices.annual / 12)}/user/month)`,
);