mirror of https://github.com/zulip/zulip.git
free trial: Send users to /upgrade after realm creation.
This commit is contained in:
parent
a5f0379e0f
commit
8784539d53
Binary file not shown.
|
@ -625,6 +625,11 @@ class StripeTest(StripeTestCase):
|
||||||
'Visa ending in 4242',
|
'Visa ending in 4242',
|
||||||
'Update card']:
|
'Update card']:
|
||||||
self.assert_in_response(substring, response)
|
self.assert_in_response(substring, response)
|
||||||
|
self.assert_not_in_success_response(["Go to your Zulip organization"], response)
|
||||||
|
|
||||||
|
with patch('corporate.views.timezone_now', return_value=self.now):
|
||||||
|
response = self.client_get("/billing/?onboarding=true")
|
||||||
|
self.assert_in_success_response(["Go to your Zulip organization"], response)
|
||||||
|
|
||||||
with patch('corporate.lib.stripe.get_latest_seat_count', return_value=12):
|
with patch('corporate.lib.stripe.get_latest_seat_count', return_value=12):
|
||||||
update_license_ledger_if_needed(realm, self.now)
|
update_license_ledger_if_needed(realm, self.now)
|
||||||
|
@ -1053,6 +1058,33 @@ class StripeTest(StripeTestCase):
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual('/upgrade/', response.url)
|
self.assertEqual('/upgrade/', response.url)
|
||||||
|
|
||||||
|
def test_redirect_for_upgrade_page(self) -> None:
|
||||||
|
user = self.example_user("iago")
|
||||||
|
self.login_user(user)
|
||||||
|
# No Customer yet;
|
||||||
|
response = self.client_get("/upgrade/")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# Customer, but no CustomerPlan;
|
||||||
|
customer = Customer.objects.create(realm=user.realm, stripe_customer_id='cus_123')
|
||||||
|
response = self.client_get("/upgrade/")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
CustomerPlan.objects.create(customer=customer, billing_cycle_anchor=timezone_now(),
|
||||||
|
billing_schedule=CustomerPlan.ANNUAL, tier=CustomerPlan.STANDARD)
|
||||||
|
response = self.client_get("/upgrade/")
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertEqual(response.url, "/billing/")
|
||||||
|
|
||||||
|
with self.settings(FREE_TRIAL_DAYS=30):
|
||||||
|
response = self.client_get("/upgrade/")
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertEqual(response.url, "/billing/")
|
||||||
|
|
||||||
|
response = self.client_get("/upgrade/?onboarding=true")
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertEqual(response.url, "/billing/?onboarding=true")
|
||||||
|
|
||||||
def test_get_latest_seat_count(self) -> None:
|
def test_get_latest_seat_count(self) -> None:
|
||||||
realm = get_realm("zulip")
|
realm = get_realm("zulip")
|
||||||
initial_count = get_latest_seat_count(realm)
|
initial_count = get_latest_seat_count(realm)
|
||||||
|
|
|
@ -129,7 +129,10 @@ def initial_upgrade(request: HttpRequest) -> HttpResponse:
|
||||||
user = request.user
|
user = request.user
|
||||||
customer = get_customer_by_realm(user.realm)
|
customer = get_customer_by_realm(user.realm)
|
||||||
if customer is not None and get_current_plan_by_customer(customer) is not None:
|
if customer is not None and get_current_plan_by_customer(customer) is not None:
|
||||||
return HttpResponseRedirect(reverse('corporate.views.billing_home'))
|
billing_page_url = reverse('corporate.views.billing_home')
|
||||||
|
if request.GET.get("onboarding") is not None:
|
||||||
|
billing_page_url = "{}?onboarding=true".format(billing_page_url)
|
||||||
|
return HttpResponseRedirect(billing_page_url)
|
||||||
|
|
||||||
percent_off = Decimal(0)
|
percent_off = Decimal(0)
|
||||||
if customer is not None and customer.default_discount is not None:
|
if customer is not None and customer.default_discount is not None:
|
||||||
|
@ -147,6 +150,7 @@ def initial_upgrade(request: HttpRequest) -> HttpResponse:
|
||||||
'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_days": settings.FREE_TRIAL_DAYS,
|
"free_trial_days": settings.FREE_TRIAL_DAYS,
|
||||||
|
"onboarding": request.GET.get("onboarding") is not None,
|
||||||
'page_params': {
|
'page_params': {
|
||||||
'seat_count': seat_count,
|
'seat_count': seat_count,
|
||||||
'annual_price': 8000,
|
'annual_price': 8000,
|
||||||
|
@ -212,6 +216,7 @@ def billing_home(request: HttpRequest) -> HttpResponse:
|
||||||
'publishable_key': STRIPE_PUBLISHABLE_KEY,
|
'publishable_key': STRIPE_PUBLISHABLE_KEY,
|
||||||
'stripe_email': stripe_customer.email,
|
'stripe_email': stripe_customer.email,
|
||||||
'CustomerPlan': CustomerPlan,
|
'CustomerPlan': CustomerPlan,
|
||||||
|
'onboarding': request.GET.get("onboarding") is not None,
|
||||||
})
|
})
|
||||||
|
|
||||||
return render(request, 'corporate/billing.html', context=context)
|
return render(request, 'corporate/billing.html', context=context)
|
||||||
|
|
|
@ -25,6 +25,7 @@ run_test('create_ajax_request', () => {
|
||||||
const form_success = "#autopay-success";
|
const form_success = "#autopay-success";
|
||||||
const form_error = "#autopay-error";
|
const form_error = "#autopay-error";
|
||||||
const form_loading = "#autopay-loading";
|
const form_loading = "#autopay-loading";
|
||||||
|
const zulip_limited_section = "#zulip-limited-section";
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
form_input_section_show: 0,
|
form_input_section_show: 0,
|
||||||
|
@ -34,6 +35,8 @@ run_test('create_ajax_request', () => {
|
||||||
form_loading_show: 0,
|
form_loading_show: 0,
|
||||||
form_loading_hide: 0,
|
form_loading_hide: 0,
|
||||||
form_success_show: 0,
|
form_success_show: 0,
|
||||||
|
zulip_limited_section_show: 0,
|
||||||
|
zulip_limited_section_hide: 0,
|
||||||
location_reload: 0,
|
location_reload: 0,
|
||||||
pushState: 0,
|
pushState: 0,
|
||||||
make_indicator: 0,
|
make_indicator: 0,
|
||||||
|
@ -79,6 +82,14 @@ run_test('create_ajax_request', () => {
|
||||||
state.form_loading_hide += 1;
|
state.form_loading_hide += 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$(zulip_limited_section).show = () => {
|
||||||
|
state.zulip_limited_section_show += 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
$(zulip_limited_section).hide = () => {
|
||||||
|
state.zulip_limited_section_hide += 1;
|
||||||
|
};
|
||||||
|
|
||||||
$("#autopay-form").serializeArray = () => {
|
$("#autopay-form").serializeArray = () => {
|
||||||
return jquery("#autopay-form").serializeArray();
|
return jquery("#autopay-form").serializeArray();
|
||||||
};
|
};
|
||||||
|
@ -87,6 +98,8 @@ run_test('create_ajax_request', () => {
|
||||||
assert.equal(state.form_input_section_hide, 1);
|
assert.equal(state.form_input_section_hide, 1);
|
||||||
assert.equal(state.form_error_hide, 1);
|
assert.equal(state.form_error_hide, 1);
|
||||||
assert.equal(state.form_loading_show, 1);
|
assert.equal(state.form_loading_show, 1);
|
||||||
|
assert.equal(state.zulip_limited_section_hide, 1);
|
||||||
|
assert.equal(state.zulip_limited_section_show, 0);
|
||||||
assert.equal(state.make_indicator, 1);
|
assert.equal(state.make_indicator, 1);
|
||||||
|
|
||||||
assert.equal(url, "/json/billing/upgrade");
|
assert.equal(url, "/json/billing/upgrade");
|
||||||
|
@ -119,12 +132,15 @@ run_test('create_ajax_request', () => {
|
||||||
assert.equal(state.form_success_show, 1);
|
assert.equal(state.form_success_show, 1);
|
||||||
assert.equal(state.form_error_hide, 2);
|
assert.equal(state.form_error_hide, 2);
|
||||||
assert.equal(state.form_loading_hide, 1);
|
assert.equal(state.form_loading_hide, 1);
|
||||||
|
assert.equal(state.zulip_limited_section_hide, 1);
|
||||||
|
assert.equal(state.zulip_limited_section_show, 0);
|
||||||
|
|
||||||
error({responseText: '{"msg": "response_message"}'});
|
error({responseText: '{"msg": "response_message"}'});
|
||||||
|
|
||||||
assert.equal(state.form_loading_hide, 2);
|
assert.equal(state.form_loading_hide, 2);
|
||||||
assert.equal(state.form_error_show, 1);
|
assert.equal(state.form_error_show, 1);
|
||||||
assert.equal(state.form_input_section_show, 1);
|
assert.equal(state.form_input_section_show, 1);
|
||||||
|
assert.equal(state.zulip_limited_section_hide, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
helpers.create_ajax_request("/json/billing/upgrade", "autopay", {id: "stripe_token_id"}, ["licenses"]);
|
helpers.create_ajax_request("/json/billing/upgrade", "autopay", {id: "stripe_token_id"}, ["licenses"]);
|
||||||
|
|
|
@ -102,6 +102,7 @@ run_test("initialize", () => {
|
||||||
helpers.is_valid_input = () => {
|
helpers.is_valid_input = () => {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
add_card_click_handler(e);
|
add_card_click_handler(e);
|
||||||
invoice_click_handler(e);
|
invoice_click_handler(e);
|
||||||
|
|
||||||
|
@ -159,6 +160,8 @@ run_test("autopay_form_fields", () => {
|
||||||
assert(document.querySelector("#autopay_loading_indicator"));
|
assert(document.querySelector("#autopay_loading_indicator"));
|
||||||
|
|
||||||
assert(document.querySelector("input[name=csrfmiddlewaretoken]"));
|
assert(document.querySelector("input[name=csrfmiddlewaretoken]"));
|
||||||
|
|
||||||
|
assert(document.querySelector("#zulip-limited-section"));
|
||||||
});
|
});
|
||||||
|
|
||||||
run_test("invoice_form_fields", () => {
|
run_test("invoice_form_fields", () => {
|
||||||
|
@ -178,4 +181,6 @@ run_test("invoice_form_fields", () => {
|
||||||
assert(document.querySelector("#invoice_loading_indicator"));
|
assert(document.querySelector("#invoice_loading_indicator"));
|
||||||
|
|
||||||
assert(document.querySelector("input[name=csrfmiddlewaretoken]"));
|
assert(document.querySelector("input[name=csrfmiddlewaretoken]"));
|
||||||
|
|
||||||
|
assert(document.querySelector("#zulip-limited-section"));
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,11 +6,16 @@ exports.create_ajax_request = function (url, form_name, stripe_token = null, num
|
||||||
const form_error = "#" + form_name + "-error";
|
const form_error = "#" + form_name + "-error";
|
||||||
const form_loading = "#" + form_name + "-loading";
|
const form_loading = "#" + form_name + "-loading";
|
||||||
|
|
||||||
|
const zulip_limited_section = "#zulip-limited-section";
|
||||||
|
const free_trial_alert_message = "#free-trial-alert-message";
|
||||||
|
|
||||||
loading.make_indicator($(form_loading_indicator),
|
loading.make_indicator($(form_loading_indicator),
|
||||||
{text: 'Processing ...', abs_positioned: true});
|
{text: 'Processing ...', abs_positioned: true});
|
||||||
$(form_input_section).hide();
|
$(form_input_section).hide();
|
||||||
$(form_error).hide();
|
$(form_error).hide();
|
||||||
$(form_loading).show();
|
$(form_loading).show();
|
||||||
|
$(zulip_limited_section).hide();
|
||||||
|
$(free_trial_alert_message).hide();
|
||||||
|
|
||||||
const data = {};
|
const data = {};
|
||||||
if (stripe_token) {
|
if (stripe_token) {
|
||||||
|
@ -45,6 +50,8 @@ exports.create_ajax_request = function (url, form_name, stripe_token = null, num
|
||||||
$(form_loading).hide();
|
$(form_loading).hide();
|
||||||
$(form_error).show().text(JSON.parse(xhr.responseText).msg);
|
$(form_error).show().text(JSON.parse(xhr.responseText).msg);
|
||||||
$(form_input_section).show();
|
$(form_input_section).show();
|
||||||
|
$(zulip_limited_section).show();
|
||||||
|
$(free_trial_alert_message).show();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -124,6 +124,15 @@
|
||||||
<div class="tab-pane" id="loading">
|
<div class="tab-pane" id="loading">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="goto-zulip-organization-link">
|
||||||
|
{% if onboarding %}
|
||||||
|
<br>
|
||||||
|
<h3>
|
||||||
|
<b><a href="/">Go to your Zulip organization</a></b>
|
||||||
|
</h3>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
<div class="support-link">
|
<div class="support-link">
|
||||||
<p>
|
<p>
|
||||||
Contact <a href="mailto:support@zulipchat.com">support@zulipchat.com</a>
|
Contact <a href="mailto:support@zulipchat.com">support@zulipchat.com</a>
|
||||||
|
|
|
@ -207,6 +207,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if onboarding %}
|
||||||
|
<div id="zulip-limited-section">
|
||||||
|
<br>
|
||||||
|
<h3>
|
||||||
|
<b><a href="/">Or start with Zulip Limited (Free) plan</a></b>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<hr>
|
||||||
<div class="support-link">
|
<div class="support-link">
|
||||||
<p>
|
<p>
|
||||||
We're happy to help!
|
We're happy to help!
|
||||||
|
|
|
@ -2150,6 +2150,10 @@ class RealmCreationTest(ZulipTestCase):
|
||||||
HTTP_HOST=string_id + ".testserver")
|
HTTP_HOST=string_id + ".testserver")
|
||||||
self.assertEqual(result.status_code, 302)
|
self.assertEqual(result.status_code, 302)
|
||||||
|
|
||||||
|
result = self.client_get(result.url, subdomain=string_id)
|
||||||
|
self.assertEqual(result.status_code, 302)
|
||||||
|
self.assertEqual(result.url, 'http://zuliptest.testserver')
|
||||||
|
|
||||||
# Make sure the realm is created
|
# Make sure the realm is created
|
||||||
realm = get_realm(string_id)
|
realm = get_realm(string_id)
|
||||||
self.assertEqual(realm.string_id, string_id)
|
self.assertEqual(realm.string_id, string_id)
|
||||||
|
@ -2158,6 +2162,46 @@ class RealmCreationTest(ZulipTestCase):
|
||||||
self.assertEqual(realm.name, realm_name)
|
self.assertEqual(realm.name, realm_name)
|
||||||
self.assertEqual(realm.subdomain, string_id)
|
self.assertEqual(realm.subdomain, string_id)
|
||||||
|
|
||||||
|
@override_settings(OPEN_REALM_CREATION=True, FREE_TRIAL_DAYS=30)
|
||||||
|
def test_create_realm_during_free_trial(self) -> None:
|
||||||
|
password = "test"
|
||||||
|
string_id = "zuliptest"
|
||||||
|
email = "user1@test.com"
|
||||||
|
realm_name = "Test"
|
||||||
|
|
||||||
|
with self.assertRaises(Realm.DoesNotExist):
|
||||||
|
get_realm(string_id)
|
||||||
|
|
||||||
|
result = self.client_post('/new/', {'email': email})
|
||||||
|
self.assertEqual(result.status_code, 302)
|
||||||
|
self.assertTrue(result["Location"].endswith(
|
||||||
|
"/accounts/new/send_confirm/%s" % (email,)))
|
||||||
|
result = self.client_get(result["Location"])
|
||||||
|
self.assert_in_response("Check your email so we can get started.", result)
|
||||||
|
|
||||||
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
||||||
|
result = self.client_get(confirmation_url)
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
|
result = self.submit_reg_form_for_user(email, password,
|
||||||
|
realm_subdomain = string_id,
|
||||||
|
realm_name=realm_name,
|
||||||
|
HTTP_HOST=string_id + ".testserver")
|
||||||
|
self.assertEqual(result.status_code, 302)
|
||||||
|
|
||||||
|
result = self.client_get(result.url, subdomain=string_id)
|
||||||
|
self.assertEqual(result.url, 'http://zuliptest.testserver/upgrade/?onboarding=true')
|
||||||
|
|
||||||
|
result = self.client_get(result.url, subdomain=string_id)
|
||||||
|
self.assert_in_success_response(["Or start with Zulip Limited (Free) plan"], result)
|
||||||
|
|
||||||
|
realm = get_realm(string_id)
|
||||||
|
self.assertEqual(realm.string_id, string_id)
|
||||||
|
self.assertEqual(get_user(email, realm).realm, realm)
|
||||||
|
|
||||||
|
self.assertEqual(realm.name, realm_name)
|
||||||
|
self.assertEqual(realm.subdomain, string_id)
|
||||||
|
|
||||||
@override_settings(OPEN_REALM_CREATION=True)
|
@override_settings(OPEN_REALM_CREATION=True)
|
||||||
def test_mailinator_signup(self) -> None:
|
def test_mailinator_signup(self) -> None:
|
||||||
result = self.client_post('/new/', {'email': "hi@mailinator.com"})
|
result = self.client_post('/new/', {'email': "hi@mailinator.com"})
|
||||||
|
|
|
@ -218,6 +218,7 @@ def register_remote_user(request: HttpRequest, result: ExternalAuthResult) -> Ht
|
||||||
# maybe_send_to_registration doesn't take these arguments, so delete them.
|
# maybe_send_to_registration doesn't take these arguments, so delete them.
|
||||||
kwargs.pop('subdomain', None)
|
kwargs.pop('subdomain', None)
|
||||||
kwargs.pop('redirect_to', None)
|
kwargs.pop('redirect_to', None)
|
||||||
|
kwargs.pop('is_realm_creation', None)
|
||||||
|
|
||||||
kwargs["password_required"] = False
|
kwargs["password_required"] = False
|
||||||
return maybe_send_to_registration(request, **kwargs)
|
return maybe_send_to_registration(request, **kwargs)
|
||||||
|
@ -247,6 +248,7 @@ def login_or_register_remote_user(request: HttpRequest, result: ExternalAuthResu
|
||||||
# Otherwise, the user has successfully authenticated to an
|
# Otherwise, the user has successfully authenticated to an
|
||||||
# account, and we need to do the right thing depending whether
|
# account, and we need to do the right thing depending whether
|
||||||
# or not they're using the mobile OTP flow or want a browser session.
|
# or not they're using the mobile OTP flow or want a browser session.
|
||||||
|
is_realm_creation = result.data_dict.get('is_realm_creation')
|
||||||
mobile_flow_otp = result.data_dict.get('mobile_flow_otp')
|
mobile_flow_otp = result.data_dict.get('mobile_flow_otp')
|
||||||
desktop_flow_otp = result.data_dict.get('desktop_flow_otp')
|
desktop_flow_otp = result.data_dict.get('desktop_flow_otp')
|
||||||
if mobile_flow_otp is not None:
|
if mobile_flow_otp is not None:
|
||||||
|
@ -256,7 +258,11 @@ def login_or_register_remote_user(request: HttpRequest, result: ExternalAuthResu
|
||||||
|
|
||||||
do_login(request, user_profile)
|
do_login(request, user_profile)
|
||||||
|
|
||||||
redirect_to = get_safe_redirect_to(result.data_dict.get('redirect_to', ''), user_profile.realm.uri)
|
redirect_to = result.data_dict.get('redirect_to', '')
|
||||||
|
if is_realm_creation is not None and settings.FREE_TRIAL_DAYS not in [None, 0]:
|
||||||
|
redirect_to = "{}?onboarding=true".format(reverse('corporate.views.initial_upgrade'))
|
||||||
|
|
||||||
|
redirect_to = get_safe_redirect_to(redirect_to, user_profile.realm.uri)
|
||||||
return HttpResponseRedirect(redirect_to)
|
return HttpResponseRedirect(redirect_to)
|
||||||
|
|
||||||
def finish_desktop_flow(request: HttpRequest, user_profile: UserProfile,
|
def finish_desktop_flow(request: HttpRequest, user_profile: UserProfile,
|
||||||
|
|
|
@ -343,7 +343,8 @@ def accounts_register(request: HttpRequest) -> HttpResponse:
|
||||||
# Because for realm creation, registration happens on the
|
# Because for realm creation, registration happens on the
|
||||||
# root domain, we need to log them into the subdomain for
|
# root domain, we need to log them into the subdomain for
|
||||||
# their new realm.
|
# their new realm.
|
||||||
return redirect_and_log_into_subdomain(ExternalAuthResult(user_profile=user_profile))
|
return redirect_and_log_into_subdomain(ExternalAuthResult(user_profile=user_profile,
|
||||||
|
data_dict={'is_realm_creation': True}))
|
||||||
|
|
||||||
# This dummy_backend check below confirms the user is
|
# This dummy_backend check below confirms the user is
|
||||||
# authenticating to the correct subdomain.
|
# authenticating to the correct subdomain.
|
||||||
|
|
|
@ -920,6 +920,7 @@ ExternalAuthDataDict = TypedDict('ExternalAuthDataDict', {
|
||||||
'full_name': str,
|
'full_name': str,
|
||||||
'email': str,
|
'email': str,
|
||||||
'is_signup': bool,
|
'is_signup': bool,
|
||||||
|
'is_realm_creation': bool,
|
||||||
'redirect_to': str,
|
'redirect_to': str,
|
||||||
'mobile_flow_otp': Optional[str],
|
'mobile_flow_otp': Optional[str],
|
||||||
'desktop_flow_otp': Optional[str],
|
'desktop_flow_otp': Optional[str],
|
||||||
|
|
Loading…
Reference in New Issue