Gate access if trial ends or plan is canceled

This commit is contained in:
Alexandre de Souza 2025-08-20 14:18:29 -03:00
parent 02deec7f13
commit 9d4a3a1c74
No known key found for this signature in database
GPG key ID: E39228FFBA346545
4 changed files with 66 additions and 4 deletions

View file

@ -346,7 +346,7 @@ defmodule Livebook.Teams.Requests do
{:ok, %{status: status} = response} when status in 200..299 -> {:ok, %{status: status} = response} when status in 200..299 ->
{:ok, response.body} {: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) return_error(response)
{:ok, %{status: 401, private: %{livebook_app_deployment: true}}} -> {:ok, %{status: 401, private: %{livebook_app_deployment: true}}} ->

View file

@ -34,7 +34,20 @@ defmodule Livebook.ZTA.LivebookTeams do
team = Livebook.Hubs.fetch_hub!(id) team = Livebook.Hubs.fetch_hub!(id)
if Livebook.Hubs.TeamClient.identity_enabled?(team.id) do 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 else
{conn, %{}} {conn, %{}}
end end
@ -157,7 +170,19 @@ defmodule Livebook.ZTA.LivebookTeams do
{halt(conn), nil} {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 {conn
|> put_session(:teams_error, true) |> put_session(:teams_error, true)
|> redirect(to: conn.request_path) |> redirect(to: conn.request_path)
@ -199,4 +224,16 @@ defmodule Livebook.ZTA.LivebookTeams do
payload: payload payload: payload
} }
end 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 end

View file

@ -6,7 +6,7 @@ defmodule LivebookWeb.Integration.AdminLiveTest do
@moduletag teams_for: :agent @moduletag teams_for: :agent
setup :teams 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] @moduletag subscribe_to_teams_topics: [:clients, :agents, :app_deployments, :app_server]
describe "authorization" do describe "authorization" do
@ -155,6 +155,27 @@ defmodule LivebookWeb.Integration.AdminLiveTest do
{:ok, view, _html} = live(conn, ~p"/settings") {:ok, view, _html} = live(conn, ~p"/settings")
assert render(view) =~ "System settings" assert render(view) =~ "System settings"
end 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 end
describe "topbar" do describe "topbar" do

View file

@ -156,6 +156,10 @@ defmodule Livebook.TeamsRPC do
:erpc.call(node, TeamsRPC, :update_deployment_group, [deployment_group, attrs]) :erpc.call(node, TeamsRPC, :update_deployment_group, [deployment_group, attrs])
end end
def update_billing_subscription(node, subscription, attrs) do
:erpc.call(node, TeamsRPC, :update_billing_subscription, [subscription, attrs])
end
# Delete resource # Delete resource
def delete_user_org(node, user_id, org_id) do def delete_user_org(node, user_id, org_id) do