Skip to content

Commit

Permalink
Add StripeConnectAccount update endpoint for assigning an external ac…
Browse files Browse the repository at this point in the history
…count
  • Loading branch information
begedin authored and joshsmith committed Dec 21, 2016
1 parent dd77535 commit 914a4ae
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 38 deletions.
10 changes: 10 additions & 0 deletions lib/code_corps/stripe_service/adapters/stripe_connect_account.ex
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ defmodule CodeCorps.StripeService.Adapters.StripeConnectAccountAdapter do
stripe_account
|> Map.from_struct
|> transform_map(@stripe_mapping)
|> add_nested_attributes(stripe_account)
|> keys_to_string
|> add_non_stripe_attributes(attributes)

Expand All @@ -91,4 +92,13 @@ defmodule CodeCorps.StripeService.Adapters.StripeConnectAccountAdapter do
params
|> Map.merge(attributes)
end

defp add_nested_attributes(map, stripe_account) do
map
|> add_external_account(stripe_account)
end

defp add_external_account(map, %Stripe.Account{external_accounts: %{data: []}}), do: map
defp add_external_account(map, %Stripe.Account{external_accounts: %{data: [head | _]}}), do: map |> do_add_external_account(head)
defp do_add_external_account(map, %{"id" => id}), do: map |> Map.put(:external_account, id)
end
21 changes: 15 additions & 6 deletions lib/code_corps/stripe_service/stripe_connect_account.ex
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
defmodule CodeCorps.StripeService.StripeConnectAccountService do
alias CodeCorps.{Repo, StripeConnectAccount}
alias CodeCorps.StripeService.Adapters.StripeConnectAccountAdapter

@api Application.get_env(:code_corps, :stripe)

# TODO: Replace with code that implements issue #564

def create(%{"country" => country_code, "organization_id" => organization_id} = attributes) do
def create(%{"country" => country_code, "organization_id" => _} = attributes) do
with {:ok, %Stripe.Account{} = account} <- @api.Account.create(%{country: country_code, managed: true}),
{:ok, params} <- StripeConnectAccountAdapter.to_params(account, attributes)
do
%CodeCorps.StripeConnectAccount{}
|> CodeCorps.StripeConnectAccount.create_changeset(params)
|> CodeCorps.Repo.insert
%StripeConnectAccount{}
|> StripeConnectAccount.create_changeset(params)
|> Repo.insert
end
end

def add_external_account(%StripeConnectAccount{id_from_stripe: stripe_id} = record, external_account) do
with {:ok, %Stripe.Account{} = stripe_account} <- @api.Account.update(stripe_id, %{external_account: external_account}),
{:ok, params} <- StripeConnectAccountAdapter.to_params(stripe_account, %{})
do
record
|> StripeConnectAccount.stripe_update_changeset(params)
|> Repo.update
end
end
end
105 changes: 78 additions & 27 deletions lib/code_corps/stripe_testing/account.ex
Original file line number Diff line number Diff line change
@@ -1,32 +1,83 @@
defmodule CodeCorps.StripeTesting.Account do
def create(_map) do
{:ok, do_create}
end

def retrieve(_id) do
{:ok, do_create}
end

