Skip to content

Commit

Permalink
Merge pull request #497 from code-corps/370-stripe-webhooks
Browse files Browse the repository at this point in the history
Update Stripe webhooks
  • Loading branch information
joshsmith committed Nov 29, 2016
2 parents f771b18 + 036379a commit a58d8d9
Show file tree
Hide file tree
Showing 56 changed files with 551 additions and 454 deletions.
3 changes: 3 additions & 0 deletions config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ config :guardian, Guardian,
secret_key: "e62fb6e2746f6b1bf8b5b735ba816c2eae1d5d76e64f18f3fc647e308b0c159e"

config :code_corps, :analytics, CodeCorps.Analytics.InMemoryAPI

# Configures stripe for dev mode
config :code_corps, :stripe, Stripe
config :code_corps, :stripe_env, :dev

config :sentry,
environment_name: Mix.env || :dev
4 changes: 4 additions & 0 deletions config/prod.exs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ config :logger, level: :info
# Configures Segment for analytics
config :code_corps, :analytics, CodeCorps.Analytics.SegmentAPI

# Configures stripe for production
config :code_corps, :stripe, Stripe
config :code_corps, :stripe_env, :prod

config :sentry,
environment_name: Mix.env || :prod

Expand Down
5 changes: 5 additions & 0 deletions config/remote-development.exs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ config :guardian, Guardian,
# Do not print debug messages in production
config :logger, level: :info


# Configures stripe for remote dev
config :code_corps, :stripe, Stripe
config :code_corps, :stripe_env, :remote_dev

# ## SSL Support
#
# To get SSL working, you will need to add the `https` key
Expand Down
4 changes: 4 additions & 0 deletions config/staging.exs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ config :code_corps, :analytics, CodeCorps.Analytics.SegmentAPI
config :sentry,
environment_name: Mix.env || :staging

# Configures stripe for staging
config :code_corps, :stripe, Stripe
config :code_corps, :stripe_env, :staging

# ## SSL Support
#
# To get SSL working, you will need to add the `https` key
Expand Down
2 changes: 2 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ config :guardian, Guardian,

config :code_corps, :analytics, CodeCorps.Analytics.TestAPI

# Configures stripe for test mode
config :code_corps, :stripe, CodeCorps.StripeTesting
config :code_corps, :stripe_env, :test

config :code_corps, :icon_color_generator, CodeCorps.RandomIconColor.TestGenerator

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule CodeCorps.Base64Image do
defmodule CodeCorps.Services.Base64ImageService do
def save_to_file("data:" <> data_string) do
[content_type, content_string] =
data_string
Expand Down
6 changes: 4 additions & 2 deletions lib/code_corps/services/base64_image_uploader.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
defmodule CodeCorps.Base64ImageUploader do
defmodule CodeCorps.Services.Base64ImageUploaderService do
use Arc.Ecto.Schema
import Ecto.Changeset, only: [cast: 3, validate_required: 2]

alias CodeCorps.Services.Base64ImageService

@doc """
Takes a changeset, a virtual origin field containing base64 image
data, and a destination field for use with `arc_ecto` to upload
Expand All @@ -18,7 +20,7 @@ defmodule CodeCorps.Base64ImageUploader do
defp do_upload_image(changeset, image_content, destination_field) do
plug_upload =
image_content
|> CodeCorps.Base64Image.save_to_file()
|> Base64ImageService.save_to_file()
|> build_plug_upload

changeset
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule CodeCorps.DonationGoalsManager do
defmodule CodeCorps.Services.DonationGoalsService do
@moduledoc """
Handles CRUD operations for donation goals.
Expand Down Expand Up @@ -71,7 +71,6 @@ defmodule CodeCorps.DonationGoalsManager do
Updates sibling goals for a `CodeCorps.DonationGoal` by:
- Finding this goal's project
- Calculating the total donations for the project
- Finding the current goal for that project
- Setting `current` on every sibling goal to `false`
- Setting `current` on the found goal to `true`
Expand All @@ -98,53 +97,52 @@ defmodule CodeCorps.DonationGoalsManager do
end
end

defp aggregate_donations(nil), do: 0
defp aggregate_donations(%CodeCorps.StripeConnectPlan{id: plan_id}) do
CodeCorps.StripeConnectSubscription
|> where([s], s.stripe_connect_plan_id == ^plan_id)
|> Repo.aggregate(:sum, :quantity)
end
@doc """
Updates all `CodeCorpes.DonationGoal` records for a project.
To be used when something related to the project's donation goals changes,
but not any of the donation goals directly.
defp default_to_zero(nil), do: 0
defp default_to_zero(value), do: value
For example, a customer could update their subscription, which would change the total
amount in monthly donations to a project, so the current goal might need updating.
It updates all goals for a project by
- finding the current goal for a project
- setting `current` for each of its siblings to false
- setting `current` for the current goal to true
"""
def update_project_goals(%Project{} = project) do
with current_goal <- find_current_goal(project)
do
update_goals(project, current_goal)
end
end

defp find_current_goal(%Project{} = project) do
amount_donated = get_amount_donated(project)
case find_lowest_not_yet_reached(project, amount_donated) do
case find_lowest_not_yet_reached(project) do
nil ->
find_largest_goal(project, amount_donated)
find_largest_goal(project)
%DonationGoal{} = donation_goal ->
donation_goal
end
end

