Pricing updates.

This commit is contained in:
Pēteris Caune 2018-03-04 20:26:30 +02:00
parent ba6995198b
commit 01fef3d054
10 changed files with 133 additions and 115 deletions

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-04 17:28
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('payments', '0004_subscription_send_invoices'),
]
operations = [
migrations.AddField(
model_name='subscription',
name='plan_name',
field=models.CharField(blank=True, max_length=50),
),
]

View File

@ -42,35 +42,12 @@ class Subscription(models.Model):
payment_method_token = models.CharField(max_length=35, blank=True) payment_method_token = models.CharField(max_length=35, blank=True)
subscription_id = models.CharField(max_length=10, blank=True) subscription_id = models.CharField(max_length=10, blank=True)
plan_id = models.CharField(max_length=10, blank=True) plan_id = models.CharField(max_length=10, blank=True)
plan_name = models.CharField(max_length=50, blank=True)
address_id = models.CharField(max_length=2, blank=True) address_id = models.CharField(max_length=2, blank=True)
send_invoices = models.BooleanField(default=True) send_invoices = models.BooleanField(default=True)
objects = SubscriptionManager() objects = SubscriptionManager()
def price(self):
if self.plan_id == "P5":
return 5
elif self.plan_id == "P50":
return 50
elif self.plan_id == "Y48":
return 48
elif self.plan_id == "Y480":
return 480
elif self.plan_id == "T144":
return 144
return 0
def period(self):
if self.plan_id.startswith("P"):
return "month"
elif self.plan_id.startswith("Y"):
return "year"
elif self.plan_id.startswith("T"):
return "3 years"
raise NotImplementedError("Unexpected plan: %s" % self.plan_id)
@property @property
def payment_method(self): def payment_method(self):
if not self.payment_method_token: if not self.payment_method_token:
@ -160,6 +137,15 @@ class Subscription(models.Model):
if result.is_success: if result.is_success:
self.subscription_id = result.subscription.id self.subscription_id = result.subscription.id
self.plan_id = plan_id self.plan_id = plan_id
if plan_id == "P20":
self.plan_name = "Standard ($20 / month)"
elif plan_id == "Y192":
self.plan_name = "Standard ($192 / year)"
elif plan_id == "P80":
self.plan_name = "Plus ($80 / month)"
elif plan_id == "Y768":
self.plan_name = "Plus ($768 / year)"
self.save() self.save()
return result return result

View File

@ -8,7 +8,7 @@ class PricingTestCase(BaseTestCase):
def test_anonymous(self): def test_anonymous(self):
r = self.client.get("/pricing/") r = self.client.get("/pricing/")
self.assertContains(r, "Unlimited Checks", status_code=200) self.assertContains(r, "Unlimited Team Members", status_code=200)
# A subscription object should have NOT been created # A subscription object should have NOT been created
assert Subscription.objects.count() == 0 assert Subscription.objects.count() == 0
@ -17,7 +17,7 @@ class PricingTestCase(BaseTestCase):
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.get("/pricing/") r = self.client.get("/pricing/")
self.assertContains(r, "Unlimited Checks", status_code=200) self.assertContains(r, "Unlimited Team Members", status_code=200)
# A subscription object still should have NOT been created # A subscription object still should have NOT been created
assert Subscription.objects.count() == 0 assert Subscription.objects.count() == 0
@ -39,10 +39,11 @@ class PricingTestCase(BaseTestCase):
def test_it_shows_active_plan(self): def test_it_shows_active_plan(self):
self.sub = Subscription(user=self.alice) self.sub = Subscription(user=self.alice)
self.sub.subscription_id = "test-id" self.sub.subscription_id = "test-id"
self.sub.plan_id = "P5" self.sub.plan_id = "P20"
self.sub.plan_name = "Standard ($20 / month)"
self.sub.save() self.sub.save()
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.get("/pricing/") r = self.client.get("/pricing/")
self.assertContains(r, "Standard (monthly)", status_code=200) self.assertContains(r, "Standard ($20 / month)", status_code=200)

View File