defp do_create do
%Stripe.Account{
business_name: "Code Corps PBC",
business_primary_color: nil,
business_url: "codecorps.org",
charges_enabled: true,
country: "US",
default_currency: "usd",
details_submitted: true,
display_name: "Code Corps Customer",
email: "volunteers@codecorps.org",
id: "acct_123",
managed: true,
metadata: %{},
statement_descriptor: "CODECORPS.ORG",
support_email: nil,
support_phone: "1234567890",
support_url: nil,
timezone: "America/Los_Angeles",
transfers_enabled: true
{:ok, create_stripe_record(%{})}
end

def retrieve(id) do
{:ok, create_stripe_record(%{"id" => id})}
end

def update(id, attributes) do
attributes =
attributes
|> CodeCorps.MapUtils.keys_to_string
|> Map.merge(%{"id" => id})

{:ok, create_stripe_record(attributes)}
end

defp create_stripe_record(attributes) do
with attributes <- account_fixture |> Map.merge(attributes) |> add_nestings
do
Stripe.Account |> Stripe.Converter.stripe_map_to_struct(attributes)
end
end

defp account_fixture do
%{
"business_name" => "Code Corps PBC",
"business_primary_color" => nil,
"business_url" => "codecorps.org",
"charges_enabled" => true,
"country" => "US",
"default_currency" => "usd",
"details_submitted" => true,
"display_name" => "Code Corps Customer",
"email" => "volunteers@codecorps.org",
"external_accounts" => %{
"object" => "list",
"data" => [],
"has_more" => false,
"total_count" => 0,
"url" => "/v1/accounts/acct_123/external_accounts"
},
"id" => "acct_123",
"managed" => true,
"metadata" => %{},
"statement_descriptor" => "CODECORPS.ORG",
"support_email" => nil,
"support_phone" => "1234567890",
"support_url" => nil,
"timezone" => "America/Los_Angeles",
"transfers_enabled" => true
}
end

defp add_nestings(map) do
map
|> add_external_account
end

defp add_external_account(%{"id" => account_id, "external_account" => external_account_id} = map) do
external_accounts_map = %{
"object" => "list",
"data" => [%{"id" => external_account_id}],
"has_more" => false,
"total_count" => 1,
"url" => "/v1/accounts/#{account_id}/external_accounts"
}

Map.put(map, "external_accounts", external_accounts_map)
end
defp add_external_account(%{"id" => account_id} = map) do
external_accounts_map = %{
"object" => "list",
"data" => [],
"has_more" => false,
"total_count" => 1,
"url" => "/v1/accounts/#{account_id}/external_accounts"
}

Map.put(map, "external_accounts", external_accounts_map)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule CodeCorps.Repo.Migrations.AddExternalAccountToStripeConnectAccounts do
use Ecto.Migration

def change do
alter table(:stripe_connect_accounts) do
add :external_account, :string
end
end
end
33 changes: 32 additions & 1 deletion test/controllers/stripe_connect_account_controller_test.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule CodeCorps.StripeConnectAccountControllerTest do
use CodeCorps.ApiCase, resource_name: :stripe_connect_account

alias CodeCorps.StripeConnectAccount

describe "show" do
@tag :authenticated
test "shows chosen resource", %{conn: conn, current_user: current_user} do
Expand Down Expand Up @@ -29,7 +31,7 @@ defmodule CodeCorps.StripeConnectAccountControllerTest do

describe "create" do
@tag :authenticated
test "creates and renders resource user is authenticated and authorized", %{conn: conn, current_user: current_user} do
test "creates and renders resource when 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 = %{ organization: organization }
Expand All @@ -50,4 +52,33 @@ defmodule CodeCorps.StripeConnectAccountControllerTest do
assert conn |> request_create(attrs) |> json_response(403)
end
end

describe "update" do
@tag :authenticated
test "updates external account on resource when 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")
stripe_connect_account = insert(:stripe_connect_account, organization: organization)

attrs = %{external_account: "ba_test123"}

assert conn |> request_update(stripe_connect_account, attrs, :skip_strategy) |> json_response(200)

updated_account = Repo.get(StripeConnectAccount, stripe_connect_account.id)
assert updated_account.external_account == "ba_test123"
end

test "does not update resource and renders 401 when unauthenticated", %{conn: conn} do
assert conn |> request_update |> json_response(401)
end

@tag :authenticated
test "does not update resource and renders 403 when not authorized", %{conn: conn} do
organization = insert(:organization)
stripe_connect_account = insert(:stripe_connect_account, organization: organization)

assert conn |> request_update(stripe_connect_account, %{}) |> json_response(403)
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ defmodule CodeCorps.StripeService.Adapters.StripeConnectAccountTestAdapter do
email: "volunteers@codecorps.org",
external_accounts: %{
object: "list",
data: [],
data: [%{"id" => "ba_123"}],
has_more: false,
total_count: 0,
url: "/v1/accounts/acct_123/external_accounts"
Expand Down Expand Up @@ -56,6 +56,7 @@ defmodule CodeCorps.StripeService.Adapters.StripeConnectAccountTestAdapter do
status: "unverified"
}
},
id: "acct_123",
managed: false,
statement_descriptor: nil,
support_email: nil,
Expand Down Expand Up @@ -87,6 +88,8 @@ defmodule CodeCorps.StripeService.Adapters.StripeConnectAccountTestAdapter do
"display_name" => "Code Corps",
"email" => "volunteers@codecorps.org",

"external_account" => "ba_123",

