Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule CodeCorps.StripeService.Events.CustomerSourceUpdated do
def handle(%{"data" => %{"object" => %{"id" => card_id, "object" => "card"}}}) do
CodeCorps.StripeService.StripePlatformCardService.update_from_stripe(card_id)
end

def handle(%{"data" => %{"object" => %{"id" => _, "object" => _}}}), do: {:error, :unsupported_object}
end
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
defmodule CodeCorps.StripeService.Events.CustomerSubscriptionDeleted do
@api Application.get_env(:code_corps, :stripe)

def handle(%{"data" => %{"object" => %{"id" => stripe_sub_id, "customer" => connect_customer_id}}}) do
CodeCorps.StripeService.StripeConnectSubscriptionService.update_from_stripe(stripe_sub_id, connect_customer_id)
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
defmodule CodeCorps.StripeService.Events.CustomerSubscriptionUpdated do
@api Application.get_env(:code_corps, :stripe)

def handle(%{"data" => %{"object" => %{"id" => stripe_sub_id, "customer" => connect_customer_id}}}) do
CodeCorps.StripeService.StripeConnectSubscriptionService.update_from_stripe(stripe_sub_id, connect_customer_id)
end
Expand Down
19 changes: 19 additions & 0 deletions lib/code_corps/stripe_service/stripe_connect_card.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ defmodule CodeCorps.StripeService.StripeConnectCardService do
end
end

def update(%StripeConnectCard{} = record, %{} = attributes) do
with {:ok, %Stripe.Card{} = updated_stripe_card} <- update_on_stripe(record, attributes)
do
{:ok, updated_stripe_card}
else
{:error, %Stripe.APIErrorResponse{} = error} -> {:error, error}
_ -> {:error, :unhandled}
end
end

defp create(%StripePlatformCard{} = platform_card, %StripeConnectCustomer{} = connect_customer, %StripePlatformCustomer{} = platform_customer, %StripeConnectAccount{} = connect_account) do
platform_customer_id = platform_customer.id_from_stripe
platform_card_id = platform_card.id_from_stripe
Expand Down Expand Up @@ -55,4 +65,13 @@ defmodule CodeCorps.StripeService.StripeConnectCardService do
|> Map.put(:stripe_connect_account_id, connect_account.id)
|> keys_to_string
end

defp update_on_stripe(%StripeConnectCard{} = record, attributes) do
@api.Card.update(
:customer,
record.stripe_platform_card.customer_id_from_stripe,
record.id_from_stripe,
attributes,
connect_account: record.stripe_connect_account.id_from_stripe)
end
end
4 changes: 2 additions & 2 deletions lib/code_corps/stripe_service/stripe_connect_customer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ defmodule CodeCorps.StripeService.StripeConnectCustomerService do
end
end

def update(%StripeConnectCustomer{stripe_connect_account: connect_account} = connect_customer, attributes) do
@api.Customer.update(connect_customer.id_from_stripe, attributes, connect_account: connect_account.id_from_stripe)
def update(%StripeConnectCustomer{id_from_stripe: id_from_stripe, stripe_connect_account: connect_account} = connect_customer, attributes) do
@api.Customer.update(id_from_stripe, attributes, connect_account: connect_account.id_from_stripe)
end

defp create(%StripePlatformCustomer{} = platform_customer, %StripeConnectAccount{} = connect_account) do
Expand Down
78 changes: 73 additions & 5 deletions lib/code_corps/stripe_service/stripe_platform_card.ex
Original file line number Diff line number Diff line change
@@ -1,27 +1,95 @@
defmodule CodeCorps.StripeService.StripePlatformCardService do
alias CodeCorps.Repo
alias CodeCorps.StripeService.Adapters.StripePlatformCardAdapter
alias CodeCorps.StripePlatformCard
alias CodeCorps.StripePlatformCustomer
alias CodeCorps.StripeService.StripeConnectCardService
alias CodeCorps.{StripeConnectCard, StripePlatformCard, StripePlatformCustomer}

alias Ecto.Multi

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

def create(%{"stripe_token" => stripe_token, "user_id" => user_id} = attributes) do
with %StripePlatformCustomer{} = customer <- get_customer(user_id),
{:ok, card} <- @api.Card.create(:customer, customer.id_from_stripe, stripe_token),
{:ok, params} <- StripePlatformCardAdapter.to_params(card, attributes)
{:ok, card} <- @api.Card.create(:customer, customer.id_from_stripe, stripe_token),
{:ok, params} <- StripePlatformCardAdapter.to_params(card, attributes)
do
%StripePlatformCard{}
|> StripePlatformCard.create_changeset(params)
|> Repo.insert
else
{:error, error} -> {:error, error}
nil -> {:error, :not_found}
{:error, %Stripe.APIErrorResponse{} = error} -> {:error, error}
{:error, %Ecto.Changeset{} = changeset} -> {:error, changeset}
end
end

