From a29eb3d3cabdb60eb3841c4a64d2adc938d035e5 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Wed, 20 Nov 2024 11:10:54 +0100 Subject: [PATCH] Switch billing controller action to teams schema behind FF (#4843) * Switch upgrade to enterprise plan view to teams schema behind FF * Switch change plan preview action to teams schema behind FF * Switch remaining billing controller actions to teams schema behind FF --- lib/plausible/billing/billing.ex | 3 +- lib/plausible/teams/adapter/read/billing.ex | 21 ++++++++++ lib/plausible/teams/billing.ex | 39 +++++++++++++++++- .../controllers/billing_controller.ex | 24 +++++------ .../controllers/billing_controller_test.exs | 41 +++++++++---------- 5 files changed, 91 insertions(+), 37 deletions(-) diff --git a/lib/plausible/billing/billing.ex b/lib/plausible/billing/billing.ex index 38ca3e111..89db62cf9 100644 --- a/lib/plausible/billing/billing.ex +++ b/lib/plausible/billing/billing.ex @@ -55,7 +55,8 @@ defmodule Plausible.Billing do do: do_change_plan(subscription, new_plan_id) end - defp do_change_plan(subscription, new_plan_id) do + @doc false + def do_change_plan(subscription, new_plan_id) do res = paddle_api().update_subscription(subscription.paddle_subscription_id, %{ plan_id: new_plan_id diff --git a/lib/plausible/teams/adapter/read/billing.ex b/lib/plausible/teams/adapter/read/billing.ex index 3c25186f2..1f33a0f70 100644 --- a/lib/plausible/teams/adapter/read/billing.ex +++ b/lib/plausible/teams/adapter/read/billing.ex @@ -4,6 +4,13 @@ defmodule Plausible.Teams.Adapter.Read.Billing do """ use Plausible.Teams.Adapter + def change_plan(user, new_plan_id) do + switch(user, + team_fn: &Plausible.Teams.Billing.change_plan(&1, new_plan_id), + user_fn: &Plausible.Billing.change_plan(&1, new_plan_id) + ) + end + def enterprise_configured?(nil), do: false def enterprise_configured?(user) do @@ -13,6 +20,13 @@ defmodule Plausible.Teams.Adapter.Read.Billing do ) end + def latest_enterprise_plan_with_prices(user, customer_ip) do + switch(user, + team_fn: &Plausible.Teams.Billing.latest_enterprise_plan_with_price(&1, customer_ip), + user_fn: &Plausible.Billing.Plans.latest_enterprise_plan_with_price(&1, customer_ip) + ) + end + def has_active_subscription?(user) do switch(user, team_fn: &Plausible.Teams.Billing.has_active_subscription?/1, @@ -20,6 +34,13 @@ defmodule Plausible.Teams.Adapter.Read.Billing do ) end + def active_subscription_for(user) do + switch(user, + team_fn: &Plausible.Teams.Billing.active_subscription_for/1, + user_fn: &Plausible.Billing.active_subscription_for/1 + ) + end + def get_subscription(user) do case user_or_team(user) do %{subscription: subscription} -> subscription diff --git a/lib/plausible/teams/billing.ex b/lib/plausible/teams/billing.ex index a9fe643c5..f2c773787 100644 --- a/lib/plausible/teams/billing.ex +++ b/lib/plausible/teams/billing.ex @@ -18,6 +18,24 @@ defmodule Plausible.Teams.Billing do @limit_sites_since ~D[2021-05-05] @site_limit_for_trials 10 + def change_plan(team, new_plan_id) do + subscription = active_subscription_for(team) + plan = Plausible.Billing.Plans.find(new_plan_id) + + limit_checking_opts = + if team.allow_next_upgrade_override do + [ignore_pageview_limit: true] + else + [] + end + + usage = quota_usage(team) + + with :ok <- + Plausible.Billing.Quota.ensure_within_plan_limits(usage, plan, limit_checking_opts), + do: Plausible.Billing.do_change_plan(subscription, new_plan_id) + end + def enterprise_configured?(nil), do: false def enterprise_configured?(%Teams.Team{} = team) do @@ -26,6 +44,19 @@ defmodule Plausible.Teams.Billing do |> Repo.exists?() end + def latest_enterprise_plan_with_price(team, customer_ip) do + enterprise_plan = + Repo.one!( + from(e in EnterprisePlan, + where: e.team_id == ^team.id, + order_by: [desc: e.inserted_at], + limit: 1 + ) + ) + + {enterprise_plan, Plausible.Billing.Plans.get_price_for(enterprise_plan, customer_ip)} + end + def has_active_subscription?(nil), do: false def has_active_subscription?(team) do @@ -34,6 +65,12 @@ defmodule Plausible.Teams.Billing do |> Repo.exists?() end + def active_subscription_for(team) do + team + |> active_subscription_query() + |> Repo.one() + end + def check_needs_to_upgrade(nil), do: {:needs_to_upgrade, :no_trial} def check_needs_to_upgrade(team) do @@ -132,7 +169,7 @@ defmodule Plausible.Teams.Billing do end end - def quota_usage(team, opts) do + def quota_usage(team, opts \\ []) do team = Teams.with_subscription(team) with_features? = Keyword.get(opts, :with_features, false) pending_site_ids = Keyword.get(opts, :pending_ownership_site_ids, []) diff --git a/lib/plausible_web/controllers/billing_controller.ex b/lib/plausible_web/controllers/billing_controller.ex index a3160a20d..630727bdc 100644 --- a/lib/plausible_web/controllers/billing_controller.ex +++ b/lib/plausible_web/controllers/billing_controller.ex @@ -30,19 +30,20 @@ defmodule PlausibleWeb.BillingController do def upgrade_to_enterprise_plan(conn, _params) do current_user = conn.assigns.current_user + subscription = Plausible.Teams.Adapter.Read.Billing.get_subscription(current_user) {latest_enterprise_plan, price} = Plans.latest_enterprise_plan_with_price(current_user, PlausibleWeb.RemoteIP.get(conn)) subscription_resumable? = - Plausible.Billing.Subscriptions.resumable?(current_user.subscription) + Plausible.Billing.Subscriptions.resumable?(subscription) subscribed_to_latest? = subscription_resumable? && - current_user.subscription.paddle_plan_id == latest_enterprise_plan.paddle_plan_id + subscription.paddle_plan_id == latest_enterprise_plan.paddle_plan_id cond do - Subscription.Status.in?(current_user.subscription, [ + Subscription.Status.in?(subscription, [ Subscription.Status.past_due(), Subscription.Status.paused() ]) -> @@ -68,8 +69,9 @@ defmodule PlausibleWeb.BillingController do def change_plan_preview(conn, %{"plan_id" => new_plan_id}) do current_user = conn.assigns.current_user + subscription = Plausible.Teams.Adapter.Read.Billing.active_subscription_for(current_user) - case preview_subscription(current_user, new_plan_id) do + case preview_subscription(subscription, new_plan_id) do {:ok, {subscription, preview_info}} -> render(conn, "change_plan_preview.html", back_link: Routes.billing_path(conn, :choose_plan), @@ -99,7 +101,7 @@ defmodule PlausibleWeb.BillingController do def change_plan(conn, %{"new_plan_id" => new_plan_id}) do current_user = conn.assigns.current_user - case Billing.change_plan(current_user, new_plan_id) do + case Plausible.Teams.Adapter.Read.Billing.change_plan(current_user, new_plan_id) do {:ok, _subscription} -> conn |> put_flash(:success, "Plan changed successfully") @@ -137,15 +139,11 @@ defmodule PlausibleWeb.BillingController do end end - defp preview_subscription(user, new_plan_id) do - subscription = Billing.active_subscription_for(user) + defp preview_subscription(nil, _new_plan_id), do: {:error, :no_subscription} - if subscription do - with {:ok, preview_info} <- Billing.change_plan_preview(subscription, new_plan_id) do - {:ok, {subscription, preview_info}} - end - else - {:error, :no_subscription} + defp preview_subscription(subscription, new_plan_id) do + with {:ok, preview_info} <- Billing.change_plan_preview(subscription, new_plan_id) do + {:ok, {subscription, preview_info}} end end end diff --git a/test/plausible_web/controllers/billing_controller_test.exs b/test/plausible_web/controllers/billing_controller_test.exs index d140dfdd6..862f7f403 100644 --- a/test/plausible_web/controllers/billing_controller_test.exs +++ b/test/plausible_web/controllers/billing_controller_test.exs @@ -24,17 +24,13 @@ defmodule PlausibleWeb.BillingControllerTest do setup [:create_user, :log_in] test "errors if usage exceeds team member limit on the new plan", %{conn: conn, user: user} do - insert(:subscription, user: user, paddle_plan_id: "123123") + subscribe_to_plan(user, "123123") - insert(:site, - memberships: [ - build(:site_membership, user: user, role: :owner), - build(:site_membership, user: build(:user)), - build(:site_membership, user: build(:user)), - build(:site_membership, user: build(:user)), - build(:site_membership, user: build(:user)) - ] - ) + site = new_site(owner: user) + + for _ <- 1..4 do + add_guest(site, role: :viewer) + end conn = post(conn, Routes.billing_path(conn, :change_plan, @v4_growth_plan)) @@ -46,9 +42,9 @@ defmodule PlausibleWeb.BillingControllerTest do conn: conn, user: user } do - insert(:subscription, user: user, paddle_plan_id: "123123") + subscribe_to_plan(user, "123123") - for _ <- 1..11, do: insert(:site, members: [user]) + for _ <- 1..11, do: new_site(owner: user) Plausible.Users.allow_next_upgrade_override(user) @@ -64,8 +60,8 @@ defmodule PlausibleWeb.BillingControllerTest do conn: conn, user: user } do - insert(:subscription, user: user, paddle_plan_id: "123123") - site = insert(:site, members: [user]) + subscribe_to_plan(user, "123123") + site = new_site(owner: user) now = NaiveDateTime.utc_now() generate_usage_for(site, 11_000, Timex.shift(now, days: -5)) @@ -91,7 +87,7 @@ defmodule PlausibleWeb.BillingControllerTest do end test "calls Paddle API to update subscription", %{conn: conn, user: user} do - insert(:subscription, user: user) + subscribe_to_plan(user, "321321") post(conn, Routes.billing_path(conn, :change_plan, "123123")) @@ -218,7 +214,7 @@ defmodule PlausibleWeb.BillingControllerTest do setup [:create_user, :log_in] test "renders preview information about the plan change", %{conn: conn, user: user} do - insert(:subscription, user: user, paddle_plan_id: @v4_growth_plan) + subscribe_to_plan(user, @v4_growth_plan) html_response = conn @@ -339,29 +335,30 @@ defmodule PlausibleWeb.BillingControllerTest do end defp configure_enterprise_plan(%{user: user}) do - insert(:enterprise_plan, - user_id: user.id, + subscribe_to_enterprise_plan(user, paddle_plan_id: "123", billing_interval: :yearly, monthly_pageview_limit: 50_000_000, site_limit: 20_000, hourly_api_request_limit: 5000, - inserted_at: Timex.now() |> Timex.shift(hours: 1) + inserted_at: NaiveDateTime.utc_now() |> NaiveDateTime.shift(hour: 1), + subscription?: false ) :ok end defp subscribe_enterprise(%{user: user}, opts \\ []) do + {paddle_plan_id, opts} = Keyword.pop(opts, :paddle_plan_id, "321") + opts = opts |> Keyword.put(:user, user) - |> Keyword.put_new(:paddle_plan_id, "321") |> Keyword.put_new(:status, Subscription.Status.active()) - insert(:subscription, opts) + user = subscribe_to_plan(user, paddle_plan_id, opts) - {:ok, user: Plausible.Users.with_subscription(user)} + {:ok, user: user} end defp get_paddle_checkout_params(element) do