"legal_entity_address_city" => nil,
"legal_entity_address_country" => "US",
"legal_entity_address_line1" => nil,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule CodeCorps.StripeService.StripeConnectAccountServiceTest do

use CodeCorps.ModelCase

alias CodeCorps.{StripeConnectAccount}
alias CodeCorps.StripeService.StripeConnectAccountService

describe "create" do
Expand All @@ -11,12 +12,22 @@ defmodule CodeCorps.StripeService.StripeConnectAccountServiceTest do

attributes = %{"country" => "US", "organization_id" => organization.id}

{:ok, %CodeCorps.StripeConnectAccount{} = connect_account} =
StripeConnectAccountService.create(attributes)
{:ok, %StripeConnectAccount{} = connect_account} =
StripeConnectAccountService.create(attributes)

assert connect_account.country == "US"
assert connect_account.organization_id == organization.id
assert connect_account.managed == true
end
end

describe "add_external_account/2" do
test "assigns the external_account property to the record" do
account = insert(:stripe_connect_account)

{:ok, %StripeConnectAccount{} = updated_account} =
StripeConnectAccountService.add_external_account(account, "ba_test123")
assert updated_account.external_account == "ba_test123"
end
end
end
9 changes: 9 additions & 0 deletions test/support/api_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ defmodule CodeCorps.ApiCase do
conn |> put(path, payload)
end

# A temporary request_update that skips building a payload
# using our faulty json_payload strategy
# Only works with attributes, not relationships
def request_update(conn, resource_or_id, attrs, :skip_strategy) do
payload = %{data: %{attributes: attrs}, type: "#{factory_name}"}
path = conn |> path_for(:update, resource_or_id)
conn |> put(path, payload)
end

def request_delete(conn), do: request_delete(conn, default_record)
def request_delete(conn, :not_found), do: request_delete(conn, -1)
def request_delete(conn, resource_or_id) do
Expand Down
17 changes: 17 additions & 0 deletions web/controllers/stripe_connect_account_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,21 @@ defmodule CodeCorps.StripeConnectAccountController do
defp handle_create_result({:ok, %StripeConnectAccount{}} = result, conn) do
result |> CodeCorps.Analytics.Segment.track(:created, conn)
end

def handle_update(conn, record, %{"external_account" => external_account}) do
with {:ok, _} = result <- StripeConnectAccountService.add_external_account(record, external_account)
do
CodeCorps.Analytics.Segment.track(result, :created, conn)
else
{:error, %Ecto.Changeset{} = changeset} -> changeset
end
end

def handle_update(conn, _record, _attributes), do: conn |> unauthorized

defp unauthorized(conn) do
conn
|> Plug.Conn.assign(:authorized, false)
|> CodeCorps.AuthenticationHelpers.handle_unauthorized
end
end
15 changes: 15 additions & 0 deletions web/models/stripe_connect_account.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ defmodule CodeCorps.StripeConnectAccount do
field :display_name, :string
field :email, :string

field :external_account, :string

field :legal_entity_address_city, :string
field :legal_entity_address_country, :string
field :legal_entity_address_line1, :string
Expand Down Expand Up @@ -115,4 +117,17 @@ defmodule CodeCorps.StripeConnectAccount do
struct
|> cast(params, @webhook_update_params)
end

@update_params [
:business_name, :business_url, :charges_enabled, :country, :default_currency,
:details_submitted, :email, :external_account, :id_from_stripe,
:support_email, :support_phone, :support_url, :transfers_enabled,
:verification_disabled_reason, :verification_due_by,
:verification_fields_needed
]

def stripe_update_changeset(struct, params) do
struct
|> cast(params, @update_params)
end
end
2 changes: 1 addition & 1 deletion web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ defmodule CodeCorps.Router do
resources "/roles", RoleController, only: [:create]
resources "/role-skills", RoleSkillController, only: [:create, :delete]
resources "/skills", SkillController, only: [:create]
resources "/stripe-connect-accounts", StripeConnectAccountController, only: [:show, :create]
resources "/stripe-connect-accounts", StripeConnectAccountController, only: [:show, :create, :update]
resources "/stripe-connect-plans", StripeConnectPlanController, only: [:show, :create]
resources "/stripe-connect-subscriptions", StripeConnectSubscriptionController, only: [:show, :create]
resources "/stripe-platform-cards", StripePlatformCardController, only: [:show, :create]
Expand Down

0 comments on commit 914a4ae

Please sign in to comment.