def update_from_stripe(card_id) do
with {:ok, %StripePlatformCard{} = record} <- get_card(card_id),
{:ok, %Stripe.Card{} = stripe_card} <- get_card_from_stripe(record),
{:ok, params} <- StripePlatformCardAdapter.to_params(stripe_card, %{})
do
perform_update(record, params)
else
nil -> {:error, :not_found}
{:error, %Stripe.APIErrorResponse{} = error} -> {:error, error}
{:error, %Ecto.Changeset{} = changeset} -> {:error, changeset}
end
end

defp get_customer(user_id) do
StripePlatformCustomer
|> CodeCorps.Repo.get_by(user_id: user_id)
end

defp get_card(card_id_from_stripe) do
record = Repo.get_by(StripePlatformCard, id_from_stripe: card_id_from_stripe)
{:ok, record}
end

defp get_card_from_stripe(%StripePlatformCard{id_from_stripe: stripe_id, customer_id_from_stripe: owner_id}) do
@api.Card.retrieve(:customer, owner_id, stripe_id)
end

defp perform_update(record, params) do
changeset = record |> StripePlatformCard.update_changeset(params)

multi =
Multi.new
|> Multi.update(:update_platform_card, changeset)
|> Multi.run(:update_connect_cards, &update_connect_cards/1)

case Repo.transaction(multi) do
{:ok, %{update_platform_card: platform_card_update, update_connect_cards: connect_card_updates}} ->
{:ok, platform_card_update, connect_card_updates}
{:error, :update_platform_card, %Ecto.Changeset{} = changeset, %{}} ->
{:error, changeset}
{:error, _failed_operation, _failed_value, _changes_so_far} ->
{:error, :unhandled}
end
end

defp update_connect_cards(%{update_platform_card: %StripePlatformCard{} = stripe_platform_card}) do
attributes = connect_card_attributes(stripe_platform_card)

case do_update_connect_cards(stripe_platform_card, attributes) do
[_h | _t] = results -> {:ok, results}
[] -> {:ok, nil}
end
end

defp connect_card_attributes(stripe_platform_card) do
stripe_platform_card |> Map.take([:exp_month, :exp_year, :name])
end

defp do_update_connect_cards(stripe_platform_card, attributes) when attributes == %{}, do: []
defp do_update_connect_cards(stripe_platform_card, attributes) do
stripe_platform_card
|> Repo.preload([stripe_connect_cards: [:stripe_connect_account, :stripe_platform_card]])
|> Map.get(:stripe_connect_cards)
|> Enum.map(&do_update_connect_card(&1, attributes))
end

defp do_update_connect_card(%StripeConnectCard{} = stripe_connect_card, attributes) do
stripe_connect_card |> StripeConnectCardService.update(attributes)
end
end
68 changes: 64 additions & 4 deletions lib/code_corps/stripe_testing/card.ex
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
defmodule CodeCorps.StripeTesting.Card do
def create(:customer, _stripe_id, _stripe_token, _opts \\ []) do
{:ok, do_create}
def create(:customer, stripe_id, _stripe_token, _opts \\ []) do
{:ok, do_create(stripe_id)}
end

defp do_create do
defp do_create(stripe_id) do
%Stripe.Card{
id: "card_19IHPnBKl1F6IRFf8w7gpdOe",
id: stripe_id,
address_city: nil,
address_country: nil,
address_line1: nil,
Expand All @@ -28,4 +28,64 @@ defmodule CodeCorps.StripeTesting.Card do
tokenization_method: nil
}
end

def retrieve(:customer, owner_id, stripe_id, _opts \\ []) do
{:ok, do_retrieve(owner_id, stripe_id)}
end

defp do_retrieve(owner_id, stripe_id) do
%Stripe.Card{
id: stripe_id,
address_city: nil,
address_country: nil,
address_line1: nil,
address_line1_check: nil,
address_line2: nil,
address_state: nil,
address_zip: nil,
address_zip_check: nil,
brand: "Visa",
country: "US",
customer: owner_id,
cvc_check: "unchecked",
dynamic_last4: nil,
exp_month: 12,
exp_year: 2020,
funding: "credit",
last4: "4242",
metadata: {},
name: "John Doe",
tokenization_method: nil
}
end

def update(:customer, owner_id, stripe_id, attributes, _opts \\ []) do
{:ok, do_update(owner_id, stripe_id, attributes)}
end

