Skip to content

Commit 575134d

Browse files
authored
Merge pull request #664 from code-corps/590-simplify-subscription-creation
Simplified subscription creation
2 parents a935a96 + 195d338 commit 575134d

33 files changed

+157
-117
lines changed

lib/code_corps/stripe_service/stripe_connect_subscription.ex renamed to lib/code_corps/stripe_service/stripe_connect_subscription_service.ex

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
defmodule CodeCorps.StripeService.StripeConnectSubscriptionService do
2-
import Ecto.Query
3-
42
alias CodeCorps.{
53
Project, Repo, StripeConnectCustomer, StripeConnectAccount,
64
StripeConnectPlan, StripeConnectSubscription, User
@@ -20,19 +18,22 @@ defmodule CodeCorps.StripeService.StripeConnectSubscriptionService do
2018
2119
- `{:ok, %StripeConnectSubscription{}}` - the created record.
2220
- `{:error, %Ecto.Changeset{}}` - the record was not created due to validation issues.
21+
- `{:error, :project_not_found}`
2322
- `{:error, :project_not_ready}` - the associated project does not meed the prerequisites for receiving donations.
23+
- `{:error, :user_not_found}`
2424
- `{:error, :user_not_ready}` - the associated user does not meet the prerequisits to donate.
2525
- `{:error, %Stripe.APIErrorResponse{}}` - there was a problem with the stripe request
26-
- `{:error, :not_found}` - one of the associated records was not found
2726
2827
# Side effects
2928
3029
- If the subscription is created or found, associated project totals will get updated
3130
- If the subscription is created or found, associated donation goal states will be updated
3231
"""
3332
def find_or_create(%{"project_id" => project_id, "quantity" => _, "user_id" => user_id} = attributes) do
34-
with {:ok, %Project{} = project} <- get_project(project_id) |> ProjectSubscribable.validate,
35-
{:ok, %User{} = user} <- get_user(user_id) |> UserCanSubscribe.validate
33+
with {:ok, %Project{} = project} <- get_project_with_preloads(project_id),
34+
{:ok, %Project{}} <- ProjectSubscribable.validate(project),
35+
{:ok, %User{} = user} <- get_user_with_preloads(user_id),
36+
{:ok, %User{}} <- UserCanSubscribe.validate(user)
3637
do
3738
{:ok, %StripeConnectSubscription{} = subscription} = do_find_or_create(project, user, attributes)
3839

@@ -41,7 +42,6 @@ defmodule CodeCorps.StripeService.StripeConnectSubscriptionService do
4142

4243
{:ok, subscription}
4344
else
44-
nil -> {:error, :not_found}
4545
failure -> failure
4646
end
4747
end
@@ -59,9 +59,8 @@ defmodule CodeCorps.StripeService.StripeConnectSubscriptionService do
5959
def update_from_stripe(stripe_id, connect_customer_id) do
6060
with {:ok, %StripeConnectAccount{} = connect_account} <- retrieve_connect_account(connect_customer_id),
6161
{:ok, %Stripe.Subscription{} = stripe_subscription} <- @api.Subscription.retrieve(stripe_id, connect_account: connect_account.id),
62-
{:ok, %StripeConnectSubscription{} = subscription} <- load_subscription(stripe_id),
63-
{:ok, params} <- stripe_subscription |> StripeConnectSubscriptionAdapter.to_params(%{}),
64-
{:ok, %Project{} = project} <- get_project(subscription)
62+
{:ok, %StripeConnectSubscription{stripe_connect_plan: %{project: project}} = subscription} <- load_subscription(stripe_id),
63+
{:ok, params} <- stripe_subscription |> StripeConnectSubscriptionAdapter.to_params(%{})
6564
do
6665
{:ok, %StripeConnectSubscription{} = subscription} = update_subscription(subscription, params)
6766

@@ -75,29 +74,30 @@ defmodule CodeCorps.StripeService.StripeConnectSubscriptionService do
7574
end
7675
end
7776

78-
defp do_find_or_create(%Project{} = project, %User{} = user, %{} = attributes) do
79-
case find(project, user) do
77+
# find_or_create
78+
79+
defp do_find_or_create(project, user, attributes) do
80+
case find(project.stripe_connect_plan, user) do
8081
nil -> create(project, user, attributes)
8182
%StripeConnectSubscription{} = subscription -> {:ok, subscription}
8283
end
8384
end
8485

85-
defp find(%Project{} = project, %User{} = user) do
86+
defp find(plan, user) do
8687
StripeConnectSubscription
87-
|> where([s], s.stripe_connect_plan_id == ^project.stripe_connect_plan.id and s.user_id == ^user.id)
88-
|> Repo.one
88+
|> Repo.get_by(stripe_connect_plan_id: plan.id, user_id: user.id)
8989
end
9090

91-
defp create(%Project{} = project, %User{} = user, attributes) do
91+
defp create(project, user, attributes) do
9292
with platform_card <- user.stripe_platform_card,
9393
platform_customer <- user.stripe_platform_customer,
9494
connect_account <- project.organization.stripe_connect_account,
9595
plan <- project.stripe_connect_plan,
9696
{:ok, connect_customer} <- StripeConnectCustomerService.find_or_create(platform_customer, connect_account, user),
9797
{:ok, connect_card} <- StripeConnectCardService.find_or_create(platform_card, connect_customer, platform_customer, connect_account),
98-
create_attributes <- to_create_attributes(connect_card, connect_customer, plan, attributes),
98+
create_attributes <- api_create_attributes(connect_card, connect_customer, plan, attributes),
9999
{:ok, subscription} <- @api.Subscription.create(create_attributes, connect_account: connect_account.id_from_stripe),
100-
insert_attributes <- to_insert_attributes(attributes, plan),
100+
insert_attributes <- local_insert_attributes(attributes, plan),
101101
{:ok, params} <- StripeConnectSubscriptionAdapter.to_params(subscription, insert_attributes),
102102
{:ok, %StripeConnectSubscription{} = stripe_connect_subscription} <- insert_subscription(params)
103103
do
@@ -108,21 +108,22 @@ defmodule CodeCorps.StripeService.StripeConnectSubscriptionService do
108108
end
109109
end
110110

111-
defp get_project(%StripeConnectSubscription{stripe_connect_plan_id: stripe_connect_plan_id}) do
112-
%StripeConnectPlan{project_id: project_id} = Repo.get(StripeConnectPlan, stripe_connect_plan_id)
113-
{:ok, get_project(project_id, [:stripe_connect_plan])}
114-
end
111+
defp get_project_with_preloads(id) do
112+
preloads = [:stripe_connect_plan, [organization: :stripe_connect_account]]
115113

116-
@default_project_preloads [:stripe_connect_plan, [{:organization, :stripe_connect_account}]]
117-
118-
defp get_project(project_id, preloads \\ @default_project_preloads) do
119-
Repo.get(Project, project_id) |> Repo.preload(preloads)
114+
case Repo.get(Project, id) |> Repo.preload(preloads) do
115+
nil -> {:error, :project_not_found}
116+
project -> {:ok, project}
117+
end
120118
end
121119

122-
@default_user_preloads [:stripe_platform_customer, [{:stripe_platform_card, :stripe_connect_cards}]]
120+
defp get_user_with_preloads(user_id) do
121+
preloads = [:stripe_platform_customer, [{:stripe_platform_card, :stripe_connect_cards}]]
123122

124-
defp get_user(user_id, preloads \\ @default_user_preloads) do
125-
Repo.get(User, user_id) |> Repo.preload(preloads)
123+
case Repo.get(User, user_id) |> Repo.preload(preloads) do
124+
nil -> {:error, :user_not_found}
125+
user -> {:ok, user}
126+
end
126127
end
127128

128129
defp insert_subscription(params) do
@@ -131,7 +132,7 @@ defmodule CodeCorps.StripeService.StripeConnectSubscriptionService do
131132
|> Repo.insert
132133
end
133134

134-
defp to_create_attributes(card, customer, plan, %{"quantity" => quantity}) do
135+
defp api_create_attributes(card, customer, plan, %{"quantity" => quantity}) do
135136
%{
136137
application_fee_percent: 5,
137138
customer: customer.id_from_stripe,
@@ -141,10 +142,12 @@ defmodule CodeCorps.StripeService.StripeConnectSubscriptionService do
141142
}
142143
end
143144

144-
defp to_insert_attributes(attrs, %StripeConnectPlan{id: stripe_connect_plan_id}) do
145+
defp local_insert_attributes(attrs, %StripeConnectPlan{id: stripe_connect_plan_id}) do
145146
attrs |> Map.merge(%{"stripe_connect_plan_id" => stripe_connect_plan_id})
146147
end
147148

149+
# update_from_stripe
150+
148151
defp retrieve_connect_account(connect_customer_id) do
149152
customer =
150153
StripeConnectCustomer
@@ -155,7 +158,10 @@ defmodule CodeCorps.StripeService.StripeConnectSubscriptionService do
155158
end
156159

157160
defp load_subscription(id_from_stripe) do
158-
subscription = Repo.get_by(StripeConnectSubscription, id_from_stripe: id_from_stripe)
161+
subscription =
162+
StripeConnectSubscription
163+
|> Repo.get_by(id_from_stripe: id_from_stripe)
164+
|> Repo.preload([stripe_connect_plan: [project: [:stripe_connect_plan, :donation_goals]]])
159165

160166
{:ok, subscription}
161167
end
File renamed without changes.

test/support/view_case.ex

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
defmodule CodeCorps.ViewCase do
2+
@moduledoc """
3+
This module defines the test case to be used by
4+
tests for views defined in the application.
5+
"""
6+
7+
use ExUnit.CaseTemplate
8+
9+
using do
10+
quote do
11+
import CodeCorps.Factories
12+
import Phoenix.View, only: [render: 3]
13+
end
14+
end
15+
16+
setup tags do
17+
:ok = Ecto.Adapters.SQL.Sandbox.checkout(CodeCorps.Repo)
18+
19+
unless tags[:async] do
20+
Ecto.Adapters.SQL.Sandbox.mode(CodeCorps.Repo, {:shared, self()})
21+
end
22+
23+
:ok
24+
end
25+
end

test/views/category_view_test.exs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
defmodule CodeCorps.CategoryViewTest do
2-
use CodeCorps.ConnCase, async: true
3-
4-
import Phoenix.View, only: [render: 3]
2+
use CodeCorps.ViewCase
53

64
test "renders all attributes and relationships properly" do
75
category = insert(:category)

test/views/changeset_view_test.exs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
defmodule CodeCorps.ChangesetViewTest do
2-
use CodeCorps.ConnCase, async: true
2+
use CodeCorps.ViewCase
33

44
alias CodeCorps.Preview
55

6-
import Phoenix.View, only: [render: 3]
7-
86
test "renders all errors properly" do
97
changeset = Preview.create_changeset(%Preview{}, %{})
108

test/views/comment_view_test.exs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
defmodule CodeCorps.CommentViewTest do
2-
use CodeCorps.ConnCase, async: true
3-
4-
import Phoenix.View, only: [render: 3]
2+
use CodeCorps.ViewCase
53

64
test "renders all attributes and relationships properly" do
75
task = insert(:task)

test/views/donation_goal_view_test.exs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
defmodule CodeCorps.DonationGoalViewTest do
2-
use CodeCorps.ConnCase, async: true
3-
4-
import Phoenix.View, only: [render: 3]
2+
use CodeCorps.ViewCase
53

64
test "renders all attributes and relationships properly" do
75
project = insert(:project)

test/views/error_view_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
defmodule CodeCorps.ErrorViewTest do
2-
use CodeCorps.ConnCase, async: true
2+
use CodeCorps.ViewCase
33

44
# Bring render/3 and render_to_string/3 for testing custom views
55
import Phoenix.View

test/views/layout_view_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
defmodule CodeCorps.LayoutViewTest do
2-
use CodeCorps.ConnCase, async: true
2+
use CodeCorps.ViewCase
33
end

test/views/organization_membership_view_test.exs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
defmodule CodeCorps.OrganizationMembershipViewTest do
2-
use CodeCorps.ConnCase, async: true
3-
4-
import Phoenix.View, only: [render: 3]
2+
use CodeCorps.ViewCase
53

64
test "renders all attributes and relationships properly" do
75
organization = insert(:organization)

0 commit comments

Comments
 (0)