-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathplans.ex
178 lines (147 loc) · 4.63 KB
/
plans.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
defmodule Plausible.Billing.Plans do
use Plausible.Repo
@unlisted_plans_v1 [
%{limit: 150_000_000, yearly_product_id: "648089", yearly_cost: "$4800"}
]
@unlisted_plans_v2 [
%{limit: 10_000_000, monthly_product_id: "655350", monthly_cost: "$250"}
]
@sandbox_plans [
%{
limit: 10_000,
monthly_product_id: "19878",
yearly_product_id: "20127",
monthly_cost: "$6",
yearly_cost: "$60"
},
%{
limit: 100_000,
monthly_product_id: "20657",
yearly_product_id: "20658",
monthly_cost: "$12.34",
yearly_cost: "$120.34"
}
]
@type plan() ::
%{
limit: non_neg_integer(),
monthly_product_id: String.t(),
yearly_product_id: String.t(),
monthly_cost: String.t(),
yearly_cost: String.t()
}
| :enterprise
def plans_for(user) do
user = Repo.preload(user, :subscription)
sandbox_plans = plans_sandbox()
v1_plans = plans_v1()
v2_plans = plans_v2()
v3_plans = plans_v3()
raw_plans =
cond do
contains?(v1_plans, user.subscription) ->
v1_plans
contains?(v2_plans, user.subscription) ->
v2_plans
contains?(v3_plans, user.subscription) ->
v3_plans
contains?(sandbox_plans, user.subscription) ->
sandbox_plans
true ->
cond do
Application.get_env(:plausible, :environment) == "dev" -> sandbox_plans
Timex.before?(user.inserted_at, ~D[2022-01-01]) -> v2_plans
true -> v3_plans
end
end
Enum.map(raw_plans, fn plan -> Map.put(plan, :volume, number_format(plan[:limit])) end)
end
def all_yearly_plan_ids do
Enum.map(all_plans(), fn plan -> plan[:yearly_product_id] end)
end
def for_product_id(product_id) do
Enum.find(all_plans(), fn plan ->
product_id in [plan[:monthly_product_id], plan[:yearly_product_id]]
end)
end
def subscription_interval(%Plausible.Billing.Subscription{paddle_plan_id: "free_10k"}),
do: "N/A"
def subscription_interval(subscription) do
case for_product_id(subscription.paddle_plan_id) do
nil ->
enterprise_plan = get_enterprise_plan(subscription)
enterprise_plan && enterprise_plan.billing_interval
plan ->
if subscription.paddle_plan_id == plan[:monthly_product_id] do
"monthly"
else
"yearly"
end
end
end
def allowance(%Plausible.Billing.Subscription{paddle_plan_id: "free_10k"}), do: 10_000
def allowance(subscription) do
found = for_product_id(subscription.paddle_plan_id)
if found do
Map.fetch!(found, :limit)
else
enterprise_plan = get_enterprise_plan(subscription)
if enterprise_plan do
enterprise_plan.monthly_pageview_limit
else
Sentry.capture_message("Unknown allowance for plan",
extra: %{
paddle_plan_id: subscription.paddle_plan_id
}
)
end
end
end
defp get_enterprise_plan(%Plausible.Billing.Subscription{} = subscription) do
Repo.get_by(Plausible.Billing.EnterprisePlan,
user_id: subscription.user_id,
paddle_plan_id: subscription.paddle_plan_id
)
end
@enterprise_level_usage 10_000_000
@spec suggested_plan(Plausible.Auth.User.t(), non_neg_integer()) :: plan()
def suggested_plan(user, usage_during_cycle) do
cond do
usage_during_cycle > @enterprise_level_usage -> :enterprise
Plausible.Auth.enterprise?(user) -> :enterprise
true -> Enum.find(plans_for(user), fn plan -> usage_during_cycle < plan[:limit] end)
end
end
defp contains?(_plans, nil), do: false
defp contains?(plans, subscription) do
Enum.any?(plans, fn plan ->
plan[:monthly_product_id] == subscription.paddle_plan_id ||
plan[:yearly_product_id] == subscription.paddle_plan_id
end)
end
defp number_format(num) do
PlausibleWeb.StatsView.large_number_format(num)
end
defp all_plans() do
plans_v1() ++
@unlisted_plans_v1 ++ plans_v2() ++ @unlisted_plans_v2 ++ plans_v3() ++ plans_sandbox()
end
defp plans_v1() do
File.read!(Application.app_dir(:plausible) <> "/priv/plans_v1.json")
|> Jason.decode!(keys: :atoms)
end
defp plans_v2() do
File.read!(Application.app_dir(:plausible) <> "/priv/plans_v2.json")
|> Jason.decode!(keys: :atoms)
end
defp plans_v3() do
File.read!(Application.app_dir(:plausible) <> "/priv/plans_v3.json")
|> Jason.decode!(keys: :atoms)
end
defp plans_sandbox() do
case Application.get_env(:plausible, :environment) do
"dev" -> @sandbox_plans
_ -> []
end
end
end