billing: User FREE_TRIAL_DAYS instead of FREE_TRIAL_MONTHS.

This commit is contained in:
Vishnu Ks 2020-05-14 21:51:23 +05:30 committed by Tim Abbott
parent 6528226a80
commit 66b1ad7002
8 changed files with 49 additions and 45 deletions

View File

@ -1,4 +1,4 @@
from datetime import datetime from datetime import datetime, timedelta
from decimal import Decimal from decimal import Decimal
from functools import wraps from functools import wraps
import logging import logging
@ -291,7 +291,7 @@ def compute_plan_parameters(
if automanage_licenses: if automanage_licenses:
next_invoice_date = add_months(billing_cycle_anchor, 1) next_invoice_date = add_months(billing_cycle_anchor, 1)
if free_trial: if free_trial:
period_end = add_months(billing_cycle_anchor, settings.FREE_TRIAL_MONTHS) period_end = billing_cycle_anchor + timedelta(days=settings.FREE_TRIAL_DAYS)
next_invoice_date = period_end next_invoice_date = period_end
return billing_cycle_anchor, next_invoice_date, period_end, price_per_license return billing_cycle_anchor, next_invoice_date, period_end, price_per_license
@ -302,7 +302,7 @@ def process_initial_upgrade(user: UserProfile, licenses: int, automanage_license
realm = user.realm realm = user.realm
customer = update_or_create_stripe_customer(user, stripe_token=stripe_token) customer = update_or_create_stripe_customer(user, stripe_token=stripe_token)
charge_automatically = stripe_token is not None charge_automatically = stripe_token is not None
free_trial = settings.FREE_TRIAL_MONTHS not in (None, 0) free_trial = settings.FREE_TRIAL_DAYS not in (None, 0)
if get_current_plan_by_customer(customer) is not None: if get_current_plan_by_customer(customer) is not None:
# Unlikely race condition from two people upgrading (clicking "Make payment") # Unlikely race condition from two people upgrading (clicking "Make payment")

View File

@ -534,10 +534,11 @@ class StripeTest(StripeTestCase):
user = self.example_user("hamlet") user = self.example_user("hamlet")
self.login_user(user) self.login_user(user)
with self.settings(FREE_TRIAL_MONTHS=2): with self.settings(FREE_TRIAL_DAYS=60):
response = self.client_get("/upgrade/") response = self.client_get("/upgrade/")
free_trial_end_date = self.now + timedelta(days=60)
self.assert_in_success_response(['Pay annually', 'Free Trial', '2 month'], response) self.assert_in_success_response(['Pay annually', 'Free Trial', '60 day'], response)
self.assertNotEqual(user.realm.plan_type, Realm.STANDARD) self.assertNotEqual(user.realm.plan_type, Realm.STANDARD)
self.assertFalse(Customer.objects.filter(realm=user.realm).exists()) self.assertFalse(Customer.objects.filter(realm=user.realm).exists())
@ -567,7 +568,7 @@ class StripeTest(StripeTestCase):
customer=customer, automanage_licenses=True, customer=customer, automanage_licenses=True,
price_per_license=8000, fixed_price=None, discount=None, billing_cycle_anchor=self.now, price_per_license=8000, fixed_price=None, discount=None, billing_cycle_anchor=self.now,
billing_schedule=CustomerPlan.ANNUAL, invoiced_through=LicenseLedger.objects.first(), billing_schedule=CustomerPlan.ANNUAL, invoiced_through=LicenseLedger.objects.first(),
next_invoice_date=add_months(self.now, 2), tier=CustomerPlan.STANDARD, next_invoice_date=free_trial_end_date, tier=CustomerPlan.STANDARD,
status=CustomerPlan.FREE_TRIAL) status=CustomerPlan.FREE_TRIAL)
LicenseLedger.objects.get( LicenseLedger.objects.get(
plan=plan, is_renewal=True, event_time=self.now, licenses=self.seat_count, plan=plan, is_renewal=True, event_time=self.now, licenses=self.seat_count,
@ -619,13 +620,13 @@ class StripeTest(StripeTestCase):
self.assertEqual(len(invoices), 0) self.assertEqual(len(invoices), 0)
customer_plan = CustomerPlan.objects.get(customer=customer) customer_plan = CustomerPlan.objects.get(customer=customer)
self.assertEqual(customer_plan.status, CustomerPlan.FREE_TRIAL) self.assertEqual(customer_plan.status, CustomerPlan.FREE_TRIAL)
self.assertEqual(customer_plan.next_invoice_date, add_months(self.now, 2)) self.assertEqual(customer_plan.next_invoice_date, free_trial_end_date)
invoice_plans_as_needed(add_months(self.now, 2)) invoice_plans_as_needed(free_trial_end_date)
customer_plan.refresh_from_db() customer_plan.refresh_from_db()
realm.refresh_from_db() realm.refresh_from_db()
self.assertEqual(customer_plan.status, CustomerPlan.ACTIVE) self.assertEqual(customer_plan.status, CustomerPlan.ACTIVE)
self.assertEqual(customer_plan.next_invoice_date, add_months(self.now, 3)) self.assertEqual(customer_plan.next_invoice_date, add_months(free_trial_end_date, 1))
self.assertEqual(realm.plan_type, Realm.STANDARD) self.assertEqual(realm.plan_type, Realm.STANDARD)
invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)] invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)]
self.assertEqual(len(invoices), 1) self.assertEqual(len(invoices), 1)
@ -643,24 +644,24 @@ class StripeTest(StripeTestCase):
"amount": 15 * 80 * 100, "description": "Zulip Standard - renewal", "amount": 15 * 80 * 100, "description": "Zulip Standard - renewal",
"plan": None, "quantity": 15, "subscription": None, "discountable": False, "plan": None, "quantity": 15, "subscription": None, "discountable": False,
"period": { "period": {
"start": datetime_to_timestamp(add_months(self.now, 2)), "start": datetime_to_timestamp(free_trial_end_date),
"end": datetime_to_timestamp(add_months(self.now, 14)) "end": datetime_to_timestamp(add_months(free_trial_end_date, 12))
}, },
} }
for key, value in invoice_item_params.items(): for key, value in invoice_item_params.items():
self.assertEqual(invoice_items[0][key], value) self.assertEqual(invoice_items[0][key], value)
invoice_plans_as_needed(add_months(self.now, 3)) invoice_plans_as_needed(add_months(free_trial_end_date, 1))
invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)] invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)]
self.assertEqual(len(invoices), 1) self.assertEqual(len(invoices), 1)
with patch('corporate.lib.stripe.get_latest_seat_count', return_value=19): with patch('corporate.lib.stripe.get_latest_seat_count', return_value=19):
update_license_ledger_if_needed(realm, add_months(self.now, 12)) update_license_ledger_if_needed(realm, add_months(free_trial_end_date, 10))
self.assertEqual( self.assertEqual(
LicenseLedger.objects.order_by('-id').values_list('licenses', 'licenses_at_next_renewal').first(), LicenseLedger.objects.order_by('-id').values_list('licenses', 'licenses_at_next_renewal').first(),
(19, 19) (19, 19)
) )
invoice_plans_as_needed(add_months(self.now, 12)) invoice_plans_as_needed(add_months(free_trial_end_date, 10))
invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)] invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)]
self.assertEqual(len(invoices), 2) self.assertEqual(len(invoices), 2)
invoice_params = { invoice_params = {
@ -673,12 +674,12 @@ class StripeTest(StripeTestCase):
"amount": 5172, "description": "Additional license (Jan 2, 2013 - Mar 2, 2013)", "amount": 5172, "description": "Additional license (Jan 2, 2013 - Mar 2, 2013)",
"discountable": False, "quantity": 4, "discountable": False, "quantity": 4,
"period": { "period": {
"start": datetime_to_timestamp(add_months(self.now, 12)), "start": datetime_to_timestamp(add_months(free_trial_end_date, 10)),
"end": datetime_to_timestamp(add_months(self.now, 14)) "end": datetime_to_timestamp(add_months(free_trial_end_date, 12))
} }
} }
invoice_plans_as_needed(add_months(self.now, 14)) invoice_plans_as_needed(add_months(free_trial_end_date, 12))
invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)] invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)]
self.assertEqual(len(invoices), 3) self.assertEqual(len(invoices), 3)
@ -687,10 +688,11 @@ class StripeTest(StripeTestCase):
user = self.example_user("hamlet") user = self.example_user("hamlet")
self.login_user(user) self.login_user(user)
with self.settings(FREE_TRIAL_MONTHS=2): free_trial_end_date = self.now + timedelta(days=60)
with self.settings(FREE_TRIAL_DAYS=60):
response = self.client_get("/upgrade/") response = self.client_get("/upgrade/")
self.assert_in_success_response(['Pay annually', 'Free Trial', '2 month'], response) self.assert_in_success_response(['Pay annually', 'Free Trial', '60 day'], response)
self.assertNotEqual(user.realm.plan_type, Realm.STANDARD) self.assertNotEqual(user.realm.plan_type, Realm.STANDARD)
self.assertFalse(Customer.objects.filter(realm=user.realm).exists()) self.assertFalse(Customer.objects.filter(realm=user.realm).exists())
@ -715,7 +717,7 @@ class StripeTest(StripeTestCase):
customer=customer, automanage_licenses=False, customer=customer, automanage_licenses=False,
price_per_license=8000, fixed_price=None, discount=None, billing_cycle_anchor=self.now, price_per_license=8000, fixed_price=None, discount=None, billing_cycle_anchor=self.now,
billing_schedule=CustomerPlan.ANNUAL, invoiced_through=LicenseLedger.objects.first(), billing_schedule=CustomerPlan.ANNUAL, invoiced_through=LicenseLedger.objects.first(),
next_invoice_date=add_months(self.now, 2), tier=CustomerPlan.STANDARD, next_invoice_date=free_trial_end_date, tier=CustomerPlan.STANDARD,
status=CustomerPlan.FREE_TRIAL) status=CustomerPlan.FREE_TRIAL)
LicenseLedger.objects.get( LicenseLedger.objects.get(
@ -754,13 +756,13 @@ class StripeTest(StripeTestCase):
mocked.reset_mock() mocked.reset_mock()
customer_plan = CustomerPlan.objects.get(customer=customer) customer_plan = CustomerPlan.objects.get(customer=customer)
self.assertEqual(customer_plan.status, CustomerPlan.FREE_TRIAL) self.assertEqual(customer_plan.status, CustomerPlan.FREE_TRIAL)
self.assertEqual(customer_plan.next_invoice_date, add_months(self.now, 2)) self.assertEqual(customer_plan.next_invoice_date, free_trial_end_date)
invoice_plans_as_needed(add_months(self.now, 2)) invoice_plans_as_needed(free_trial_end_date)
customer_plan.refresh_from_db() customer_plan.refresh_from_db()
realm.refresh_from_db() realm.refresh_from_db()
self.assertEqual(customer_plan.status, CustomerPlan.ACTIVE) self.assertEqual(customer_plan.status, CustomerPlan.ACTIVE)
self.assertEqual(customer_plan.next_invoice_date, add_months(self.now, 14)) self.assertEqual(customer_plan.next_invoice_date, add_months(free_trial_end_date, 12))
self.assertEqual(realm.plan_type, Realm.STANDARD) self.assertEqual(realm.plan_type, Realm.STANDARD)
invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)] invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)]
self.assertEqual(len(invoices), 1) self.assertEqual(len(invoices), 1)
@ -778,22 +780,22 @@ class StripeTest(StripeTestCase):
"amount": 123 * 80 * 100, "description": "Zulip Standard - renewal", "amount": 123 * 80 * 100, "description": "Zulip Standard - renewal",
"plan": None, "quantity": 123, "subscription": None, "discountable": False, "plan": None, "quantity": 123, "subscription": None, "discountable": False,
"period": { "period": {
"start": datetime_to_timestamp(add_months(self.now, 2)), "start": datetime_to_timestamp(free_trial_end_date),
"end": datetime_to_timestamp(add_months(self.now, 14)) "end": datetime_to_timestamp(add_months(free_trial_end_date, 12))
}, },
} }
for key, value in invoice_item_params.items(): for key, value in invoice_item_params.items():
self.assertEqual(invoice_items[0][key], value) self.assertEqual(invoice_items[0][key], value)
invoice_plans_as_needed(add_months(self.now, 3)) invoice_plans_as_needed(add_months(free_trial_end_date, 1))
invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)] invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)]
self.assertEqual(len(invoices), 1) self.assertEqual(len(invoices), 1)
invoice_plans_as_needed(add_months(self.now, 12)) invoice_plans_as_needed(add_months(free_trial_end_date, 10))
invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)] invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)]
self.assertEqual(len(invoices), 1) self.assertEqual(len(invoices), 1)
invoice_plans_as_needed(add_months(self.now, 14)) invoice_plans_as_needed(add_months(free_trial_end_date, 12))
invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)] invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)]
self.assertEqual(len(invoices), 2) self.assertEqual(len(invoices), 2)
@ -1284,12 +1286,14 @@ class StripeTest(StripeTestCase):
@patch("corporate.lib.stripe.billing_logger.info") @patch("corporate.lib.stripe.billing_logger.info")
def test_downgrade_free_trial(self, mock_: Mock) -> None: def test_downgrade_free_trial(self, mock_: Mock) -> None:
user = self.example_user("hamlet") user = self.example_user("hamlet")
with self.settings(FREE_TRIAL_MONTHS=2):
free_trial_end_date = self.now + timedelta(days=60)
with self.settings(FREE_TRIAL_DAYS=60):
with patch("corporate.lib.stripe.timezone_now", return_value=self.now): with patch("corporate.lib.stripe.timezone_now", return_value=self.now):
self.local_upgrade(self.seat_count, True, CustomerPlan.ANNUAL, 'token') self.local_upgrade(self.seat_count, True, CustomerPlan.ANNUAL, 'token')
plan = CustomerPlan.objects.get() plan = CustomerPlan.objects.get()
self.assertEqual(plan.next_invoice_date, add_months(self.now, 2)) self.assertEqual(plan.next_invoice_date, free_trial_end_date)
self.assertEqual(get_realm('zulip').plan_type, Realm.STANDARD) self.assertEqual(get_realm('zulip').plan_type, Realm.STANDARD)
self.assertEqual(plan.status, CustomerPlan.FREE_TRIAL) self.assertEqual(plan.status, CustomerPlan.FREE_TRIAL)

