Skip to content

Commit

Permalink
Add support for nested credit card creation
Browse files Browse the repository at this point in the history
  • Loading branch information
sorentwo committed Feb 1, 2016
1 parent 32a6df9 commit b6ee3c7
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 10 deletions.
60 changes: 60 additions & 0 deletions lib/credit_card.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
defmodule Braintree.CreditCard do

@type timestamp :: {{integer, integer, integer}, {integer, integer, integer}}

@type t :: %__MODULE__{
bin: String.t,
card_type: String.t,
cardholder_name: String.t,
commercial: String.t,
country_of_issuance: String.t,
customer_id: String.t,
customer_location: String.t,
debit: String.t,
default: String.t,
durbin_regulated: String.t,
expiration_month: String.t,
expiration_year: String.t,
expired: String.t,
healthcare: String.t,
image_url: String.t,
issuing_bank: String.t,
last_4: String.t,
payroll: String.t,
prepaid: String.t,
token: String.t,
unique_number_identifier: String.t,
created_at: timestamp,
updated_at: timestamp,
venmo_sdk: boolean,
subscriptions: [],
verifications: []
}

defstruct bin: nil,
card_type: nil,
cardholder_name: nil,
commercial: "Unknown",
country_of_issuance: "Unknown",
customer_id: nil,
customer_location: nil,
debit: "Unknown",
default: false,
durbin_regulated: "Unknown",
expiration_month: nil,
expiration_year: nil,
expired: nil,
healthcare: "Unknown",
image_url: nil,
issuing_bank: "Unknown",
last_4: nil,
payroll: "Unknown",
prepaid: "Unknown",
token: nil,
unique_number_identifier: nil,
created_at: nil,
updated_at: nil,
venmo_sdk: "Unknown",
subscriptions: [],
verifications: []
end
17 changes: 14 additions & 3 deletions lib/customer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule Braintree.Customer do

import Braintree.Util, only: [atomize: 1]

alias Braintree.CreditCard

@type timestamp :: {{integer, integer, integer}, {integer, integer, integer}}

