mirror of
https://github.com/plausible/analytics.git
synced 2024-11-22 02:27:57 +03:00
List plan benefits on the new upgrade page (#3444)
* change team member limits for new v4 plans * duplicate business plans with unlimited team members We need to do this because we want grandfathered users to have unlimited team members on business plans as well. Otherwise we'd have to build overrides on the subscription level when checking the limit. * refactor generating plan structs * move Plan module into a separate file * remove not needed conditions * add generation field to plans * sync the sanbox plan limits and features with plan generations * implement displaying plan benefits * add grandfathering notice * plug in the real v3 business plan IDs * optimize N/A text color for darkmode * use String.to_existing_atom instead Co-authored-by: Vini Brasil <vini@hey.com> * Remove the unnecessary part of a comment Co-authored-by: Vini Brasil <vini@hey.com> * make the Plan.new function simpler * use exlamation marks --------- Co-authored-by: Vini Brasil <vini@hey.com>
This commit is contained in:
parent
957138a8ec
commit
2ada3d700f
95
lib/plausible/billing/plan.ex
Normal file
95
lib/plausible/billing/plan.ex
Normal file
@ -0,0 +1,95 @@
|
||||
defmodule Plausible.Billing.Plan do
|
||||
@moduledoc false
|
||||
|
||||
@derive Jason.Encoder
|
||||
@enforce_keys ~w(kind generation site_limit monthly_pageview_limit team_member_limit features volume monthly_product_id yearly_product_id)a
|
||||
defstruct @enforce_keys ++ [:monthly_cost, :yearly_cost]
|
||||
|
||||
@type t() ::
|
||||
%__MODULE__{
|
||||
kind: atom(),
|
||||
generation: non_neg_integer(),
|
||||
monthly_pageview_limit: non_neg_integer(),
|
||||
site_limit: non_neg_integer(),
|
||||
team_member_limit: non_neg_integer() | :unlimited,
|
||||
volume: String.t(),
|
||||
monthly_cost: Money.t() | nil,
|
||||
yearly_cost: Money.t() | nil,
|
||||
monthly_product_id: String.t() | nil,
|
||||
yearly_product_id: String.t() | nil,
|
||||
features: [atom()]
|
||||
}
|
||||
| :enterprise
|
||||
|
||||
def build!(raw_params, file_name) when is_map(raw_params) do
|
||||
raw_params
|
||||
|> put_kind()
|
||||
|> put_generation()
|
||||
|> put_volume()
|
||||
|> put_team_member_limit!(file_name)
|
||||
|> put_features!(file_name)
|
||||
|> new!()
|
||||
end
|
||||
|
||||
defp new!(params) do
|
||||
struct!(__MODULE__, params)
|
||||
end
|
||||
|
||||
defp put_kind(params) do
|
||||
Map.put(params, :kind, String.to_existing_atom(params.kind))
|
||||
end
|
||||
|
||||
# Due to grandfathering, we sometimes need to check the "generation"
|
||||
# (e.g. v1, v2, etc...) of a user's subscription plan. For instance,
|
||||
# on prod, the users subscribed to a v2 plan are only supposed to
|
||||
# see v2 plans when they go to the upgrade page.
|
||||
#
|
||||
# In the `dev` environment though, "sandbox" plans are used, which
|
||||
# unlike production plans, contain multiple generations of plans in
|
||||
# the same file for testing purposes.
|
||||
defp put_generation(params) do
|
||||
Map.put(params, :generation, params.generation)
|
||||
end
|
||||
|
||||
defp put_volume(params) do
|
||||
volume =
|
||||
params.monthly_pageview_limit
|
||||
|> PlausibleWeb.StatsView.large_number_format()
|
||||
|
||||
Map.put(params, :volume, volume)
|
||||
end
|
||||
|
||||
defp put_team_member_limit!(params, file_name) do
|
||||
team_member_limit =
|
||||
case params.team_member_limit do
|
||||
number when is_integer(number) ->
|
||||
number
|
||||
|
||||
"unlimited" ->
|
||||
:unlimited
|
||||
|
||||
other ->
|
||||
raise ArgumentError,
|
||||
"Failed to parse team member limit #{inspect(other)} from #{file_name}.json"
|
||||
end
|
||||
|
||||
Map.put(params, :team_member_limit, team_member_limit)
|
||||
end
|
||||
|
||||
defp put_features!(params, file_name) do
|
||||
features =
|
||||
Plausible.Billing.Feature.list()
|
||||
|> Enum.filter(fn module ->
|
||||
to_string(module.name()) in params.features
|
||||
end)
|
||||
|
||||
if length(features) == length(params.features) do
|
||||
Map.put(params, :features, features)
|
||||
else
|
||||
raise(
|
||||
ArgumentError,
|
||||
"Unrecognized feature(s) in #{inspect(params.features)} (#{file_name}.json)"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,30 +1,3 @@
|
||||
defmodule Plausible.Billing.Plan do
|
||||
@moduledoc false
|
||||
|
||||
@derive Jason.Encoder
|
||||
@enforce_keys ~w(kind site_limit monthly_pageview_limit team_member_limit features volume monthly_product_id yearly_product_id)a
|
||||
defstruct @enforce_keys ++ [:monthly_cost, :yearly_cost]
|
||||
|
||||
@type t() ::
|
||||
%__MODULE__{
|
||||
kind: atom(),
|
||||
monthly_pageview_limit: non_neg_integer(),
|
||||
site_limit: non_neg_integer(),
|
||||
team_member_limit: non_neg_integer() | :unlimited,
|
||||
volume: String.t(),
|
||||
monthly_cost: Money.t() | nil,
|
||||
yearly_cost: Money.t() | nil,
|
||||
monthly_product_id: String.t() | nil,
|
||||
yearly_product_id: String.t() | nil,
|
||||
features: [atom()]
|
||||
}
|
||||
| :enterprise
|
||||
|
||||
def new(params) when is_map(params) do
|
||||
struct!(__MODULE__, params)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Plausible.Billing.Plans do
|
||||
alias Plausible.Billing.Subscriptions
|
||||
use Plausible.Repo
|
||||
@ -46,36 +19,7 @@ defmodule Plausible.Billing.Plans do
|
||||
path
|
||||
|> File.read!()
|
||||
|> Jason.decode!(keys: :atoms!)
|
||||
|> Enum.map(fn raw ->
|
||||
team_member_limit =
|
||||
case raw.team_member_limit do
|
||||
number when is_integer(number) -> number
|
||||
"unlimited" -> :unlimited
|
||||
_any -> raise ArgumentError, "Failed to parse team member limit from plan JSON files"
|
||||
end
|
||||
|
||||
features =
|
||||
Plausible.Billing.Feature.list()
|
||||
|> Enum.filter(fn module ->
|
||||
to_string(module.name()) in raw.features
|
||||
end)
|
||||
|
||||
if length(features) != length(raw.features),
|
||||
do:
|
||||
raise(
|
||||
ArgumentError,
|
||||
"Unrecognized feature(s) in #{inspect(raw.features)} (#{f}.json)"
|
||||
)
|
||||
|
||||
volume = PlausibleWeb.StatsView.large_number_format(raw.monthly_pageview_limit)
|
||||
|
||||
raw
|
||||
|> Map.put(:volume, volume)
|
||||
|> Map.put(:kind, String.to_atom(raw.kind))
|
||||
|> Map.put(:team_member_limit, team_member_limit)
|
||||
|> Map.put(:features, features)
|
||||
|> Plan.new()
|
||||
end)
|
||||
|> Enum.map(&Plan.build!(&1, f))
|
||||
|
||||
Module.put_attribute(__MODULE__, f, plans_list)
|
||||
|
||||
@ -94,26 +38,34 @@ defmodule Plausible.Billing.Plans do
|
||||
def growth_plans_for(%User{} = user) do
|
||||
user = Plausible.Users.with_subscription(user)
|
||||
v4_available = FunWithFlags.enabled?(:business_tier, for: user)
|
||||
owned_plan_id = user.subscription && user.subscription.paddle_plan_id
|
||||
owned_plan = get_regular_plan(user.subscription)
|
||||
|
||||
cond do
|
||||
find(owned_plan_id, @plans_v1) -> @plans_v1
|
||||
find(owned_plan_id, @plans_v2) -> @plans_v2
|
||||
find(owned_plan_id, @plans_v3) -> @plans_v3
|
||||
find(owned_plan_id, plans_sandbox()) -> plans_sandbox()
|
||||
Application.get_env(:plausible, :environment) == "dev" -> plans_sandbox()
|
||||
Timex.before?(user.inserted_at, ~D[2022-01-01]) -> @plans_v2
|
||||
v4_available -> Enum.filter(@plans_v4, &(&1.kind == :growth))
|
||||
true -> @plans_v3
|
||||
!owned_plan -> if v4_available, do: @plans_v4, else: @plans_v3
|
||||
owned_plan.kind == :business -> @plans_v4
|
||||
owned_plan.generation == 1 -> @plans_v1
|
||||
owned_plan.generation == 2 -> @plans_v2
|
||||
owned_plan.generation == 3 -> @plans_v3
|
||||
owned_plan.generation == 4 -> @plans_v4
|
||||
end
|
||||
|> Enum.filter(&(&1.kind == :growth))
|
||||
end
|
||||
|
||||
def business_plans() do
|
||||
Enum.filter(@plans_v4, &(&1.kind == :business))
|
||||
def business_plans_for(%User{} = user) do
|
||||
user = Plausible.Users.with_subscription(user)
|
||||
owned_plan = get_regular_plan(user.subscription)
|
||||
|
||||
cond do
|
||||
Application.get_env(:plausible, :environment) == "dev" -> plans_sandbox()
|
||||
owned_plan && owned_plan.generation < 4 -> @plans_v3
|
||||
true -> @plans_v4
|
||||
end
|
||||
|> Enum.filter(&(&1.kind == :business))
|
||||
end
|
||||
|
||||
def available_plans_with_prices(%User{} = user) do
|
||||
(growth_plans_for(user) ++ business_plans())
|
||||
(growth_plans_for(user) ++ business_plans_for(user))
|
||||
|> with_prices()
|
||||
|> Enum.group_by(& &1.kind)
|
||||
end
|
||||
@ -261,7 +213,7 @@ defmodule Plausible.Billing.Plans do
|
||||
|
||||
available_plans =
|
||||
if business_tier?(user.subscription),
|
||||
do: business_plans(),
|
||||
do: business_plans_for(user),
|
||||
else: growth_plans_for(user)
|
||||
|
||||
Enum.find(available_plans, &(usage_during_cycle < &1.monthly_pageview_limit))
|
||||
|
@ -64,6 +64,24 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
end
|
||||
|
||||
def render(assigns) do
|
||||
growth_plan_to_render =
|
||||
assigns.selected_growth_plan || List.last(assigns.available_plans.growth)
|
||||
|
||||
business_plan_to_render =
|
||||
assigns.selected_business_plan || List.last(assigns.available_plans.business)
|
||||
|
||||
growth_benefits = growth_benefits(growth_plan_to_render)
|
||||
|
||||
business_benefits = business_benefits(business_plan_to_render, growth_benefits)
|
||||
|
||||
assigns =
|
||||
assigns
|
||||
|> assign(:growth_plan_to_render, growth_plan_to_render)
|
||||
|> assign(:business_plan_to_render, business_plan_to_render)
|
||||
|> assign(:growth_benefits, growth_benefits)
|
||||
|> assign(:business_benefits, business_benefits)
|
||||
|> assign(:enterprise_benefits, enterprise_benefits(business_benefits))
|
||||
|
||||
~H"""
|
||||
<div class="bg-gray-100 dark:bg-gray-900 pt-1 pb-12 sm:pb-16 text-gray-900 dark:text-gray-100">
|
||||
<div class="mx-auto max-w-7xl px-6 lg:px-20">
|
||||
@ -84,26 +102,20 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
<.plan_box
|
||||
kind={:growth}
|
||||
owned={@owned_plan && Map.get(@owned_plan, :kind) == :growth}
|
||||
plan_to_render={
|
||||
if @selected_growth_plan,
|
||||
do: @selected_growth_plan,
|
||||
else: List.last(@available_plans.growth)
|
||||
}
|
||||
plan_to_render={@growth_plan_to_render}
|
||||
benefits={@growth_benefits}
|
||||
available={!!@selected_growth_plan}
|
||||
{assigns}
|
||||
/>
|
||||
<.plan_box
|
||||
kind={:business}
|
||||
owned={@owned_plan && Map.get(@owned_plan, :kind) == :business}
|
||||
plan_to_render={
|
||||
if @selected_business_plan,
|
||||
do: @selected_business_plan,
|
||||
else: List.last(@available_plans.business)
|
||||
}
|
||||
plan_to_render={@business_plan_to_render}
|
||||
benefits={@business_benefits}
|
||||
available={!!@selected_business_plan}
|
||||
{assigns}
|
||||
/>
|
||||
<.enterprise_plan_box />
|
||||
<.enterprise_plan_box benefits={@enterprise_benefits} />
|
||||
</div>
|
||||
<p class="mx-auto mt-8 max-w-2xl text-center text-lg leading-8 text-gray-600 dark:text-gray-400">
|
||||
<.usage usage={@usage} />
|
||||
@ -275,30 +287,31 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
</.paddle_button>
|
||||
<% end %>
|
||||
</div>
|
||||
<ul
|
||||
role="list"
|
||||
class="mt-8 space-y-3 text-sm leading-6 text-gray-600 dark:text-gray-100 xl:mt-10"
|
||||
>
|
||||
<li class="flex gap-x-3">
|
||||
<.check_icon class="text-indigo-600 dark:text-green-600" /> 5 products
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<.check_icon class="text-indigo-600 dark:text-green-600" /> Up to 1,000 subscribers
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<.check_icon class="text-indigo-600 dark:text-green-600" /> Basic analytics
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<.check_icon class="text-indigo-600 dark:text-green-600" /> 48-hour support response time
|
||||
</li>
|
||||
</ul>
|
||||
<%= if @kind == :growth && @plan_to_render.generation < 4 do %>
|
||||
<.growth_grandfathering_notice />
|
||||
<% else %>
|
||||
<ul
|
||||
role="list"
|
||||
class="mt-8 space-y-3 text-sm leading-6 text-gray-600 dark:text-gray-100 xl:mt-10"
|
||||
>
|
||||
<.plan_benefit :for={benefit <- @benefits}><%= benefit %></.plan_benefit>
|
||||
</ul>
|
||||
<% end %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp growth_grandfathering_notice(assigns) do
|
||||
~H"""
|
||||
<ul class="mt-8 space-y-3 text-sm leading-6 text-gray-600 text-justify dark:text-gray-100 xl:mt-10">
|
||||
Your subscription has been grandfathered in at the same rate and terms as when you first joined. If you don't need the "Business" features, you're welcome to stay on this plan. You can adjust the pageview limit or change the billing frequency of this grandfathered plan. If you're interested in business features, you can upgrade to the new "Business" plan.
|
||||
</ul>
|
||||
"""
|
||||
end
|
||||
|
||||
def render_price_info(%{available: false} = assigns) do
|
||||
~H"""
|
||||
<p class="mt-6 flex items-baseline gap-x-1">
|
||||
<p id={"#{@kind}-custom-price"} class="mt-6 flex items-baseline gap-x-1">
|
||||
<span class="text-4xl font-bold tracking-tight text-gray-900 dark:text-white">
|
||||
Custom
|
||||
</span>
|
||||
@ -356,6 +369,18 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
"""
|
||||
end
|
||||
|
||||
slot :inner_block, required: true
|
||||
attr :icon_color, :string, default: "indigo-600"
|
||||
|
||||
defp plan_benefit(assigns) do
|
||||
~H"""
|
||||
<li class="flex gap-x-3">
|
||||
<.check_icon class={"text-#{@icon_color} dark:text-green-600"} />
|
||||
<%= render_slot(@inner_block) %>
|
||||
</li>
|
||||
"""
|
||||
end
|
||||
|
||||
defp contact_button(assigns) do
|
||||
~H"""
|
||||
<.link
|
||||
@ -372,7 +397,10 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
|
||||
defp enterprise_plan_box(assigns) do
|
||||
~H"""
|
||||
<div class="rounded-3xl px-6 sm:px-8 py-4 sm:py-6 bg-gray-900 shadow-xl dark:bg-gray-800 dark:ring-gray-600">
|
||||
<div
|
||||
id="enterprise-plan-box"
|
||||
class="rounded-3xl px-6 sm:px-8 py-4 sm:py-6 bg-gray-900 shadow-xl dark:bg-gray-800 dark:ring-gray-600"
|
||||
>
|
||||
<h3 class="text-lg font-semibold leading-8 text-white dark:text-gray-100">Enterprise</h3>
|
||||
<p class="mt-6 flex items-baseline gap-x-1">
|
||||
<span class="text-4xl font-bold tracking-tight text-white dark:text-gray-100">
|
||||
@ -385,25 +413,9 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
role="list"
|
||||
class="mt-8 space-y-3 text-sm leading-6 xl:mt-10 text-gray-300 dark:text-gray-100"
|
||||
>
|
||||
<li class="flex gap-x-3">
|
||||
<.check_icon class="text-white dark:text-green-600" /> Unlimited products
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<.check_icon class="text-white dark:text-green-600" /> Unlimited subscribers
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<.check_icon class="text-white dark:text-green-600" /> Advanced analytics
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<.check_icon class="text-white dark:text-green-600" />
|
||||
1-hour, dedicated support response time
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<.check_icon class="text-white dark:text-green-600" /> Marketing automations
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<.check_icon class="text-white dark:text-green-600" /> Custom reporting tools
|
||||
</li>
|
||||
<.plan_benefit :for={benefit <- @benefits}>
|
||||
<%= if is_binary(benefit), do: benefit, else: benefit.(assigns) %>
|
||||
</.plan_benefit>
|
||||
</ul>
|
||||
</div>
|
||||
"""
|
||||
@ -471,7 +483,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
|
||||
defp price_tag(%{plan_to_render: %Plan{monthly_cost: nil}} = assigns) do
|
||||
~H"""
|
||||
<span class="text-4xl font-bold tracking-tight text-gray-900">
|
||||
<span class="text-4xl font-bold tracking-tight text-gray-900 dark:text-gray-100">
|
||||
N/A
|
||||
</span>
|
||||
"""
|
||||
@ -620,6 +632,77 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
PlausibleWeb.StatsView.large_number_format(volume)
|
||||
end
|
||||
|
||||
defp growth_benefits(plan) do
|
||||
[
|
||||
team_member_limit_benefit(plan),
|
||||
site_limit_benefit(plan),
|
||||
"Intuitive, fast and privacy-friendly dashboard",
|
||||
"Email/Slack reports",
|
||||
"Google Analytics import"
|
||||
]
|
||||
|> Kernel.++(feature_benefits(plan))
|
||||
end
|
||||
|
||||
defp business_benefits(plan, growth_benefits) do
|
||||
[
|
||||
"Everything in Growth",
|
||||
team_member_limit_benefit(plan),
|
||||
site_limit_benefit(plan)
|
||||
]
|
||||
|> Kernel.++(feature_benefits(plan))
|
||||
|> Kernel.--(growth_benefits)
|
||||
|> Kernel.++(["Priority support"])
|
||||
end
|
||||
|
||||
defp enterprise_benefits(business_benefits) do
|
||||
team_members =
|
||||
if "Up to 10 team members" in business_benefits,
|
||||
do: "10+ team members",
|
||||
else: nil
|
||||
|
||||
[
|
||||
"Everything in Business",
|
||||
team_members,
|
||||
"50+ sites",
|
||||
&sites_api_benefit/1,
|
||||
"Technical onboarding"
|
||||
]
|
||||
|> Enum.filter(& &1)
|
||||
end
|
||||
|
||||
defp team_member_limit_benefit(%Plan{} = plan) do
|
||||
case plan.team_member_limit do
|
||||
:unlimited -> "Unlimited team members"
|
||||
number -> "Up to #{number} team members"
|
||||
end
|
||||
end
|
||||
|
||||
defp site_limit_benefit(%Plan{} = plan), do: "Up to #{plan.site_limit} sites"
|
||||
|
||||
defp feature_benefits(%Plan{} = plan) do
|
||||
Enum.map(plan.features, fn feature_mod ->
|
||||
case feature_mod.name() do
|
||||
:goals -> "Goals and custom events"
|
||||
:revenue_goals -> "Ecommerce revenue attribution"
|
||||
_ -> feature_mod.display_name()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp sites_api_benefit(assigns) do
|
||||
~H"""
|
||||
<p>
|
||||
Sites API access for
|
||||
<.link
|
||||
class="text-indigo-500 hover:text-indigo-400"
|
||||
href="https://plausible.io/white-label-web-analytics"
|
||||
>
|
||||
reselling
|
||||
</.link>
|
||||
</p>
|
||||
"""
|
||||
end
|
||||
|
||||
defp contact_link(), do: @contact_link
|
||||
|
||||
defp billing_faq_link(), do: @billing_faq_link
|
||||
|
@ -1,6 +1,7 @@
|
||||
[
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":1,
|
||||
"monthly_pageview_limit":10000,
|
||||
"monthly_product_id":"558018",
|
||||
"yearly_product_id":"572810",
|
||||
@ -10,6 +11,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":1,
|
||||
"monthly_pageview_limit":100000,
|
||||
"monthly_product_id":"558745",
|
||||
"yearly_product_id":"590752",
|
||||
@ -19,6 +21,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":1,
|
||||
"monthly_pageview_limit":200000,
|
||||
"monthly_product_id":"597485",
|
||||
"yearly_product_id":"597486",
|
||||
@ -28,6 +31,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":1,
|
||||
"monthly_pageview_limit":500000,
|
||||
"monthly_product_id":"597487",
|
||||
"yearly_product_id":"597488",
|
||||
@ -37,6 +41,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":1,
|
||||
"monthly_pageview_limit":1000000,
|
||||
"monthly_product_id":"597642",
|
||||
"yearly_product_id":"597643",
|
||||
@ -46,6 +51,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":1,
|
||||
"monthly_pageview_limit":2000000,
|
||||
"monthly_product_id":"597309",
|
||||
"yearly_product_id":"597310",
|
||||
@ -55,6 +61,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":1,
|
||||
"monthly_pageview_limit":5000000,
|
||||
"monthly_product_id":"597311",
|
||||
"yearly_product_id":"597312",
|
||||
@ -64,6 +71,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":1,
|
||||
"monthly_pageview_limit":10000000,
|
||||
"monthly_product_id":"642352",
|
||||
"yearly_product_id":"642354",
|
||||
@ -73,6 +81,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":1,
|
||||
"monthly_pageview_limit":20000000,
|
||||
"monthly_product_id":"642355",
|
||||
"yearly_product_id":"642356",
|
||||
@ -82,6 +91,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":1,
|
||||
"monthly_pageview_limit":50000000,
|
||||
"monthly_product_id":"650652",
|
||||
"yearly_product_id":"650653",
|
||||
|
@ -1,6 +1,7 @@
|
||||
[
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":2,
|
||||
"monthly_pageview_limit":10000,
|
||||
"monthly_product_id":"654177",
|
||||
"yearly_product_id":"653232",
|
||||
@ -10,6 +11,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":2,
|
||||
"monthly_pageview_limit":100000,
|
||||
"monthly_product_id":"654178",
|
||||
"yearly_product_id":"653234",
|
||||
@ -19,6 +21,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":2,
|
||||
"monthly_pageview_limit":200000,
|
||||
"monthly_product_id":"653237",
|
||||
"yearly_product_id":"653236",
|
||||
@ -28,6 +31,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":2,
|
||||
"monthly_pageview_limit":500000,
|
||||
"monthly_product_id":"653238",
|
||||
"yearly_product_id":"653239",
|
||||
@ -37,6 +41,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":2,
|
||||
"monthly_pageview_limit":1000000,
|
||||
"monthly_product_id":"653240",
|
||||
"yearly_product_id":"653242",
|
||||
@ -46,6 +51,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":2,
|
||||
"monthly_pageview_limit":2000000,
|
||||
"monthly_product_id":"653253",
|
||||
"yearly_product_id":"653254",
|
||||
@ -55,6 +61,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":2,
|
||||
"monthly_pageview_limit":5000000,
|
||||
"monthly_product_id":"653255",
|
||||
"yearly_product_id":"653256",
|
||||
@ -64,6 +71,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":2,
|
||||
"monthly_pageview_limit":10000000,
|
||||
"monthly_product_id":"654181",
|
||||
"yearly_product_id":"653257",
|
||||
@ -73,6 +81,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":2,
|
||||
"monthly_pageview_limit":20000000,
|
||||
"monthly_product_id":"654182",
|
||||
"yearly_product_id":"653258",
|
||||
@ -82,6 +91,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":2,
|
||||
"monthly_pageview_limit":50000000,
|
||||
"monthly_product_id":"654183",
|
||||
"yearly_product_id":"653259",
|
||||
|
@ -1,6 +1,7 @@
|
||||
[
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":10000,
|
||||
"monthly_product_id":"749342",
|
||||
"yearly_product_id":"749343",
|
||||
@ -10,6 +11,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":100000,
|
||||
"monthly_product_id":"749344",
|
||||
"yearly_product_id":"749345",
|
||||
@ -19,6 +21,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":200000,
|
||||
"monthly_product_id":"749346",
|
||||
"yearly_product_id":"749347",
|
||||
@ -28,6 +31,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":500000,
|
||||
"monthly_product_id":"749348",
|
||||
"yearly_product_id":"749349",
|
||||
@ -37,6 +41,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":1000000,
|
||||
"monthly_product_id":"749350",
|
||||
"yearly_product_id":"749352",
|
||||
@ -46,6 +51,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":2000000,
|
||||
"monthly_product_id":"749353",
|
||||
"yearly_product_id":"749355",
|
||||
@ -55,6 +61,7 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":5000000,
|
||||
"monthly_product_id":"749356",
|
||||
"yearly_product_id":"749357",
|
||||
@ -64,11 +71,92 @@
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":10000000,
|
||||
"monthly_product_id":"749358",
|
||||
"yearly_product_id":"749359",
|
||||
"site_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":10000,
|
||||
"monthly_product_id":"857481",
|
||||
"yearly_product_id":"857482",
|
||||
"site_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":100000,
|
||||
"monthly_product_id":"857483",
|
||||
"yearly_product_id":"857484",
|
||||
"site_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":200000,
|
||||
"monthly_product_id":"857486",
|
||||
"yearly_product_id":"857487",
|
||||
"site_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":500000,
|
||||
"monthly_product_id":"857490",
|
||||
"yearly_product_id":"857491",
|
||||
"site_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":1000000,
|
||||
"monthly_product_id":"857493",
|
||||
"yearly_product_id":"857494",
|
||||
"site_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":2000000,
|
||||
"monthly_product_id":"857495",
|
||||
"yearly_product_id":"857496",
|
||||
"site_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":5000000,
|
||||
"monthly_product_id":"857498",
|
||||
"yearly_product_id":"857500",
|
||||
"site_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":10000000,
|
||||
"monthly_product_id":"857501",
|
||||
"yearly_product_id":"857502",
|
||||
"site_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
}
|
||||
]
|
||||
|
@ -1,146 +1,162 @@
|
||||
[
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":10000,
|
||||
"monthly_product_id":"857097",
|
||||
"yearly_product_id":"857079",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":100000,
|
||||
"monthly_product_id":"857098",
|
||||
"yearly_product_id":"857080",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":200000,
|
||||
"monthly_product_id":"857099",
|
||||
"yearly_product_id":"857081",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":500000,
|
||||
"monthly_product_id":"857100",
|
||||
"yearly_product_id":"857082",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":1000000,
|
||||
"monthly_product_id":"857101",
|
||||
"yearly_product_id":"857083",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":2000000,
|
||||
"monthly_product_id":"857102",
|
||||
"yearly_product_id":"857084",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":5000000,
|
||||
"monthly_product_id":"857103",
|
||||
"yearly_product_id":"857085",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":10000000,
|
||||
"monthly_product_id":"857104",
|
||||
"yearly_product_id":"857086",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":10000,
|
||||
"monthly_product_id":"857105",
|
||||
"yearly_product_id":"857087",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":100000,
|
||||
"monthly_product_id":"857106",
|
||||
"yearly_product_id":"857088",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":200000,
|
||||
"monthly_product_id":"857107",
|
||||
"yearly_product_id":"857089",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":500000,
|
||||
"monthly_product_id":"857108",
|
||||
"yearly_product_id":"857090",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":1000000,
|
||||
"monthly_product_id":"857109",
|
||||
"yearly_product_id":"857091",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":2000000,
|
||||
"monthly_product_id":"857110",
|
||||
"yearly_product_id":"857092",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":5000000,
|
||||
"monthly_product_id":"857111",
|
||||
"yearly_product_id":"857093",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":10000000,
|
||||
"monthly_product_id":"857112",
|
||||
"yearly_product_id":"857094",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props","revenue_goals","funnels","stats_api"]
|
||||
}
|
||||
]
|
||||
|
@ -1,146 +1,162 @@
|
||||
[
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":1,
|
||||
"monthly_pageview_limit":10000,
|
||||
"monthly_product_id":"63842",
|
||||
"yearly_product_id":"63859",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"site_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":2,
|
||||
"monthly_pageview_limit":100000,
|
||||
"monthly_product_id":"63843",
|
||||
"yearly_product_id":"63860",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"site_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":200000,
|
||||
"monthly_product_id":"63844",
|
||||
"yearly_product_id":"63861",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"site_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":500000,
|
||||
"monthly_product_id":"63845",
|
||||
"yearly_product_id":"63862",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"features":["goals","props","stats_api"]
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":1000000,
|
||||
"monthly_product_id":"63846",
|
||||
"yearly_product_id":"63863",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"features":["goals","props","stats_api"]
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":2000000,
|
||||
"monthly_product_id":"63847",
|
||||
"yearly_product_id":"63864",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"features":["goals","props","stats_api"]
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":5000000,
|
||||
"monthly_product_id":"63848",
|
||||
"yearly_product_id":"63865",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"features":["goals","props","stats_api"]
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":10000000,
|
||||
"monthly_product_id":"63849",
|
||||
"yearly_product_id":"63866",
|
||||
"site_limit":10,
|
||||
"team_member_limit":5,
|
||||
"features":["goals","props","stats_api"]
|
||||
"team_member_limit":3,
|
||||
"features":["goals"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":10000,
|
||||
"monthly_product_id":"63850",
|
||||
"yearly_product_id":"63867",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props", "revenue_goals", "funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":100000,
|
||||
"monthly_product_id":"63851",
|
||||
"yearly_product_id":"63868",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props", "revenue_goals", "funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":3,
|
||||
"monthly_pageview_limit":200000,
|
||||
"monthly_product_id":"63852",
|
||||
"yearly_product_id":"63869",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":"unlimited",
|
||||
"features":["goals","props", "revenue_goals", "funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":500000,
|
||||
"monthly_product_id":"63853",
|
||||
"yearly_product_id":"63870",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props", "revenue_goals", "funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":1000000,
|
||||
"monthly_product_id":"63854",
|
||||
"yearly_product_id":"63871",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props", "revenue_goals", "funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":2000000,
|
||||
"monthly_product_id":"63855",
|
||||
"yearly_product_id":"63872",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props", "revenue_goals", "funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":5000000,
|
||||
"monthly_product_id":"63856",
|
||||
"yearly_product_id":"63873",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props", "revenue_goals", "funnels","stats_api"]
|
||||
},
|
||||
{
|
||||
"kind":"business",
|
||||
"generation":4,
|
||||
"monthly_pageview_limit":10000000,
|
||||
"monthly_product_id":"63857",
|
||||
"yearly_product_id":"63874",
|
||||
"site_limit":50,
|
||||
"team_member_limit":50,
|
||||
"team_member_limit":10,
|
||||
"features":["goals","props", "revenue_goals", "funnels","stats_api"]
|
||||
}
|
||||
]
|
||||
|
@ -1,6 +1,7 @@
|
||||
[
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":1,
|
||||
"monthly_pageview_limit":150000000,
|
||||
"yearly_product_id":"648089",
|
||||
"monthly_product_id":null,
|
||||
|
@ -1,6 +1,7 @@
|
||||
[
|
||||
{
|
||||
"kind":"growth",
|
||||
"generation":2,
|
||||
"monthly_pageview_limit":10000000,
|
||||
"monthly_product_id":"655350",
|
||||
"yearly_product_id":null,
|
||||
|
@ -5,30 +5,22 @@ defmodule Plausible.Billing.PlansTest do
|
||||
@v1_plan_id "558018"
|
||||
@v2_plan_id "654177"
|
||||
@v4_plan_id "857097"
|
||||
@v3_business_plan_id "857481"
|
||||
@v4_business_plan_id "857105"
|
||||
|
||||
describe "getting subscription plans for user" do
|
||||
test "growth_plans_for/1 shows v1 pricing for users who are already on v1 pricing" do
|
||||
test "growth_plans_for/1 returns v1 plans for users who are already on v1 pricing" do
|
||||
user = insert(:user, subscription: build(:subscription, paddle_plan_id: @v1_plan_id))
|
||||
|
||||
assert List.first(Plans.growth_plans_for(user)).monthly_product_id == @v1_plan_id
|
||||
end
|
||||
|
||||
test "growth_plans_for/1 shows v2 pricing for users who are already on v2 pricing" do
|
||||
test "growth_plans_for/1 returns v2 plans for users who are already on v2 pricing" do
|
||||
user = insert(:user, subscription: build(:subscription, paddle_plan_id: @v2_plan_id))
|
||||
|
||||
assert List.first(Plans.growth_plans_for(user)).monthly_product_id == @v2_plan_id
|
||||
end
|
||||
|
||||
test "growth_plans_for/1 shows v2 pricing for users who signed up in 2021" do
|
||||
user = insert(:user, inserted_at: ~N[2021-12-31 00:00:00])
|
||||
|
||||
assert List.first(Plans.growth_plans_for(user)).monthly_product_id == @v2_plan_id
|
||||
end
|
||||
|
||||
test "growth_plans_for/1 shows v4 pricing for everyone else" do
|
||||
test "growth_plans_for/1 returns v4 plans for everyone else" do
|
||||
user = insert(:user)
|
||||
|
||||
assert List.first(Plans.growth_plans_for(user)).monthly_product_id == @v4_plan_id
|
||||
end
|
||||
|
||||
@ -41,11 +33,28 @@ defmodule Plausible.Billing.PlansTest do
|
||||
end)
|
||||
end
|
||||
|
||||
test "business_plans/0 returns only v4 business plans" do
|
||||
Plans.business_plans()
|
||||
|> Enum.each(fn plan ->
|
||||
assert plan.kind == :business
|
||||
end)
|
||||
test "growth_plans_for/1 returns the latest generation of growth plans for a user with a business subscription" do
|
||||
user =
|
||||
insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_business_plan_id))
|
||||
|
||||
assert List.first(Plans.growth_plans_for(user)).monthly_product_id == @v4_plan_id
|
||||
end
|
||||
|
||||
test "business_plans_for/1 returns v3 business plans for a legacy subscriber" do
|
||||
user = insert(:user, subscription: build(:subscription, paddle_plan_id: @v2_plan_id))
|
||||
|
||||
business_plans = Plans.business_plans_for(user)
|
||||
|
||||
assert Enum.all?(business_plans, &(&1.kind == :business))
|
||||
assert List.first(business_plans).monthly_product_id == @v3_business_plan_id
|
||||
end
|
||||
|
||||
test "business_plans_for/1 returns v4 business plans for everyone else" do
|
||||
user = insert(:user)
|
||||
business_plans = Plans.business_plans_for(user)
|
||||
|
||||
assert Enum.all?(business_plans, &(&1.kind == :business))
|
||||
assert List.first(business_plans).monthly_product_id == @v4_business_plan_id
|
||||
end
|
||||
|
||||
test "available_plans_with_prices/1" do
|
||||
@ -58,7 +67,7 @@ defmodule Plausible.Billing.PlansTest do
|
||||
end)
|
||||
|
||||
assert Enum.find(business_plans, fn plan ->
|
||||
(%Money{} = plan.monthly_cost) && plan.monthly_product_id == @v4_business_plan_id
|
||||
(%Money{} = plan.monthly_cost) && plan.monthly_product_id == @v3_business_plan_id
|
||||
end)
|
||||
end
|
||||
|
||||
@ -177,6 +186,14 @@ defmodule Plausible.Billing.PlansTest do
|
||||
"749355",
|
||||
"749357",
|
||||
"749359",
|
||||
"857482",
|
||||
"857484",
|
||||
"857487",
|
||||
"857491",
|
||||
"857494",
|
||||
"857496",
|
||||
"857500",
|
||||
"857502",
|
||||
"857079",
|
||||
"857080",
|
||||
"857081",
|
||||
|
@ -6,6 +6,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
@v1_plan_id "558018"
|
||||
@v2_plan_id "654177"
|
||||
@v3_plan_id "749342"
|
||||
@v3_business_plan_id "857481"
|
||||
|
||||
describe "site_limit/1" do
|
||||
test "returns 50 when user is on an old plan" do
|
||||
@ -356,8 +357,15 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
|
||||
user_on_business = insert(:user, subscription: build(:business_subscription))
|
||||
|
||||
assert 5 == Quota.team_member_limit(user_on_growth)
|
||||
assert 50 == Quota.team_member_limit(user_on_business)
|
||||
assert 3 == Quota.team_member_limit(user_on_growth)
|
||||
assert 10 == Quota.team_member_limit(user_on_business)
|
||||
end
|
||||
|
||||
test "returns unlimited when user is on a v3 business plan" do
|
||||
user =
|
||||
insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_business_plan_id))
|
||||
|
||||
assert :unlimited == Quota.team_member_limit(user)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -8,6 +8,7 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
@v1_10k_yearly_plan_id "572810"
|
||||
@v4_growth_200k_yearly_plan_id "857081"
|
||||
@v4_business_5m_monthly_plan_id "857111"
|
||||
@v3_business_10k_monthly_plan_id "857481"
|
||||
|
||||
@monthly_interval_button ~s/label[phx-click="set_interval"][phx-value-interval="monthly"]/
|
||||
@yearly_interval_button ~s/label[phx-click="set_interval"][phx-value-interval="yearly"]/
|
||||
@ -27,6 +28,8 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
@business_current_label "#{@business_plan_box} #current-label"
|
||||
@business_checkout_button "#business-checkout"
|
||||
|
||||
@enterprise_plan_box "#enterprise-plan-box"
|
||||
|
||||
describe "for a user with no subscription" do
|
||||
setup [:create_user, :log_in]
|
||||
|
||||
@ -41,6 +44,41 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
assert doc =~ "+ VAT if applicable"
|
||||
end
|
||||
|
||||
test "displays plan benefits", %{conn: conn} do
|
||||
{:ok, _lv, doc} = get_liveview(conn)
|
||||
|
||||
growth_box = text_of_element(doc, @growth_plan_box)
|
||||
business_box = text_of_element(doc, @business_plan_box)
|
||||
enterprise_box = text_of_element(doc, @enterprise_plan_box)
|
||||
|
||||
assert growth_box =~ "Up to 3 team members"
|
||||
assert growth_box =~ "Up to 10 sites"
|
||||
assert growth_box =~ "Intuitive, fast and privacy-friendly dashboard"
|
||||
assert growth_box =~ "Email/Slack reports"
|
||||
assert growth_box =~ "Google Analytics import"
|
||||
assert growth_box =~ "Goals and custom events"
|
||||
|
||||
assert business_box =~ "Everything in Growth"
|
||||
assert business_box =~ "Up to 10 team members"
|
||||
assert business_box =~ "Up to 50 sites"
|
||||
assert business_box =~ "Stats API"
|
||||
assert business_box =~ "Custom Properties"
|
||||
assert business_box =~ "Funnels"
|
||||
assert business_box =~ "Ecommerce revenue attribution"
|
||||
assert business_box =~ "Priority support"
|
||||
|
||||
refute business_box =~ "Goals and custom events"
|
||||
|
||||
assert enterprise_box =~ "Everything in Business"
|
||||
assert enterprise_box =~ "10+ team members"
|
||||
assert enterprise_box =~ "50+ sites"
|
||||
assert enterprise_box =~ "Sites API access for"
|
||||
assert enterprise_box =~ "Technical onboarding"
|
||||
|
||||
assert text_of_attr(find(doc, "#{@enterprise_plan_box} p a"), "href") =~
|
||||
"https://plausible.io/white-label-web-analytics"
|
||||
end
|
||||
|
||||
test "default billing interval is monthly, and can switch to yearly", %{conn: conn} do
|
||||
{:ok, lv, doc} = get_liveview(conn)
|
||||
|
||||
@ -107,16 +145,16 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
|
||||
doc = lv |> element(@slider_input) |> render_change(%{slider: 8})
|
||||
|
||||
assert text_of_element(doc, @growth_plan_box) =~ "Custom"
|
||||
assert text_of_element(doc, "#growth-custom-price") =~ "Custom"
|
||||
assert text_of_element(doc, @growth_plan_box) =~ "Contact us"
|
||||
assert text_of_element(doc, @business_plan_box) =~ "Custom"
|
||||
assert text_of_element(doc, "#business-custom-price") =~ "Custom"
|
||||
assert text_of_element(doc, @business_plan_box) =~ "Contact us"
|
||||
|
||||
doc = lv |> element(@slider_input) |> render_change(%{slider: 7})
|
||||
|
||||
refute text_of_element(doc, @growth_plan_box) =~ "Custom"
|
||||
refute text_of_element(doc, "#growth-custom-price") =~ "Custom"
|
||||
refute text_of_element(doc, @growth_plan_box) =~ "Contact us"
|
||||
refute text_of_element(doc, @business_plan_box) =~ "Custom"
|
||||
refute text_of_element(doc, "#business-custom-price") =~ "Custom"
|
||||
refute text_of_element(doc, @business_plan_box) =~ "Contact us"
|
||||
end
|
||||
|
||||
@ -175,6 +213,41 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
refute doc =~ "What happens if I go over my page views limit?"
|
||||
end
|
||||
|
||||
test "displays plan benefits", %{conn: conn} do
|
||||
{:ok, _lv, doc} = get_liveview(conn)
|
||||
|
||||
growth_box = text_of_element(doc, @growth_plan_box)
|
||||
business_box = text_of_element(doc, @business_plan_box)
|
||||
enterprise_box = text_of_element(doc, @enterprise_plan_box)
|
||||
|
||||
assert growth_box =~ "Up to 3 team members"
|
||||
assert growth_box =~ "Up to 10 sites"
|
||||
assert growth_box =~ "Intuitive, fast and privacy-friendly dashboard"
|
||||
assert growth_box =~ "Email/Slack reports"
|
||||
assert growth_box =~ "Google Analytics import"
|
||||
assert growth_box =~ "Goals and custom events"
|
||||
|
||||
assert business_box =~ "Everything in Growth"
|
||||
assert business_box =~ "Up to 10 team members"
|
||||
assert business_box =~ "Up to 50 sites"
|
||||
assert business_box =~ "Stats API"
|
||||
assert business_box =~ "Custom Properties"
|
||||
assert business_box =~ "Funnels"
|
||||
assert business_box =~ "Ecommerce revenue attribution"
|
||||
assert business_box =~ "Priority support"
|
||||
|
||||
refute business_box =~ "Goals and custom events"
|
||||
|
||||
assert enterprise_box =~ "Everything in Business"
|
||||
assert enterprise_box =~ "10+ team members"
|
||||
assert enterprise_box =~ "50+ sites"
|
||||
assert enterprise_box =~ "Sites API access for"
|
||||
assert enterprise_box =~ "Technical onboarding"
|
||||
|
||||
assert text_of_attr(find(doc, "#{@enterprise_plan_box} p a"), "href") =~
|
||||
"https://plausible.io/white-label-web-analytics"
|
||||
end
|
||||
|
||||
test "displays usage", %{conn: conn, user: user} do
|
||||
site = insert(:site, members: [user])
|
||||
|
||||
@ -302,6 +375,51 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a user with a v3 business (unlimited team members) subscription plan" do
|
||||
setup [:create_user, :log_in]
|
||||
|
||||
setup %{user: user} = context do
|
||||
create_subscription_for(user, paddle_plan_id: @v3_business_10k_monthly_plan_id)
|
||||
{:ok, context}
|
||||
end
|
||||
|
||||
test "displays plan benefits", %{conn: conn} do
|
||||
{:ok, _lv, doc} = get_liveview(conn)
|
||||
|
||||
growth_box = text_of_element(doc, @growth_plan_box)
|
||||
business_box = text_of_element(doc, @business_plan_box)
|
||||
enterprise_box = text_of_element(doc, @enterprise_plan_box)
|
||||
|
||||
assert growth_box =~ "Up to 3 team members"
|
||||
assert growth_box =~ "Up to 10 sites"
|
||||
assert growth_box =~ "Intuitive, fast and privacy-friendly dashboard"
|
||||
assert growth_box =~ "Email/Slack reports"
|
||||
assert growth_box =~ "Google Analytics import"
|
||||
assert growth_box =~ "Goals and custom events"
|
||||
|
||||
assert business_box =~ "Everything in Growth"
|
||||
assert business_box =~ "Unlimited team members"
|
||||
assert business_box =~ "Up to 50 sites"
|
||||
assert business_box =~ "Stats API"
|
||||
assert business_box =~ "Custom Properties"
|
||||
assert business_box =~ "Funnels"
|
||||
assert business_box =~ "Ecommerce revenue attribution"
|
||||
assert business_box =~ "Priority support"
|
||||
|
||||
refute business_box =~ "Goals and custom events"
|
||||
|
||||
assert enterprise_box =~ "Everything in Business"
|
||||
assert enterprise_box =~ "50+ sites"
|
||||
assert enterprise_box =~ "Sites API access for"
|
||||
assert enterprise_box =~ "Technical onboarding"
|
||||
|
||||
refute enterprise_box =~ "team members"
|
||||
|
||||
assert text_of_attr(find(doc, "#{@enterprise_plan_box} p a"), "href") =~
|
||||
"https://plausible.io/white-label-web-analytics"
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a user with a past_due subscription" do
|
||||
setup [:create_user, :log_in, :create_past_due_subscription]
|
||||
|
||||
@ -391,10 +509,13 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
describe "for a grandfathered user" do
|
||||
setup [:create_user, :log_in]
|
||||
|
||||
test "on a v1 plan, Growth tiers are available at 20M, 50M, 50M+, but Business tiers are not",
|
||||
%{conn: conn, user: user} do
|
||||
setup %{user: user} = context do
|
||||
create_subscription_for(user, paddle_plan_id: @v1_10k_yearly_plan_id)
|
||||
{:ok, context}
|
||||
end
|
||||
|
||||
test "on a v1 plan, Growth tiers are available at 20M, 50M, 50M+, but Business tiers are not",
|
||||
%{conn: conn} do
|
||||
{:ok, lv, _doc} = get_liveview(conn)
|
||||
|
||||
doc = lv |> element(@slider_input) |> render_change(%{slider: 8})
|
||||
@ -419,6 +540,42 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
refute text_of_element(doc, @business_plan_box) =~ "Contact us"
|
||||
refute text_of_element(doc, @growth_plan_box) =~ "Contact us"
|
||||
end
|
||||
|
||||
test "displays grandfathering notice in the Growth box instead of benefits", %{conn: conn} do
|
||||
{:ok, _lv, doc} = get_liveview(conn)
|
||||
growth_box = text_of_element(doc, @growth_plan_box)
|
||||
assert growth_box =~ "Your subscription has been grandfathered"
|
||||
refute growth_box =~ "Intuitive, fast and privacy-friendly dashboard"
|
||||
end
|
||||
|
||||
test "displays business and enterprise plan benefits", %{conn: conn} do
|
||||
{:ok, _lv, doc} = get_liveview(conn)
|
||||
|
||||
business_box = text_of_element(doc, @business_plan_box)
|
||||
enterprise_box = text_of_element(doc, @enterprise_plan_box)
|
||||
|
||||
assert business_box =~ "Everything in Growth"
|
||||
assert business_box =~ "Funnels"
|
||||
assert business_box =~ "Ecommerce revenue attribution"
|
||||
assert business_box =~ "Priority support"
|
||||
|
||||
refute business_box =~ "Goals and custom events"
|
||||
refute business_box =~ "Unlimited team members"
|
||||
refute business_box =~ "Up to 50 sites"
|
||||
refute business_box =~ "Stats API"
|
||||
refute business_box =~ "Custom Properties"
|
||||
|
||||
assert enterprise_box =~ "Everything in Business"
|
||||
assert enterprise_box =~ "50+ sites"
|
||||
assert enterprise_box =~ "Sites API access for"
|
||||
assert enterprise_box =~ "Technical onboarding"
|
||||
|
||||
assert text_of_attr(find(doc, "#{@enterprise_plan_box} p a"), "href") =~
|
||||
"https://plausible.io/white-label-web-analytics"
|
||||
|
||||
refute enterprise_box =~ "10+ team members"
|
||||
refute enterprise_box =~ "Unlimited team members"
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a free_10k subscription" do
|
||||
|
Loading…
Reference in New Issue
Block a user