mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	billing: User FREE_TRIAL_DAYS instead of FREE_TRIAL_MONTHS.
This commit is contained in:
		@@ -1,4 +1,4 @@
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from datetime import datetime, timedelta
 | 
			
		||||
from decimal import Decimal
 | 
			
		||||
from functools import wraps
 | 
			
		||||
import logging
 | 
			
		||||
@@ -291,7 +291,7 @@ def compute_plan_parameters(
 | 
			
		||||
    if automanage_licenses:
 | 
			
		||||
        next_invoice_date = add_months(billing_cycle_anchor, 1)
 | 
			
		||||
    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
 | 
			
		||||
    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
 | 
			
		||||
    customer = update_or_create_stripe_customer(user, stripe_token=stripe_token)
 | 
			
		||||
    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:
 | 
			
		||||
        # Unlikely race condition from two people upgrading (clicking "Make payment")
 | 
			
		||||
 
 | 
			
		||||
@@ -534,10 +534,11 @@ class StripeTest(StripeTestCase):
 | 
			
		||||
        user = self.example_user("hamlet")
 | 
			
		||||
        self.login_user(user)
 | 
			
		||||
 | 
			
		||||
        with self.settings(FREE_TRIAL_MONTHS=2):
 | 
			
		||||
        with self.settings(FREE_TRIAL_DAYS=60):
 | 
			
		||||
            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.assertFalse(Customer.objects.filter(realm=user.realm).exists())
 | 
			
		||||
 | 
			
		||||
@@ -567,7 +568,7 @@ class StripeTest(StripeTestCase):
 | 
			
		||||
                customer=customer, automanage_licenses=True,
 | 
			
		||||
                price_per_license=8000, fixed_price=None, discount=None, billing_cycle_anchor=self.now,
 | 
			
		||||
                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)
 | 
			
		||||
            LicenseLedger.objects.get(
 | 
			
		||||
                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)
 | 
			
		||||
            customer_plan = CustomerPlan.objects.get(customer=customer)
 | 
			
		||||
            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()
 | 
			
		||||
            realm.refresh_from_db()
 | 
			
		||||
            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)
 | 
			
		||||
            invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)]
 | 
			
		||||
            self.assertEqual(len(invoices), 1)
 | 
			
		||||
@@ -643,24 +644,24 @@ class StripeTest(StripeTestCase):
 | 
			
		||||
                "amount": 15 * 80 * 100, "description": "Zulip Standard - renewal",
 | 
			
		||||
                "plan": None, "quantity": 15, "subscription": None, "discountable": False,
 | 
			
		||||
                "period": {
 | 
			
		||||
                    "start": datetime_to_timestamp(add_months(self.now, 2)),
 | 
			
		||||
                    "end": datetime_to_timestamp(add_months(self.now, 14))
 | 
			
		||||
                    "start": datetime_to_timestamp(free_trial_end_date),
 | 
			
		||||
                    "end": datetime_to_timestamp(add_months(free_trial_end_date, 12))
 | 
			
		||||
                },
 | 
			
		||||
            }
 | 
			
		||||
            for key, value in invoice_item_params.items():
 | 
			
		||||
                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)]
 | 
			
		||||
            self.assertEqual(len(invoices), 1)
 | 
			
		||||
 | 
			
		||||
            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(
 | 
			
		||||
                LicenseLedger.objects.order_by('-id').values_list('licenses', 'licenses_at_next_renewal').first(),
 | 
			
		||||
                (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)]
 | 
			
		||||
            self.assertEqual(len(invoices), 2)
 | 
			
		||||
            invoice_params = {
 | 
			
		||||
@@ -673,12 +674,12 @@ class StripeTest(StripeTestCase):
 | 
			
		||||
                "amount": 5172, "description": "Additional license (Jan 2, 2013 - Mar 2, 2013)",
 | 
			
		||||
                "discountable": False, "quantity": 4,
 | 
			
		||||
                "period": {
 | 
			
		||||
                    "start": datetime_to_timestamp(add_months(self.now, 12)),
 | 
			
		||||
                    "end": datetime_to_timestamp(add_months(self.now, 14))
 | 
			
		||||
                    "start": datetime_to_timestamp(add_months(free_trial_end_date, 10)),
 | 
			
		||||
                    "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)]
 | 
			
		||||
            self.assertEqual(len(invoices), 3)
 | 
			
		||||
 | 
			
		||||
@@ -687,10 +688,11 @@ class StripeTest(StripeTestCase):
 | 
			
		||||
        user = self.example_user("hamlet")
 | 
			
		||||
        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/")
 | 
			
		||||
 | 
			
		||||
            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.assertFalse(Customer.objects.filter(realm=user.realm).exists())
 | 
			
		||||
 | 
			
		||||
@@ -715,7 +717,7 @@ class StripeTest(StripeTestCase):
 | 
			
		||||
                customer=customer, automanage_licenses=False,
 | 
			
		||||
                price_per_license=8000, fixed_price=None, discount=None, billing_cycle_anchor=self.now,
 | 
			
		||||
                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)
 | 
			
		||||
 | 
			
		||||
            LicenseLedger.objects.get(
 | 
			
		||||
@@ -754,13 +756,13 @@ class StripeTest(StripeTestCase):
 | 
			
		||||
            mocked.reset_mock()
 | 
			
		||||
            customer_plan = CustomerPlan.objects.get(customer=customer)
 | 
			
		||||
            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()
 | 
			
		||||
            realm.refresh_from_db()
 | 
			
		||||
            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)
 | 
			
		||||
            invoices = [invoice for invoice in stripe.Invoice.list(customer=stripe_customer.id)]
 | 
			
		||||
            self.assertEqual(len(invoices), 1)
 | 
			
		||||
@@ -778,22 +780,22 @@ class StripeTest(StripeTestCase):
 | 
			
		||||
                "amount": 123 * 80 * 100, "description": "Zulip Standard - renewal",
 | 
			
		||||
                "plan": None, "quantity": 123, "subscription": None, "discountable": False,
 | 
			
		||||
                "period": {
 | 
			
		||||
                    "start": datetime_to_timestamp(add_months(self.now, 2)),
 | 
			
		||||
                    "end": datetime_to_timestamp(add_months(self.now, 14))
 | 
			
		||||
                    "start": datetime_to_timestamp(free_trial_end_date),
 | 
			
		||||
                    "end": datetime_to_timestamp(add_months(free_trial_end_date, 12))
 | 
			
		||||
                },
 | 
			
		||||
            }
 | 
			
		||||
            for key, value in invoice_item_params.items():
 | 
			
		||||
                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)]
 | 
			
		||||
            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)]
 | 
			
		||||
            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)]
 | 
			
		||||
            self.assertEqual(len(invoices), 2)
 | 
			
		||||
 | 
			
		||||