View File

@ -146,7 +146,7 @@ def initial_upgrade(request: HttpRequest) -> HttpResponse:
'min_invoiced_licenses': max(seat_count, MIN_INVOICED_LICENSES), 'min_invoiced_licenses': max(seat_count, MIN_INVOICED_LICENSES),
'default_invoice_days_until_due': DEFAULT_INVOICE_DAYS_UNTIL_DUE, 'default_invoice_days_until_due': DEFAULT_INVOICE_DAYS_UNTIL_DUE,
'plan': "Zulip Standard", 'plan': "Zulip Standard",
"free_trial_months": settings.FREE_TRIAL_MONTHS, "free_trial_days": settings.FREE_TRIAL_DAYS,
'page_params': { 'page_params': {
'seat_count': seat_count, 'seat_count': seat_count,
'annual_price': 8000, 'annual_price': 8000,

View File

@ -18,9 +18,9 @@
<div class="page-content"> <div class="page-content">
<div class="main"> <div class="main">
<h1>{% trans %}Upgrade to {{ plan }}{% endtrans %}</h1> <h1>{% trans %}Upgrade to {{ plan }}{% endtrans %}</h1>
{% if free_trial_months %} {% if free_trial_days %}
<div class="alert alert-info"> <div class="alert alert-info">
Upgrade now to start your {{ free_trial_months }} month Free Trial of Zulip Standard. Upgrade now to start your {{ free_trial_days }} day Free Trial of Zulip Standard.
</div> </div>
{% endif %} {% endif %}
@ -44,7 +44,7 @@
<input type="hidden" name="signed_seat_count" value="{{ signed_seat_count }}"> <input type="hidden" name="signed_seat_count" value="{{ signed_seat_count }}">
<input type="hidden" name="salt" value="{{ salt }}"> <input type="hidden" name="salt" value="{{ salt }}">
<input type="hidden" name="billing_modality" value="charge_automatically"> <input type="hidden" name="billing_modality" value="charge_automatically">
{% if free_trial_months %} {% if free_trial_days %}
<p> <p>
You won't be charged during the Free Trial. You can also downgrade back to Zulip Limited You won't be charged during the Free Trial. You can also downgrade back to Zulip Limited
during the Free Trial. during the Free Trial.
@ -94,7 +94,7 @@
<div id="license-automatic-section"> <div id="license-automatic-section">
<p> <p>
{% if free_trial_months %} {% if free_trial_days %}
After the Free Trial, you&rsquo;ll be charged After the Free Trial, you&rsquo;ll be charged
<b>$<span id="charged_amount"></span></b> for <b>{{ seat_count }}</b> <b>$<span id="charged_amount"></span></b> for <b>{{ seat_count }}</b>
users. users.
@ -116,7 +116,7 @@
<div id="license-manual-section"> <div id="license-manual-section">
<p> <p>
{% if free_trial_months %} {% if free_trial_days %}
Enter the number of users you would like to pay for after the Free Trial.<br> Enter the number of users you would like to pay for after the Free Trial.<br>
You'll need to manually add licenses to add or invite You'll need to manually add licenses to add or invite
additional users. additional users.
@ -175,7 +175,7 @@
</label> </label>
</div> </div>
<p> <p>
{% if free_trial_months %} {% if free_trial_days %}
Enter the number of users you would like to pay for.<br> Enter the number of users you would like to pay for.<br>
We'll email you an invoice after the Free Trial. We'll email you an invoice after the Free Trial.
Invoices can be paid by ACH transfer or credit card. Invoices can be paid by ACH transfer or credit card.

View File

@ -87,16 +87,16 @@
</a> </a>
{% elif realm_plan_type == 2 %} {% elif realm_plan_type == 2 %}
<a href="/upgrade" class="button green"> <a href="/upgrade" class="button green">
{% if free_trial_months %} {% if free_trial_days %}
Start {{ free_trial_months }} month Free Trial Start {{ free_trial_days }} day Free Trial
{% else %} {% else %}
Buy Standard Buy Standard
{% endif %} {% endif %}
</a> </a>
{% else %} {% else %}
<a href="/upgrade" class="button green"> <a href="/upgrade" class="button green">
{% if free_trial_months %} {% if free_trial_days %}
Start {{ free_trial_months }} month Free Trial Start {{ free_trial_days }} day Free Trial
{% else %} {% else %}
Buy Standard Buy Standard
{% endif %} {% endif %}

View File

@ -26,7 +26,7 @@ def apps_view(request: HttpRequest, _: str) -> HttpResponse:
def plans_view(request: HttpRequest) -> HttpResponse: def plans_view(request: HttpRequest) -> HttpResponse:
realm = get_realm_from_request(request) realm = get_realm_from_request(request)
realm_plan_type = 0 realm_plan_type = 0
free_trial_months = settings.FREE_TRIAL_MONTHS free_trial_days = settings.FREE_TRIAL_DAYS
if realm is not None: if realm is not None:
realm_plan_type = realm.plan_type realm_plan_type = realm.plan_type
if realm.plan_type == Realm.SELF_HOSTED and settings.PRODUCTION: if realm.plan_type == Realm.SELF_HOSTED and settings.PRODUCTION:
@ -36,7 +36,7 @@ def plans_view(request: HttpRequest) -> HttpResponse:
return TemplateResponse( return TemplateResponse(
request, request,
"zerver/plans.html", "zerver/plans.html",
context={"realm_plan_type": realm_plan_type, 'free_trial_months': free_trial_months}, context={"realm_plan_type": realm_plan_type, 'free_trial_days': free_trial_days},
) )
@add_google_analytics @add_google_analytics

View File

@ -363,7 +363,7 @@ ARCHIVED_DATA_VACUUMING_DELAY_DAYS = 7
# are available to all realms. # are available to all realms.
BILLING_ENABLED = False BILLING_ENABLED = False
FREE_TRIAL_MONTHS = None FREE_TRIAL_DAYS = None
# Automatically catch-up soft deactivated users when running the # Automatically catch-up soft deactivated users when running the
# `soft-deactivate-users` cron. Turn this off if the server has 10Ks of # `soft-deactivate-users` cron. Turn this off if the server has 10Ks of

View File

@ -155,7 +155,7 @@ THUMBNAIL_IMAGES = True
SEARCH_PILLS_ENABLED = bool(os.getenv('SEARCH_PILLS_ENABLED', False)) SEARCH_PILLS_ENABLED = bool(os.getenv('SEARCH_PILLS_ENABLED', False))
BILLING_ENABLED = True BILLING_ENABLED = True
FREE_TRIAL_MONTHS = None FREE_TRIAL_DAYS = None
# Test Custom TOS template rendering # Test Custom TOS template rendering
TERMS_OF_SERVICE = 'corporate/terms.md' TERMS_OF_SERVICE = 'corporate/terms.md'