Skip to content

Commit

Permalink
Merge branch 'master' into payment-intent
Browse files Browse the repository at this point in the history
  • Loading branch information
nkezhaya committed Oct 16, 2019
2 parents 667487c + ff8380d commit 67e34d8
Show file tree
Hide file tree
Showing 28 changed files with 277 additions and 317 deletions.
19 changes: 17 additions & 2 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ config :stripe_mock, StripeMockWeb.Endpoint,
url: [host: "localhost"],
http: [:inet6, port: System.get_env("PORT") || 12111],
secret_key_base: "ug9ATr9o7f/N2inxnW+SlrNVP7Ok+f9gAP43yHfqqm/bgFZSLeY6vQOY+wp562Iz",
render_errors: [view: StripeMockWeb.ErrorView, accepts: ~w(json)],
pubsub: [name: StripeMock.PubSub, adapter: Phoenix.PubSub.PG2]
render_errors: [view: StripeMockWeb.ErrorView, accepts: ~w(json)]

config :stripe_mock, ecto_repos: [StripeMock.Repo]

# Configures Elixir's Logger
config :logger, :console,
Expand All @@ -23,6 +24,20 @@ config :logger, :console,
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason

{uri, _} = System.cmd("pg_tmp", ["-t"])

[username, host, port, database] =
Regex.scan(~r/(\w+)@([\w\d\.]+)\:(\d+)\/(\w+)/i, uri, capture: :all_but_first) |> List.flatten()

config :stripe_mock, StripeMock.Repo,
username: username,
password: "",
database: database,
hostname: host,
port: port,
pool_size: 2,
migration_primary_key: [name: :id, type: :binary_id]

# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"
5 changes: 3 additions & 2 deletions lib/stripe_mock/api/card.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ defmodule StripeMock.API.Card do

schema "cards" do
field :brand, :string
field :created, :integer
field :deleted, :boolean
field :metadata, StripeMock.Type.Metadata, default: %{}
field :metadata, :map, default: %{}
field :last4, :string
field :source, :string

Expand All @@ -15,6 +14,8 @@ defmodule StripeMock.API.Card do
field :cvc, :string

belongs_to :customer, API.Customer

timestamps()
end

@doc false
Expand Down
95 changes: 31 additions & 64 deletions lib/stripe_mock/api/charge.ex
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
defmodule StripeMock.API.Charge do
use StripeMock.Schema
alias StripeMock.Repo

@foreign_key_type :binary_id
schema "charges" do
field :amount, :integer
field :capture, :boolean, default: false
field :currency, :string
field :description, :string
field :metadata, StripeMock.Type.Metadata, default: %{}
field :metadata, :map, default: %{}
field :statement_descriptor, :string
field :transfer_group, :string

field :source, :string, virtual: true

belongs_to :customer, API.Customer
belongs_to :source, API.Card
belongs_to :card, API.Card
belongs_to :token, API.Token

timestamps()
end

@doc false
Expand All @@ -26,99 +29,63 @@ defmodule StripeMock.API.Charge do
:customer_id,
:description,
:metadata,
:source_id,
:source,
:statement_descriptor,
:transfer_group
])
|> validate_required([:amount, :currency])
|> set_customer_and_source()
|> validate_required(:source_id)
|> validate_required([:amount, :currency, :card_id])
|> put_common_fields()
end

@doc false
def update_changeset(charge, attrs) do
charge
|> cast(attrs, [
:customer_id,
:description,
:metadata,
:transfer_group
])
|> validate_required([:amount, :currency])
|> put_common_fields()
end

@doc false
def capture_changeset(payment_intent, charge) do
end

defp set_customer_and_source(changeset) do
customer =
with customer_id when not is_nil(customer_id) <- get_field(changeset, :customer_id) do
case Repo.fetch(API.Customer, customer_id) do
{:ok, customer} -> customer
_ -> throw({:not_found, :customer_id})
end
else
_ -> nil
end