@@ -1284,12 +1286,14 @@ class StripeTest(StripeTestCase):
 | 
			
		||||
    @patch("corporate.lib.stripe.billing_logger.info")
 | 
			
		||||
    def test_downgrade_free_trial(self, mock_: Mock) -> None:
 | 
			
		||||
        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):
 | 
			
		||||
                self.local_upgrade(self.seat_count, True, CustomerPlan.ANNUAL, 'token')
 | 
			
		||||
 | 
			
		||||
            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(plan.status, CustomerPlan.FREE_TRIAL)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -146,7 +146,7 @@ def initial_upgrade(request: HttpRequest) -> HttpResponse:
 | 
			
		||||
        'min_invoiced_licenses': max(seat_count, MIN_INVOICED_LICENSES),
 | 
			
		||||
        'default_invoice_days_until_due': DEFAULT_INVOICE_DAYS_UNTIL_DUE,
 | 
			
		||||
        'plan': "Zulip Standard",
 | 
			
		||||
        "free_trial_months": settings.FREE_TRIAL_MONTHS,
 | 
			
		||||
        "free_trial_days": settings.FREE_TRIAL_DAYS,
 | 
			
		||||
        'page_params': {
 | 
			
		||||
            'seat_count': seat_count,
 | 
			
		||||
            'annual_price': 8000,
 | 
			
		||||
 
 | 
			
		||||
@@ -18,9 +18,9 @@
 | 
			
		||||
        <div class="page-content">
 | 
			
		||||
            <div class="main">
 | 
			
		||||
                <h1>{% trans %}Upgrade to {{ plan }}{% endtrans %}</h1>
 | 
			
		||||
                {% if free_trial_months %}
 | 
			
		||||
                {% if free_trial_days %}
 | 
			
		||||
                <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>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
 | 
			
		||||
@@ -44,7 +44,7 @@
 | 
			
		||||
                                <input type="hidden" name="signed_seat_count" value="{{ signed_seat_count }}">
 | 
			
		||||
                                <input type="hidden" name="salt" value="{{ salt }}">
 | 
			
		||||
                                <input type="hidden" name="billing_modality" value="charge_automatically">
 | 
			
		||||
                                {% if free_trial_months %}
 | 
			
		||||
                                {% if free_trial_days %}
 | 
			
		||||
                                <p>
 | 
			
		||||
                                    You won't be charged during the Free Trial. You can also downgrade back to Zulip Limited
 | 
			
		||||
                                    during the Free Trial.
 | 
			
		||||
@@ -94,7 +94,7 @@
 | 
			
		||||
 | 
			
		||||
                                <div id="license-automatic-section">
 | 
			
		||||
                                    <p>
 | 
			
		||||
                                        {% if free_trial_months %}
 | 
			
		||||
                                        {% if free_trial_days %}
 | 
			
		||||
                                        After the Free Trial, you’ll be charged
 | 
			
		||||
                                        <b>$<span id="charged_amount"></span></b> for <b>{{ seat_count }}</b>
 | 
			
		||||
                                        users.
 | 
			
		||||
@@ -116,7 +116,7 @@
 | 
			
		||||
                                <div id="license-manual-section">
 | 
			
		||||
 | 
			
		||||
                                    <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>
 | 
			
		||||
                                        You'll need to manually add licenses to add or invite
 | 
			
		||||
                                        additional users.
 | 
			
		||||
@@ -175,7 +175,7 @@
 | 
			
		||||
                                    </label>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <p>
 | 
			
		||||
                                    {% if free_trial_months %}
 | 
			
		||||
                                    {% if free_trial_days %}
 | 
			
		||||
                                    Enter the number of users you would like to pay for.<br>
 | 
			
		||||
                                    We'll email you an invoice after the Free Trial.
 | 
			
		||||
                                    Invoices can be paid by ACH transfer or credit card.
 | 
			
		||||
 
 | 
			
		||||
@@ -87,16 +87,16 @@
 | 
			
		||||
                                    </a>
 | 
			
		||||
                                    {% elif realm_plan_type == 2 %}
 | 
			
		||||
                                    <a href="/upgrade" class="button green">
 | 
			
		||||
                                        {% if free_trial_months %}
 | 
			
		||||
                                        Start {{ free_trial_months }} month Free Trial
 | 
			
		||||
                                        {% if free_trial_days %}
 | 
			
		||||
                                        Start {{ free_trial_days }} day Free Trial
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                        Buy Standard
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                    </a>
 | 
			
		||||
                                    {% else %}
 | 
			
		||||
                                    <a href="/upgrade" class="button green">
 | 
			
		||||
                                        {% if free_trial_months %}
 | 
			
		||||
                                        Start {{ free_trial_months }} month Free Trial
 | 
			
		||||
                                        {% if free_trial_days %}
 | 
			
		||||
                                        Start {{ free_trial_days }} day Free Trial
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                        Buy Standard
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ def apps_view(request: HttpRequest, _: str) -> HttpResponse:
 | 
			
		||||
def plans_view(request: HttpRequest) -> HttpResponse:
 | 
			
		||||
    realm = get_realm_from_request(request)
 | 
			
		||||
    realm_plan_type = 0
 | 
			
		||||
    free_trial_months = settings.FREE_TRIAL_MONTHS
 | 
			
		||||
    free_trial_days = settings.FREE_TRIAL_DAYS
 | 
			
		||||
    if realm is not None:
 | 
			
		||||
        realm_plan_type = realm.plan_type
 | 
			
		||||
        if realm.plan_type == Realm.SELF_HOSTED and settings.PRODUCTION:
 | 
			
		||||
@@ -36,7 +36,7 @@ def plans_view(request: HttpRequest) -> HttpResponse:
 | 
			
		||||
    return TemplateResponse(
 | 
			
		||||
        request,
 | 
			
		||||
        "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
 | 
			
		||||
 
 | 
			
		||||
@@ -363,7 +363,7 @@ ARCHIVED_DATA_VACUUMING_DELAY_DAYS = 7
 | 
			
		||||
# are available to all realms.
 | 
			
		||||
BILLING_ENABLED = False
 | 
			
		||||
 | 
			
		||||
FREE_TRIAL_MONTHS = None
 | 
			
		||||
FREE_TRIAL_DAYS = None
 | 
			
		||||
 | 
			
		||||
# Automatically catch-up soft deactivated users when running the
 | 
			
		||||
# `soft-deactivate-users` cron. Turn this off if the server has 10Ks of
 | 
			
		||||
 
 | 
			
		||||
@@ -155,7 +155,7 @@ THUMBNAIL_IMAGES = True
 | 
			
		||||
SEARCH_PILLS_ENABLED = bool(os.getenv('SEARCH_PILLS_ENABLED', False))
 | 
			
		||||
 | 
			
		||||
BILLING_ENABLED = True
 | 
			
		||||
FREE_TRIAL_MONTHS = None
 | 
			
		||||
FREE_TRIAL_DAYS = None
 | 
			
		||||
 | 
			
		||||
# Test Custom TOS template rendering
 | 
			
		||||
TERMS_OF_SERVICE = 'corporate/terms.md'
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user