feat(portal): Add legal_name field to accounts and sync it with new stripe metadata key (#4771)

Closes #4761
This commit is contained in:
Andrew Dryga
2024-04-24 15:45:32 -06:00
committed by GitHub
parent 8b2306e5e6
commit 09f0402387
9 changed files with 102 additions and 65 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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"]

View File

@@ -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

View File

@@ -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

View File

@@ -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", %{

View File

@@ -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: [

View File

@@ -84,6 +84,13 @@ defmodule Web.Settings.Billing do
<%= @account.metadata.stripe.billing_email %>
</:value>
</.vertical_table_row>
<.vertical_table_row>
<:label>Billing Name</:label>
<:value>
<%= @account.legal_name %>
</:value>
</.vertical_table_row>
</.vertical_table>
</:content>
</.section>