diff --git a/README.md b/README.md index 7b1f4227..fb508bd4 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ address = %Address{ opts = [address: address, currency: "eur"] -case Gringotts.purchase(:payment_worker, Stripe, 10, card, opts) do +case Gringotts.purchase(Stripe, 10, card, opts) do {:ok, %{authorization: authorization}} -> IO.puts("Payment authorized #{authorization}") @@ -91,15 +91,14 @@ end ## Supported Gateways -* [Stripe](https://stripe.com/) - AT, AU, BE, CA, CH, DE, DK, ES, FI, FR, GB, IE, IN, IT, LU, NL, NO, SE, SG, US -* [PAYMILL](https://paymill.com) - AD, AT, BE, BG, CH, CY, CZ, DE, DK, EE, ES, FI, FO, FR, GB, GI, GR, HU, IE, IL, IS, IT, LI, LT, LU, LV, MT, NL, NO, PL, PT, RO, SE, SI, SK, TR, VA * [Authorize.Net](http://www.authorize.net/) - AD, AT, AU, BE, BG, CA, CH, CY, CZ, DE, DK, ES, FI, FR, GB, GB, GI, GR, HU, IE, IT, LI, LU, MC, MT, NL, NO, PL, PT, RO, SE, SI, SK, SM, TR, US, VA - -* [MONEI](http://www.monei.net/) - AD, AT, BE, BG, CA, CH, CY, CZ, DE, DK, EE, ES, FI, FO, FR, GB, GI, GR, HU, IE, IL, IS, IT, LI, LT, LU, LV, MT, NL, NO, PL, PT, RO, SE, SI, SK, TR, US, VA * [CAMS: Central Account Management System](https://www.centralams.com/) - AU, US +* [MONEI](http://www.monei.net/) - AD, AT, BE, BG, CA, CH, CY, CZ, DE, DK, EE, ES, FI, FO, FR, GB, GI, GR, HU, IE, IL, IS, IT, LI, LT, LU, LV, MT, NL, NO, PL, PT, RO, SE, SI, SK, TR, US, VA +* [PAYMILL](https://paymill.com) - AD, AT, BE, BG, CH, CY, CZ, DE, DK, EE, ES, FI, FO, FR, GB, GI, GR, HU, IE, IL, IS, IT, LI, LT, LU, LV, MT, NL, NO, PL, PT, RO, SE, SI, SK, TR, VA +* [Stripe](https://stripe.com/) - AT, AU, BE, CA, CH, DE, DK, ES, FI, FR, GB, IE, IN, IT, LU, NL, NO, SE, SG, US +* [TREXLE](https://docs.trexle.com/) - AD, AE, AT, AU, BD, BE, BG, BN, CA, CH, CY, CZ, DE, DK, EE, EG, ES, FI, FR, GB, GI, GR, HK, HU, ID, IE, IL, IM, IN, IS, IT, JO, KW, LB, LI, LK, LT, LU, LV, MC, MT, MU, MV, MX, MY, NL, NO, NZ, OM, PH, PL, PT, QA, RO, SA, SE, SG, SI, SK, SM, TR, TT, UM, US, VA, VN, ZA * [Wirecard](http://www.wirecard.com) - AD, CY, GI, IM, MT, RO, CH, AT, DK, GR, IT, MC, SM, TR, BE, EE, HU, LV, NL, SK, GB, BG, FI, IS, LI, NO, SI, VA, FR, IL, LT, PL, ES, CZ, DE, IE, LU, PT, SE - ## Road Map - Support more gateways on an on-going basis. diff --git a/lib/gringotts.ex b/lib/gringotts.ex index cbeed485..d1440490 100644 --- a/lib/gringotts.ex +++ b/lib/gringotts.ex @@ -36,14 +36,6 @@ defmodule Gringotts do The public API is designed in such a way that library users end up passing mostly a standard params for almost all requests. - ### Worker Name - eg: :payment_worker - - The standard central supervised worker responsible for delegating/calling all - the payment specific methods such as `authorise` & `purchase`. - - > This option is going to be removed in our next version. - ### Gateway Name eg: Gringotts.Gateways.Stripe @@ -103,15 +95,15 @@ defmodule Gringotts do @options [currency: "usd"] - Gringotts.purchase(:payment_worker, Gringotts.Gateways.Stripe, 5, @payment, @options) + Gringotts.purchase(Gringotts.Gateways.Stripe, 5, @payment, @options) This method is expected to authorize payment and transparently trigger eventual settlement. Preferably it is implemented as a single call to the gateway, but it can also be implemented as chained `authorize` and `capture` calls. """ - def purchase(worker, gateway, amount, card, opts \\ []) do + def purchase(gateway, amount, card, opts \\ []) do validate_config(gateway) - call(worker, {:purchase, gateway, amount, card, opts}) + call(:payment_worker, {:purchase, gateway, amount, card, opts}) end @doc """ @@ -135,11 +127,11 @@ defmodule Gringotts do @options [currency: "usd"] - Gringotts.authorize(:payment_worker, Gringotts.Gateways.Stripe, 5, @payment, @options) + Gringotts.authorize(Gringotts.Gateways.Stripe, 5, @payment, @options) """ - def authorize(worker, gateway, amount, card, opts \\ []) do + def authorize(gateway, amount, card, opts \\ []) do validate_config(gateway) - call(worker, {:authorize, gateway, amount, card, opts}) + call(:payment_worker, {:authorize, gateway, amount, card, opts}) end @doc """ @@ -169,11 +161,11 @@ defmodule Gringotts do id = "ch_1BYvGkBImdnrXiZwet3aKkQE" - Gringotts.capture(:payment_worker, Gringotts.Gateways.Stripe, id, 5) + Gringotts.capture(Gringotts.Gateways.Stripe, id, 5) """ - def capture(worker, gateway, id, amount, opts \\ []) do + def capture(gateway, id, amount, opts \\ []) do validate_config(gateway) - call(worker, {:capture, gateway, id, amount, opts}) + call(:payment_worker, {:capture, gateway, id, amount, opts}) end @doc """ @@ -198,12 +190,12 @@ defmodule Gringotts do id = "ch_1BYvGkBImdnrXiZwet3aKkQE" - Gringotts.void(:payment_worker, Gringotts.Gateways.Stripe, id) + Gringotts.void(Gringotts.Gateways.Stripe, id) """ - def void(worker, gateway, id, opts \\ []) do + def void(gateway, id, opts \\ []) do validate_config(gateway) - call(worker, {:void, gateway, id, opts}) + call(:payment_worker, {:void, gateway, id, opts}) end @doc """ @@ -225,11 +217,11 @@ defmodule Gringotts do id = "ch_1BYvGkBImdnrXiZwet3aKkQE" - Gringotts.refund(:payment_worker, Gringotts.Gateways.Stripe, 5, id) + Gringotts.refund(Gringotts.Gateways.Stripe, 5, id) """ - def refund(worker, gateway, amount, id, opts \\ []) do + def refund(gateway, amount, id, opts \\ []) do validate_config(gateway) - call(worker, {:refund, gateway, amount, id, opts}) + call(:payment_worker, {:refund, gateway, amount, id, opts}) end @doc """ @@ -258,11 +250,11 @@ defmodule Gringotts do id = "ch_1BYvGkBImdnrXiZwet3aKkQE" - Gringotts.store(:payment_worker, Gringotts.Gateways.Stripe, @payment) + Gringotts.store(Gringotts.Gateways.Stripe, @payment) """ - def store(worker, gateway, card, opts \\ []) do + def store(gateway, card, opts \\ []) do validate_config(gateway) - call(worker, {:store, gateway, card, opts}) + call(:payment_worker, {:store, gateway, card, opts}) end @doc """ @@ -274,11 +266,11 @@ defmodule Gringotts do customer_id = "random_customer" - Gringotts.unstore(:payment_worker, Gringotts.Gateways.Stripe, customer_id) + Gringotts.unstore(Gringotts.Gateways.Stripe, customer_id) """ - def unstore(worker, gateway, customer_id, opts \\ []) do + def unstore(gateway, customer_id, opts \\ []) do validate_config(gateway) - call(worker, {:unstore, gateway, customer_id, opts}) + call(:payment_worker, {:unstore, gateway, customer_id, opts}) end # TODO: This is runtime error reporting fix this so that it does compile diff --git a/lib/gringotts/gateways/authorize_net.ex b/lib/gringotts/gateways/authorize_net.ex index d9e4e87a..2f87243a 100644 --- a/lib/gringotts/gateways/authorize_net.ex +++ b/lib/gringotts/gateways/authorize_net.ex @@ -161,7 +161,7 @@ defmodule Gringotts.Gateways.AuthorizeNet do ] iex> card = %CreditCard{number: "5424000000000015", year: 2020, month: 12, verification_code: "999"} iex> amount = 5 - iex> result = Gringotts.purchase(:payment_worker, Gringotts.Gateways.AuthorizeNet, amount, card, opts) + iex> result = Gringotts.purchase(Gringotts.Gateways.AuthorizeNet, amount, card, opts) """ @spec purchase(Float, CreditCard.t, Keyword.t) :: tuple def purchase(amount, payment, opts) do @@ -214,7 +214,7 @@ defmodule Gringotts.Gateways.AuthorizeNet do ] iex> card = %CreditCard{number: "5424000000000015", year: 2020, month: 12, verification_code: "999"} iex> amount = 5 - iex> result = Gringotts.authorize(:payment_worker, Gringotts.Gateways.AuthorizeNet, amount, card, opts) + iex> result = Gringotts.authorize(Gringotts.Gateways.AuthorizeNet, amount, card, opts) """ @spec authorize(Float, CreditCard.t, Keyword.t) :: tuple def authorize(amount, payment, opts) do @@ -251,7 +251,7 @@ defmodule Gringotts.Gateways.AuthorizeNet do ] iex> amount = 5 iex> id = "123456" - iex> result = Gringotts.capture(:payment_worker, Gringotts.Gateways.AuthorizeNet, id, amount, opts) + iex> result = Gringotts.capture(Gringotts.Gateways.AuthorizeNet, id, amount, opts) """ @spec capture(String.t, Float, Keyword.t) :: tuple def capture(id, amount, opts) do @@ -281,7 +281,7 @@ defmodule Gringotts.Gateways.AuthorizeNet do ] iex> id = "123456" iex> amount = 5 - iex> result = Gringotts.refund(:payment_worker, Gringotts.Gateways.AuthorizeNet, amount, id, opts) + iex> result = Gringotts.refund(Gringotts.Gateways.AuthorizeNet, amount, id, opts) """ @spec refund(Float, String.t, Keyword.t) :: tuple def refund(amount, id, opts) do @@ -305,7 +305,7 @@ defmodule Gringotts.Gateways.AuthorizeNet do ref_id: "123456" ] iex> id = "123456" - iex> result = Gringotts.void(:payment_worker, Gringotts.Gateways.AuthorizeNet, id, opts) + iex> result = Gringotts.void(Gringotts.Gateways.AuthorizeNet, id, opts) """ @spec void(String.t, Keyword.t) :: tuple def void(id, opts) do @@ -348,7 +348,7 @@ defmodule Gringotts.Gateways.AuthorizeNet do validation_mode: "testMode" ] iex> card = %CreditCard{number: "5424000000000015", year: 2020, month: 12, verification_code: "999"} - iex> result = Gringotts.store(:payment_worker, Gringotts.Gateways.AuthorizeNet, card, opts) + iex> result = Gringotts.store(Gringotts.Gateways.AuthorizeNet, card, opts) """ @spec store(CreditCard.t, Keyword.t) :: tuple def store(card, opts) do @@ -369,7 +369,7 @@ defmodule Gringotts.Gateways.AuthorizeNet do ## Example iex> id = "123456" iex> opts = [] - iex> result = Gringotts.store(:payment_worker, Gringotts.Gateways.AuthorizeNet, id, opts) + iex> result = Gringotts.store(Gringotts.Gateways.AuthorizeNet, id, opts) """ @spec unstore(String.t, Keyword.t) :: tuple diff --git a/lib/gringotts/gateways/cams.ex b/lib/gringotts/gateways/cams.ex index 23b71282..eeb70227 100644 --- a/lib/gringotts/gateways/cams.ex +++ b/lib/gringotts/gateways/cams.ex @@ -116,7 +116,7 @@ defmodule Gringotts.Gateways.Cams do options = [currency: "USD"] money = 100 - iex> Gringotts.purchase(:payment_worker, Gringotts.Gateways.Cams, money, payment, options) + iex> Gringotts.purchase(Gringotts.Gateways.Cams, money, payment, options) """ @spec purchase(number, CreditCard.t, Keyword) :: Response def purchase(money, payment, options) do @@ -150,7 +150,7 @@ defmodule Gringotts.Gateways.Cams do options = [currency: "USD"] money = 100 - iex> Gringotts.authorize(:payment_worker, Gringotts.Gateways.Cams, money, payment, options) + iex> Gringotts.authorize(Gringotts.Gateways.Cams, money, payment, options) """ @spec authorize(number, CreditCard.t, Keyword) :: Response def authorize(money, payment, options) do @@ -175,7 +175,7 @@ defmodule Gringotts.Gateways.Cams do options = [currency: "USD"] money = 100 - iex> Gringotts.capture(:payment_worker, Gringotts.Gateways.Cams, money, authorization, options) + iex> Gringotts.capture(Gringotts.Gateways.Cams, money, authorization, options) """ @spec capture(number, String.t, Keyword) :: Response def capture(money, authorization, options) do @@ -202,7 +202,7 @@ defmodule Gringotts.Gateways.Cams do options = [currency: "USD"] money = 100 - iex> Gringotts.refund(:payment_worker, Gringotts.Gateways.Cams, money, authorization, options) + iex> Gringotts.refund(Gringotts.Gateways.Cams, money, authorization, options) """ @spec refund(number, String.t, Keyword) :: Response def refund(money, authorization, options) do @@ -223,7 +223,7 @@ defmodule Gringotts.Gateways.Cams do authorization = "3904093075" options = [] - iex> Gringotts.void(:payment_worker, Gringotts.Gateways.Cams, authorization, options) + iex> Gringotts.void(Gringotts.Gateways.Cams, authorization, options) """ @spec void(String.t, Keyword) :: Response def void(authorization , options) do diff --git a/lib/gringotts/gateways/monei.ex b/lib/gringotts/gateways/monei.ex index ab577008..82549e71 100644 --- a/lib/gringotts/gateways/monei.ex +++ b/lib/gringotts/gateways/monei.ex @@ -180,7 +180,7 @@ defmodule Gringotts.Gateways.Monei do iex> opts = [currency: "EUR"] # The default currency is EUR, and this is just for an example. iex> card = %Gringotts.CreditCard{first_name: "Jo", last_name: "Doe", number: "4200000000000000", year: 2099, month: 12, verification_code: "123", brand: "VISA"} - iex> auth_result = Gringotts.authorize(:payment_worker, Gringotts.Gateways.Monei, 40, card, opts) + iex> auth_result = Gringotts.authorize(Gringotts.Gateways.Monei, 40, card, opts) iex> auth_result.id # This is the authorization ID """ @spec authorize(number, CreditCard.t(), keyword) :: {:ok | :error, Response} @@ -221,7 +221,7 @@ defmodule Gringotts.Gateways.Monei do iex> opts = [currency: "EUR"] # The default currency is EUR, and this is just for an example. iex> card = %Gringotts.CreditCard{first_name: "Jo", last_name: "Doe", number: "4200000000000000", year: 2099, month: 12, verification_code: "123", brand: "VISA"} - iex> capture_result = Gringotts.capture(:payment_worker, Gringotts.Gateways.Monei, 35, auth_result.id, opts) + iex> capture_result = Gringotts.capture(Gringotts.Gateways.Monei, 35, auth_result.id, opts) """ @spec capture(number, String.t(), keyword) :: {:ok | :error, Response} def capture(amount, payment_id, opts) @@ -254,7 +254,7 @@ defmodule Gringotts.Gateways.Monei do iex> opts = [currency: "EUR"] # The default currency is EUR, and this is just for an example. iex> card = %Gringotts.CreditCard{first_name: "Jo", last_name: "Doe", number: "4200000000000000", year: 2099, month: 12, verification_code: "123", brand: "VISA"} - iex> purchase_result = Gringotts.purchase(:payment_worker, Gringotts.Gateways.Monei, 40, card, opts) + iex> purchase_result = Gringotts.purchase(Gringotts.Gateways.Monei, 40, card, opts) """ @spec purchase(number, CreditCard.t(), keyword) :: {:ok | :error, Response} def purchase(amount, card = %CreditCard{}, opts) when is_integer(amount) do @@ -305,7 +305,7 @@ defmodule Gringotts.Gateways.Monei do iex> opts = [currency: "EUR"] # The default currency is EUR, and this is just for an example. iex> card = %Gringotts.CreditCard{first_name: "Jo", last_name: "Doe", number: "4200000000000000", year: 2099, month: 12, verification_code: "123", brand: "VISA"} - iex> void_result = Gringotts.void(:payment_worker, Gringotts.Gateways.Monei, auth_result.id, opts) + iex> void_result = Gringotts.void(Gringotts.Gateways.Monei, auth_result.id, opts) """ @spec void(String.t(), keyword) :: {:ok | :error, Response} def void(payment_id, opts) @@ -334,7 +334,7 @@ defmodule Gringotts.Gateways.Monei do iex> opts = [currency: "EUR"] # The default currency is EUR, and this is just for an example. iex> card = %Gringotts.CreditCard{first_name: "Jo", last_name: "Doe", number: "4200000000000000", year: 2099, month: 12, verification_code: "123", brand: "VISA"} - iex> refund_result = Gringotts.refund(:payment_worker, Gringotts.Gateways.Monei, purchase_result.id, opts) + iex> refund_result = Gringotts.refund(Gringotts.Gateways.Monei, purchase_result.id, opts) """ @spec refund(number, String.t(), keyword) :: {:ok | :error, Response} def refund(amount, payment_id, opts) when is_integer(amount) do @@ -374,7 +374,7 @@ defmodule Gringotts.Gateways.Monei do iex> opts = [currency: "EUR"] # The default currency is EUR, and this is just for an example. iex> card = %Gringotts.CreditCard{first_name: "Jo", last_name: "Doe", number: "4200000000000000", year: 2099, month: 12, verification_code: "123", brand: "VISA"} - iex> store_result = Gringotts.store(:payment_worker, Gringotts.Gateways.Monei, card, opts) + iex> store_result = Gringotts.store(Gringotts.Gateways.Monei, card, opts) """ @spec store(CreditCard.t(), keyword) :: {:ok | :error, Response} def store(%CreditCard{} = card, opts) do diff --git a/lib/gringotts/gateways/paymill.ex b/lib/gringotts/gateways/paymill.ex index 53f232ff..b128e061 100644 --- a/lib/gringotts/gateways/paymill.ex +++ b/lib/gringotts/gateways/paymill.ex @@ -55,7 +55,7 @@ defmodule Gringotts.Gateways.Paymill do options = [] - iex> Gringotts.authorize(:payment_worker, Gringotts.Gateways.Paymill, amount, card, options) + iex> Gringotts.authorize(Gringotts.Gateways.Paymill, amount, card, options) """ @spec authorize(number, String.t | CreditCard.t, Keyword) :: {:ok | :error, Response} def authorize(amount, card_or_token, options) do @@ -80,7 +80,7 @@ defmodule Gringotts.Gateways.Paymill do options = [] - iex> Gringotts.purchase(:payment_worker, Gringotts.Gateways.Paymill, amount, card, options) + iex> Gringotts.purchase(Gringotts.Gateways.Paymill, amount, card, options) """ @spec purchase(number, CreditCard.t, Keyword) :: {:ok | :error, Response} def purchase(amount, card, options) do @@ -98,7 +98,7 @@ defmodule Gringotts.Gateways.Paymill do options = [] - iex> Gringotts.capture(:payment_worker, Gringotts.Gateways.Paymill, token, amount, options) + iex> Gringotts.capture(Gringotts.Gateways.Paymill, token, amount, options) """ @spec capture(String.t, number, Keyword) :: {:ok | :error, Response} def capture(authorization, amount, options) do @@ -115,7 +115,7 @@ defmodule Gringotts.Gateways.Paymill do options = [] - iex> Gringotts.void(:payment_worker, Gringotts.Gateways.Paymill, token, options) + iex> Gringotts.void(Gringotts.Gateways.Paymill, token, options) """ @spec void(String.t, Keyword) :: {:ok | :error, Response} def void(authorization, options) do diff --git a/lib/gringotts/gateways/stripe.ex b/lib/gringotts/gateways/stripe.ex index 5abdbd23..3df0beac 100644 --- a/lib/gringotts/gateways/stripe.ex +++ b/lib/gringotts/gateways/stripe.ex @@ -92,7 +92,7 @@ defmodule Gringotts.Gateways.Stripe do iex> opts = [currency: "usd"] iex> amount = 10 - iex> Gringotts.authorize(:payment_worker, Gringotts.Gateways.Stripe, amount, payment, opts) + iex> Gringotts.authorize(Gringotts.Gateways.Stripe, amount, payment, opts) """ @spec authorize(Float, Map, List) :: Map def authorize(amount, payment, opts \\ []) do @@ -119,7 +119,7 @@ defmodule Gringotts.Gateways.Stripe do iex> opts = [currency: "usd"] iex> amount = 5 - iex> Gringotts.purchase(:payment_worker, Gringotts.Gateways.Stripe, amount, payment, opts) + iex> Gringotts.purchase(Gringotts.Gateways.Stripe, amount, payment, opts) """ @spec purchase(Float, Map, List) :: Map def purchase(amount, payment, opts \\ []) do @@ -145,7 +145,7 @@ defmodule Gringotts.Gateways.Stripe do iex> amount = 5 iex> opts = [] - iex> Gringotts.capture(:payment_worker, Gringotts.Gateways.Stripe, id, amount, opts) + iex> Gringotts.capture(Gringotts.Gateways.Stripe, id, amount, opts) """ @spec capture(String.t, Float, List) :: Map def capture(id, amount, opts \\ []) do @@ -178,7 +178,7 @@ defmodule Gringotts.Gateways.Stripe do iex> id = "ch_1BYvGkBImdnrXiZwet3aKkQE" iex> opts = [] - iex> Gringotts.void(:payment_worker, Gringotts.Gateways.Stripe, id, opts) + iex> Gringotts.void(Gringotts.Gateways.Stripe, id, opts) """ @spec void(String.t, List) :: Map def void(id, opts \\ []) do @@ -200,7 +200,7 @@ defmodule Gringotts.Gateways.Stripe do iex> id = "ch_1BYvGkBImdnrXiZwet3aKkQE" iex> opts = [] - iex> Gringotts.refund(:payment_worker, Gringotts.Gateways.Stripe, amount, id, opts) + iex> Gringotts.refund(Gringotts.Gateways.Stripe, amount, id, opts) """ @spec refund(Float, String.t, List) :: Map def refund(amount, id, opts \\ []) do @@ -226,7 +226,7 @@ defmodule Gringotts.Gateways.Stripe do iex> opts = [] - iex> Gringotts.store(:payment_worker, Gringotts.Gateways.Stripe, payment, opts) + iex> Gringotts.store(Gringotts.Gateways.Stripe, payment, opts) """ @spec store(Map, List) :: Map def store(payment, opts \\ []) do @@ -245,7 +245,7 @@ defmodule Gringotts.Gateways.Stripe do The following session shows how one would unstore a already stored payment source. iex> id = "cus_BwpLX2x4ecEUgD" - iex> Gringotts.unstore(:payment_worker, Gringotts.Gateways.Stripe, id, opts) + iex> Gringotts.unstore(Gringotts.Gateways.Stripe, id, opts) """ @spec unstore(String.t) :: Map def unstore(id, opts \\ []), do: commit(:delete, "customers/#{id}", [], opts) diff --git a/lib/gringotts/gateways/trexle.ex b/lib/gringotts/gateways/trexle.ex new file mode 100644 index 00000000..1bc315fb --- /dev/null +++ b/lib/gringotts/gateways/trexle.ex @@ -0,0 +1,247 @@ +defmodule Gringotts.Gateways.Trexle do + + @moduledoc """ + Trexle Payment Gateway Implementation: + + For further details, please refer [Trexle API documentation](https://docs.trexle.com/). + + Following are the features that have been implemented for the Trexle Gateway: + + | Action | Method | + | ------ | ------ | + | Authorize | `authorize/3` | + | Purchase | `purchase/3` | + | Capture | `capture/3` | + | Refund | `refund/3` | + | Store | `store/2` | + + ## The `opts` argument + A `Keyword` list `opts` passed as an optional argument for transactions with the gateway. Following are the keys + supported: + + * email + * ip_address + * description + + ## Trexle account registeration with `Gringotts` + After creating your account successfully on [Trexle](https://docs.trexle.com/) follow the [dashboard link](https://trexle.com/dashboard/api-keys) to fetch the secret api_key. + + Your Application config must look something like this: + + config :gringotts, Gringotts.Gateways.Trexle, + adapter: Gringotts.Gateways.Trexle, + api_key: "Secret API key", + default_currency: "USD" + """ + + @base_url "https://core.trexle.com/api/v1/" + + use Gringotts.Gateways.Base + use Gringotts.Adapter, required_config: [:api_key, :default_currency] + import Poison, only: [decode: 1] + alias Gringotts.{Response} + + @doc """ + Performs the authorization of the card to be used for payment. + + Authorizes your card with the given amount and returns a charge token and captured status as false in response. + + ### Example + ``` + iex> amount = 100 + + iex> card = %{ + name: "John Doe", + number: "5200828282828210", + expiry_month: 1, + expiry_year: 2018, + cvc: "123", + address_line1: "456 My Street", + address_city: "Ottawa", + address_postcode: "K1C2N6", + address_state: "ON", + address_country: "CA" + } + + iex> options = [email: "john@trexle.com", ip_address: "66.249.79.118" , description: "Store Purchase 1437598192"] + + iex> Gringotts.authorize(:payment_worker, Gringotts.Gateways.Trexle, amount, card, options) + ``` + """ + + @spec authorize(float, map, list) :: map + def authorize(amount, payment, opts \\ []) do + params = create_params_for_auth_or_purchase(amount, payment, opts, false) + commit(:post, "charges", params, opts) + end + + @doc """ + Performs the amount transfer from the customer to the merchant. + + The actual amount deduction performed by Trexle using the customer's card info. + + ## Example + ``` + iex> card = %{ + name: "John Doe", + number: "5200828282828210", + expiry_month: 1, + expiry_year: 2018, + cvc: "123", + address_line1: "456 My Street", + address_city: "Ottawa", + address_postcode: "K1C2N6", + address_state: "ON", + address_country: "CA" + } + + iex> options = [email: "john@trexle.com", ip_address: "66.249.79.118" ,description: "Store Purchase 1437598192"] + + iex> amount = 50 + + iex> Gringotts.purchase(:payment_worker, Gringotts.Gateways.Trexle, amount, card, options) + ``` + """ + + @spec purchase(float, map, list) :: map + def purchase(amount, payment, opts \\ []) do + params = create_params_for_auth_or_purchase(amount, payment, opts) + commit(:post, "charges", params, opts) + end + + @doc """ + Captures a particular amount using the charge token of a pre authorized card. + + The amount specified should be less than or equal to the amount given prior to capture while authorizing the card. + If the amount mentioned is less than the amount given in authorization process, the mentioned amount is debited. + Please note that multiple captures can't be performed for a given charge token from the authorization process. + + ### Example + ``` + iex> amount = 100 + + iex> token = "charge_6a5fcdc6cdbf611ee3448a9abad4348b2afab3ec" + + iex> Gringotts.capture(:payment_worker, Gringotts.Gateways.Trexle, token, amount) + ``` + """ + + @spec capture(String.t, float, list) :: map + def capture(charge_token, amount, opts \\ []) do + params = [amount: amount] + commit(:put, "charges/#{charge_token}/capture", params, opts) + end + + @doc """ + Refunds the amount to the customer's card with reference to a prior transfer. + + Trexle processes a full or partial refund worth `amount`, referencing a + previous `purchase/3` or `capture/3`. + + Multiple refund can be performed for the same charge token from purchase or capture done before performing refund action unless the cumulative amount is less than the amount given while authorizing. + + ## Example + The following session shows how one would refund a previous purchase (and similarily for captures). + ``` + iex> amount = 5 + + iex> token = "charge_668d3e169b27d4938b39246cb8c0890b0bd84c3c" + + iex> options = [email: "john@trexle.com", ip_address: "66.249.79.118", description: "Store Purchase 1437598192"] + + iex> Gringotts.refund(:payment_worker, Gringotts.Gateways.Trexle, amount, token, options) + ``` + """ + + @spec refund(float, String.t, list) :: map + def refund(amount, charge_token, opts \\ []) do + params = [amount: amount] + commit(:post, "charges/#{charge_token}/refunds", params, opts) + end + + @doc """ + Stores the card information for future use. + + ## Example + The following session shows how one would store a card (a payment-source) for future use. + ``` + iex> card = %{ + name: "John Doe", + number: "5200828282828210", + expiry_month: 1, + expiry_year: 2018, + cvc: "123", + address_line1: "456 My Street", + address_city: "Ottawa", + address_postcode: "K1C2N6", + address_state: "ON", + address_country: "CA" + } + + iex> options = [email: "john@trexle.com", ip_address: "66.249.79.118", description: "Store Purchase 1437598192"] + + iex> Gringotts.store(:payment_worker, Gringotts.Gateways.Trexle, card, options) + ``` + """ + + @spec store(map, list) :: map + def store(payment, opts \\ []) do + params = [email: @email]++card_params(payment) + commit(:post, "customers", params, opts) + end + + defp create_params_for_auth_or_purchase(amount, payment, opts, capture \\ true) do + [ + capture: capture, + amount: amount, + currency: opts[:config][:default_currency], + email: opts[:email], + ip_address: opts[:ip_address], + description: opts[:description] + ] ++ card_params(payment) + end + + defp card_params(%{} = card) do + [ + "card[name]": card[:name], + "card[number]": card[:number], + "card[expiry_year]": card[:expiry_year], + "card[expiry_month]": card[:expiry_month], + "card[cvc]": card[:cvc], + "card[address_line1]": card[:address_line1], + "card[address_city]": card[:address_city], + "card[address_postcode]": card[:address_postcode], + "card[address_state]": card[:address_state], + "card[address_country]": card[:address_country] + ] + end + + defp commit(method, path, params \\ [], opts \\ []) do + auth_token = "Basic #{Base.encode64(opts[:config][:api_key])}" + headers = [{"Content-Type", "application/x-www-form-urlencoded"}, {"Authorization", auth_token}] + data = params_to_string(params) + options = [hackney: [:insecure, basic_auth: {opts[:config][:api_key], "password"}]] + url = "#{@base_url}#{path}" + response = HTTPoison.request(method, url, data, headers, options) + response |> respond + end + + @spec respond(term) :: + {:ok, Response} | + {:error, Response} + defp respond(response) + + defp respond({:ok, %{status_code: code, body: body}}) when code in [200, 201] do + case decode(body) do + {:ok, results} -> {:ok, Response.success(raw: results, status_code: code)} + end + end + + defp respond({:ok, %{status_code: status_code, body: body}}) do + {:error, Response.error(status_code: status_code, raw: body)} + end + + defp respond({:error, %HTTPoison.Error{} = error}) do + {:error, Response.error(code: error.id, reason: :network_fail?, description: "HTTPoison says '#{error.reason}'")} + end +end diff --git a/mix.lock b/mix.lock index 167e146f..8ac3cafb 100644 --- a/mix.lock +++ b/mix.lock @@ -1,29 +1,29 @@ -%{"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [], [], "hexpm"}, - "bypass": {:hex, :bypass, "0.8.1", "16d409e05530ece4a72fabcf021a3e5c7e15dcc77f911423196a0c551f2a15ca", [], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, +%{"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, + "bypass": {:hex, :bypass, "0.8.1", "16d409e05530ece4a72fabcf021a3e5c7e15dcc77f911423196a0c551f2a15ca", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [:rebar3], [], "hexpm"}, - "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, - "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"}, - "credo": {:hex, :credo, "0.8.10", "261862bb7363247762e1063713bb85df2bbd84af8d8610d1272cd9c1943bba63", [], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}], "hexpm"}, - "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [], [], "hexpm"}, - "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [], [], "hexpm"}, - "elixir_xml_to_map": {:hex, :elixir_xml_to_map, "0.1.1", "57e924cd11731947bfd245ce57d0b8dd8b7168bf8edb20cd156a2982ca96fdfa", [], [{:erlsom, "~>1.4", [hex: :erlsom, repo: "hexpm", optional: false]}], "hexpm"}, - "erlsom": {:hex, :erlsom, "1.4.1", "53dbacf35adfea6f0714fd0e4a7b0720d495e88c5e24e12c5dc88c7b62bd3e49", [], [], "hexpm"}, + "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, + "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"}, + "credo": {:hex, :credo, "0.8.10", "261862bb7363247762e1063713bb85df2bbd84af8d8610d1272cd9c1943bba63", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}], "hexpm"}, + "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"}, + "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"}, + "elixir_xml_to_map": {:hex, :elixir_xml_to_map, "0.1.1", "57e924cd11731947bfd245ce57d0b8dd8b7168bf8edb20cd156a2982ca96fdfa", [:mix], [{:erlsom, "~>1.4", [hex: :erlsom, repo: "hexpm", optional: false]}], "hexpm"}, + "erlsom": {:hex, :erlsom, "1.4.1", "53dbacf35adfea6f0714fd0e4a7b0720d495e88c5e24e12c5dc88c7b62bd3e49", [:rebar3], [], "hexpm"}, "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, - "excoveralls": {:hex, :excoveralls, "0.7.5", "339e433e5d3bce09400dc8de7b9040741a409c93917849916c136a0f51fdc183", [], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, - "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"}, + "excoveralls": {:hex, :excoveralls, "0.8.0", "99d2691d3edf8612f128be3f9869c4d44b91c67cec92186ce49470ae7a7404cf", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, + "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"}, "hackney": {:hex, :hackney, "1.10.1", "c38d0ca52ea80254936a32c45bb7eb414e7a96a521b4ce76d00a69753b157f21", [:rebar3], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, "httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, "idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, - "inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, - "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [], [], "hexpm"}, + "inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, + "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm"}, "meck": {:hex, :meck, "0.8.9", "64c5c0bd8bcca3a180b44196265c8ed7594e16bcc845d0698ec6b4e577f48188", [:rebar3], [], "hexpm"}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, - "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [], [], "hexpm"}, - "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, + "mime": {:hex, :mime, "1.2.0", "78adaa84832b3680de06f88f0997e3ead3b451a440d183d688085be2d709b534", [:mix], [], "hexpm"}, + "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, "mock": {:hex, :mock, "0.3.1", "994f00150f79a0ea50dc9d86134cd9ebd0d177ad60bd04d1e46336cdfdb98ff9", [:mix], [{:meck, "~> 0.8.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"}, - "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, + "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, - "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [], [], "hexpm"}, + "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"}, "xml_builder": {:hex, :xml_builder, "0.1.2", "b48ab9ed0a24f43a6061e0c21deda88b966a2121af5c445d4fc550dd822e23dc", [:mix], [], "hexpm"}} diff --git a/test/gateways/trexle_test.exs b/test/gateways/trexle_test.exs new file mode 100644 index 00000000..146d5d05 --- /dev/null +++ b/test/gateways/trexle_test.exs @@ -0,0 +1,198 @@ +defmodule Gringotts.Gateways.TrexleTest do + + Code.require_file "../mocks/trexle_mock.exs", __DIR__ + use ExUnit.Case, async: false + alias Gringotts.Gateways.TrexleMock, as: MockResponse + alias Gringotts.Gateways.Trexle + + import Mock + + @valid_card %{ + name: "John Doe", + number: "5200828282828210", + expiry_month: 1, + expiry_year: 2018, + cvc: "123", + address_line1: "456 My Street", + address_city: "Ottawa", + address_postcode: "K1C2N6", + address_state: "ON", + address_country: "CA" + } + + @invalid_card %{ + name: "John Doe", + number: "5200828282828210", + expiry_month: 1, + expiry_year: 2010, + cvc: "123", + address_line1: "456 My Street", + address_city: "Ottawa", + address_postcode: "K1C2N6", + address_state: "ON", + address_country: "CA" + } + + @amount 100 + @bad_amount 20 + + @valid_token "J5RGMpDlFlTfv9mEFvNWYoqHufyukPP4" + @invalid_token "30" + + @opts [ + config: %{api_key: "J5RGMpDlFlTfv9mEFvNWYoqHufyukPP4", default_currency: "USD"}, + email: "john@trexle.com", + ip_address: "66.249.79.118", + description: "Store Purchase 1437598192" + ] + + @missingip_opts [ + config: %{api_key: "J5RGMpDlFlTfv9mEFvNWYoqHufyukPP4", default_currency: "USD"}, + email: "john@trexle.com", + description: "Store Purchase 1437598192" + ] + + @invalid_opts [ + config: %{api_key: "J5RGMpDlFlTfv9mEFvNWYoqHufyukPP4"}, + email: "john@trexle.com", + ip_address: "66.249.79.118", + description: "Store Purchase 1437598192" + ] + + describe "validation arguments check" do + test "with no currency passed in config" do + assert_raise ArgumentError, fn -> + Trexle.validate_config(@invalid_opts) + end + end + end + + describe "purchase" do + test "with valid card" do + with_mock HTTPoison, + [request: fn(_method, _url, _body, _headers, _options) -> MockResponse.test_for_purchase_with_valid_card end] do + {:ok, response} = Trexle.purchase(@amount, @valid_card, @opts) + assert response.status_code == 201 + assert response.raw["response"]["success"] == true + assert response.raw["response"]["captured"] == false + end + end + + test "with invalid card" do + with_mock HTTPoison, + [request: fn(_method, _url, _body, _headers, _options) -> MockResponse.test_for_purchase_with_invalid_card end] do + {:error, response} = Trexle.purchase(@amount, @invalid_card, @opts) + assert response.status_code == 400 + assert response.success == false + assert response.raw == ~s({"error":"Payment failed","detail":"Your card's expiration year is invalid."}) + end + end + + test "with invalid amount" do + with_mock HTTPoison, + [request: fn(_method, _url, _body, _headers, _options) -> MockResponse.test_for_purchase_with_invalid_amount end] do + {:error, response} = Trexle.purchase(@bad_amount, @valid_card, @opts) + assert response.status_code == 400 + assert response.success == false + assert response.raw == ~s({"error":"Payment failed","detail":"Amount must be at least 50 cents"}) + end + end + end + + describe "authorize" do + test "with valid card" do + with_mock HTTPoison, + [request: fn(_method, _url, _body, _headers, _options) -> MockResponse.test_for_authorize_with_valid_card end] do + {:ok, response} = Trexle.authorize(@amount, @invalid_card, @opts) + assert response.status_code == 201 + assert response.raw["response"]["success"] == true + assert response.raw["response"]["captured"] == false + end + end + + test "with invalid card" do + with_mock HTTPoison, + [request: fn(_method, _url, _body, _headers, _options) -> MockResponse.test_for_authorize_with_invalid_card end] do + {:error, response} = Trexle.authorize(@amount, @invalid_card, @opts) + assert response.status_code == 400 + assert response.success == false + assert response.raw == ~s({"error":"Payment failed","detail":"Your card's expiration year is invalid."}) + end + end + + test "with invalid amount" do + with_mock HTTPoison, + [request: fn(_method, _url, _body, _headers, _options) -> MockResponse.test_for_authorize_with_invalid_amount end] do + {:error, response} = Trexle.authorize(@amount, @valid_card, @opts) + assert response.status_code == 400 + assert response.success == false + assert response.raw == ~s({"error":"Payment failed","detail":"Amount must be at least 50 cents"}) + end + end + + test "with missing ip address" do + with_mock HTTPoison, + [request: fn(_method, _url, _body, _headers, _options) -> MockResponse.test_for_authorize_with_missing_ip_address end] do + {:error, response} = Trexle.authorize(@amount, @valid_card, @missingip_opts) + assert response.status_code == 500 + assert response.success == false + assert response.raw == ~s({"error":"ip_address is missing"}) + end + end + end + + describe "refund" do + test "with valid token" do + with_mock HTTPoison, + [request: fn(_method, _url, _body, _headers, _options) -> MockResponse.test_for_authorize_with_valid_card end] do + {:ok, response} = Trexle.refund(@amount, @valid_token, @opts) + assert response.status_code == 201 + assert response.raw["response"]["success"] == true + assert response.raw["response"]["captured"] == false + end + end + + test "with invalid token" do + with_mock HTTPoison, + [request: fn(_method, _url, _body, _headers, _options) -> MockResponse.test_for_authorize_with_invalid_amount end] do + {:error, response} = Trexle.refund(@amount, @invalid_token, @opts) + assert response.status_code == 400 + assert response.success == false + assert response.raw == ~s({"error":"Payment failed","detail":"Amount must be at least 50 cents"}) + end + end + end + + describe "capture" do + test "with valid chargetoken" do + with_mock HTTPoison, + [request: fn(_method, _url, _body, _headers, _options) -> MockResponse.test_for_capture_with_valid_chargetoken end] do + {:ok, response} = Trexle.capture(@valid_token, @amount, @opts) + assert response.status_code == 200 + assert response.raw["response"]["success"] == true + assert response.raw["response"]["captured"] == true + assert response.raw["response"]["status_message"] == "Transaction approved" + end + end + + test "test_for_capture_with_invalid_chargetoken" do + with_mock HTTPoison, + [request: fn(_method, _url, _body, _headers, _options) -> MockResponse.test_for_capture_with_invalid_chargetoken end] do + {:error, response} = Trexle.capture(@invalid_token, @amount, @opts) + assert response.status_code == 400 + assert response.success == false + assert response.raw == ~s({"error":"Capture failed","detail":"invalid token"}) + end + end + end + + describe "store" do + test "with valid card" do + with_mock HTTPoison, + [request: fn(_method, _url, _body, _headers, _options) -> MockResponse.test_for_store_with_valid_card end] do + {:ok, response} = Trexle.store(@valid_card, @opts) + assert response.status_code == 201 + end + end + end +end diff --git a/test/gringotts_test.exs b/test/gringotts_test.exs index 07a56009..d4634e14 100644 --- a/test/gringotts_test.exs +++ b/test/gringotts_test.exs @@ -49,33 +49,33 @@ defmodule GringottsTest do end test "authorization" do - assert authorize(:payment_worker, GringottsTest.FakeGateway, 100, :card, []) == + assert authorize(GringottsTest.FakeGateway, 100, :card, []) == :authorization_response end test "purchase" do - assert purchase(:payment_worker, GringottsTest.FakeGateway, 100, :card, []) == + assert purchase(GringottsTest.FakeGateway, 100, :card, []) == :purchase_response end test "capture" do - assert capture(:payment_worker, GringottsTest.FakeGateway, 1234, 100, []) == :capture_response + assert capture(GringottsTest.FakeGateway, 1234, 100, []) == :capture_response end test "void" do - assert void(:payment_worker, GringottsTest.FakeGateway, 1234, []) == :void_response + assert void(GringottsTest.FakeGateway, 1234, []) == :void_response end test "refund" do - assert refund(:payment_worker, GringottsTest.FakeGateway, 100, 1234, []) == :refund_response + assert refund(GringottsTest.FakeGateway, 100, 1234, []) == :refund_response end test "store" do - assert store(:payment_worker, GringottsTest.FakeGateway, :card, []) == :store_response + assert store(GringottsTest.FakeGateway, :card, []) == :store_response end test "unstore" do - assert unstore(:payment_worker, GringottsTest.FakeGateway, 123, []) == :unstore_response + assert unstore(GringottsTest.FakeGateway, 123, []) == :unstore_response end test "validate_config when some required config is missing" do @@ -84,7 +84,7 @@ defmodule GringottsTest do assert_raise( ArgumentError, "expected [:other_secret] to be set, got: [adapter: GringottsTest.FakeGateway, some_auth_info: :merchant_secret_key]\n", - fn -> authorize(:payment_worker, GringottsTest.FakeGateway, 100, :card, []) end + fn -> authorize(GringottsTest.FakeGateway, 100, :card, []) end ) end end diff --git a/test/integration/gateways/monei_test.exs b/test/integration/gateways/monei_test.exs index 04624f31..516164ea 100644 --- a/test/integration/gateways/monei_test.exs +++ b/test/integration/gateways/monei_test.exs @@ -26,7 +26,7 @@ defmodule Gringotts.Integration.Gateways.MoneiTest do end test "authorize." do - case Gringotts.authorize(:payment_worker, Gateway, 3.1, @card) do + case Gringotts.authorize(Gateway, 3.1, @card) do {:ok, response} -> assert response.code == "000.100.110" assert response.description == "Request successfully processed in 'Merchant in Integrator Test Mode'" @@ -37,7 +37,7 @@ defmodule Gringotts.Integration.Gateways.MoneiTest do @tag :skip test "capture." do - case Gringotts.capture(:payment_worker, Gateway, 32.00, "s") do + case Gringotts.capture(Gateway, 32.00, "s") do {:ok, response} -> assert response.code == "000.100.110" assert response.description == "Request successfully processed in 'Merchant in Integrator Test Mode'" @@ -48,7 +48,7 @@ defmodule Gringotts.Integration.Gateways.MoneiTest do end test "purchase." do - case Gringotts.purchase(:payment_worker, Gateway, 32, @card) do + case Gringotts.purchase(Gateway, 32, @card) do {:ok, response} -> assert response.code == "000.100.110" assert response.description == "Request successfully processed in 'Merchant in Integrator Test Mode'" diff --git a/test/mocks/trexle_mock.exs b/test/mocks/trexle_mock.exs new file mode 100644 index 00000000..e169c078 --- /dev/null +++ b/test/mocks/trexle_mock.exs @@ -0,0 +1,239 @@ +defmodule Gringotts.Gateways.TrexleMock do + + def test_for_purchase_with_valid_card do + {:ok, + %HTTPoison.Response{ + body: "{\"response\":{\"token\":\"charge_3e89c6f073606ac1efe62e76e22dd7885441dc72\",\"success\":true,\"captured\":false}}", + headers: [ + {"Date", "Fri, 22 Dec 2017 11:57:28 GMT"}, + {"Content-Type", "application/json; charset=UTF-8"}, + {"ETag", "W/\"5a9f44c457a4fdd0478c82ec1af64816\""}, + {"Cache-Control", "max-age=0, private, must-revalidate"}, + {"X-Request-Id", "9b2a1d30-9bca-48f2-862e-4090766689cb"}, + {"X-Runtime", "0.777520"}, + {"Content-Length", "104"}, + {"X-Powered-By", "PleskLin"} + ], + request_url: "https://core.trexle.com/api/v1//charges", + status_code: 201 + } + } + end + + def test_for_purchase_with_invalid_card do + {:ok, + %HTTPoison.Response{ + body: "{\"error\":\"Payment failed\",\"detail\":\"Your card's expiration year is invalid.\"}", + headers: [ + {"Date", "Fri, 22 Dec 2017 13:20:50 GMT"}, + {"Content-Type", "application/json; charset=UTF-8"}, + {"Cache-Control", "no-cache"}, + {"X-Request-Id", "eb8100a1-8ffa-47da-9623-8d3b2af51b84"}, + {"X-Runtime", "0.445244"}, + {"Content-Length", "77"}, + {"X-Powered-By", "PleskLin"}, + {"Connection", "close"} + ], + request_url: "https://core.trexle.com/api/v1//charges", + status_code: 400 + } + } + end + + def test_for_purchase_with_invalid_amount do + {:ok, + %HTTPoison.Response{ + body: "{\"error\":\"Payment failed\",\"detail\":\"Amount must be at least 50 cents\"}", + headers: [ + {"Date", "Sat, 23 Dec 2017 18:16:33 GMT"}, + {"Content-Type", "application/json; charset=UTF-8"}, + {"Cache-Control", "no-cache"}, + {"X-Request-Id", "4ce2eea4-3ea9-4345-ac85-9bc45f22f5ac"}, + {"X-Runtime", "0.476058"}, + {"Content-Length", "70"}, + {"X-Powered-By", "PleskLin"}, + {"Connection", "close"} + ], + request_url: "https://core.trexle.com/api/v1//charges", + status_code: 400 + } + } + end + + def test_for_authorize_with_valid_card do + {:ok, + %HTTPoison.Response{ + body: "{\"response\":{\"token\":\"charge_8ab2b21a2f02495f5c36b34d129c8a0e836add32\",\"success\":true,\"captured\":false}}", + headers: [ + {"Date", "Sat, 23 Dec 2017 18:33:31 GMT"}, + {"Content-Type", "application/json; charset=UTF-8"}, + {"ETag", "W/\"ec4f2df0607614f67286ac46eb994150\""}, + {"Cache-Control", "max-age=0, private, must-revalidate"}, + {"X-Request-Id", "51d28d13-81e5-41fd-b711-1b6531fdd3dd"}, + {"X-Runtime", "0.738395"}, + {"Content-Length", "104"}, + {"X-Powered-By", "PleskLin"} + ], + request_url: "https://core.trexle.com/api/v1//charges", + status_code: 201 + } + } + end + + def test_for_authorize_with_invalid_card do + {:ok, + %HTTPoison.Response{ + body: "{\"error\":\"Payment failed\",\"detail\":\"Your card's expiration year is invalid.\"}", + headers: [ + {"Date", "Sat, 23 Dec 2017 18:25:40 GMT"}, + {"Content-Type", "application/json; charset=UTF-8"}, + {"Cache-Control", "no-cache"}, + {"X-Request-Id", "239e7054-9500-4a87-bf3b-09456d550b6d"}, + {"X-Runtime", "0.466670"}, + {"Content-Length", "77"}, + {"X-Powered-By", "PleskLin"}, + {"Connection", "close"} + ], + request_url: "https://core.trexle.com/api/v1//charges", + status_code: 400 + } + } + end + + def test_for_authorize_with_invalid_amount do + {:ok, + %HTTPoison.Response{ + body: "{\"error\":\"Payment failed\",\"detail\":\"Amount must be at least 50 cents\"}", + headers: [ + {"Date", "Sat, 23 Dec 2017 18:40:10 GMT"}, + {"Content-Type", "application/json; charset=UTF-8"}, + {"Cache-Control", "no-cache"}, + {"X-Request-Id", "d58db806-8016-4a0e-8519-403a969fa1a7"}, + {"X-Runtime", "0.494636"}, + {"Content-Length", "70"}, + {"X-Powered-By", "PleskLin"}, + {"Connection", "close"} + ], + request_url: "https://core.trexle.com/api/v1//charges", + status_code: 400 + } + } + end + + def test_for_authorize_with_missing_ip_address do + {:ok, + %HTTPoison.Response{body: "{\"error\":\"ip_address is missing\"}", + headers: [ + {"Date", "Thu, 28 Dec 2017 12:22:43 GMT"}, + {"Content-Type", "application/json; charset=UTF-8"}, + {"Cache-Control", "no-cache"}, + {"X-Request-Id", "97bae548-a446-42e9-b792-8c505c38f4c1"}, + {"X-Runtime", "0.005652"}, {"Content-Length", "33"}, + {"X-Powered-By", "PleskLin"}, {"Connection", "close"} + ], + request_url: "https://core.trexle.com/api/v1/charges", + status_code: 500 + } + } + end + + def test_for_refund_with_valid_token do + {:ok, + %HTTPoison.Response{ + body: "{\"response\":{\"token\":\"refund_a86a757cc6bdabab50d6ebbfcdcd4db4e04198dd\",\"success\":true,\"amount\":50,\"charge\":\"charge_cb17a0c34e870a479dfa13bd873e7ce7e090ec9b\",\"status_message\":\"Transaction approved\"}}", + headers: [ + {"Date", "Sat, 23 Dec 2017 18:55:41 GMT"}, + {"Content-Type", "application/json; charset=UTF-8"}, + {"ETag", "W/\"7410ae0b45094aadada390f5c947a58a\""}, + {"Cache-Control", "max-age=0, private, must-revalidate"}, + {"X-Request-Id", "b1c94703-7fb4-48f2-b1b4-32e3b6a87e57"}, + {"X-Runtime", "1.097186"}, + {"Content-Length", "198"}, + {"X-Powered-By", "PleskLin"} + ], + request_url: "https://core.trexle.com/api/v1//charges/charge_cb17a0c34e870a479dfa13bd873e7ce7e090ec9b/refunds", + status_code: 201 + } + } + end + + def test_for_refund_with_invalid_token do + {:ok, + %HTTPoison.Response{ + body: "{\"error\":\"Refund failed\",\"detail\":\"invalid token\"}", + headers: [ + {"Date", "Sat, 23 Dec 2017 18:53:09 GMT"}, + {"Content-Type", "application/json; charset=UTF-8"}, + {"Cache-Control", "no-cache"}, + {"X-Request-Id", "276fd8f5-dc21-40be-8add-fa76aabbfc5b"}, + {"X-Runtime", "0.009374"}, + {"Content-Length", "50"}, + {"X-Powered-By", "PleskLin"}, + {"Connection", "close"} + ], + request_url: "https://core.trexle.com/api/v1//charges/34/refunds", + status_code: 400 + } + } + end + + def test_for_capture_with_valid_chargetoken do + {:ok, + %HTTPoison.Response{ + body: "{\"response\":{\"token\":\"charge_cb17a0c34e870a479dfa13bd873e7ce7e090ec9b\",\"success\":true,\"captured\":true,\"amount\":50,\"status_message\":\"Transaction approved\"}}", + headers: [ + {"Date", "Sat, 23 Dec 2017 18:49:50 GMT"}, + {"Content-Type", "application/json; charset=UTF-8"}, + {"ETag", "W/\"26f05a32c0d0a27b180bbe777488fd5f\""}, + {"Cache-Control", "max-age=0, private, must-revalidate"}, + {"X-Request-Id", "97ca2db6-fd4f-4a5b-ae45-01fae9c13668"}, + {"X-Runtime", "1.092051"}, + {"Content-Length", "155"}, + {"X-Powered-By", "PleskLin"} + ], + request_url: "https://core.trexle.com/api/v1//charges/charge_cb17a0c34e870a479dfa13bd873e7ce7e090ec9b/capture", + status_code: 200 + } + } + end + + def test_for_capture_with_invalid_chargetoken do + {:ok, + %HTTPoison.Response{ + body: "{\"error\":\"Capture failed\",\"detail\":\"invalid token\"}", + headers: [ + {"Date", "Sat, 23 Dec 2017 18:47:18 GMT"}, + {"Content-Type", "application/json; charset=UTF-8"}, + {"Cache-Control", "no-cache"}, + {"X-Request-Id", "b46ecb8d-7df8-4c5f-b87f-c53fae364e79"}, + {"X-Runtime", "0.010255"}, + {"Content-Length", "51"}, + {"X-Powered-By", "PleskLin"}, + {"Connection", "close"} + ], + request_url: "https://core.trexle.com/api/v1//charges/30/capture", + status_code: 400 + } + } + end + + def test_for_store_with_valid_card do + {:ok, + %HTTPoison.Response{ + body: "{\"response\":{\"token\":\"token_94e333959850270460e89a86bad2246613528cbb\",\"card\":{\"token\":\"token_2a1ba29522e4a377fafa62e8e204f76ad8ba8f1e\",\"scheme\":\"master\",\"display_number\":\"XXXX-XXXX-XXXX-8210\",\"expiry_year\":2018,\"expiry_month\":1,\"cvc\":123,\"name\":\"John Doe\",\"address_line1\":\"456 My Street\",\"address_line2\":null,\"address_city\":\"Ottawa\",\"address_state\":\"ON\",\"address_postcode\":\"K1C2N6\",\"address_country\":\"CA\",\"primary\":true}}}", + headers: [ + {"Date", "Sat, 23 Dec 2017 19:32:58 GMT"}, + {"Content-Type", "application/json; charset=UTF-8"}, + {"ETag", "W/\"c4089eabe907fc2327dd565503242b58\""}, + {"Cache-Control", "max-age=0, private, must-revalidate"}, + {"X-Request-Id", "1a334b22-8e01-4e1b-8b58-90dfd0b7c12f"}, + {"X-Runtime", "0.122441"}, + {"Content-Length", "422"}, + {"X-Powered-By", "PleskLin"} + ], + request_url: "https://core.trexle.com/api/v1//customers", + status_code: 201 + } + } + end +end