defp do_update(owner_id, stripe_id, %{name: name, exp_month: exp_month, exp_year: exp_year}) do
%Stripe.Card{
id: stripe_id,
address_city: nil,
address_country: nil,
address_line1: nil,
address_line1_check: nil,
address_line2: nil,
address_state: nil,
address_zip: nil,
address_zip_check: nil,
brand: "Visa",
country: "US",
customer: owner_id,
cvc_check: "unchecked",
dynamic_last4: nil,
exp_month: exp_month,
exp_year: exp_year,
funding: "credit",
last4: "4242",
metadata: {},
name: name,
tokenization_method: nil
}
end
end
26 changes: 25 additions & 1 deletion test/controllers/stripe_platform_events_controller_test.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule CodeCorps.StripePlatformEventsControllerTest do
use CodeCorps.ConnCase

alias CodeCorps.StripePlatformCustomer
alias CodeCorps.{StripePlatformCard,StripePlatformCustomer}

setup do
conn =
Expand All @@ -12,6 +12,12 @@ defmodule CodeCorps.StripePlatformEventsControllerTest do
{:ok, conn: conn}
end

@card %{
"id" => "card_19LEnDBKl1F6IRFfjLfJRYuN",
"object" => "card",
"customer" => "cus_9e9KNE2beHhfLy"
}

defp event_for(object, type) do
%{
"api_version" => "2016-07-06",
Expand Down Expand Up @@ -56,4 +62,22 @@ defmodule CodeCorps.StripePlatformEventsControllerTest do
assert platform_customer.email == "hardcoded@test.com"
end
end

describe "customer.source.updated" do
test "returns 200 and updates card when one matches", %{conn: conn} do
event = event_for(@card, "customer.source.updated")
stripe_id = @card["id"]
platform_customer_id = @card["customer"]

insert(:stripe_platform_customer, id_from_stripe: platform_customer_id)
platform_card = insert(:stripe_platform_card, id_from_stripe: stripe_id, customer_id_from_stripe: platform_customer_id)

path = stripe_platform_events_path(conn, :create)
assert conn |> post(path, event) |> response(200)

updated_card = Repo.get_by(StripePlatformCard, id: platform_card.id)
# hardcoded in StripeTesting.Card
assert updated_card.name == "John Doe"
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
defmodule CodeCorps.StripeService.StripeConnectCardServiceTest do
use ExUnit.Case, async: true

use CodeCorps.ModelCase

alias CodeCorps.StripeConnectCard
alias CodeCorps.StripeService.StripeConnectCardService

describe "update/1" do
@attributes %{name: "John Doe", exp_month: 6, exp_year: 2030}

test "it just updates the connect card on Stripe API, not locally" do
connect_card = insert(:stripe_connect_card)

connect_card =
StripeConnectCard
|> Repo.get(connect_card.id)
|> Repo.preload([:stripe_platform_card, :stripe_connect_account])

updated_at = connect_card.updated_at

{:ok, %Stripe.Card{} = stripe_card} =
StripeConnectCardService.update(connect_card, @attributes)

assert stripe_card.id == connect_card.id_from_stripe
assert stripe_card.name == "John Doe"
assert stripe_card.exp_year == 2030
assert stripe_card.exp_month == 6

connect_card = Repo.get(StripeConnectCard, connect_card.id)

assert connect_card.updated_at == updated_at
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
defmodule CodeCorps.StripeService.StripePlatformCardServiceTest do
use ExUnit.Case, async: true

use CodeCorps.ModelCase

alias CodeCorps.StripePlatformCard
alias CodeCorps.StripeService.StripePlatformCardService

describe "update_from_stripe/1" do
test "it just updates the platform card if there is nothing associated to update" do
platform_card = insert(:stripe_platform_card)

{:ok, %StripePlatformCard{} = platform_card, nil} =
StripePlatformCardService.update_from_stripe(platform_card.id_from_stripe)

assert platform_card.exp_year == 2020
end

# TODO: We can't really do this test until we are able to mock stripe API data
# test "it returns an {:error, changeset} if there are validation errors with the platform_card" do
# platform_card = insert(:stripe_platform_card)

# {:error, changeset} =
# StripePlatformCardService.update_from_stripe(platform_card.id_from_stripe)

# refute changeset.valid?
# end

test "it also updates the associated connect cards if there are any" do
platform_card = insert(:stripe_platform_card)

[connect_card_1, connect_card_2] = insert_pair(:stripe_connect_card, stripe_platform_card: platform_card)

{:ok, %StripePlatformCard{} = platform_card, connect_updates} =
StripePlatformCardService.update_from_stripe(platform_card.id_from_stripe)

assert platform_card.exp_year == 2020

platform_card = Repo.get(StripePlatformCard, platform_card.id)
assert platform_card.exp_year == 2020

[
{:ok, %Stripe.Card{} = stripe_record_1},
{:ok, %Stripe.Card{} = stripe_record_2}
] = connect_updates

assert stripe_record_1.id == connect_card_1.id_from_stripe
assert stripe_record_1.exp_year == 2020
assert stripe_record_2.id == connect_card_2.id_from_stripe
assert stripe_record_2.exp_year == 2020
end
end
end
Loading