source =
case fetch_source(get_field(changeset, :source_id)) do
:invalid -> throw({:not_found, :source_id})
case fetch_source(get_field(changeset, :source)) do
nil -> throw({:not_found, :source})
source -> source
end

case {get_field(changeset, :customer_id), get_field(changeset, :source_id)} do
{nil, nil} ->
validate_required(changeset, :source_id)

{nil, _source_id} ->
case source do
%{card: %{customer_id: nil}} -> changeset
_ -> validate_required(changeset, :customer_id)
end

{customer_id, nil} ->
# TODO: Get the default payment method.
API.Card
|> Repo.all()
|> Enum.filter(&(&1.customer_id == customer_id))
|> case do
[source | _] -> put_change(changeset, :source_id, source.id)
[] -> throw({:not_found, :source_id})
end

{nil, "card_" <> _} ->
throw({:not_found, :source_id})

{_customer_id, "card_" <> _} ->
if source.customer_id != customer.id do
throw({:not_found, :source_id})
else
changeset
end
case source do
nil ->
add_error(changeset, :base, "either source or customer is required")

{_customer_id, "tok_" <> _} ->
%API.Card{} ->
changeset
|> put_change(:card_id, source.id)
|> validate_required(:customer_id)

_ ->
add_error(changeset, :base, "either source or customer is required")
%API.Token{} ->
changeset
|> put_change(:token_id, source.id)
|> put_change(:card_id, source.card_id)
end
|> ensure_source()
catch
{:not_found, field} -> add_error(changeset, field, "not found")
end

defp ensure_source(changeset) do
with source when is_map(source) <- fetch_source(get_field(changeset, :source_id)) do
changeset
else
_ -> add_error(changeset, :source, "is invalid")
end
defp fetch_source(nil) do
nil
end

defp fetch_source("tok_" <> _ = token_id), do: Repo.get(API.Token, token_id)
defp fetch_source("card_" <> _ = card_id), do: Repo.get(API.Card, card_id)
defp fetch_source(nil), do: nil
defp fetch_source(_), do: :invalid
defp fetch_source(source) do
card = Repo.get(API.Card, source)
token = Repo.get(API.Token, source)

card || token
end
end
6 changes: 3 additions & 3 deletions lib/stripe_mock/api/customer.ex
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
defmodule StripeMock.API.Customer do
use StripeMock.Schema

@primary_key {:id, :binary_id, autogenerate: false}
schema "customers" do
field :created, :integer
field :currency, :string, default: "usd"
field :deleted, :boolean, default: false
field :description, :string
field :email, :string
field :metadata, StripeMock.Type.Metadata, default: %{}
field :metadata, :map, default: %{}
field :name, :string
field :phone, :string

timestamps()
end

@doc false
Expand Down
24 changes: 18 additions & 6 deletions lib/stripe_mock/api/operations/card.ex
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
defmodule StripeMock.API.Operations.Card do
import Ecto.Query
alias Ecto.Multi
alias StripeMock.Repo
alias StripeMock.API.Card
alias StripeMock.API.{Card, Source}

def list_cards(customer) do
Card
|> where([c], not c.deleted)
|> where([c], c.customer_id == ^customer.id)
|> Repo.all()
|> Enum.filter(&(&1.customer_id == customer.id))
end

def get_card(id), do: Repo.fetch(Card, id)
def get_card!(id), do: Repo.get!(Card, id)

def create_card(customer, attrs) do
%Card{customer_id: customer.id}
|> Card.create_changeset(attrs)
|> Repo.insert()
Multi.new()
|> Multi.insert(:card, Card.create_changeset(%Card{customer_id: customer.id}, attrs))
|> Multi.run(:source, fn _repo, %{card: card} ->
%Source{}
|> Source.changeset(%{card_id: card.id})
|> Repo.insert()
end)
|> Repo.transaction()
|> case do
{:ok, %{card: card}} -> {:ok, card}
{:error, _, value, _} -> {:error, value}
end
end