@ -12,7 +12,7 @@ class SetPlanTestCase(BaseTestCase):
mock.Subscription.create.return_value.is_success = True mock.Subscription.create.return_value.is_success = True
mock.Subscription.create.return_value.subscription.id = "t-sub-id" mock.Subscription.create.return_value.subscription.id = "t-sub-id"
def run_set_plan(self, plan_id="P5"): def run_set_plan(self, plan_id="P20"):
form = {"plan_id": plan_id} form = {"plan_id": plan_id}
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
return self.client.post("/pricing/set_plan/", form, follow=True) return self.client.post("/pricing/set_plan/", form, follow=True)
@ -31,12 +31,13 @@ class SetPlanTestCase(BaseTestCase):
# Subscription should be filled out: # Subscription should be filled out:
sub = Subscription.objects.get(user=self.alice) sub = Subscription.objects.get(user=self.alice)
self.assertEqual(sub.subscription_id, "t-sub-id") self.assertEqual(sub.subscription_id, "t-sub-id")
self.assertEqual(sub.plan_id, "P5") self.assertEqual(sub.plan_id, "P20")
self.assertEqual(sub.plan_name, "Standard ($20 / month)")
# User's profile should have a higher limits # User's profile should have a higher limits
self.profile.refresh_from_db() self.profile.refresh_from_db()
self.assertEqual(self.profile.ping_log_limit, 1000) self.assertEqual(self.profile.ping_log_limit, 1000)
self.assertEqual(self.profile.check_limit, 500) self.assertEqual(self.profile.check_limit, 50)
self.assertEqual(self.profile.team_limit, 9) self.assertEqual(self.profile.team_limit, 9)
self.assertEqual(self.profile.sms_limit, 50) self.assertEqual(self.profile.sms_limit, 50)
self.assertEqual(self.profile.sms_sent, 0) self.assertEqual(self.profile.sms_sent, 0)
@ -52,20 +53,49 @@ class SetPlanTestCase(BaseTestCase):
self.profile.sms_sent = 1 self.profile.sms_sent = 1
self.profile.save() self.profile.save()
r = self.run_set_plan("Y48") r = self.run_set_plan("Y192")
self.assertRedirects(r, "/accounts/profile/billing/") self.assertRedirects(r, "/accounts/profile/billing/")
# Subscription should be filled out: # Subscription should be filled out:
sub = Subscription.objects.get(user=self.alice) sub = Subscription.objects.get(user=self.alice)
self.assertEqual(sub.subscription_id, "t-sub-id") self.assertEqual(sub.subscription_id, "t-sub-id")
self.assertEqual(sub.plan_id, "Y48") self.assertEqual(sub.plan_id, "Y192")
self.assertEqual(sub.plan_name, "Standard ($192 / year)")
# User's profile should have a higher limits
self.profile.refresh_from_db()
self.assertEqual(self.profile.ping_log_limit, 1000)
self.assertEqual(self.profile.check_limit, 50)
self.assertEqual(self.profile.team_limit, 9)
self.assertEqual(self.profile.sms_limit, 50)
self.assertEqual(self.profile.sms_sent, 0)
# braintree.Subscription.cancel should have not been called
assert not mock.Subscription.cancel.called
@patch("hc.payments.models.braintree")
def test_plus_works(self, mock):
self._setup_mock(mock)
self.profile.sms_limit = 0
self.profile.sms_sent = 1
self.profile.save()
r = self.run_set_plan("P80")
self.assertRedirects(r, "/accounts/profile/billing/")
# Subscription should be filled out:
sub = Subscription.objects.get(user=self.alice)
self.assertEqual(sub.subscription_id, "t-sub-id")
self.assertEqual(sub.plan_id, "P80")
self.assertEqual(sub.plan_name, "Plus ($80 / month)")
# User's profile should have a higher limits # User's profile should have a higher limits
self.profile.refresh_from_db() self.profile.refresh_from_db()
self.assertEqual(self.profile.ping_log_limit, 1000) self.assertEqual(self.profile.ping_log_limit, 1000)
self.assertEqual(self.profile.check_limit, 500) self.assertEqual(self.profile.check_limit, 500)
self.assertEqual(self.profile.team_limit, 9) self.assertEqual(self.profile.team_limit, 500)
self.assertEqual(self.profile.sms_limit, 50) self.assertEqual(self.profile.sms_limit, 500)
self.assertEqual(self.profile.sms_sent, 0) self.assertEqual(self.profile.sms_sent, 0)
# braintree.Subscription.cancel should have not been called # braintree.Subscription.cancel should have not been called
@ -77,7 +107,7 @@ class SetPlanTestCase(BaseTestCase):
self.sub = Subscription(user=self.alice) self.sub = Subscription(user=self.alice)
self.sub.subscription_id = "test-id" self.sub.subscription_id = "test-id"
self.sub.plan_id = "P5" self.sub.plan_id = "P20"
self.sub.save() self.sub.save()
self.profile.sms_limit = 1 self.profile.sms_limit = 1

View File

