From 09f0402387be05da60517db9818b822f61b61e2f Mon Sep 17 00:00:00 2001 From: Andrew Dryga Date: Wed, 24 Apr 2024 15:45:32 -0600 Subject: [PATCH] feat(portal): Add legal_name field to accounts and sync it with new stripe metadata key (#4771) Closes #4761 --- .../domain/lib/domain/accounts/account.ex | 2 + .../lib/domain/accounts/account/changeset.ex | 14 +++- elixir/apps/domain/lib/domain/billing.ex | 19 ++++-- .../lib/domain/billing/event_handler.ex | 7 +- .../lib/domain/billing/stripe/api_client.ex | 38 +++++------ ...20240424175929_add_accounts_legal_name.exs | 13 ++++ .../apps/domain/test/domain/billing_test.exs | 66 ++++++++----------- .../domain/test/support/fixtures/accounts.ex | 1 + .../apps/web/lib/web/live/settings/billing.ex | 7 ++ 9 files changed, 102 insertions(+), 65 deletions(-) create mode 100644 elixir/apps/domain/priv/repo/migrations/20240424175929_add_accounts_legal_name.exs diff --git a/elixir/apps/domain/lib/domain/accounts/account.ex b/elixir/apps/domain/lib/domain/accounts/account.ex index d7c16a73a..1da998e14 100644 --- a/elixir/apps/domain/lib/domain/accounts/account.ex +++ b/elixir/apps/domain/lib/domain/accounts/account.ex @@ -5,6 +5,8 @@ defmodule Domain.Accounts.Account do field :name, :string field :slug, :string + field :legal_name, :string + # Updated by the billing subscription metadata fields embeds_one :features, Domain.Accounts.Features, on_replace: :delete embeds_one :limits, Domain.Accounts.Limits, on_replace: :delete diff --git a/elixir/apps/domain/lib/domain/accounts/account/changeset.ex b/elixir/apps/domain/lib/domain/accounts/account/changeset.ex index a5e89aaca..0c237218d 100644 --- a/elixir/apps/domain/lib/domain/accounts/account/changeset.ex +++ b/elixir/apps/domain/lib/domain/accounts/account/changeset.ex @@ -16,14 +16,15 @@ defmodule Domain.Accounts.Account.Changeset do def create(attrs) do %Account{} - |> cast(attrs, [:name, :slug]) + |> cast(attrs, [:name, :legal_name, :slug]) |> changeset() end def update_profile_and_config(%Account{} = account, attrs) do account - |> cast(attrs, [:name]) + |> cast(attrs, [:name, :legal_name]) |> validate_name() + |> validate_legal_name() |> cast_embed(:config, with: &Config.Changeset.changeset/2) end @@ -32,6 +33,7 @@ defmodule Domain.Accounts.Account.Changeset do |> cast(attrs, [ :slug, :name, + :legal_name, :disabled_reason, :disabled_at, :warning, @@ -44,6 +46,7 @@ defmodule Domain.Accounts.Account.Changeset do defp changeset(changeset) do changeset |> validate_name() + |> validate_legal_name() |> validate_slug() |> prepare_changes(&put_default_slug/1) |> cast_embed(:config, with: &Config.Changeset.changeset/2) @@ -59,6 +62,13 @@ defmodule Domain.Accounts.Account.Changeset do |> validate_length(:name, min: 3, max: 64) end + defp validate_legal_name(changeset) do + changeset + |> put_default_value(:legal_name, from: :name) + |> trim_change(:legal_name) + |> validate_length(:legal_name, min: 1, max: 255) + end + defp validate_slug(changeset) do changeset |> validate_length(:slug, min: 3, max: 100) diff --git a/elixir/apps/domain/lib/domain/billing.ex b/elixir/apps/domain/lib/domain/billing.ex index 8da59ba20..ad85deaf1 100644 --- a/elixir/apps/domain/lib/domain/billing.ex +++ b/elixir/apps/domain/lib/domain/billing.ex @@ -111,9 +111,15 @@ defmodule Domain.Billing do email = get_customer_email(account) with {:ok, %{"id" => customer_id, "email" => customer_email}} <- - APIClient.create_customer(secret_key, account.id, account.name, account.slug, email) do + APIClient.create_customer(secret_key, account.legal_name, email, %{ + account_id: account.id, + account_name: account.name, + account_slug: account.slug + }) do Accounts.update_account(account, %{ - metadata: %{stripe: %{customer_id: customer_id, billing_email: customer_email}} + metadata: %{ + stripe: %{customer_id: customer_id, billing_email: customer_email} + } }) else {:ok, {status, body}} -> @@ -146,9 +152,12 @@ defmodule Domain.Billing do APIClient.update_customer( secret_key, customer_id, - account.id, - account.name, - account.slug + account.legal_name, + %{ + account_id: account.id, + account_name: account.name, + account_slug: account.slug + } ) do {:ok, account} else diff --git a/elixir/apps/domain/lib/domain/billing/event_handler.ex b/elixir/apps/domain/lib/domain/billing/event_handler.ex index fedfd458b..e220349d1 100644 --- a/elixir/apps/domain/lib/domain/billing/event_handler.ex +++ b/elixir/apps/domain/lib/domain/billing/event_handler.ex @@ -31,7 +31,8 @@ defmodule Domain.Billing.EventHandler do }) do attrs = %{ - name: customer_name, + name: customer_metadata["account_name"] || customer_name, + legal_name: customer_name, metadata: %{stripe: %{billing_email: customer_email}} } |> put_if_not_nil(:slug, customer_metadata["account_slug"]) @@ -239,7 +240,8 @@ defmodule Domain.Billing.EventHandler do end attrs = %{ - name: customer_name, + name: metadata["account_name"] || customer_name, + legal_name: customer_name, slug: account_slug, metadata: %{ stripe: %{ @@ -340,6 +342,7 @@ defmodule Domain.Billing.EventHandler do subscription_metadata, stripe_metadata_overrides ) do + # feature_fields = Accounts.Features.__schema__(:fields) |> Enum.map(&to_string/1) limit_fields = Accounts.Limits.__schema__(:fields) |> Enum.map(&to_string/1) metadata_fields = ["support_type"] diff --git a/elixir/apps/domain/lib/domain/billing/stripe/api_client.ex b/elixir/apps/domain/lib/domain/billing/stripe/api_client.ex index 3a1343792..d9e227208 100644 --- a/elixir/apps/domain/lib/domain/billing/stripe/api_client.ex +++ b/elixir/apps/domain/lib/domain/billing/stripe/api_client.ex @@ -25,31 +25,31 @@ defmodule Domain.Billing.Stripe.APIClient do [conn_opts: [transport_opts: transport_opts]] end - def create_customer(api_token, id, name, slug, email) do + def create_customer(api_token, name, email, metadata) do + metadata_params = + for {key, value} <- metadata, into: %{} do + {"metadata[#{key}]", value} + end + body = - URI.encode_query( - %{ - "name" => name, - "metadata[account_id]" => id, - "metadata[account_slug]" => slug - } - |> put_if_not_nil("email", email), - :www_form - ) + metadata_params + |> Map.put("name", name) + |> put_if_not_nil("email", email) + |> URI.encode_query(:www_form) request(api_token, :post, "customers", body) end - def update_customer(api_token, customer_id, id, name, slug) do + def update_customer(api_token, customer_id, name, metadata) do + metadata_params = + for {key, value} <- metadata, into: %{} do + {"metadata[#{key}]", value} + end + body = - URI.encode_query( - %{ - "name" => name, - "metadata[account_id]" => id, - "metadata[account_slug]" => slug - }, - :www_form - ) + metadata_params + |> Map.put("name", name) + |> URI.encode_query(:www_form) request(api_token, :post, "customers/#{customer_id}", body) end diff --git a/elixir/apps/domain/priv/repo/migrations/20240424175929_add_accounts_legal_name.exs b/elixir/apps/domain/priv/repo/migrations/20240424175929_add_accounts_legal_name.exs new file mode 100644 index 000000000..5c8d0c650 --- /dev/null +++ b/elixir/apps/domain/priv/repo/migrations/20240424175929_add_accounts_legal_name.exs @@ -0,0 +1,13 @@ +defmodule Domain.Repo.Migrations.AddAccountsLegalName do + use Ecto.Migration + + def change do + alter table(:accounts) do + add(:legal_name, :string) + end + + execute("UPDATE accounts SET legal_name = name") + + execute("ALTER TABLE accounts ALTER COLUMN legal_name SET NOT NULL") + end +end diff --git a/elixir/apps/domain/test/domain/billing_test.exs b/elixir/apps/domain/test/domain/billing_test.exs index 555c4b70a..b541ce429 100644 --- a/elixir/apps/domain/test/domain/billing_test.exs +++ b/elixir/apps/domain/test/domain/billing_test.exs @@ -375,9 +375,10 @@ defmodule Domain.BillingTest do assert_receive {:bypass_request, %{request_path: "/v1/customers"} = conn} assert conn.params == %{ - "name" => account.name, + "name" => account.legal_name, "metadata" => %{ "account_id" => account.id, + "account_name" => account.name, "account_slug" => account.slug } } @@ -527,6 +528,8 @@ defmodule Domain.BillingTest do assert handle_events([event]) == :ok assert account = Repo.get_by(Domain.Accounts.Account, slug: "bigcompany") + assert account.name == "New Account Name" + assert account.legal_name == "New Account Name" assert account.metadata.stripe.customer_id == customer_id assert account.metadata.stripe.billing_email == "iown@bigcompany.com" assert account.metadata.stripe.subscription_id @@ -545,8 +548,12 @@ defmodule Domain.BillingTest do %{request_path: "/v1/customers/" <> ^customer_id, params: params}} assert params == %{ - "metadata" => %{"account_id" => account.id, "account_slug" => account.slug}, - "name" => "New Account Name" + "metadata" => %{ + "account_id" => account.id, + "account_name" => account.name, + "account_slug" => account.slug + }, + "name" => account.legal_name } assert_receive {:bypass_request, %{request_path: "/v1/subscriptions", params: params}} @@ -647,6 +654,8 @@ defmodule Domain.BillingTest do assert handle_events([event]) == :ok assert account = Repo.get_by(Domain.Accounts.Account, slug: "bigcompany") + assert account.name == "New Account Name" + assert account.legal_name == "New Account Name" assert account.metadata.stripe.customer_id == customer_id assert account.metadata.stripe.billing_email == "iown@bigcompany.com" assert account.metadata.stripe.subscription_id @@ -669,8 +678,12 @@ defmodule Domain.BillingTest do }} assert params == %{ - "metadata" => %{"account_id" => account.id, "account_slug" => account.slug}, - "name" => "New Account Name" + "metadata" => %{ + "account_id" => account.id, + "account_name" => account.name, + "account_slug" => account.slug + }, + "name" => account.legal_name } assert_receive {:bypass_request, @@ -686,38 +699,6 @@ defmodule Domain.BillingTest do } end - test "updates an account from stripe on customer.updated event", %{account: account} do - customer_metadata = %{ - "account_id" => account.id, - "account_slug" => "this_is_a_new_slug" - } - - Bypass.open() - |> Stripe.mock_fetch_customer_endpoint(account, %{ - "metadata" => customer_metadata - }) - |> Stripe.mock_update_customer_endpoint(account) - - event = - Stripe.build_event( - "customer.updated", - Stripe.customer_object( - account.metadata.stripe.customer_id, - "Updated Account Name", - "iown@bigcompany.com", - customer_metadata - ) - ) - - assert handle_events([event]) == :ok - - assert account = Repo.one(Domain.Accounts.Account) - assert account.name == "Updated Account Name" - assert account.slug == "this_is_a_new_slug" - - assert account.metadata.stripe.billing_email == "iown@bigcompany.com" - end - test "disables the account on when subscription is deleted", %{ account: account, customer_id: customer_id @@ -788,6 +769,17 @@ defmodule Domain.BillingTest do account: account, customer_id: customer_id } do + account = + Fixtures.Accounts.update_account(account, %{ + limits: %{ + service_accounts_count: 10101 + }, + features: %{ + flow_activities: true, + traffic_filters: true + } + }) + Bypass.open() |> Stripe.mock_fetch_customer_endpoint(account) |> Stripe.mock_fetch_product_endpoint("prod_Na6dGcTsmU0I4R", %{ diff --git a/elixir/apps/domain/test/support/fixtures/accounts.ex b/elixir/apps/domain/test/support/fixtures/accounts.ex index 92599f9c4..4308f46ba 100644 --- a/elixir/apps/domain/test/support/fixtures/accounts.ex +++ b/elixir/apps/domain/test/support/fixtures/accounts.ex @@ -8,6 +8,7 @@ defmodule Domain.Fixtures.Accounts do Enum.into(attrs, %{ name: "acc-#{unique_num}", + legal_name: "l-acc-#{unique_num}", slug: "acc_#{unique_num}", config: %{ clients_upstream_dns: [ diff --git a/elixir/apps/web/lib/web/live/settings/billing.ex b/elixir/apps/web/lib/web/live/settings/billing.ex index a4800e5a8..6e7d7dc93 100644 --- a/elixir/apps/web/lib/web/live/settings/billing.ex +++ b/elixir/apps/web/lib/web/live/settings/billing.ex @@ -84,6 +84,13 @@ defmodule Web.Settings.Billing do <%= @account.metadata.stripe.billing_email %> + + <.vertical_table_row> + <:label>Billing Name + <:value> + <%= @account.legal_name %> + +