def create_customer_card_from_source(customer, source, metadata) do
result =
source.card
|> Ecto.Changeset.change(%{customer_id: customer.id, metadata: metadata})
|> Ecto.Changeset.change(%{customer_id: customer.id, metadata: metadata || %{}})
|> Repo.update()

source
Expand Down
35 changes: 12 additions & 23 deletions lib/stripe_mock/api/operations/charge.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule StripeMock.API.Operations.Charge do
alias StripeMock.Repo
alias StripeMock.API.{Card, Charge, Token}
alias StripeMock.API.Charge

def list_charges() do
Charge
Expand All @@ -14,20 +14,6 @@ defmodule StripeMock.API.Operations.Charge do
end
end

defp preload_source(charges) when is_list(charges) do
Enum.map(charges, &preload_source/1)
end

defp preload_source({:ok, charge}) do
{:ok, preload_source(charge)}
end

defp preload_source(%Charge{} = charge) do
%{charge | source: fetch_source(charge.source_id)}
end

defp preload_source(any), do: any

def create_charge(attrs \\ %{}) do
%Charge{}
|> Charge.create_changeset(attrs)
Expand All @@ -37,19 +23,22 @@ defmodule StripeMock.API.Operations.Charge do

def update_charge(%Charge{} = charge, attrs) do
charge
|> Charge.create_changeset(attrs)
|> Charge.update_changeset(attrs)
|> Repo.update()
|> preload_source()
end

defp fetch_source("card_" <> _ = card_id), do: Repo.get(Card, card_id)
defp preload_source({:ok, charge}) do
{:ok, preload_source(charge)}
end

defp fetch_source("tok_" <> _ = token_id) do
case Repo.get(Token, token_id) do
%{card_id: card_id} -> fetch_source(card_id)
_ -> nil
end
defp preload_source(%Charge{} = charge) do
Repo.preload(charge, [:card, :token])
end

defp preload_source([_ | _] = charges) do
Repo.preload(charges, [:card, :token])
end

defp fetch_source(nil), do: nil
defp preload_source(arg), do: arg
end
24 changes: 19 additions & 5 deletions lib/stripe_mock/api/operations/token.ex
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
defmodule StripeMock.API.Operations.Token do
import Ecto.Query

alias Ecto.Multi
alias StripeMock.Repo
alias StripeMock.API.Token
alias StripeMock.API.{Token, Source}

def get_token(id) do
Repo.fetch(Token, id)
Token
|> preload(:card)
|> Repo.fetch(id)
end

def create_token(attrs) do
%Token{}
|> Token.changeset(attrs)
|> Repo.insert()
Multi.new()
|> Multi.insert(:token, Token.changeset(%Token{}, attrs))
|> Multi.run(:source, fn _repo, %{token: token} ->
%Source{}
|> Source.changeset(%{token_id: token.id})
|> Repo.insert()
end)
|> Repo.transaction()
|> case do
{:ok, %{token: token}} -> {:ok, token}
{:error, _, value, _} -> {:error, value}
end
end
end
6 changes: 3 additions & 3 deletions lib/stripe_mock/api/refund.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ defmodule StripeMock.API.Refund do
use StripeMock.Schema
alias StripeMock.Repo

@foreign_key_type :binary_id
schema "refunds" do
field :amount, :integer
field :created, :integer
field :metadata, StripeMock.Type.Metadata, default: %{}
field :metadata, :map, default: %{}
field :reason, :string

belongs_to :charge, API.Charge

timestamps()
end

@doc false
Expand Down
14 changes: 14 additions & 0 deletions lib/stripe_mock/api/source.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
defmodule StripeMock.API.Source do
use StripeMock.Schema

schema "sources" do
belongs_to :card, API.Card
belongs_to :token, API.Card
end

@doc false
def changeset(token, attrs) do
token
|> cast(attrs, [:card_id, :token_id])
end
end
Loading

0 comments on commit 67e34d8

Please sign in to comment.