@ -93,7 +93,7 @@ def log_and_bail(request, result):
@require_POST @require_POST
def set_plan(request): def set_plan(request):
plan_id = request.POST["plan_id"] plan_id = request.POST["plan_id"]
if plan_id not in ("", "P5", "P50", "Y48", "Y480", "T144"): if plan_id not in ("", "P20", "P80", "Y192", "Y768"):
return HttpResponseBadRequest() return HttpResponseBadRequest()
sub = Subscription.objects.for_user(request.user) sub = Subscription.objects.for_user(request.user)
@ -117,14 +117,14 @@ def set_plan(request):
# Update user's profile # Update user's profile
profile = request.user.profile profile = request.user.profile
if plan_id in ("P5", "Y48", "T144"): if plan_id in ("P20", "Y192"):
profile.ping_log_limit = 1000 profile.ping_log_limit = 1000
profile.check_limit = 500 profile.check_limit = 50
profile.team_limit = 9 profile.team_limit = 9
profile.sms_limit = 50 profile.sms_limit = 50
profile.sms_sent = 0 profile.sms_sent = 0
profile.save() profile.save()
elif plan_id in ("P50", "Y480"): elif plan_id in ("P80", "Y768"):
profile.ping_log_limit = 1000 profile.ping_log_limit = 1000
profile.check_limit = 500 profile.check_limit = 500
profile.team_limit = 500 profile.team_limit = 500

View File

