diff --git a/lib/code_corps/stripe_service/adapters/stripe_connect_account.ex b/lib/code_corps/stripe_service/adapters/stripe_connect_account.ex index f329fa598..351eedd47 100644 --- a/lib/code_corps/stripe_service/adapters/stripe_connect_account.ex +++ b/lib/code_corps/stripe_service/adapters/stripe_connect_account.ex @@ -1,23 +1,79 @@ defmodule CodeCorps.StripeService.Adapters.StripeConnectAccountAdapter do - import CodeCorps.MapUtils, only: [rename: 3, keys_to_string: 1] + import CodeCorps.MapUtils, only: [keys_to_string: 1] + import CodeCorps.StripeService.Util, only: [transform_map: 2] @stripe_attributes [ :business_name, :business_url, :charges_enabled, :country, :default_currency, :details_submitted, :email, :id, :managed, :support_email, :support_phone, :support_url, :transfers_enabled ] + @doc """ + Mapping of stripe record attributes to locally stored attributes + Format is {:local_key, [:nesting, :of, :stripe, :keys]} + """ + @stripe_mapping [ + {:id_from_stripe, [:id]}, + {:business_name, [:business_name]}, + {:business_url, [:business_url]}, + {:charges_enabled, [:charges_enabled]}, + {:country, [:country]}, + {:default_currency, [:default_currency]}, + {:details_submitted, [:details_submitted]}, + {:display_name, [:display_name]}, + {:email, [:email]}, + {:legal_entity_address_city, [:legal_entity, :address, :city]}, + {:legal_entity_address_country, [:legal_entity, :address, :country]}, + {:legal_entity_address_line1, [:legal_entity, :address, :line1]}, + {:legal_entity_address_line2, [:legal_entity, :address, :line2]}, + {:legal_entity_address_postal_code, [:legal_entity, :address, :postal_code]}, + {:legal_entity_address_state, [:legal_entity, :address, :state]}, + {:legal_entity_business_name, [:legal_entity, :business_name]}, + {:legal_entity_business_tax_id_provided, [:legal_entity, :business_tax_id_provided]}, + {:legal_entity_business_vat_id_provided, [:legal_entity, :business_vat_id_provided]}, + {:legal_entity_dob_day, [:legal_entity, :dob, :day]}, + {:legal_entity_dob_month, [:legal_entity, :dob, :month]}, + {:legal_entity_dob_year, [:legal_entity, :dob, :year]}, + {:legal_entity_first_name, [:legal_entity, :first_name]}, + {:legal_entity_last_name, [:legal_entity, :last_name]}, + {:legal_entity_gender, [:legal_entity, :gender]}, + {:legal_entity_maiden_name, [:legal_entity, :maiden_name]}, + {:legal_entity_personal_address_city, [:legal_entity, :personal_address, :city]}, + {:legal_entity_personal_address_country, [:legal_entity, :personal_address, :country]}, + {:legal_entity_personal_address_line1, [:legal_entity, :personal_address, :line1]}, + {:legal_entity_personal_address_line2, [:legal_entity, :personal_address, :line2]}, + {:legal_entity_personal_address_postal_code, [:legal_entity, :personal_address, :postal_code]}, + {:legal_entity_personal_address_state, [:legal_entity, :personal_address, :state]}, + {:legal_entity_phone_number, [:legal_entity, :phone_number]}, + {:legal_entity_personal_id_number_provided, [:legal_entity, :personal_id_number_provided]}, + {:legal_entity_ssn_last_4_provided, [:legal_entity, :ssn_last_4_provided]}, + {:legal_entity_type, [:legal_entity, :type]}, + {:managed, [:managed]}, + {:support_email, [:support_email]}, + {:support_phone, [:support_phone]}, + {:support_url, [:support_url]}, + {:transfers_enabled, [:transfers_enabled]}, + {:verification_disabled_reason, [:verification, :disabled_reason]}, + {:verification_due_by, [:verification, :due_by]}, + {:verification_fields_needed, [:verification, :fields_needed]} + ] + + @doc """ + Transforms a `%Stripe.Account{}` and a set of local attributes into a + map of parameters used to create or update a `StripeConnectAccount` record. + """ def to_params(%Stripe.Account{} = stripe_account, %{} = attributes) do result = stripe_account |> Map.from_struct - |> Map.take(@stripe_attributes) - |> rename(:id, :id_from_stripe) + |> transform_map(@stripe_mapping) |> keys_to_string |> add_non_stripe_attributes(attributes) {:ok, result} end + # Names of attributes which we need to store localy, + # but are not part of the Stripe API record @non_stripe_attributes ["organization_id"] defp add_non_stripe_attributes(%{} = params, %{} = attributes) do diff --git a/lib/code_corps/stripe_service/util.ex b/lib/code_corps/stripe_service/util.ex new file mode 100644 index 000000000..27066a769 --- /dev/null +++ b/lib/code_corps/stripe_service/util.ex @@ -0,0 +1,26 @@ +defmodule CodeCorps.StripeService.Util do + @moduledoc """ + Utility functions for handling the Stripe API. + """ + + @doc """ + Takes a source map and a list of tuples representing how the source map + should be transformed into a new map, then applies the mapping + operation on each field + """ + def transform_map(api_map, mapping), do: mapping |> Enum.reduce(%{}, &map_field(&1, &2, api_map)) + + # Takes a tuple which contains a target field and a source path, + # then puts value on the source path from the source map + # into to target map under the target field name. + # Example: + # + # - `source_map` is `%{path: %{to: %{field: some_value}} } + # - `source_path` is `[:path, :to, :field]` + # - `target_field` is `:path_to_field` + # - `some_value` will be put into `target_map`, under the key `:path_to_field` + defp map_field({target_field, source_path}, target_map, source_map) do + value = get_in(source_map, source_path) + target_map |> Map.put(target_field, value) + end +end diff --git a/mix.lock b/mix.lock index 9c951752f..aa70ce05e 100644 --- a/mix.lock +++ b/mix.lock @@ -55,7 +55,7 @@ "segment": {:git, "https://github.com/stueccles/analytics-elixir.git", "8fe520c16a8a9290d55c849bf4d67420396e1cdd", []}, "sentry": {:hex, :sentry, "2.0.2", "f08638758f7bf891e238466009f6cd702fc26d87286663af26927a78ed149346", [:mix], [{:hackney, "~> 1.6.1", [hex: :hackney, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: true]}, {:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}, {:uuid, "~> 1.0", [hex: :uuid, optional: false]}]}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:rebar, :make], []}, - "stripity_stripe": {:git, "https://github.com/code-corps/stripity_stripe.git", "df6c770882d74d76807e3aa91ba7ee2763a80e49", [branch: "2.0"]}, + "stripity_stripe": {:git, "https://github.com/code-corps/stripity_stripe.git", "b9c2262377bb0300423cc4d44e9d4414870bb0b4", [branch: "2.0"]}, "timber": {:hex, :timber, "0.4.7", "df3fcd79bcb4eb4b53874d906ef5f3a212937b4bc7b7c5b244745202cc389443", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: true]}, {:phoenix, "~> 1.2", [hex: :phoenix, optional: true]}, {:plug, "~> 1.2", [hex: :plug, optional: true]}, {:poison, "~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}]}, "timex": {:hex, :timex, "3.1.5", "413d6d8d6f0162a5d47080cb8ca520d790184ac43e097c95191c7563bf25b428", [:mix], [{:combine, "~> 0.7", [hex: :combine, optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, optional: false]}]}, "timex_ecto": {:hex, :timex_ecto, "3.0.5", "3ec6c25e10d2c0020958e5df64d2b5e690e441faa2c2259da8bc6bd3d7f39256", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: false]}, {:timex, "~> 3.0", [hex: :timex, optional: false]}]}, diff --git a/priv/repo/migrations/20161221085759_add_legal_entity_fields_to_stripe_connect_account.exs b/priv/repo/migrations/20161221085759_add_legal_entity_fields_to_stripe_connect_account.exs new file mode 100644 index 000000000..fe08fae27 --- /dev/null +++ b/priv/repo/migrations/20161221085759_add_legal_entity_fields_to_stripe_connect_account.exs @@ -0,0 +1,45 @@ +defmodule CodeCorps.Repo.Migrations.AddLegalEntityFieldsToStripeConnectAccount do + use Ecto.Migration + + def change do + alter table(:stripe_connect_accounts) do + add :legal_entity_address_city, :string + add :legal_entity_address_country, :string + add :legal_entity_address_line1, :string + add :legal_entity_address_line2, :string + add :legal_entity_address_postal_code, :string + add :legal_entity_address_state, :string + + add :legal_entity_business_name, :string + add :legal_entity_business_tax_id_provided, :boolean, default: false + add :legal_entity_business_vat_id_provided, :boolean, default: false + add :legal_entity_dob_day, :string + add :legal_entity_dob_month, :string + add :legal_entity_dob_year, :string + + add :legal_entity_first_name, :string + add :legal_entity_last_name, :string + add :legal_entity_gender, :string + add :legal_entity_maiden_name, :string + + add :legal_entity_personal_address_city, :string + add :legal_entity_personal_address_country, :string + add :legal_entity_personal_address_line1, :string + add :legal_entity_personal_address_line2, :string + add :legal_entity_personal_address_postal_code, :string + add :legal_entity_personal_address_state, :string + + add :legal_entity_phone_number, :string + + add :legal_entity_personal_id_number_provided, :boolean, default: false + add :legal_entity_ssn_last_4_provided, :boolean, default: false + + add :legal_entity_type, :string + + add :legal_entity_verification_details, :string + add :legal_entity_verification_details_code, :string + add :legal_entity_verification_document, :string + add :legal_entity_verification_status, :string + end + end +end diff --git a/test/controllers/stripe_connect_account_controller_test.exs b/test/controllers/stripe_connect_account_controller_test.exs index bfbef9cb8..37f500747 100644 --- a/test/controllers/stripe_connect_account_controller_test.exs +++ b/test/controllers/stripe_connect_account_controller_test.exs @@ -32,10 +32,7 @@ defmodule CodeCorps.StripeConnectAccountControllerTest do test "creates and renders resource user is authenticated and authorized", %{conn: conn, current_user: current_user} do organization = insert(:organization) insert(:organization_membership, member: current_user, organization: organization, role: "owner") - attrs = %{ - access_code: "ac_123", - organization: organization - } + attrs = %{ organization: organization } assert conn |> request_create(attrs) |> json_response(201) user_id = current_user.id @@ -49,10 +46,7 @@ defmodule CodeCorps.StripeConnectAccountControllerTest do @tag :authenticated test "does not create resource and renders 403 when not authorized", %{conn: conn} do organization = insert(:organization) - attrs = %{ - access_code: "ac_123", - organization: organization - } + attrs = %{ organization: organization } assert conn |> request_create(attrs) |> json_response(403) end end diff --git a/test/lib/code_corps/stripe_service/adapters/stripe_connect_account_test.exs b/test/lib/code_corps/stripe_service/adapters/stripe_connect_account_test.exs index cc21babbc..fa8d1e428 100644 --- a/test/lib/code_corps/stripe_service/adapters/stripe_connect_account_test.exs +++ b/test/lib/code_corps/stripe_service/adapters/stripe_connect_account_test.exs @@ -4,40 +4,139 @@ defmodule CodeCorps.StripeService.Adapters.StripeConnectAccountTestAdapter do import CodeCorps.StripeService.Adapters.StripeConnectAccountAdapter, only: [to_params: 2] @stripe_connect_account %Stripe.Account{ + id: "acct_123", business_name: "Code Corps PBC", - business_primary_color: nil, business_url: "codecorps.org", - charges_enabled: true, + charges_enabled: false, country: "US", default_currency: "usd", - details_submitted: true, - display_name: "Code Corps Customer", + details_submitted: false, + display_name: "Code Corps", email: "volunteers@codecorps.org", - id: "acct_123", + external_accounts: %{ + object: "list", + data: [], + has_more: false, + total_count: 0, + url: "/v1/accounts/acct_123/external_accounts" + }, + legal_entity: %{ + address: %{ + city: nil, + country: "US", + line1: nil, + line2: nil, + postal_code: nil, + state: nil + }, + business_name: nil, + business_tax_id_provided: false, + dob: %{ + day: nil, + month: nil, + year: nil + }, + first_name: nil, + last_name: nil, + personal_address: %{ + city: nil, + country: "US", + line1: nil, + line2: nil, + postal_code: nil, + state: nil + }, + personal_id_number_provided: false, + ssn_last_4_provided: false, + type: nil, + verification: %{ + details: nil, + details_code: "failed_other", + document: "fil_12345", + status: "unverified" + } + }, managed: false, - metadata: %{}, - statement_descriptor: "CODECORPS.ORG", + statement_descriptor: nil, support_email: nil, support_phone: "1234567890", - support_url: nil, - timezone: "America/Los_Angeles", - transfers_enabled: true + timezone: "US/Pacific", + transfers_enabled: false, + verification: %{ + disabled_reason: "fields_needed", + due_by: nil, + fields_needed: [ + "business_url", + "external_account", + "product_description", + "support_phone", + "tos_acceptance.date", + "tos_acceptance.ip" + ] + } } @local_map %{ "id_from_stripe" => "acct_123", "business_name" => "Code Corps PBC", "business_url" => "codecorps.org", - "charges_enabled" => true, + "charges_enabled" => false, "country" => "US", "default_currency" => "usd", - "details_submitted" => true, + "details_submitted" => false, + "display_name" => "Code Corps", "email" => "volunteers@codecorps.org", + + "legal_entity_address_city" => nil, + "legal_entity_address_country" => "US", + "legal_entity_address_line1" => nil, + "legal_entity_address_line2" => nil, + "legal_entity_address_postal_code" => nil, + "legal_entity_address_state" => nil, + + "legal_entity_business_name" => nil, + "legal_entity_business_tax_id_provided" => false, + "legal_entity_business_vat_id_provided" => nil, + + "legal_entity_dob_day" => nil, + "legal_entity_dob_month" => nil, + "legal_entity_dob_year" => nil, + + "legal_entity_personal_address_city" => nil, + "legal_entity_personal_address_country" => "US", + "legal_entity_personal_address_line2" => nil, + "legal_entity_personal_address_line1" => nil, + "legal_entity_personal_address_postal_code" => nil, + "legal_entity_personal_address_state" => nil, + + "legal_entity_personal_id_number_provided" => false, + "legal_entity_ssn_last_4_provided" => false, + + "legal_entity_phone_number" => nil, + + "legal_entity_type" => nil, + + "legal_entity_first_name" => nil, + "legal_entity_last_name" => nil, + "legal_entity_gender" => nil, + "legal_entity_maiden_name" => nil, + "managed" => false, "support_email" => nil, "support_phone" => "1234567890", "support_url" => nil, - "transfers_enabled" => true + "transfers_enabled" => false, + + "verification_disabled_reason" => "fields_needed", + "verification_due_by" => nil, + "verification_fields_needed" => [ + "business_url", + "external_account", + "product_description", + "support_phone", + "tos_acceptance.date", + "tos_acceptance.ip" + ] } describe "to_params/2" do diff --git a/test/views/stripe_connect_account_view_test.exs b/test/views/stripe_connect_account_view_test.exs index b70fba84f..6ef49837e 100644 --- a/test/views/stripe_connect_account_view_test.exs +++ b/test/views/stripe_connect_account_view_test.exs @@ -16,7 +16,6 @@ defmodule CodeCorps.StripeConnectAccountViewTest do expected_json = %{ "data" => %{ "attributes" => %{ - "access-code" => account.access_code, "business-name" => account.business_name, "business-url" => account.business_url, "can-accept-donations" => true, diff --git a/web/models/stripe_connect_account.ex b/web/models/stripe_connect_account.ex index 6a9b2e820..6827ddf73 100644 --- a/web/models/stripe_connect_account.ex +++ b/web/models/stripe_connect_account.ex @@ -14,18 +14,55 @@ defmodule CodeCorps.StripeConnectAccount do field :details_submitted, :boolean field :display_name, :string field :email, :string + + field :legal_entity_address_city, :string + field :legal_entity_address_country, :string + field :legal_entity_address_line1, :string + field :legal_entity_address_line2, :string + field :legal_entity_address_postal_code, :string + field :legal_entity_address_state, :string + field :legal_entity_business_name, :string + field :legal_entity_business_tax_id, :string, virtual: true + field :legal_entity_business_tax_id_provided, :boolean, default: false + field :legal_entity_business_vat_id, :string, virtual: true + field :legal_entity_business_vat_id_provided, :boolean, default: false + field :legal_entity_dob_day, :string + field :legal_entity_dob_month, :string + field :legal_entity_dob_year, :string + field :legal_entity_first_name, :string + field :legal_entity_last_name, :string + field :legal_entity_gender, :string + field :legal_entity_maiden_name, :string + field :legal_entity_personal_address_city, :string + field :legal_entity_personal_address_country, :string + field :legal_entity_personal_address_line1, :string + field :legal_entity_personal_address_line2, :string + field :legal_entity_personal_address_postal_code, :string + field :legal_entity_personal_address_state, :string + field :legal_entity_phone_number, :string + field :legal_entity_personal_id_number, :string, virtual: true + field :legal_entity_personal_id_number_provided, :boolean, default: false + field :legal_entity_ssn_last_4, :string, virtual: true + field :legal_entity_ssn_last_4_provided, :boolean, default: false + field :legal_entity_type, :string + field :legal_entity_verification_details, :string + field :legal_entity_verification_details_code, :string + field :legal_entity_verification_document, :string + field :legal_entity_verification_status, :string + field :id_from_stripe, :string, null: false - field :managed, :boolean + field :managed, :boolean, default: true + field :support_email, :string field :support_phone, :string field :support_url, :string + field :transfers_enabled, :boolean + field :verification_disabled_reason, :string field :verification_due_by, Ecto.DateTime field :verification_fields_needed, {:array, :string} - field :access_code, :string, virtual: true - belongs_to :organization, CodeCorps.Organization timestamps() @@ -46,14 +83,34 @@ defmodule CodeCorps.StripeConnectAccount do |> assoc_constraint(:organization) end + # Fields that get updated when we handle an "account.updated" webhook @webhook_update_params [ :business_name, :business_url, :charges_enabled, :country, - :default_currency, :details_submitted, :email, :managed, :support_email, - :support_phone, :support_url, :transfers_enabled, - :verification_disabled_reason, :verification_due_by, - :verification_fields_needed + :default_currency, :details_submitted, :display_name, :email, + :legal_entity_address_city, :legal_entity_address_country, + :legal_entity_address_line1, :legal_entity_address_line2, + :legal_entity_address_postal_code, :legal_entity_address_state, + :legal_entity_business_name, + :legal_entity_business_tax_id_provided, :legal_entity_business_vat_id_provided, + :legal_entity_dob_day, :legal_entity_dob_month, :legal_entity_dob_year, + :legal_entity_first_name, :legal_entity_last_name, + :legal_entity_gender, :legal_entity_maiden_name, + :legal_entity_personal_address_city, :legal_entity_personal_address_country, + :legal_entity_personal_address_line1, :legal_entity_personal_address_line2, + :legal_entity_personal_address_postal_code, :legal_entity_personal_address_state, + :legal_entity_phone_number, + :legal_entity_personal_id_number_provided, :legal_entity_ssn_last_4_provided, + :legal_entity_type, + :legal_entity_verification_details, :legal_entity_verification_details_code, + :legal_entity_verification_document, :legal_entity_verification_status, + :managed, :support_email, :support_phone, :support_url, + :transfers_enabled, + :verification_disabled_reason, :verification_due_by, :verification_fields_needed ] + @doc """ + Changeset used to update the record while handling an "account.updated" webhook + """ def webhook_update_changeset(struct, params \\ %{}) do struct |> cast(params, @webhook_update_params) diff --git a/web/views/stripe_connect_account_view.ex b/web/views/stripe_connect_account_view.ex index 0c9c35298..a6605d4f0 100644 --- a/web/views/stripe_connect_account_view.ex +++ b/web/views/stripe_connect_account_view.ex @@ -4,7 +4,7 @@ defmodule CodeCorps.StripeConnectAccountView do use JaSerializer.PhoenixView attributes [ - :access_code, :business_name, :business_url, :can_accept_donations, + :business_name, :business_url, :can_accept_donations, :charges_enabled, :country, :default_currency, :details_submitted, :display_name, :email, :id_from_stripe, :inserted_at, :managed, :support_email, :support_phone, :support_url, :transfers_enabled,