diff --git a/lib/code_corps/stripe_service/adapters/stripe_file_upload.ex b/lib/code_corps/stripe_service/adapters/stripe_file_upload.ex new file mode 100644 index 000000000..b8ab7d206 --- /dev/null +++ b/lib/code_corps/stripe_service/adapters/stripe_file_upload.ex @@ -0,0 +1,44 @@ +defmodule CodeCorps.StripeService.Adapters.StripeFileUploadAdapter do + @moduledoc """ + Used for conversion between stripe api payload maps and maps + usable for creation of `StripeFileUpload` records locally + """ + + import CodeCorps.MapUtils, only: [rename: 3, keys_to_string: 1] + + @stripe_attributes [:created, :id, :purpose, :size, :type,] + + @doc """ + Converts a struct received from the Stripe API into a map that can be used + to create a `CodeCorps.StripeFileUpload` record + """ + def to_params(%Stripe.FileUpload{} = stripe_file_upload, %{} = attributes) do + result = + stripe_file_upload + |> Map.from_struct + |> Map.take(@stripe_attributes) + |> rename(:id, :id_from_stripe) + |> keys_to_string + |> add_non_stripe_attributes(attributes) + + {:ok, result} + end + + @non_stripe_attributes ["stripe_connect_account_id"] + + defp add_non_stripe_attributes(%{} = params, %{} = attributes) do + attributes + |> get_non_stripe_attributes + |> add_to(params) + end + + defp get_non_stripe_attributes(%{} = attributes) do + attributes + |> Map.take(@non_stripe_attributes) + end + + defp add_to(%{} = attributes, %{} = params) do + params + |> Map.merge(attributes) + end +end diff --git a/lib/code_corps/stripe_service/stripe_connect_account.ex b/lib/code_corps/stripe_service/stripe_connect_account.ex index 1ce5a7526..876f980c2 100644 --- a/lib/code_corps/stripe_service/stripe_connect_account.ex +++ b/lib/code_corps/stripe_service/stripe_connect_account.ex @@ -1,6 +1,6 @@ defmodule CodeCorps.StripeService.StripeConnectAccountService do - alias CodeCorps.{Repo, StripeConnectAccount} - alias CodeCorps.StripeService.Adapters.StripeConnectAccountAdapter + alias CodeCorps.{Repo, StripeConnectAccount, StripeFileUpload} + alias CodeCorps.StripeService.Adapters.{StripeConnectAccountAdapter, StripeFileUploadAdapter} @api Application.get_env(:code_corps, :stripe) @@ -23,4 +23,34 @@ defmodule CodeCorps.StripeService.StripeConnectAccountService do |> Repo.update end end + + def add_vertification_document( + %StripeConnectAccount{id_from_stripe: account_id} = record, + %{"legal_entity_verification_document" => document} = attributes + ) do + with {:ok, %Stripe.FileUpload{} = stripe_file_upload} <- Stripe.FileUpload.retrieve(document), + {:ok, %Stripe.Account{} = stripe_account} <- Stripe.Account.update(account_id, %{legal_entity: %{verification: %{document: document}}}), + {:ok, file_upload_params} <- StripeFileUploadAdapter.to_params(stripe_file_upload, attributes), + {:ok, connect_account_params} <- StripeConnectAccountAdapter.to_params(stripe_account, attributes), + account_changeset <- record |> StripeConnectAccount.webhook_update_changeset(connect_account_params), + file_changeset <- %StripeFileUpload{} |> StripeFileUpload.create_changeset(file_upload_params) + do + multi = + Multi.new + |> Multi.update(:stripe_connect_account, account_changeset) + |> Multi.insert(:stripe_file_upload, file_changeset) + + case Repo.transaction(multi) do + {:ok, %{stripe_connect_account: account, stripe_file_upload: _}} -> + {:ok, account} + {:error, :stripe_connect_account, %Ecto.Changeset{} = account_changeset, %{}} -> + {:error, account_changeset} + # If creating the file failed due to validation, we add a generic error + # to the account changeset and return that to be rendered. + {:error, :stripe_file_upload, %Ecto.Changeset{} = _, _} -> + account_changeset |> Ecto.Changeset.add_error(:legal_entity_verification_document, "is invalid") + {:error, account_changeset} + end + end + end end diff --git a/test/models/stripe_file_upload_test.exs b/test/models/stripe_file_upload_test.exs index b50c732e6..9216d0114 100644 --- a/test/models/stripe_file_upload_test.exs +++ b/test/models/stripe_file_upload_test.exs @@ -9,15 +9,15 @@ defmodule CodeCorps.StripeFileUploadTest do @invalid_attrs %{} - describe "create_identity_document_changeset/2" do + describe "create_changeset/2" do test "reports as valid when attributes are valid" do - changeset = StripeFileUpload.create_identity_document_changeset(%StripeFileUpload{}, @valid_attrs) + changeset = StripeFileUpload.create_changeset(%StripeFileUpload{}, @valid_attrs) assert changeset.valid? end test "reports as invalid when attributes are invalid" do - changeset = StripeFileUpload.create_identity_document_changeset(%StripeFileUpload{}, @invalid_attrs) + changeset = StripeFileUpload.create_changeset(%StripeFileUpload{}, @invalid_attrs) refute changeset.valid? assert changeset.errors[:id_from_stripe] == {"can't be blank", []} @@ -26,7 +26,7 @@ defmodule CodeCorps.StripeFileUploadTest do test "can optionally belong to a StripeConnectAccount" do stripe_connect_account_id = insert(:stripe_connect_account).id changes = Map.merge(@valid_attrs, %{stripe_connect_account_id: stripe_connect_account_id}) - changeset = StripeFileUpload.create_identity_document_changeset(%StripeFileUpload{}, changes) + changeset = StripeFileUpload.create_changeset(%StripeFileUpload{}, changes) assert changeset.valid? end @@ -34,7 +34,7 @@ defmodule CodeCorps.StripeFileUploadTest do test "existing StripeConnectAccount association is required" do stripe_connect_account_id = "abc456" changes = Map.merge(@valid_attrs, %{stripe_connect_account_id: stripe_connect_account_id}) - changeset = StripeFileUpload.create_identity_document_changeset(%StripeFileUpload{}, changes) + changeset = StripeFileUpload.create_changeset(%StripeFileUpload{}, changes) refute changeset.valid? assert changeset.errors[:stripe_connect_account_id] == {"is invalid", [type: :id]} diff --git a/web/models/stripe_connect_account.ex b/web/models/stripe_connect_account.ex index b5c6a2ac7..c65181b7e 100644 --- a/web/models/stripe_connect_account.ex +++ b/web/models/stripe_connect_account.ex @@ -136,9 +136,6 @@ defmodule CodeCorps.StripeConnectAccount do |> assoc_constraint(:organization) end - @doc """ - Changeset used to update the record while handling an "account.updated" webhook - """ def webhook_update_changeset(struct, params \\ %{}) do struct |> cast(params, @stripe_params) diff --git a/web/models/stripe_file_upload.ex b/web/models/stripe_file_upload.ex index 682c6a587..293bc714b 100644 --- a/web/models/stripe_file_upload.ex +++ b/web/models/stripe_file_upload.ex @@ -14,11 +14,10 @@ defmodule CodeCorps.StripeFileUpload do timestamps() end - def create_identity_document_changeset(struct, params \\ %{}) do + def create_changeset(struct, params \\ %{}) do struct |> cast(params, [:created, :id_from_stripe, :purpose, :size, :type, :url, :stripe_connect_account_id]) |> validate_required([:id_from_stripe]) |> assoc_constraint(:stripe_connect_account) end - end