@ -37,3 +37,12 @@ span.loading {
.billing-empty { .billing-empty {
color: #888; color: #888;
} }
.at-limit span {
display: inline-block;
background-color: #FFD54F;
font-weight: bold;
font-size: 12px;
border-radius: 2px;
padding: 2px 6px;
}

View File

@ -1,13 +1,13 @@
$(function () { $(function () {
$("#period-controls :input").change(function() { $("#period-controls :input").change(function() {
if (this.value == "monthly") { if (this.value == "monthly") {
$("#s-price").text("$5"); $("#s-price").text("$20");
$("#p-price").text("$50"); $("#p-price").text("$80");
} }
if (this.value == "annual") { if (this.value == "annual") {
$("#s-price").text("$4"); $("#s-price").text("$16");
$("#p-price").text("$40"); $("#p-price").text("$64");
} }
}); });
}); });

View File

@ -46,13 +46,7 @@
{% if sub is None or sub.plan_id == "" %} {% if sub is None or sub.plan_id == "" %}
Free Free
{% else %} {% else %}
{% if sub.plan_id == "P5" or sub.plan_id == "Y48" or sub.plan_id == "T144" %} {{ sub.plan_name }}
Standard
{% elif sub.plan_id == "P50" or sub.plan_id == "Y480" %}
Plus
{% endif %}
(${{ sub.price }} per {{ sub.period }})
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
@ -66,24 +60,21 @@
{% endif %} {% endif %}
<tr> <tr>
<td>Checks Used</td> <td>Checks Used</td>
<td> <td {% if num_checks >= profile.check_limit %} class="at-limit" {% endif %}>
{{ num_checks }} of <span>{{ num_checks }} of {{ profile.check_limit }}</span>
{% if sub.plan_id %}
unlimited
{% else %}
{{ profile.check_limit }}
{% endif %}
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Team Size</td> <td>Team Size</td>
<td> <td {% if team_size >= profile.team_limit %} class="at-limit" {% endif %}>
<span>
{{ team_size }} of {{ team_size }} of
{% if profile.team_limit == 500 %} {% if profile.team_limit == 500 %}
unlimited unlimited
{% else %} {% else %}
{{ team_max }} {{ team_max }}
{% endif %} {% endif %}
</span>
</td> </td>
</tr> </tr>
</table> </table>
@ -219,57 +210,46 @@
Enjoy free service. Enjoy free service.
</label> </label>
<h2>Standard <small>Unlimited checks, 20 team members</small></h2> <h2>Standard <small>50 checks, 10 team members</small></h2>
<label class="radio-container"> <label class="radio-container">
<input <input
type="radio" type="radio"
name="plan_id" name="plan_id"
value="P5" value="P20"
{% if sub.plan_id == "P5" %} checked {% endif %}> {% if sub.plan_id == "P20" %} checked {% endif %}>
<span class="radiomark"></span> <span class="radiomark"></span>
Monthly, $5/month Monthly, $20 / month
</label> </label>
<label class="radio-container"> <label class="radio-container">
<input <input
type="radio" type="radio"
name="plan_id" name="plan_id"
value="Y48" value="Y192"
{% if sub.plan_id == "Y48" %} checked {% endif %}> {% if sub.plan_id == "Y192" %} checked {% endif %}>
<span class="radiomark"></span> <span class="radiomark"></span>
Annual, $48/year (20% off monthly) Annual, $192 / year (20% off monthly)
</label>
<h2>Plus <small>500 checks, unlimited team members</small></h2>
<label class="radio-container">
<input
type="radio"
name="plan_id"
value="P80"
{% if sub.plan_id == "P80" %} checked {% endif %}>
<span class="radiomark"></span>
Monthly, $80/month
</label> </label>
<label class="radio-container"> <label class="radio-container">
<input <input
type="radio" type="radio"
name="plan_id" name="plan_id"
value="T144" value="Y768"
{% if sub.plan_id == "T144" %} checked {% endif %}> {% if sub.plan_id == "Y768" %} checked {% endif %}>
<span class="radiomark"></span> <span class="radiomark"></span>
3-year, $144 per 3 years (20% off monthly) Annual, $768/year (20% off monthly)
</label>
<h2>Plus <small>Unlimited checks, unlimited team members</small></h2>
<label class="radio-container">
<input
type="radio"
name="plan_id"
value="P50"
{% if sub.plan_id == "P50" %} checked {% endif %}>
<span class="radiomark"></span>
Monthly, $50/month
</label>
<label class="radio-container">
<input
type="radio"
name="plan_id"
value="Y480"
{% if sub.plan_id == "Y480" %} checked {% endif %}>
<span class="radiomark"></span>
Annual, $480/year (20% off monthly)
</label> </label>
<div class="alert alert-warning"> <div class="alert alert-warning">

View File

@ -45,7 +45,7 @@
<div class="alert alert-info"> <div class="alert alert-info">
<strong>Check limit reached.</strong> <strong>Check limit reached.</strong>
To add more checks, please To add more checks, please
<a href="{% url 'hc-pricing' %}">upgrade your account!</a> <a href="{% url 'hc-billing' %}">upgrade your account!</a>
</div> </div>
{% endif %} {% endif %}
</div> </div>

View File

@ -13,29 +13,21 @@
<div class="col-md-12"> <div class="col-md-12">
<div id="subscription-status" class="jumbotron"> <div id="subscription-status" class="jumbotron">
<p> <p>
Your account is currently on the
{% if sub.plan_id == "P5" %}
<strong>Standard (monthly)</strong>
{% elif sub.plan_id == "Y48" %}
<strong>Standard (annual)</strong>
{% elif sub.plan_id == "T144" %}
<strong>Standard (3 years)</strong>
{% elif sub.plan_id == "Y480" %}
<strong>Plus (annual)</strong>
{% elif sub.plan_id == "P50" %}
<strong>Plus (monthly)</strong>
{% else %}
<strong>Free</strong>
{% endif %}
plan.
{% if sub.plan_id %} {% if sub.plan_id %}
You are paying Your account is currently on the
<strong>${{ sub.price }}</strong> / {{ sub.period }}. <strong>{{ sub.plan_name }}</strong>
{% endif %} plan. Thank you for supporting {% site_name %}!
{% else %}
Your account is currently on the
<strong>Free</strong> plan.
{% endif %}
</p> </p>
<p> <p>
{% if sub.plan_id %}
<a class="btn btn-default" href="{% url 'hc-billing' %}">Billing Details</a> <a class="btn btn-default" href="{% url 'hc-billing' %}">Billing Details</a>
{% else %}
<a class="btn btn-default" href="{% url 'hc-billing' %}">Billing Details and Plan Upgrades</a>
{% endif %}
</p> </p>
</div> </div>
</div> </div>
@ -100,12 +92,12 @@
<div class="panel-body text-center"> <div class="panel-body text-center">
<h1>Standard</h1> <h1>Standard</h1>
<h2> <h2>
<span id="s-price">$5</span><span class="mo">/mo</span> <span id="s-price">$20</span><span class="mo">/mo</span>
</h2> </h2>
</div> </div>
<ul class="list-group text-center"> <ul class="list-group text-center">
<li class="list-group-item">Unlimited Checks</li> <li class="list-group-item">50 Checks</li>
<li class="list-group-item">10 Team Members</li> <li class="list-group-item">10 Team Members</li>
<li class="list-group-item">1000 log entries per check</li> <li class="list-group-item">1000 log entries per check</li>
<li class="list-group-item">50 SMS alerts per month</li> <li class="list-group-item">50 SMS alerts per month</li>
@ -128,12 +120,12 @@
<div class="panel-body text-center"> <div class="panel-body text-center">
<h1>Plus</h1> <h1>Plus</h1>
<h2> <h2>
<span id="p-price">$50</span><span class="mo">/mo</span> <span id="p-price">$80</span><span class="mo">/mo</span>
</h2> </h2>
</div> </div>
<ul class="list-group text-center"> <ul class="list-group text-center">
<li class="list-group-item">Unlimited Checks</li> <li class="list-group-item">500 Checks</li>
<li class="list-group-item">Unlimited Team Members</li> <li class="list-group-item">Unlimited Team Members</li>
<li class="list-group-item">1000 log entries per check</li> <li class="list-group-item">1000 log entries per check</li>
<li class="list-group-item">500 SMS alerts per month</li> <li class="list-group-item">500 SMS alerts per month</li>