| defmodule PlausibleWeb.BillingControllerTest do |
| use PlausibleWeb.ConnCase, async: true |
|
|
| require Plausible.Billing.Subscription.Status |
| alias Plausible.Billing.Subscription |
|
|
| @v4_growth_plan "857097" |
| @v4_business_plan "857105" |
|
|
| describe "GET /choose-plan" do |
| setup [:create_user, :log_in] |
|
|
| test "redirects to enterprise upgrade page if user has an enterprise plan configured", |
| %{conn: conn, user: user} do |
| subscribe_to_enterprise_plan(user, paddle_plan_id: "123") |
|
|
| conn = get(conn, Routes.billing_path(conn, :choose_plan)) |
| assert redirected_to(conn) == Routes.billing_path(conn, :upgrade_to_enterprise_plan) |
| end |
| end |
|
|
| describe "POST /change-plan" do |
| setup [:create_user, :log_in] |
|
|
| test "errors if usage exceeds team member limit on the new plan", %{conn: conn, user: user} do |
| subscribe_to_plan(user, "123123") |
|
|
| 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)) |
|
|
| assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ |
| "Unable to subscribe to this plan because the following limits are exceeded: team member limit" |
| end |
|
|
| test "errors if usage exceeds site limit even when user.next_upgrade_override is true", %{ |
| conn: conn, |
| user: user |
| } do |
| subscribe_to_plan(user, "123123") |
|
|
| team = |
| user |
| |> team_of() |
| |> Ecto.Changeset.change(allow_next_upgrade_override: true) |
| |> Plausible.Repo.update!() |
|
|
| for _ <- 1..11, do: new_site(owner: user) |
|
|
| conn = post(conn, Routes.billing_path(conn, :change_plan, @v4_growth_plan)) |
|
|
| subscription = Plausible.Repo.get_by(Plausible.Billing.Subscription, team_id: team.id) |
|
|
| assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "are exceeded: site limit" |
| assert subscription.paddle_plan_id == "123123" |
| end |
|
|
| test "can override allowing to upgrade when pageview limit is exceeded", %{ |
| conn: conn, |
| user: user |
| } do |
| subscribe_to_plan(user, "123123") |
| team = team_of(user) |
| site = new_site(owner: user) |
| now = NaiveDateTime.utc_now() |
|
|
| generate_usage_for(site, 11_000, NaiveDateTime.shift(now, day: -5)) |
| generate_usage_for(site, 11_000, NaiveDateTime.shift(now, day: -35)) |
|
|
| conn1 = post(conn, Routes.billing_path(conn, :change_plan, @v4_growth_plan)) |
|
|
| subscription = Plausible.Repo.get_by(Plausible.Billing.Subscription, team_id: team.id) |
|
|
| assert Phoenix.Flash.get(conn1.assigns.flash, :error) =~ |
| "are exceeded: monthly pageview limit" |
|
|
| assert subscription.paddle_plan_id == "123123" |
|
|
| user |
| |> team_of() |
| |> Ecto.Changeset.change(allow_next_upgrade_override: true) |
| |> Plausible.Repo.update!() |
|
|
| conn2 = post(conn, Routes.billing_path(conn, :change_plan, @v4_growth_plan)) |
|
|
| subscription = Plausible.Repo.reload!(subscription) |
|
|
| assert Phoenix.Flash.get(conn2.assigns.flash, :success) =~ "Plan changed successfully" |
| assert subscription.paddle_plan_id == @v4_growth_plan |
| end |
|
|
| test "calls Paddle API to update subscription", %{conn: conn, user: user} do |
| subscribe_to_plan(user, "321321") |
| team = team_of(user) |
|
|
| post(conn, Routes.billing_path(conn, :change_plan, "123123")) |
|
|
| subscription = Plausible.Repo.get_by(Plausible.Billing.Subscription, team_id: team.id) |
| assert subscription.paddle_plan_id == "123123" |
| assert subscription.next_bill_date == ~D[2019-07-10] |
| assert subscription.next_bill_amount == "6.00" |
| end |
| end |
|
|
| describe "GET /billing/upgrade-success" do |
| setup [:create_user, :create_team, :log_in] |
|
|
| test "shows success page after user subscribes", %{conn: conn} do |
| conn = get(conn, Routes.billing_path(conn, :upgrade_success)) |
|
|
| assert html_response(conn, 200) =~ "Your account is being upgraded" |
| end |
| end |
|
|
| @configured_enterprise_plan_paddle_plan_id "123" |
|
|
| describe "GET /upgrade-to-enterprise-plan (no existing subscription)" do |
| setup [:create_user, :log_in, :configure_enterprise_plan] |
|
|
| test "displays basic page content", %{conn: conn} do |
| doc = |
| conn |
| |> get(Routes.billing_path(conn, :upgrade_to_enterprise_plan)) |
| |> html_response(200) |
|
|
| assert doc =~ "Upgrade to Enterprise" |
| assert doc =~ "prepared a custom enterprise plan for your account with the following limits" |
| assert doc =~ "Questions?" |
| assert doc =~ "Contact us" |
| assert doc =~ "+ VAT if applicable" |
| assert doc =~ "Click the button below to upgrade" |
| assert doc =~ "Pay securely via Paddle" |
| end |
|
|
| test "displays info about the enterprise plan to upgrade to", %{conn: conn} do |
| doc = |
| conn |
| |> get(Routes.billing_path(conn, :upgrade_to_enterprise_plan)) |
| |> html_response(200) |
|
|
| assert doc =~ ~r/Up to\s*<b>\s*50M\s*<\/b>\s*monthly pageviews/ |
| assert doc =~ ~r/Up to\s*<b>\s*20k\s*<\/b>\s*sites/ |
| assert doc =~ ~r/Up to\s*<b>\s*5k\s*<\/b>\s*hourly api requests/ |
| assert doc =~ ~r/The plan is priced at\s*<b>\s*€123\s*<\/b>\s*/ |
| assert doc =~ "per year" |
| end |
|
|
| test "data-product attribute on the checkout link is the paddle_plan_id of the enterprise plan", |
| %{conn: conn, user: user} do |
| {:ok, team} = Plausible.Teams.get_by_owner(user) |
|
|
| doc = |
| conn |
| |> get(Routes.billing_path(conn, :upgrade_to_enterprise_plan)) |
| |> html_response(200) |
|
|
| assert %{ |
| "disableLogout" => true, |
| "email" => user.email, |
| "passthrough" => "ee:#{ee?()};user:#{user.id};team:#{team.id}", |
| "product" => @configured_enterprise_plan_paddle_plan_id, |
| "success" => Routes.billing_path(PlausibleWeb.Endpoint, :upgrade_success), |
| "theme" => "none" |
| } == get_paddle_checkout_params(find(doc, "#paddle-button")) |
| end |
| end |
|
|
| describe "GET /upgrade-to-enterprise-plan (active subscription, new enterprise plan configured)" do |
| setup [:create_user, :log_in, :subscribe_enterprise, :configure_enterprise_plan] |
|
|
| test "displays basic page content", %{conn: conn} do |
| doc = |
| conn |
| |> get(Routes.billing_path(conn, :upgrade_to_enterprise_plan)) |
| |> html_response(200) |
|
|
| assert doc =~ "Change subscription plan" |
| assert doc =~ "prepared your account for an upgrade to custom limits" |
| assert doc =~ "+ VAT if applicable" |
| assert doc =~ "calculate the prorated amount that your card will be charged" |
| assert doc =~ "Preview changes" |
| assert doc =~ "Questions?" |
| assert doc =~ "Contact us" |
| end |
|
|
| test "displays info about the enterprise plan to upgrade to", %{conn: conn} do |
| doc = |
| conn |
| |> get(Routes.billing_path(conn, :upgrade_to_enterprise_plan)) |
| |> html_response(200) |
|
|
| assert doc =~ ~r/Up to\s*<b>\s*50M\s*<\/b>\s*monthly pageviews/ |
| assert doc =~ ~r/Up to\s*<b>\s*20k\s*<\/b>\s*sites/ |
| assert doc =~ ~r/Up to\s*<b>\s*5k\s*<\/b>\s*hourly api requests/ |
| assert doc =~ ~r/The plan is priced at\s*<b>\s*€123\s*<\/b>\s*/ |
| assert doc =~ "per year" |
| end |
|
|
| test "preview changes links to :change_plan_preview action", %{conn: conn} do |
| doc = |
| conn |
| |> get(Routes.billing_path(conn, :upgrade_to_enterprise_plan)) |
| |> html_response(200) |
|
|
| preview_changes_link = find(doc, "#preview-changes") |
| assert text(preview_changes_link) == "Preview changes" |
|
|
| assert text_of_attr(preview_changes_link, "href") == |
| Routes.billing_path( |
| PlausibleWeb.Endpoint, |
| :change_plan_preview, |
| @configured_enterprise_plan_paddle_plan_id |
| ) |
| end |
| end |
|
|
| describe "GET /billing/change-plan/preview/:plan_id" do |
| setup [:create_user, :log_in] |
|
|
| test "renders preview information about the plan change", %{conn: conn, user: user} do |
| subscribe_to_plan(user, @v4_growth_plan) |
|
|
| html_response = |
| conn |
| |> get(Routes.billing_path(conn, :change_plan_preview, @v4_business_plan)) |
| |> html_response(200) |
|
|
| assert html_response =~ |
| "Your card will be charged a pro-rated amount for the current billing period" |
|
|
| assert html_response =~ "€-72.6" |
| assert html_response =~ "Back" |
| assert html_response =~ ~s[<a href="/billing/choose-plan"] |
| end |
|
|
| test "flashes error and redirects to choose-plan page", %{conn: conn, user: user} do |
| |
| new_site(owner: user) |
| conn = get(conn, Routes.billing_path(conn, :change_plan_preview, @v4_business_plan)) |
|
|
| assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ |
| "Something went wrong with loading your plan change information. Please try again, or contact us at support@plausible.io if the issue persists." |
|
|
| assert redirected_to(conn) == Routes.billing_path(conn, :choose_plan) |
| end |
| end |
|
|
| describe "GET /upgrade-to-enterprise-plan (already subscribed to latest enterprise plan)" do |
| setup [:create_user, :log_in, :configure_enterprise_plan] |
|
|
| setup context do |
| subscribe_enterprise(context, paddle_plan_id: @configured_enterprise_plan_paddle_plan_id) |
| end |
|
|
| test "renders contact note", %{conn: conn} do |
| doc = |
| conn |
| |> get(Routes.billing_path(conn, :upgrade_to_enterprise_plan)) |
| |> html_response(200) |
|
|
| assert doc =~ "Looking to adjust your plan?" |
| assert doc =~ "You're currently on a custom plan." |
|
|
| assert LazyHTML.text(LazyHTML.from_document(doc)) =~ |
| "please contact us at hello@plausible.io" |
| end |
| end |
|
|
| describe "GET /upgrade-to-enterprise-plan (subscription past_due or paused)" do |
| setup [:create_user, :log_in, :configure_enterprise_plan] |
|
|
| test "redirects to /settings when past_due", %{conn: conn} = context do |
| subscribe_enterprise(context, status: Subscription.Status.past_due()) |
| conn = get(conn, Routes.billing_path(conn, :upgrade_to_enterprise_plan)) |
| assert redirected_to(conn) == Routes.settings_path(conn, :subscription) |
| end |
|
|
| test "redirects to /settings when paused", %{conn: conn} = context do |
| subscribe_enterprise(context, status: Subscription.Status.paused()) |
| conn = get(conn, Routes.billing_path(conn, :upgrade_to_enterprise_plan)) |
| assert redirected_to(conn) == Routes.settings_path(conn, :subscription) |
| end |
| end |
|
|
| describe "GET /upgrade-to-enterprise-plan (deleted enterprise subscription)" do |
| setup [:create_user, :log_in, :configure_enterprise_plan] |
|
|
| setup context do |
| subscribe_enterprise(context, |
| paddle_plan_id: @configured_enterprise_plan_paddle_plan_id, |
| status: Subscription.Status.deleted() |
| ) |
|
|
| context |
| end |
|
|
| test "displays the same content as for a user without a subscription", %{conn: conn} do |
| doc = |
| conn |
| |> get(Routes.billing_path(conn, :upgrade_to_enterprise_plan)) |
| |> html_response(200) |
|
|
| assert doc =~ "Upgrade to Enterprise" |
| assert doc =~ "prepared a custom enterprise plan for your account with the following limits" |
| assert doc =~ "Questions?" |
| assert doc =~ "Contact us" |
| assert doc =~ "+ VAT if applicable" |
| assert doc =~ "Click the button below to upgrade" |
| assert doc =~ "Pay securely via Paddle" |
| end |
|
|
| test "still allows to subscribe back to the same plan", %{conn: conn} do |
| doc = |
| conn |
| |> get(Routes.billing_path(conn, :upgrade_to_enterprise_plan)) |
| |> html_response(200) |
|
|
| assert doc =~ ~r/Up to\s*<b>\s*50M\s*<\/b>\s*monthly pageviews/ |
| assert doc =~ ~r/Up to\s*<b>\s*20k\s*<\/b>\s*sites/ |
| assert doc =~ ~r/Up to\s*<b>\s*5k\s*<\/b>\s*hourly api requests/ |
| assert doc =~ ~r/The plan is priced at\s*<b>\s*€123\s*<\/b>\s*/ |
| assert doc =~ "per year" |
| end |
|
|
| test "renders paddle button with the correct checkout params", |
| %{conn: conn, user: user} do |
| {:ok, team} = Plausible.Teams.get_by_owner(user) |
|
|
| doc = |
| conn |
| |> get(Routes.billing_path(conn, :upgrade_to_enterprise_plan)) |
| |> html_response(200) |
|
|
| assert %{ |
| "disableLogout" => true, |
| "email" => user.email, |
| "passthrough" => "ee:#{ee?()};user:#{user.id};team:#{team.id}", |
| "product" => @configured_enterprise_plan_paddle_plan_id, |
| "success" => Routes.billing_path(PlausibleWeb.Endpoint, :upgrade_success), |
| "theme" => "none" |
| } == get_paddle_checkout_params(find(doc, "#paddle-button")) |
| end |
| end |
|
|
| defp configure_enterprise_plan(%{user: user}) do |
| 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: 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 = Keyword.put_new(opts, :status, Subscription.Status.active()) |
|
|
| user = subscribe_to_plan(user, paddle_plan_id, opts) |
|
|
| {:ok, user: user} |
| end |
|
|
| defp get_paddle_checkout_params(element) do |
| with onclick <- text_of_attr(element, "onclick"), |
| [[_, checkout_params_str]] <- Regex.scan(~r/Paddle\.Checkout\.open\((.*?)\)/, onclick), |
| {:ok, checkout_params} <- Jason.decode(checkout_params_str) do |
| checkout_params |
| end |
| end |
| end |
|
|