@type t :: %__MODULE__{
Expand Down Expand Up @@ -39,13 +41,22 @@ defmodule Braintree.Customer do
paypal_accounts: [],
coinbase_accounts: []

@spec create(Map.t) :: {:ok, t} | {:error, t}
@spec create(Map.t) :: {:ok, t} | {:error, Map.t}
def create(params \\ %{}) do
case post("customers", %{customer: params}) do
{:ok, %{"customer" => customer}} -> {:ok, structize(customer)}
{:ok, %{"customer" => customer}} -> {:ok, construct(customer)}
{:error, something} -> {:error, something}
end
end

def structize(map), do: struct(__MODULE__, atomize(map))
@doc false
def construct(map) do
company = struct(__MODULE__, atomize(map))

%{company | credit_cards: construct_cards(company.credit_cards)}
end

defp construct_cards(credit_cards) do
Enum.map(credit_cards, &(struct(CreditCard, atomize(&1))))
end
end
12 changes: 6 additions & 6 deletions lib/http.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ defmodule Braintree.HTTP do
end
"""
@spec request(atom, binary, binary, headers, Keyword.t) ::
{:ok, Response.t | AsyncResponse.t} | {:error, Error.t}
{:ok, Response.t | AsyncResponse.t} | {:error, integer, Response.t} | {:error, Error.t}
def request(method, url, body, headers \\ [], options \\ []) do
super(method, url, body, headers, options ++ @options) |> process_response
end
Expand All @@ -58,7 +58,7 @@ defmodule Braintree.HTTP do
environment = Application.get_env(:braintree, :environment, :sandbox)
merchant_id = Application.get_env(:braintree, :merchant_id)

Keyword.get(@endpoints, environment) <> merchant_id <> "/" <> path
Keyword.fetch!(@endpoints, environment) <> merchant_id <> "/" <> path
end

@doc false
Expand Down Expand Up @@ -89,11 +89,11 @@ defmodule Braintree.HTTP do
when code >= 200 and code <= 399,
do: {:ok, body}

def process_response({_, %Response{status_code: code, body: body}}),
do: {:error, code, body}
def process_response({:ok, %Response{body: body}}),
do: {:error, body}

def process_response({code, %HTTPoison.Error{reason: reason}}),
do: {:error, code, inspect(reason)}
def process_response({_code, %HTTPoison.Error{reason: reason}}),
do: {:error, inspect(reason)}

## Helpers

Expand Down
4 changes: 4 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Braintree.Mixfile do
[app: :braintree,
version: "0.0.1",
elixir: "~> 1.2",
elixirc_paths: elixirc_paths(Mix.env),
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
aliases: aliases,
Expand All @@ -15,6 +16,9 @@ defmodule Braintree.Mixfile do
[applications: [:logger, :httpoison]]
end

defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]

defp deps do
[httpoison: "~> 0.8",
quinn: "~> 0.0.4"]
Expand Down
23 changes: 22 additions & 1 deletion test/customer_test.exs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
defmodule Braintree.CustomerTest do
use ExUnit.Case, async: true

alias Braintree.Customer

test "all customer attributes are included" do
customer = %Braintree.Customer{
customer = %Customer{
company: "Soren",
email: "parker@example.com",
first_name: "Parker",
Expand All @@ -19,4 +21,23 @@ defmodule Braintree.CustomerTest do
assert customer.credit_cards == []
assert customer.paypal_accounts == []
end

test "construct/1 converts a map to known structs" do
customer = Customer.construct(%{
"company" => "Soren",
"email" => "parker@example.com",
"credit_cards" => [%{
"bin" => "12345",
"card_type" => "Visa"
}]
})

assert customer.company == "Soren"
assert Enum.any?(customer.credit_cards)

[card] = customer.credit_cards

assert card.bin == "12345"
assert card.card_type == "Visa"
end
end
28 changes: 28 additions & 0 deletions test/integration/customer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ defmodule Braintree.Integration.CustomerTest do
@moduletag :integration

alias Braintree.Customer
alias Braintree.Test.CreditCardNumbers

def master_card do
CreditCardNumbers.master_cards |> List.first
end

test "create/1 without any params" do
assert {:ok, _customer} = Customer.create()
Expand All @@ -29,4 +34,27 @@ defmodule Braintree.Integration.CustomerTest do
assert customer.created_at
assert customer.updated_at
end

test "create/1 with a credit card" do
{:ok, customer} = Customer.create(
first_name: "Parker",
last_name: "Selbert",
credit_card: %{
number: master_card,
expiration_date: "01/2016",
cvv: "100"
}
)

assert customer.first_name == "Parker"
assert customer.last_name == "Selbert"

[card] = customer.credit_cards

assert card.bin == String.slice(master_card, 0..5)
assert card.last_4 == String.slice(master_card, -4..-1)
assert card.expiration_month == "01"
assert card.expiration_year == "2016"
assert card.unique_number_identifier =~ ~r/\A\w{32}\z/
end
end
79 changes: 79 additions & 0 deletions test/support/credit_card_numbers.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
defmodule Braintree.Test.CreditCardNumbers do
@moduledoc """
# The functions contained in the Braintree.Test.CreditCardNumbers module
# provide credit card numbers that should be used when working in the sandbox
# environment. The sandbox will not accept any credit card numbers other than
# the ones listed below.
#
# See http://www.braintreepayments.com/docs/ruby/reference/sandbox
"""

def all do
am_exes ++
carte_blanches ++
diners_clubs ++
discovers ++
jcbs ++
master_cards ++
unknowns ++
visas
end

def am_exes do
~w(378282246310005 371449635398431 378734493671000)
end

def carte_blanches do
~w(30569309025904)
end

def diners_clubs do
~w(38520000023237)
end

def discovers do
~w(6011111111111117 6011000990139424)
end

def jcbs do
~w(3530111333300000 3566002020360505)
end

def master_cards do
~w(5105105105105100 5555555555554444)
end

def unknowns do
~w(1000000000000008)
end

def visas do
~w(4009348888881881 4012888888881881 4111111111111111 4000111111111115 4500600000000061)
end

defmodule FailsSandboxVerification do
@moduledoc """
These are vendor specific numbers that will always fail verification.
"""

def all do
[am_ex, discover, master_card, visa]
end

def am_ex do
"378734493671000"
end

def discover do
"6011000990139424"
end

def master_card do
"5105105105105100"
end

def visa do
"4000111111111115"
end
end
end

0 comments on commit b6ee3c7

Please sign in to comment.