diff --git a/lib/plausible/billing/plans.ex b/lib/plausible/billing/plans.ex index 9ccdd4ee1011e5cb36249fe548767c0a522cbfc8..08eb6299cdf6e42ae04e601e737bfaf44033de23 100644 --- a/lib/plausible/billing/plans.ex +++ b/lib/plausible/billing/plans.ex @@ -1,21 +1,16 @@ defmodule Plausible.Billing.Plans do - @plans_v1 File.read!(Application.app_dir(:plausible) <> "/priv/plans_v1.json") - |> Jason.decode!(keys: :atoms) - @plans_v2 File.read!(Application.app_dir(:plausible) <> "/priv/plans_v2.json") - |> Jason.decode!(keys: :atoms) - @unlisted_plans_v1 [ %{limit: 150_000_000, yearly_product_id: "648089", yearly_cost: "$4800"} ] - @v2_pricing_date ~D[2021-05-11] + @v2_pricing_date ~D[2017-05-11] def plans_for(user) do raw_plans = if Timex.before?(user.inserted_at, @v2_pricing_date) do - @plans_v1 + plans_v1() else - @plans_v2 + plans_v2() end Enum.map(raw_plans, fn plan -> Map.put(plan, :volume, number_format(plan[:limit])) end) @@ -75,6 +70,16 @@ defmodule Plausible.Billing.Plans do end defp all_plans() do - @plans_v1 ++ @unlisted_plans_v1 ++ @plans_v2 + plans_v1() ++ @unlisted_plans_v1 ++ plans_v2() + 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 end diff --git a/lib/plausible_web/controllers/billing_controller.ex b/lib/plausible_web/controllers/billing_controller.ex index 52660ff2ba5c80772190beb7668e73ad6dbec198..81a4966f19e564eb3e7038e8344d7c44684b83df 100644 --- a/lib/plausible_web/controllers/billing_controller.ex +++ b/lib/plausible_web/controllers/billing_controller.ex @@ -81,6 +81,7 @@ defmodule PlausibleWeb.BillingController do def upgrade(conn, _params) do usage = Plausible.Billing.usage(conn.assigns[:current_user]) today = Timex.today() + IO.inspect(Plausible.Billing.Plans.plans_for(conn.assigns[:current_user])) render(conn, "upgrade.html", usage: usage, diff --git a/lib/plausible_web/templates/billing/_plan_option.html.eex b/lib/plausible_web/templates/billing/_plan_option.html.eex index d1db3b7f9df94ed1646241ed9616644cf6556e32..1c7cdf91c68c2c8fa4fdd002d38a6089f661d543 100644 --- a/lib/plausible_web/templates/billing/_plan_option.html.eex +++ b/lib/plausible_web/templates/billing/_plan_option.html.eex @@ -1,7 +1,7 @@ -<tr @click="volume = '<%= @volume %>'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': volume === '<%= @volume %>', 'border-b border-gray-200 cursor-pointer': volume !== '<%= @volume %>'}"> - <td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '<%= @volume %>'}"><%= @volume %></td> - <td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '<%= @volume %>'}"> - <span x-show="billingCycle === 'monthly'"><%= @monthly_price %> / mo</span> - <span x-show="billingCycle === 'yearly'"><%= @yearly_price %> / yr</span> +<tr @click="volume = '<%= @plan[:volume] %>'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': volume === '<%= @plan[:volume] %>', 'border-b border-gray-200 cursor-pointer': volume !== '<%= @plan[:volume] %>'}"> + <td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '<%= @plan[:volume] %>'}"><%= @plan[:volume] %></td> + <td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '<%= @plan[:volume] %>'}"> + <span x-show="billingCycle === 'monthly'"><span x-text="priceFor(<%= @plan[:monthly_product_id] %>)"></span> / mo</span> + <span x-cloak x-show="billingCycle === 'yearly'"><span x-text="priceFor(<%= @plan[:yearly_product_id] %>)"></span> / yr</span> </td> </tr> diff --git a/lib/plausible_web/templates/billing/upgrade.html.eex b/lib/plausible_web/templates/billing/upgrade.html.eex index 4c642b4f4ace322c27554302d25929cbe7d60809..4786798827f786d7d51a89125bdd92bdfdf019bd 100644 --- a/lib/plausible_web/templates/billing/upgrade.html.eex +++ b/lib/plausible_web/templates/billing/upgrade.html.eex @@ -1,16 +1,13 @@ +<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/fetch-jsonp/1.1.3/fetch-jsonp.min.js"></script> <script> - window.plans = function() { + plans = function() { return { rawPlans: <%= raw Jason.encode!(Plausible.Billing.Plans.plans_for(@user)) %>, + localizedPlans: null, volume: '10k', billingCycle: 'monthly', selectedPlanPrice() { - var selectedPlan = this.rawPlans.find((plan) => plan.volume === this.volume) - if (this.billingCycle === 'monthly'){ - return selectedPlan.monthly_cost - } else { - return selectedPlan.yearly_cost - } + return this.priceFor(this.selectedPlanProductId()) }, selectedPlanProductId() { var selectedPlan = this.rawPlans.find((plan) => plan.volume === this.volume) @@ -19,6 +16,31 @@ } else { return selectedPlan.yearly_product_id } + }, + priceFor(productId) { + var plan = this.localizedPlans.find(plan => plan.product_id === Number(productId)) + var currency = { + 'USD': '$', + 'EUR': '€', + 'GBP': '£' + }[plan.currency] + + return currency + plan.price.gross + }, + fetchPlans() { + var rawPlans = <%= raw Jason.encode!(Plausible.Billing.Plans.plans_for(@user)) %> + var productIds = [] + + rawPlans.forEach((plan) => { + productIds.push(plan.monthly_product_id) + productIds.push(plan.yearly_product_id) + }) + + fetchJsonp('https://checkout.paddle.com/api/2.0/prices?product_ids=' + productIds.join(',')) + .then((res) => res.json()) + .then((data) => { + this.localizedPlans = data.response.products + }) } } } @@ -30,7 +52,7 @@ <div> <div class="flex flex-col w-full max-w-4xl px-4 mx-auto mt-4 md:flex-row"> - <div x-data="window.plans()" class="flex-1 px-8 py-4 mt-8 mb-4 bg-white rounded shadow-md dark:bg-gray-800"> + <div x-init="fetchPlans()" x-data="window.plans()" class="flex-1 px-8 py-4 mt-8 mb-4 bg-white rounded shadow-md dark:bg-gray-800"> <div class="w-full py-4 dark:text-gray-100"> <span>You've used <b><%= PlausibleWeb.AuthView.delimit_integer(@usage) %></b> billable pageviews in the last 30 days</span> </div> @@ -48,7 +70,7 @@ <div class="pt-6"></div> - <div class="flex flex-col"> + <template x-if="localizedPlans" class="flex flex-col"> <div class="py-2 -my-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8"> <div class="inline-block min-w-full overflow-hidden align-middle border-b border-gray-200 shadow sm:rounded-lg dark:border-t dark:border-l dark:border-r dark:shadow-none"> <table class="min-w-full"> @@ -64,16 +86,18 @@ </thead> <tbody class="bg-white dark:bg-gray-800"> <%= for plan <- Plausible.Billing.Plans.plans_for(@user) do %> - <%= render("_plan_option.html", volume: PlausibleWeb.StatsView.large_number_format(plan[:limit]), monthly_price: plan[:monthly_cost], yearly_price: plan[:yearly_cost]) %> + <%= render("_plan_option.html", plan: plan) %> <% end %> </tbody> </table> </div> </div> - </div> + </template> + + <div x-show="!localizedPlans" class="mx-auto my-56 loading sm"><div></div></div> <div class="mt-6 text-right"> - <div class="text-sm font-medium dark:text-gray-100">Due today: <b x-text="selectedPlanPrice()">$6</b></div> + <div class="text-sm font-medium dark:text-gray-100">Due today: <template x-if="localizedPlans"><b x-text="selectedPlanPrice()">$6</b></template></div> <div class="mb-4 text-xs font-medium dark:text-gray-100">+ VAT if applicable</div> <span class="inline-flex rounded-md shadow-sm"> <button type="button" data-theme="none" :data-product="selectedPlanProductId()" data-email="<%= @conn.assigns[:current_user].email %>" data-disable-logout="true" data-passthrough="<%= @conn.assigns[:current_user].id %>" data-success="/billing/upgrade-success" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-indigo-600 border border-transparent paddle_button leading-5 rounded-md hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:ring active:bg-indigo-700 transition ease-in-out duration-150"> diff --git a/priv/plans_v2.json b/priv/plans_v2.json index 67c2098d1f9584654ab300dc9c07ac7edc8e3a97..434ac751dcb852cf18eab38fff7f23dc5d759369 100644 --- a/priv/plans_v2.json +++ b/priv/plans_v2.json @@ -2,14 +2,14 @@ { "limit":10000, "monthly_cost":"$6", - "monthly_product_id":"558018", + "monthly_product_id":"654177", "yearly_cost":"$60", "yearly_product_id":"653232" }, { "limit":100000, "monthly_cost":"$12", - "monthly_product_id":"558745", + "monthly_product_id":"654178", "yearly_cost":"$120", "yearly_product_id":"653234" }, @@ -30,9 +30,9 @@ { "limit":1000000, "monthly_cost":"$50", - "monthly_product_id":"597642", + "monthly_product_id":"653240", "yearly_cost":"$500", - "yearly_product_id":"597643" + "yearly_product_id":"653242" }, { "limit":2000000, @@ -51,21 +51,21 @@ { "limit":10000000, "monthly_cost":"$150", - "monthly_product_id":"642352", + "monthly_product_id":"654181", "yearly_cost":"$1500", "yearly_product_id":"653257" }, { "limit":20000000, "monthly_cost":"$225", - "monthly_product_id":"642355", + "monthly_product_id":"654182", "yearly_cost":"$2250", "yearly_product_id":"653258" }, { "limit":50000000, "monthly_cost":"$330", - "monthly_product_id":"650652", + "monthly_product_id":"654183", "yearly_cost":"$3300", "yearly_product_id":"653259" }