diff --git a/lib/livebook/teams/requests.ex b/lib/livebook/teams/requests.ex index 121911155..d1cd96600 100644 --- a/lib/livebook/teams/requests.ex +++ b/lib/livebook/teams/requests.ex @@ -346,7 +346,7 @@ defmodule Livebook.Teams.Requests do {:ok, %{status: status} = response} when status in 200..299 -> {:ok, response.body} - {:ok, %{status: status} = response} when status in [410, 422] -> + {:ok, %{status: status} = response} when status in [402, 410, 422] -> return_error(response) {:ok, %{status: 401, private: %{livebook_app_deployment: true}}} -> diff --git a/lib/livebook/zta/livebook_teams.ex b/lib/livebook/zta/livebook_teams.ex index 15ff3e4b8..5534280f9 100644 --- a/lib/livebook/zta/livebook_teams.ex +++ b/lib/livebook/zta/livebook_teams.ex @@ -34,7 +34,20 @@ defmodule Livebook.ZTA.LivebookTeams do team = Livebook.Hubs.fetch_hub!(id) if Livebook.Hubs.TeamClient.identity_enabled?(team.id) do - handle_request(conn, team, conn.params) + if Livebook.Hubs.TeamClient.billing_good_standing?(team.id) do + handle_request(conn, team, conn.params) + else + {conn + |> put_status(:payment_required) + |> put_view(LivebookWeb.ErrorHTML) + |> put_root_layout(false) + |> render("error.html", %{ + status: 402, + title: billing_status_title(team.billing_status.type), + details: billing_status_message(team.billing_status.type) + }) + |> halt(), nil} + end else {conn, %{}} end @@ -157,7 +170,19 @@ defmodule Livebook.ZTA.LivebookTeams do {halt(conn), nil} - _ -> + {:error, %{"errors" => %{"detail" => "Payment Required"}}} -> + {conn + |> put_status(:payment_required) + |> put_view(LivebookWeb.ErrorHTML) + |> put_root_layout(false) + |> render("error.html", %{ + status: 402, + title: billing_status_title(team.billing_status.type), + details: billing_status_message(team.billing_status.type) + }) + |> halt(), nil} + + _otherwise -> {conn |> put_session(:teams_error, true) |> redirect(to: conn.request_path) @@ -199,4 +224,16 @@ defmodule Livebook.ZTA.LivebookTeams do payload: payload } end + + # TODO: this is just a placeholder, so the copy must be improved + defp billing_status_title(:canceled), do: "Your plan has been canceled." + defp billing_status_title(:trial_ended), do: "Your trial has ended." + + defp billing_status_message(:canceled) do + "An admin of your organization needs to subscribe again to a paid plan." + end + + defp billing_status_message(:trial_ended) do + "An admin of your organization needs to subscribe to a paid plan." + end end diff --git a/test/livebook_teams/web/admin_live_test.exs b/test/livebook_teams/web/admin_live_test.exs index 1aecdcfed..b85f1947d 100644 --- a/test/livebook_teams/web/admin_live_test.exs +++ b/test/livebook_teams/web/admin_live_test.exs @@ -6,7 +6,7 @@ defmodule LivebookWeb.Integration.AdminLiveTest do @moduletag teams_for: :agent setup :teams - @moduletag subscribe_to_hubs_topics: [:connection] + @moduletag subscribe_to_hubs_topics: [:connection, :crud] @moduletag subscribe_to_teams_topics: [:clients, :agents, :app_deployments, :app_server] describe "authorization" do @@ -155,6 +155,27 @@ defmodule LivebookWeb.Integration.AdminLiveTest do {:ok, view, _html} = live(conn, ~p"/settings") assert render(view) =~ "System settings" end + + test "renders payment required if org is in bad standing", + %{conn: conn, node: node, subscription: subscription, team: %{id: id}} do + # Assert the event from test setup + assert_receive {:hub_changed, ^id}, 3_000 + + # Then, assert the event sent after receiving the connected event, + # so we can assert when the `{:org_updated, org_updated}` event + # is sent to Team client. + assert_receive {:hub_changed, ^id}, 3_000 + + {:ok, view, _html} = live(conn, ~p"/settings") + assert render(view) =~ "System settings" + + TeamsRPC.update_billing_subscription(node, subscription, stripe_status: :canceled) + assert_receive {:hub_changed, ^id}, 3_000 + + assert conn + |> get(~p"/") + |> html_response(402) =~ "Your plan has been canceled." + end end describe "topbar" do diff --git a/test/support/integration/teams_rpc.ex b/test/support/integration/teams_rpc.ex index d6e5dc136..244344a42 100644 --- a/test/support/integration/teams_rpc.ex +++ b/test/support/integration/teams_rpc.ex @@ -156,6 +156,10 @@ defmodule Livebook.TeamsRPC do :erpc.call(node, TeamsRPC, :update_deployment_group, [deployment_group, attrs]) end + def update_billing_subscription(node, subscription, attrs) do + :erpc.call(node, TeamsRPC, :update_billing_subscription, [subscription, attrs]) + end + # Delete resource def delete_user_org(node, user_id, org_id) do