defmodule PlausibleWeb.BillingController do use PlausibleWeb, :controller use Plausible.Repo require Logger require Plausible.Billing.Subscription.Status alias Plausible.Billing alias Plausible.Billing.Subscription plug PlausibleWeb.RequireAccountPlug plug Plausible.Plugs.AuthorizeTeamAccess, [:owner, :billing] def ping_subscription(%Plug.Conn{} = conn, _params) do subscribed? = Plausible.Teams.Billing.has_active_subscription?(conn.assigns.current_team) json(conn, %{is_subscribed: subscribed?}) end def choose_plan(conn, _params) do team = conn.assigns.current_team if Plausible.Teams.Billing.enterprise_configured?(team) do redirect(conn, to: Routes.billing_path(conn, :upgrade_to_enterprise_plan)) else render(conn, "choose_plan.html", live_module: PlausibleWeb.Live.ChoosePlan, hide_header?: true, disable_global_notices?: true, skip_plausible_tracking: true, connect_live_socket: true ) end end def upgrade_to_enterprise_plan(conn, _params) do team = conn.assigns.current_team subscription = Plausible.Teams.Billing.get_subscription(team) {latest_enterprise_plan, price} = Plausible.Teams.Billing.latest_enterprise_plan_with_price( team, PlausibleWeb.RemoteIP.get(conn) ) subscription_resumable? = Plausible.Billing.Subscriptions.resumable?(subscription) subscribed_to_latest? = subscription_resumable? && subscription.paddle_plan_id == latest_enterprise_plan.paddle_plan_id cond do Subscription.Status.in?(subscription, [ Subscription.Status.past_due(), Subscription.Status.paused() ]) -> redirect(conn, to: Routes.settings_path(conn, :subscription)) subscribed_to_latest? -> render(conn, "change_enterprise_plan_contact_us.html", skip_plausible_tracking: true) true -> render(conn, "upgrade_to_enterprise_plan.html", latest_enterprise_plan: latest_enterprise_plan, price: price, subscription_resumable: subscription_resumable?, contact_link: "https://plausible.io/contact", skip_plausible_tracking: true ) end end def upgrade_success(conn, _params) do render(conn, "upgrade_success.html", disable_global_notices?: true) end def change_plan_preview(conn, %{"plan_id" => new_plan_id}) do team = conn.assigns.current_team current_user = conn.assigns.current_user subscription = Plausible.Teams.Billing.active_subscription_for(team) 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), skip_plausible_tracking: true, subscription: subscription, preview_info: preview_info ) _ -> msg = "Something went wrong with loading your plan change information. Please try again, or contact us at support@plausible.io if the issue persists." Sentry.capture_message("Error loading change plan preview", extra: %{ message: msg, new_plan_id: new_plan_id, team_id: team.id, user_id: current_user.id } ) conn |> put_flash(:error, msg) |> redirect(to: Routes.billing_path(conn, :choose_plan)) end end def change_plan(conn, %{"new_plan_id" => new_plan_id}) do team = conn.assigns.current_team case Plausible.Teams.Billing.change_plan(team, new_plan_id) do {:ok, _subscription} -> conn |> put_flash(:success, "Plan changed successfully") |> redirect(to: Routes.settings_path(conn, :subscription)) {:error, e} -> msg = case e do {:over_plan_limits, exceeded_limits} -> "Unable to subscribe to this plan because the following limits are exceeded: #{PlausibleWeb.TextHelpers.pretty_list(exceeded_limits)}" %{"code" => 147} -> # https://developer.paddle.com/api-reference/intro/api-error-codes "We were unable to charge your card. Click 'update billing info' to update your payment details and try again." %{"message" => msg} when not is_nil(msg) -> msg _ -> "Something went wrong. Please try again or contact support at support@plausible.io" end Sentry.capture_message("Error changing plans", extra: %{ errors: inspect(e), message: msg, new_plan_id: new_plan_id, team_id: team.id, user_id: conn.assigns.current_user.id } ) conn |> put_flash(:error, msg) |> redirect(to: Routes.settings_path(conn, :subscription)) end end defp preview_subscription(nil, _new_plan_id), do: {: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