Skip to content

Commit

Permalink
Add Stripe.Card
Browse files Browse the repository at this point in the history
  • Loading branch information
green-arrow authored and joshsmith committed Nov 12, 2016
1 parent ae5c995 commit 3b796b2
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 0 deletions.
134 changes: 134 additions & 0 deletions lib/stripe/card.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
defmodule Stripe.Card do
@moduledoc """
Work with Stripe card objects.
You can:
- Create a card
- Retrieve a card
- Update a card
- Delete a card
All requests require `owner_type` and `owner_id` parameters to be specified.
`owner_type` must be one of the following:
* `:customer`,
* `:recipient`.
`owner_id` must be the ID of the owning object.
This module does not yet support managed accounts.
Recipients may be deprecated for your version of the API. They have
been replaced by managed accounts (see
https://stripe.com/docs/connect/managed-accounts), which you should use
if you're creating a new platform.
Stripe API reference: https://stripe.com/docs/api#cards
"""

alias Stripe.Util

@type t :: %__MODULE__{}
@type source :: :customer | :recipient

defstruct [
:id, :address_city, :address_country, :address_line1,
:address_line1_check, :address_line2, :address_state,
:address_zip, :address_zip_check, :brand, :country,
:customer, :cvc_check, :dynamic_last4, :exp_month, :exp_year,
:fingerprint, :funding, :last4, :metadata, :name, :recipient,
:tokenization_method
]

@valid_update_keys [
:address_city, :address_country, :address_line1, :address_line2,
:address_state, :address_zip, :exp_month, :exp_year, :metadata, :name
]

defp endpoint_for_owner(owner_type, owner_id) do
case owner_type do
:customer -> "customers/#{owner_id}/sources"
:recipient -> "recipients/#{owner_id}/cards"
end
end

@doc """
Create a card.
This requires a `token` created by a library like Stripe.js.
For PCI compliance reasons you should not send a card's number or CVC
to your own server.
If you want to create a card with your server without a token, you
can use the low-level API.
"""
@spec create(source, String.t, String.t, Keyword.t) :: {:ok, t} | {:error, Exception.t}
def create(owner_type, owner_id, token, opts \\ []) do
endpoint = endpoint_for_owner(owner_type, owner_id)
body =
to_create_body(owner_type, token)
|> Util.map_keys_to_atoms()

case Stripe.request(:post, endpoint, body, %{}, opts) do
{:ok, result} -> {:ok, Util.stripe_response_to_struct(%__MODULE__{}, result)}
{:error, error} -> {:error, error}
end
end

@spec to_create_body(source, String.t) :: map
defp to_create_body(owner_type, token) do
case owner_type do
:customer -> %{source: token}
:recipient -> %{external_account: token}
end
end

@doc """
Retrieve a card.
"""
@spec retrieve(source, String.t, String.t, Keyword.t) :: {:ok, t} | {:error, Exception.t}
def retrieve(owner_type, owner_id, card_id, opts \\ []) do
endpoint = endpoint_for_owner(owner_type, owner_id) <> "/" <> card_id

case Stripe.request(:get, endpoint, %{}, %{}, opts) do
{:ok, result} -> {:ok, Util.stripe_response_to_struct(%__MODULE__{}, result)}
{:error, error} -> {:error, error}
end
end

@doc """
Update a card.
Takes the `id` and a map of changes
"""
@spec update(source, String.t, String.t, map, Keyword.t) :: {:ok, t} | {:error, Exception.t}
def update(owner_type, owner_id, card_id, changes, opts \\ []) do
endpoint = endpoint_for_owner(owner_type, owner_id) <> "/" <> card_id

card =
changes
|> Util.map_keys_to_atoms()
|> Map.take(@valid_update_keys)
|> Util.drop_nil_keys()

case Stripe.request(:post, endpoint, card, %{}, opts) do
{:ok, result} -> {:ok, Util.stripe_response_to_struct(%__MODULE__{}, result)}
{:error, error} -> {:error, error}
end
end

@doc """
Delete a card.
"""
@spec delete(source, String.t, String.t, Keyword.t) :: :ok | {:error, Exception.t}
def delete(owner_type, owner_id, card_id, opts \\ []) do
endpoint = endpoint_for_owner(owner_type, owner_id) <> "/" <> card_id

case Stripe.request(:delete, endpoint, %{}, %{}, opts) do
{:ok, _} -> :ok
{:error, error} -> {:error, error}
end
end
end
16 changes: 16 additions & 0 deletions lib/stripe/util.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
defmodule Stripe.Util do

@type stripe_response :: %{String.t => any}

@spec stripe_response_to_struct(struct, stripe_response) :: struct
def stripe_response_to_struct(struct, stripe_response) do
keys = case struct do
%{__struct__: _t} -> struct |> Map.from_struct |> Map.keys
_ -> raise "First argument to 'stripe_response_to_struct' must be a struct."
end

Enum.reduce keys, struct, fn (atom, acc) ->
str = to_string(atom)
value = Map.get(stripe_response, str)
Map.put(acc, atom, value)
end
end

def drop_nil_keys(map) do
Enum.reject(map, fn
{_, nil} -> true
Expand Down

0 comments on commit 3b796b2

Please sign in to comment.