defp find_largest_goal(%Project{id: project_id}, amount_donated) do
defp find_largest_goal(%Project{id: project_id, total_monthly_donated: total_monthly_donated}) do
DonationGoal
|> where([d], d.project_id == ^project_id and d.amount <= ^amount_donated)
|> where([d], d.project_id == ^project_id and d.amount <= ^total_monthly_donated)
|> order_by(desc: :amount)
|> limit(1)
|> Repo.one
end

defp find_lowest_not_yet_reached(%Project{id: project_id}, amount_donated) do
defp find_lowest_not_yet_reached(%Project{id: project_id, total_monthly_donated: total_monthly_donated}) do
DonationGoal
|> where([d], d.project_id == ^project_id and d.amount > ^amount_donated)
|> where([d], d.project_id == ^project_id and d.amount > ^total_monthly_donated)
|> order_by(asc: :amount)
|> limit(1)
|> Repo.one
end

defp get_amount_donated(%Project{id: project_id}) do
# TODO: This should be simplified by having
# subscriptions relate to projects instead of plans
# and by caching the total amount on the project itself

CodeCorps.StripeConnectPlan
|> Repo.get_by(project_id: project_id)
|> aggregate_donations
|> default_to_zero
end

defp update_goals(%Project{id: project_id}, %DonationGoal{} = donation_goal) do
DonationGoal
|> where([d], d.project_id == ^project_id)
Expand All @@ -155,4 +153,7 @@ defmodule CodeCorps.DonationGoalsManager do
|> DonationGoal.set_current_changeset(%{current: true})
|> Repo.update()
end
# if there is no candidate for a current goal,
# then there are no goals at all, so we do nothing
defp update_goals(%Project{}, nil), do: nil
end
2 changes: 1 addition & 1 deletion lib/code_corps/services/markdown_renderer.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule CodeCorps.MarkdownRenderer do
defmodule CodeCorps.Services.MarkdownRendererService do
def render_markdown_to_html(changeset, source_field, destination_field) do
case changeset do
%Ecto.Changeset{valid?: false} ->
Expand Down
25 changes: 25 additions & 0 deletions lib/code_corps/services/project.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule CodeCorps.Services.ProjectService do
@moduledoc """
Handles special CRUD operations for `CodeCorps.Project`.
"""

import Ecto.Query

alias CodeCorps.{Project, Repo, StripeConnectPlan, StripeConnectSubscription}

def update_project_totals(%Project{stripe_connect_plan: %StripeConnectPlan{id: plan_id}} = project) do
total_monthly_donated =
StripeConnectSubscription
|> where([s], s.status == "active" and s.stripe_connect_plan_id == ^plan_id)
|> Repo.aggregate(:sum, :quantity)

total_monthly_donated = default_to_zero(total_monthly_donated)

project
|> Project.update_total_changeset(%{total_monthly_donated: total_monthly_donated})
|> Repo.update
end

defp default_to_zero(nil), do: 0
defp default_to_zero(value), do: value
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule CodeCorps.StripeService.Adapters.StripeConnectAccount do
defmodule CodeCorps.StripeService.Adapters.StripeConnectAccountAdapter do
import CodeCorps.MapUtils, only: [rename: 3, keys_to_string: 1]

@stripe_attributes [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule CodeCorps.StripeService.Adapters.StripeConnectCard do
defmodule CodeCorps.StripeService.Adapters.StripeConnectCardAdapter do
import CodeCorps.MapUtils, only: [rename: 3, keys_to_string: 1]

@stripe_attributes [:id]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule CodeCorps.StripeService.Adapters.StripeConnectCustomer do
defmodule CodeCorps.StripeService.Adapters.StripeConnectCustomerAdapter do
import CodeCorps.MapUtils, only: [rename: 3, keys_to_string: 1]

def to_params(%Stripe.Customer{} = customer, %{} = attributes) do
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule CodeCorps.StripeService.Adapters.StripeConnectPlan do
defmodule CodeCorps.StripeService.Adapters.StripeConnectPlanAdapter do
@moduledoc """
Used for conversion between stripe api payload maps and maps
usable for creation of `StripeConnectPlan` records locally
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule CodeCorps.StripeService.Adapters.StripeConnectSubscription do
defmodule CodeCorps.StripeService.Adapters.StripeConnectSubscriptionAdapter do
@moduledoc """
Used for conversion between stripe api payload maps and maps
usable for creation of StripeConnectSubscription records locally
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule CodeCorps.StripeService.Adapters.StripePlatformCard do
defmodule CodeCorps.StripeService.Adapters.StripePlatformCardAdapter do
import CodeCorps.MapUtils, only: [rename: 3, keys_to_string: 1]

@stripe_attributes [:brand, :customer, :cvc_check, :exp_month, :exp_year, :id, :last4, :name, :user_id]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule CodeCorps.StripeService.Adapters.StripePlatformCustomer do
defmodule CodeCorps.StripeService.Adapters.StripePlatformCustomerAdapter do
import CodeCorps.MapUtils, only: [rename: 3, keys_to_string: 1]

def to_params(%Stripe.Customer{} = customer, %{} = attributes) do
Expand Down
Loading

0 comments on commit a58d8d9

Please sign in to comment.