diff --git a/.credo.exs b/.credo.exs index 9381d3f7..07079234 100644 --- a/.credo.exs +++ b/.credo.exs @@ -132,11 +132,7 @@ # Deprecated checks (these will be deleted after a grace period) # - {Credo.Check.Readability.Specs, false}, - {Credo.Check.Warning.NameRedeclarationByAssignment, false}, - {Credo.Check.Warning.NameRedeclarationByCase, false}, - {Credo.Check.Warning.NameRedeclarationByDef, false}, - {Credo.Check.Warning.NameRedeclarationByFn, false}, + {Credo.Check.Readability.Specs, false} # Custom checks can be created using `mix credo.gen.check`. # diff --git a/.scripts/post-commit b/.scripts/post-commit index ada60194..0af54c84 100755 --- a/.scripts/post-commit +++ b/.scripts/post-commit @@ -7,6 +7,6 @@ RED='\033[1;31m' LGRAY='\033[1;30m' NC='\033[0m' # No Color -printf "${RED}Running 'mix credo --strict --format=oneline' on project...${NC}\n" -mix credo --strict --format=oneline +printf "${RED}Running 'mix credo --strict' on project...${NC}\n" +mix credo --strict echo diff --git a/.travis.yml b/.travis.yml index d12dcef4..43b9619d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,10 +20,9 @@ matrix: - elixir: "1.5.3" script: - mix coveralls.json - - elixir: "1.6.2" + - elixir: "1.6.5" notifications: email: recipients: - - ananya95+travis@gmail.com - ashish+travis@aviabird.com diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b42e88..06b30a57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,37 @@ # Changelog +## [`v1.1.1-rc`][tag-1_1_1-rc] (2018-06-04) + +### Added + +* **core:** Remove the protocol implementation for `ex_money` since they + implement it for us + [e1cb32](https://github.com/kipcole9/money/commit/e1cb325a28a8318864ff1cbfbbb67574379a82c0) +* **development:** Removed those annoying compiler warnings! +* **docs:** Add docs about the money protocol. + +### Changed + +Remove support of [Wirecard](http://wirecard.com/) as we failed to implement it completely and it is +not at all usable. It is being archived in +[`wirecard`](https://github.com/aviabird/gringotts/tree/wirecard) branch. + ## [`v1.1.0`][tag-1_1_0] (2018-04-22) ### Added * **api:** Introduces a `Money` protocol ([#71][pr#71]) -* **core:** Introduces Response.t ([#119][pr#91]) -* **development:** Adds a useful mix task gringotts.new ([#78][pr#78]) +* **core:** Introduces `Response.t` ([#119][pr#91]) +* **development:** Adds a useful mix task `gringotts.new` ([#78][pr#78]) to help + with adding more gateways! * **docs:** Adds changelog, contributing guide ([#117][pr#117]) ### Changed -* **api:** Deprecates use of `floats` for money amounts, check issue [#62][iss#62] ([#71][pr#71]) -* **core:** Removes payment worker, no application, no worker now after josevalim [pointed it][jose-feedback] ([#118][pr#118]) +* **api:** Deprecates use of `floats` for money amounts, check issue + [#62][iss#62] ([#71][pr#71]) +* **core:** Removes payment worker, no application, no worker now after + @josevalim [pointed it][joses-feedback] ([#118][pr#118]) [iss#62]: https://github.com/aviabird/gringotts/issues/62 [pr#71]: https://github.com/aviabird/gringotts/pulls/71 @@ -21,14 +40,13 @@ [pr#117]: https://github.com/aviabird/gringotts/pulls/117 [pr#78]:https://github.com/aviabird/gringotts/pulls/78 [pr#86]:https://github.com/aviabird/gringotts/pulls/86 -[jose-feedback]:https://elixirforum.com/t/gringotts-a-complete-payment-library-for-elixir-and-phoenix-framework/11054/41 - +[joses-feedback]:https://elixirforum.com/t/gringotts-a-complete-payment-library-for-elixir-and-phoenix-framework/11054/41 ## [`v1.0.2`][tag-1_0_2] (2017-12-27) ### Added -* New Gateway: **Trexle** +* Gringotts now supports [Trexle](http://trexle.com/) as well :tada: ### Changed @@ -49,12 +67,13 @@ * **api:** Initial public API release. * **core:** Single worker architecture, config fetched from `config.exs` * **api:** Supported Gateways: - - Stripe - - MONEI - - Paymill - - WireCard - - CAMSa + - [Stripe](http://stripe.com/) + - [MONEI](http://monei.net/) + - [Paymill](https://www.paymill.com/en/) + - [WireCard](http://wirecard.com/) + - [CAMS](http://www.centralams.com/) +[tag-1_1_1_rc]: https://github.com/aviabird/gringotts/releases/tag/v1.1.1-rc [tag-1_1_0]: https://github.com/aviabird/gringotts/compare/1.1.0...1.0.2 [tag-1_0_2]: https://github.com/aviabird/gringotts/compare/1.0.2...1.0.1 [tag-1_0_1]: https://github.com/aviabird/gringotts/compare/1.0.1...1.0.0 diff --git a/README.md b/README.md index 8f4f4a82..4ad35834 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@
- Gringotts is a payment processing library in Elixir integrating various payment gateways, drawing motivation for Shopify's activemerchant
gem. Checkout the Demo here.
+ Gringotts is a payment processing library in Elixir integrating various payment gateways, drawing motivation for Shopify's activemerchant
gem. Checkout the demo here.
@@ -25,10 +25,10 @@ Add `gringotts` to the list of dependencies of your application.
def deps do
[
- {:gringotts, "~> 1.0"},
+ {:gringotts, "~> 1.1"},
# ex_money provides an excellent Money library, and integrates
# out-of-the-box with Gringotts
- {:ex_money, "~> 1.1.0"}
+ {:ex_money, "> 2.5.0"}
]
end
```
@@ -51,11 +51,12 @@ config :gringotts, Gringotts.Gateways.Monei,
entityId: "your_secret_channel_id"
```
-Copy and paste this code in a module or an `IEx` session
+Copy and paste this code in a module or an `IEx` session, or use this handy
+[`.iex.exs`][monei-bindings] for all the bindings.
```elixir
alias Gringotts.Gateways.Monei
-alias Gringotts.{CreditCard}
+alias Gringotts.CreditCard
# a fake sample card that will work now because the Gateway is by default
# in "test" mode.
@@ -83,18 +84,51 @@ end
[hexpm]: https://hex.pm/packages/gringotts
[monei]: http://www.monei.net
+[monei-bindings]: https://gist.github.com/oyeb/a2e2ac5986cc90a12a6136f6bf1357e5
+
+## On the `Gringotts.Money` protocol and money representation
+
+All financial applications must take proper care when representing money in
+their system. Using simple `float`ing values might lead to losses in the real
+world due to [various reasons][floating-issues].
+
+Most payment gateways are strict about the formatting of the `amount` in the
+request, hence we cannot render arbitrary floating amounts like
+`$4.99999`. Moreover, such amounts might mean something to your application but
+they don't have any value in the real world (since you can't charge someone for
+a fraction of a US cent).
+
+Your application **must round** such amounts before invoking Gringotts **and manage
+any remainders sensibly** yourself.
+
+> Gringotts may perform rounding using the [`half-even`][wiki-half-even]
+strategy, but it will discard remainders if any.
+
+### Supported "Money" libraries
+
+Gringotts does not ship with any library to work with monies. You are free to
+choose any monie library you wish, as long as they implement the
+[`Gringotts.Money`][protocol] for their type!
+
+That said, we recommend [`ex_money`][ex_money] (above [`v2.6.0`][2_6_0]) to
+represent monies. You just have to add it in your `deps()`.
+
+[protocol]: https://github.com/aviabird/gringotts/blob/dev/lib/gringotts/money.ex
+[floating-issues]: https://elixirforum.com/t/comparison-of-decimals-not-logical/770/21
+[wiki-half-even]: https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
+[ex-money]: https://github.com/kipcole9/money
+[2_6_0]: https://github.com/kipcole9/money/releases/tag/v2.6.0
## Supported Gateways
-| Gateway | Supported countries |
-| ------ | ----- |
-| [Authorize.Net][anet] | 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 |
-| [CAMS][cams] | AU, US |
-| [MONEI][monei] | DE, EE, ES, FR, IT, US |
-| [PAYMILL][paymill] | 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][stripe] | AT, AU, BE, CA, CH, DE, DK, ES, FI, FR, GB, IE, IN, IT, LU, NL, NO, SE, SG, US |
-| [TREXLE][trexle] | 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][wirecard] | AD, AT, BE, BG, CH, CY, CZ, DE, DK, EE, ES, FI, FR, GB, GI, GR, HU, IE, IL, IM, IS, IT, LI, LT, LU, LV, MC, MT, NL, NO, PL, PT, RO, SE, SI, SK, SM, TR, VA |
+| Gateway | PCI compliance | `purchase` | `authorize` | `capture` | `void` | `refund` | (card) `store` | (card) `unstore` |
+|-----------------------|----------------|------------|-------------|-----------|----------|----------|----------------|------------------|
+| [Authorize.Net][anet] | mandatory | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| [CAMS][cams] | mandatory | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
+| [MONEI][monei] | mandatory | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
+| [PAYMILL][paymill] | optional | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
+| [Stripe][stripe] | optional | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| [TREXLE][trexle] | mandatory | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ |
[anet]: http://www.authorize.net/
[cams]: https://www.centralams.com/
@@ -105,7 +139,7 @@ end
[wirecard]: http://www.wirecard.com
[demo]: https://gringottspay.herokuapp.com/
-## Road Map
+## [Road Map][roadmap]
Apart from supporting more and more gateways, we also keep a somewhat detailed
plan for the future on our [wiki][roadmap].
@@ -116,19 +150,6 @@ plan for the future on our [wiki][roadmap].
Gringotts has a nice ring to it. Also [this][reason].
-#### 2. What is the worker doing in the middle?
-
-We wanted to "supervise" our payments, and power utilities to process recurring
-payments, subscriptions with it. But yes, as of now, it is a bottle neck and
-unnecessary.
-
-It's slated to be removed in [`v2.0.0`][milestone-2_0_0_alpha] and any
-supervised/async/parallel work can be explicitly managed via native elixir
-constructs.
-
-**In fact, it's already been removed from our [dev](#) branch.**
-
-[milestone-2_0_0_alpha]: https://github.com/aviabird/gringotts/milestone/3
[reason]: http://harrypotter.wikia.com/wiki/Gringotts
## License
diff --git a/lib/gringotts.ex b/lib/gringotts.ex
index 161d1ae8..63b23f6f 100644
--- a/lib/gringotts.ex
+++ b/lib/gringotts.ex
@@ -9,9 +9,6 @@ defmodule Gringotts do
## Standard API arguments
- All requests to Gringotts are served by a supervised worker, this might be
- made optional in future releases.
-
### `gateway` (Module) Name
The `gateway` to which this request is made. This is required in all API calls
@@ -29,24 +26,24 @@ defmodule Gringotts do
the transaction. `amount` is polymorphic thanks to the `Gringotts.Money`
protocol which can even be implemented by your very own custom Money type!
+ > Currently, we support only [`ex_money`][ex_money]'s `Money` type. Please
+ don't forget to add the `ex_money` lib to your deps!
+
#### Note
Gringotts supports [`ex_money`][ex_money] out of the box, just drop `ex_money`
types in this argument and everything will work as expected.
- > Support for [`monetized`][monetized] and [`money`][money] is on the
- > way, track it [here][iss-money-lib-support]!
-
- Otherwise, just wrap your `amount` with the `currency` together in a `Map` like so,
- money = %{value: Decimal.new("100.50"), currency: "USD"}
+ > Support for [`monetized`][monetized] and [`money`][money] would be nice, but
+ is currently not planned.
> When this highly precise `amount` is serialized into the network request, we
> use a potentially lossy `Gringotts.Money.to_string/1` or
> `Gringotts.Money.to_integer/1` to perform rounding (if required) using the
> [`half-even`][wiki-half-even] strategy.
>
- > **Hence, to ensure transparency, protect sanity and save _real_ money, we
- > STRONGLY RECOMMEND that merchants perform any required rounding and handle
+ > **Hence, to protect your interests, and save _real_ money, we STRONGLY
+ > RECOMMEND that (you) merchants perform any required rounding and handle
> remainders in their application logic -- before passing the `amount` to
> Gringotts's API.**
@@ -63,14 +60,23 @@ defmodule Gringotts do
[ex_money]: https://hexdocs.pm/ex_money/readme.html
[monetized]: https://hexdocs.pm/monetized/
[money]: https://hexdocs.pm/money/Money.html
- [iss-money-lib-support]: https://github.com/aviabird/gringotts/projects/3#card-6801146
[wiki-half-even]: https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
### `card`, a payment source
- Gringotts provides a `Gringotts.CreditCard` type to hold card parameters
- which merchants fetch from their clients. The same type can also hold Debit
- card details.
+ Gringotts provides a `Gringotts.CreditCard` type to hold card parameters which
+ merchants fetch from their clients. You (the merchant) needs to be PCI-DSS
+ compliant to be able to use this.
+
+ > The same type can also hold Debit card details.
+
+ Some gateways do not provide direct API integrations even for PCI compliant
+ merchants, and force every merchant to use their client-side integration to
+ generate a card token.
+
+ Thus for such gateways, Gringotts accepts a `string` card-token instead of (or
+ in addition to) a `CreditCard.t` struct. Please refer the "Notes" section of
+ your chosen gateway to find what the card argument accepts.
#### Note
@@ -153,9 +159,14 @@ defmodule Gringotts do
gateway,
amount = Money.new("4.2", :USD)
- # IF YOU DON'T USE ex_money
- # amount = %{value: Decimal.new("4.2"), currency: "EUR"}
- card = %Gringotts.CreditCard{first_name: "Harry", last_name: "Potter", number: "4200000000000000", year: 2099, month: 12, verification_code: "123", brand: "VISA"}
+ card = %Gringotts.CreditCard{
+ first_name: "Harry",
+ last_name: "Potter",
+ number: "4200000000000000",
+ year: 2099, month: 12,
+ verification_code: "123",
+ brand: "VISA"
+ }
{:ok, auth_result} = Gringotts.authorize(Gringotts.Gateways.XYZ, amount, card, opts)
"""
def authorize(gateway, amount, card, opts \\ []) do
@@ -175,10 +186,8 @@ defmodule Gringotts do
To capture $4.20 on a previously authorized payment worth $4.20 by referencing
the obtained authorization `id` with the `XYZ` gateway,
+ auth_result # The result of an `authorize/3` request.
amount = Money.new("4.2", :USD)
- # IF YOU DON'T USE ex_money
- # amount = %{value: Decimal.new("4.2"), currency: "EUR"}
- card = %Gringotts.CreditCard{first_name: "Harry", last_name: "Potter", number: "4200000000000000", year: 2099, month: 12, verification_code: "123", brand: "VISA"}
Gringotts.capture(Gringotts.Gateways.XYZ, amount, auth_result.id, opts)
"""
def capture(gateway, id, amount, opts \\ []) do
@@ -204,9 +213,14 @@ defmodule Gringotts do
To process a purchase worth $4.2, with the `XYZ` gateway,
amount = Money.new("4.2", :USD)
- # IF YOU DON'T USE ex_money
- # amount = %{value: Decimal.new("4.2"), currency: "EUR"}
- card = %Gringotts.CreditCard{first_name: "Harry", last_name: "Potter", number: "4200000000000000", year: 2099, month: 12, verification_code: "123", brand: "VISA"}
+ card = %Gringotts.CreditCard{
+ first_name: "Harry",
+ last_name: "Potter",
+ number: "4200000000000000",
+ year: 2099, month: 12,
+ verification_code: "123",
+ brand: "VISA"
+ }
Gringotts.purchase(Gringotts.Gateways.XYZ, amount, card, opts)
"""
def purchase(gateway, amount, card, opts \\ []) do
@@ -225,8 +239,6 @@ defmodule Gringotts do
gateway,
amount = Money.new("4.2", :USD)
- # IF YOU DON'T USE ex_money
- # amount = %{value: Decimal.new("4.2"), currency: "EUR"}
Gringotts.purchase(Gringotts.Gateways.XYZ, amount, id, opts)
"""
def refund(gateway, amount, id, opts \\ []) do
@@ -237,7 +249,7 @@ defmodule Gringotts do
@doc """
Stores the payment-source data for later use, returns a `token`.
- > The token must be returned in the `Response.authorization` field.
+ > The token must be returned in the `Response.token` field.
## Note
@@ -247,7 +259,14 @@ defmodule Gringotts do
To store a card (a payment-source) for future use, with the `XYZ` gateway,
- card = %Gringotts.CreditCard{first_name: "Jo", last_name: "Doe", number: "4200000000000000", year: 2099, month: 12, verification_code: "123", brand: "VISA"}
+ card = %Gringotts.CreditCard{
+ first_name: "Harry",
+ last_name: "Potter",
+ number: "4200000000000000",
+ year: 2099, month: 12,
+ verification_code: "123",
+ brand: "VISA"
+ }
Gringotts.store(Gringotts.Gateways.XYZ, card, opts)
"""
def store(gateway, card, opts \\ []) do
diff --git a/lib/gringotts/gateways/authorize_net.ex b/lib/gringotts/gateways/authorize_net.ex
index 2612bb7a..ffa1396d 100644
--- a/lib/gringotts/gateways/authorize_net.ex
+++ b/lib/gringotts/gateways/authorize_net.ex
@@ -21,25 +21,25 @@ defmodule Gringotts.Gateways.AuthorizeNet do
optional arguments for transactions with the Authorize.Net gateway. The
following keys are supported:
- | Key | Remarks |
- | ---- | ------- |
- | `customer` | |
- | `invoice` | |
- | `bill_to` | |
- | `ship_to` | |
- | `customer_ip` | |
- | `order` | |
- | `lineitems` | |
- | `ref_id` | |
- | `tax` | |
- | `duty` | |
- | `shipping` | |
- | `po_number` | |
- | `customer_type` | |
- | `customer_profile_id` | |
- | `profile` | |
-
- To know more about these keywords visit [Request and Response][req-resp] tabs for each
+ | Key |
+ | ---- |
+ | `customer` |
+ | `invoice` |
+ | `bill_to` |
+ | `ship_to` |
+ | `customer_ip` |
+ | `order` |
+ | `lineitems` |
+ | `ref_id` |
+ | `tax` |
+ | `duty` |
+ | `shipping` |
+ | `po_number` |
+ | `customer_type` |
+ | `customer_profile_id` |
+ | `profile` |
+
+ To know more about these keywords check the [Request and Response][req-resp] tabs for each
API method.
[docs]: https://developer.authorize.net/api/reference/index.html
@@ -48,10 +48,12 @@ defmodule Gringotts.Gateways.AuthorizeNet do
## Notes
1. Though Authorize.Net supports [multiple currencies][currencies] however,
- multiple currencies in one account are not supported in _this_ module. A
- merchant would need multiple Authorize.Net accounts, one for each chosen
- currency.
- 2. The responses of this module include a non-standard field: `:cavv_result`.
+ multiple currencies in one account is not supported. A merchant would need
+ multiple Authorize.Net accounts, one for each chosen currency. Please refer
+ the section on "Supported acquirers and currencies" [here][currencies].
+ 2. _You, the merchant needs to be PCI-DSS Compliant if you wish to use this
+ module. Your server will recieve sensitive card and customer information._
+ 3. The responses of this module include a non-standard field: `:cavv_result`.
- `:cavv_result` is the "cardholder authentication verification response
code". In case of Mastercard transactions, this field will always be
`nil`. Please refer the "Response Format" section in the [docs][docs] for
@@ -122,11 +124,7 @@ defmodule Gringotts.Gateways.AuthorizeNet do
@aut_net_namespace "AnetApi/xml/v1/schema/AnetApiSchema.xsd"
- alias Gringotts.{
- CreditCard,
- Response,
- Money
- }
+ alias Gringotts.{CreditCard, Money, Response}
@doc """
Transfers `amount` from the customer to the merchant.
diff --git a/lib/gringotts/gateways/cams.ex b/lib/gringotts/gateways/cams.ex
index 2f1d5805..1bcffb2c 100644
--- a/lib/gringotts/gateways/cams.ex
+++ b/lib/gringotts/gateways/cams.ex
@@ -120,7 +120,7 @@ defmodule Gringotts.Gateways.Cams do
use Gringotts.Gateways.Base
use Gringotts.Adapter, required_config: [:username, :password]
- alias Gringotts.{CreditCard, Response, Money}
+ alias Gringotts.{CreditCard, Money, Response}
alias Gringotts.Gateways.Cams.ResponseHandler, as: ResponseParser
@live_url "https://secure.centralams.com/gw/api/transact.php"
diff --git a/lib/gringotts/gateways/global_collect.ex b/lib/gringotts/gateways/global_collect.ex
index 6075451d..41d69f3e 100644
--- a/lib/gringotts/gateways/global_collect.ex
+++ b/lib/gringotts/gateways/global_collect.ex
@@ -152,7 +152,7 @@ defmodule Gringotts.Gateways.GlobalCollect do
import Poison, only: [decode: 1]
- alias Gringotts.{Money, CreditCard, Response}
+ alias Gringotts.{CreditCard, Money, Response}
@brand_map %{
VISA: "1",
diff --git a/lib/gringotts/gateways/monei.ex b/lib/gringotts/gateways/monei.ex
index 4d659656..b8a05c76 100644
--- a/lib/gringotts/gateways/monei.ex
+++ b/lib/gringotts/gateways/monei.ex
@@ -43,8 +43,6 @@ defmodule Gringotts.Gateways.Monei do
| [`shipping`][sa] | Location of recipient of goods, for logistics. |
| [`shipping_customer`][c] | Recipient details, could be different from `customer`. |
- > These keys are being implemented, track progress in [issue #36][iss36]!
-
[extra-arg-docs]: https://docs.monei.net/reference/parameters
[ba]: https://docs.monei.net/reference/parameters#billing-address
[cart]: https://docs.monei.net/reference/parameters#cart
@@ -54,7 +52,6 @@ defmodule Gringotts.Gateways.Monei do
[m]: https://docs.monei.net/reference/parameters#merchant
[t]: https://docs.monei.net/reference/parameters#tokenization
[sa]: https://docs.monei.net/reference/parameters#shipping-address
- [iss36]: https://github.com/aviabird/gringotts/issues/36
## Registering your MONEI account at `Gringotts`
@@ -81,6 +78,8 @@ defmodule Gringotts.Gateways.Monei do
## Scope of this module
+ * _You, the merchant needs to be PCI-DSS Compliant if you wish to use this
+ module. Your server will recieve sensitive card and customer information._
* MONEI does not process money in cents, and the `amount` is rounded to 2
decimal places.
* Although MONEI supports payments from [various][all-card-list]
@@ -148,7 +147,7 @@ defmodule Gringotts.Gateways.Monei do
use Gringotts.Gateways.Base
use Gringotts.Adapter, required_config: [:userId, :entityId, :password]
import Poison, only: [decode: 1]
- alias Gringotts.{CreditCard, Response, Money}
+ alias Gringotts.{CreditCard, Money, Response}
@base_url "https://test.monei-api.net"
@default_headers ["Content-Type": "application/x-www-form-urlencoded", charset: "UTF-8"]
diff --git a/lib/gringotts/gateways/paymill.ex b/lib/gringotts/gateways/paymill.ex
index e9ab3015..bb3dae13 100644
--- a/lib/gringotts/gateways/paymill.ex
+++ b/lib/gringotts/gateways/paymill.ex
@@ -43,9 +43,9 @@ defmodule Gringotts.Gateways.Paymill do
* PAYMILL processes money in the sub-divided unit of currency (ie, in case of
USD it works in cents).
- * PAYMILL does not offer direct API integration for [PCI DSS][pci-dss]
- compliant merchants, everyone must use PAYMILL as if they are not PCI
- compliant.
+ * This module does not offer direct API integration for [PCI DSS][pci-dss]
+ compliant merchants. Hence, you can use this module even if your
+ infrastructure (servers) are not PCI-DSS compliant!
* To use their product, a merchant (aka user of this library) would have to
use their [Bridge (js integration)][bridge] (or equivalent) in your
application frontend to collect Credit/Debit Card data.
@@ -94,7 +94,7 @@ defmodule Gringotts.Gateways.Paymill do
use Gringotts.Gateways.Base
use Gringotts.Adapter, required_config: [:private_key, :public_key]
- alias Gringotts.{Response, Money}
+ alias Gringotts.{Money, Response}
@base_url "https://api.paymill.com/v2.1/"
@headers [{"Content-Type", "application/x-www-form-urlencoded"}]
diff --git a/lib/gringotts/gateways/stripe.ex b/lib/gringotts/gateways/stripe.ex
index 94a8ce48..691d11b5 100644
--- a/lib/gringotts/gateways/stripe.ex
+++ b/lib/gringotts/gateways/stripe.ex
@@ -18,27 +18,48 @@ defmodule Gringotts.Gateways.Stripe do
optional arguments for transactions with the Stripe gateway. The following keys
are supported:
- | Key | Remark | Status |
- | ---- | --- | ---- |
- | `currency` | | **Implemented** |
- | `capture` | | **Implemented** |
- | `description` | | **Implemented** |
- | `metadata` | | **Implemented** |
- | `receipt_email` | | **Implemented** |
- | `shipping` | | **Implemented** |
- | `customer` | | **Implemented** |
- | `source` | | **Implemented** |
- | `statement_descriptor` | | **Implemented** |
- | `charge` | | **Implemented** |
- | `reason` | | **Implemented** |
- | `account_balance` | | Not implemented |
- | `business_vat_id` | | Not implemented |
- | `coupon` | | Not implemented |
- | `default_source` | | Not implemented |
- | `email` | | Not implemented |
- | `shipping` | | Not implemented |
+ | Key | Status |
+ | ---- | ---- |
+ | `currency` | **Implemented** |
+ | `capture` | **Implemented** |
+ | `description` | **Implemented** |
+ | `metadata` | **Implemented** |
+ | `receipt_email` | **Implemented** |
+ | `shipping` | **Implemented** |
+ | `customer` | **Implemented** |
+ | `source` | **Implemented** |
+ | `statement_descriptor` | **Implemented** |
+ | `charge` | **Implemented** |
+ | `reason` | **Implemented** |
+ | `account_balance` | Not implemented |
+ | `business_vat_id` | Not implemented |
+ | `coupon` | Not implemented |
+ | `default_source` | Not implemented |
+ | `email` | Not implemented |
+ | `shipping` | Not implemented |
+
+ ## Note
+
+ _This module can be used by both PCI-DSS compliant as well as non-compliant
+ merchants!_
+
+ ### I'm not PCI-DSS compliant
+
+ No worries, both `authorize/3` and `purchase/3` accept a
+ "payment-source-identifier" (a `string`) instead of a `CreditCard.t`
+ struct. You'll have to generate this identifier using [Stripe.js and
+ Elements][stripe-js] client-side.
+
+ ### I'm PCI-DSS compliant
+
+ In that case, you need not use [Stripe.js or Elements][stripe-js] and can
+ directly accept the client's card info and pass the `CreditCard.t` struct to
+ this module's functions.
+
+ [stripe-js]: https://stripe.com/docs/sources/cards
## Registering your Stripe account at `Gringotts`
+
After [making an account on Stripe](https://stripe.com/), head
to the dashboard and find your account `secrets` in the `API` section.
@@ -59,11 +80,7 @@ defmodule Gringotts.Gateways.Stripe do
use Gringotts.Gateways.Base
use Gringotts.Adapter, required_config: [:secret_key]
- alias Gringotts.{
- CreditCard,
- Address,
- Money
- }
+ alias Gringotts.{Address, CreditCard, Money}
@doc """
Performs a (pre) Authorize operation.
@@ -72,15 +89,19 @@ defmodule Gringotts.Gateways.Stripe do
places a hold on the transaction amount in the customer’s issuing bank and
also triggers risk management. Funds are not transferred.
- Stripe returns an `charge_id` which should be stored at your side and can be used later to:
+ Stripe returns an `charge_id` which should be stored at your side and can be
+ used later to:
* `capture/3` an amount.
* `void/2` a pre-authorization.
## Note
- Uncaptured charges expire in 7 days. For more information, [see authorizing charges and settling later](https://support.stripe.com/questions/can-i-authorize-a-charge-and-then-wait-to-settle-it-later).
+ Uncaptured charges expire in 7 days. For more information, [see authorizing
+ charges and settling
+ later](https://support.stripe.com/questions/can-i-authorize-a-charge-and-then-wait-to-settle-it-later).
## Example
- The following session shows how one would (pre) authorize a payment of $10 on a sample `card`.
+ The following session shows how one would (pre) authorize a payment of $10 on
+ a sample `card`.
iex> card = %CreditCard{
first_name: "John",
@@ -105,7 +126,7 @@ defmodule Gringotts.Gateways.Stripe do
iex> Gringotts.authorize(Gringotts.Gateways.Stripe, amount, card, opts)
"""
@spec authorize(Money.t(), CreditCard.t() | String.t(), keyword) :: map
- def authorize(amount, payment, opts \\ []) do
+ def authorize(amount, payment, opts) do
params = create_params_for_auth_or_purchase(amount, payment, opts, false)
commit(:post, "charges", params, opts)
end
@@ -143,7 +164,7 @@ defmodule Gringotts.Gateways.Stripe do
iex> Gringotts.purchase(Gringotts.Gateways.Stripe, amount, card, opts)
"""
@spec purchase(Money.t(), CreditCard.t() | String.t(), keyword) :: map
- def purchase(amount, payment, opts \\ []) do
+ def purchase(amount, payment, opts) do
params = create_params_for_auth_or_purchase(amount, payment, opts)
commit(:post, "charges", params, opts)
end
@@ -155,8 +176,9 @@ defmodule Gringotts.Gateways.Stripe do
equal to the amount used in the pre-authorization referenced by `charge_id`.
## Note
- Stripe allows partial captures and release the remaining amount back to the payment source. Thus, the same
- pre-authorisation `charge_id` cannot be used to perform multiple captures.
+ Stripe allows partial captures and release the remaining amount back to the
+ payment source. Thus, the same pre-authorisation `charge_id` cannot be used to
+ perform multiple captures.
## Example
The following session shows how one would (partially) capture a previously
@@ -169,7 +191,7 @@ defmodule Gringotts.Gateways.Stripe do
iex> Gringotts.capture(Gringotts.Gateways.Stripe, id, amount, opts)
"""
@spec capture(String.t(), Money.t(), keyword) :: map
- def capture(id, amount, opts \\ []) do
+ def capture(id, amount, opts) do
params = optional_params(opts) ++ amount_params(amount)
commit(:post, "charges/#{id}/capture", params, opts)
end
@@ -202,7 +224,7 @@ defmodule Gringotts.Gateways.Stripe do
iex> Gringotts.void(Gringotts.Gateways.Stripe, id, opts)
"""
@spec void(String.t(), keyword) :: map
- def void(id, opts \\ []) do
+ def void(id, opts) do
params = optional_params(opts)
commit(:post, "charges/#{id}/refund", params, opts)
end
@@ -224,7 +246,7 @@ defmodule Gringotts.Gateways.Stripe do
iex> Gringotts.refund(Gringotts.Gateways.Stripe, amount, id, opts)
"""
@spec refund(Money.t(), String.t(), keyword) :: map
- def refund(amount, id, opts \\ []) do
+ def refund(amount, id, opts) do
params = optional_params(opts) ++ amount_params(amount)
commit(:post, "charges/#{id}/refund", params, opts)
end
@@ -232,8 +254,9 @@ defmodule Gringotts.Gateways.Stripe do
@doc """
Stores the payment-source data for later use.
- Stripe can store the payment-source details, for example card which can be used to effectively
- to process One-Click and Recurring_ payments, and return a `customer_id` for reference.
+ Stripe can store the payment-source details, for example card which can be
+ used to effectively to process One-Click and Recurring_ payments, and return a
+ `customer_id` for reference.
## Example
The following session shows how one would store a card (a payment-source) for
@@ -261,7 +284,7 @@ defmodule Gringotts.Gateways.Stripe do
iex> Gringotts.store(Gringotts.Gateways.Stripe, card, opts)
"""
@spec store(CreditCard.t() | String.t(), keyword) :: map
- def store(payment, opts \\ []) do
+ def store(payment, opts) do
params = optional_params(opts) ++ source_params(payment, opts)
commit(:post, "customers", params, opts)
end
@@ -269,18 +292,19 @@ defmodule Gringotts.Gateways.Stripe do
@doc """
Deletes previously stored payment-source data.
- Deletes the already stored payment source,
- so that it cannot be used again for capturing
- payments.
+ Deletes the already stored payment source, so that it cannot be used again for
+ capturing payments.
## Examples
- The following session shows how one would unstore a already stored payment source.
+ The following session shows how one would unstore a already stored payment
+ source.
+
iex> id = "cus_BwpLX2x4ecEUgD"
iex> Gringotts.unstore(Gringotts.Gateways.Stripe, id, opts)
"""
- @spec unstore(String.t()) :: map
- def unstore(id, opts \\ []), do: commit(:delete, "customers/#{id}", [], opts)
+ @spec unstore(String.t(), keyword) :: map
+ def unstore(id, opts), do: commit(:delete, "customers/#{id}", [], opts)
# Private methods
diff --git a/lib/gringotts/gateways/trexle.ex b/lib/gringotts/gateways/trexle.ex
index a9c3d988..669e720d 100644
--- a/lib/gringotts/gateways/trexle.ex
+++ b/lib/gringotts/gateways/trexle.ex
@@ -14,6 +14,11 @@ defmodule Gringotts.Gateways.Trexle do
| Refund | `refund/3` |
| Store | `store/2` |
+ ## PCI compliance is mandatory!
+
+ _You, the merchant needs to be PCI-DSS Compliant if you wish to use this
+ module! Your server will recieve sensitive card and customer information._
+
## The `opts` argument
Most `Gringotts` API calls accept an optional `keyword` list `opts` to supply
@@ -32,7 +37,7 @@ defmodule Gringotts.Gateways.Trexle do
After [creating your account][dashboard] successfully on Trexle, head to the dashboard and find
your account "secrets" in the [`API keys`][keys] section.
- Here's how the secrets map to the required configuration parameters for MONEI:
+ Here's how the secrets map to the required configuration parameters for Trexle:
| Config parameter | Trexle secret |
| ------- | ---- |
@@ -83,7 +88,7 @@ defmodule Gringotts.Gateways.Trexle do
use Gringotts.Gateways.Base
use Gringotts.Adapter, required_config: [:api_key]
import Poison, only: [decode: 1]
- alias Gringotts.{Response, CreditCard, Address, Money}
+ alias Gringotts.{Address, CreditCard, Money, Response}
@doc """
Performs a (pre) Authorize operation.
@@ -92,7 +97,7 @@ defmodule Gringotts.Gateways.Trexle do
places a hold on the transaction `amount` in the customer’s issuing bank and
also triggers risk management. Funds are not transferred.
- Trexle returns a "charge token", avaliable in the `Response.authorization`
+ Trexle returns a "charge token", avaliable in the `Response.id`
field, which can be used in future to perform a `capture/3`.
### Example
@@ -133,10 +138,10 @@ defmodule Gringotts.Gateways.Trexle do
@doc """
Captures a pre-authorized `amount`.
- `amount` is transferred to the merchant account by MONEI when it is smaller or
+ `amount` is transferred to the merchant account by Trexle when it is smaller or
equal to the amount used in the pre-authorization referenced by `charge_token`.
- Trexle returns a "charge token", avaliable in the `Response.authorization`
+ Trexle returns a "charge token", avaliable in the `Response.id`
field, which can be used in future to perform a `refund/2`.
## Note
@@ -210,7 +215,7 @@ defmodule Gringotts.Gateways.Trexle do
Trexle processes a full or partial refund worth `amount`, referencing a
previous `purchase/3` or `capture/3`.
- Trexle returns a "refund token", avaliable in the `Response.authorization`
+ Trexle returns a "refund token", avaliable in the `Response.id`
field.
Multiple, partial refunds can be performed on the same "charge token"
diff --git a/lib/gringotts/gateways/wire_card.ex b/lib/gringotts/gateways/wire_card.ex
deleted file mode 100644
index 939a0fef..00000000
--- a/lib/gringotts/gateways/wire_card.ex
+++ /dev/null
@@ -1,405 +0,0 @@
-# call => Gringotts.Gateways.WireCard.authorize(100, creditcard, options)
-import XmlBuilder
-
-defmodule Gringotts.Gateways.WireCard do
- @moduledoc """
- WireCard System Plugins
- """
- @test_url "https://c3-test.wirecard.com/secure/ssl-gateway"
- @live_url "https://c3.wirecard.com/secure/ssl-gateway"
- @homepage_url "http://www.wirecard.com"
-
- @doc """
- Wirecard only allows phone numbers with a format like this: +xxx(yyy)zzz-zzzz-ppp, where:
- xxx = Country code
- yyy = Area or city code
- zzz-zzzz = Local number
- ppp = PBX extension
- For example, a typical U.S. or Canadian number would be "+1(202)555-1234-739" indicating PBX extension 739 at phone
- number 5551234 within area code 202 (country code 1).
- """
- @valid_phone_format ~r/\+\d{1,3}(\(?\d{3}\)?)?\d{3}-\d{4}-\d{3}/
- @default_currency "EUR"
- @default_amount 100
- use Gringotts.Gateways.Base
- use Gringotts.Adapter, required_config: [:login, :password, :signature]
-
- alias Gringotts.{
- CreditCard,
- Address,
- Response
- }
-
- import Poison, only: [decode!: 1]
-
- @doc """
- Authorization - the second parameter may be a CreditCard or
- a String which represents a GuWID reference to an earlier
- transaction. If a GuWID is given, rather than a CreditCard,
- then then the :recurring option will be forced to "Repeated"
- ===========================================================
- TODO: Mandatorily check for :login,:password, :signature in options
- Note: payment_menthod for now is only credit_card and
- TODO: change it so it can also have GuWID
- ================================================
- E.g: =>
- creditcard = %CreditCard{
- number: "4200000000000000",
- month: 12,
- year: 2018,
- first_name: "Longbob",
- last_name: "Longsen",
- verification_code: "123",
- brand: "visa"
- }
- address = %{
- name: "Jim Smith",
- address1: "456 My Street",
- address2: "Apt 1",
- company: "Widgets Inc",
- city: "Ottawa",
- state: "ON",
- zip: "K1C2N6",
- country: "CA",
- phone: "(555)555-5555",
- fax: "(555)555-6666"
- }
- options = [
- config: %{
- login: "00000031629CA9FA",
- password: "TestXAPTER",
- signature: "00000031629CAFD5",
- },
- order_id: 1,
- billing_address: address,
- description: 'Wirecard remote test purchase',
- email: "soleone@example.com",
- ip: "127.0.0.1",
- test: true
- ]
- """
- @spec authorize(Integer | Float, CreditCard.t() | String.t(), Keyword) :: {:ok, Map}
- def authorize(money, payment_method, options \\ [])
-
- def authorize(money, %CreditCard{} = creditcard, options) do
- options = Keyword.put(options, :credit_card, creditcard)
- commit(:post, :preauthorization, money, options)
- end
-
- def authorize(money, authorization, options) when is_binary(authorization) do
- options = Keyword.put(options, :preauthorization, authorization)
- commit(:post, :preauthorization, money, options)
- end
-
- @doc """
- Capture - the first paramter here should be a GuWid/authorization.
- Authorization is obtained by authorizing the creditcard.
- """
- @spec capture(String.t(), Float, Keyword) :: {:ok, Map}
- def capture(authorization, money, options \\ []) when is_binary(authorization) do
- options = Keyword.put(options, :preauthorization, authorization)
- commit(:post, :capture, money, options)
- end
-
- @doc """
- Purchase - the second parameter may be a CreditCard or
- a String which represents a GuWID reference to an earlier
- transaction. If a GuWID is given, rather than a CreditCard,
- then then the :recurring option will be forced to "Repeated"
- """
- @spec purchase(Float | Integer, CreditCard | String.t(), Keyword) :: {:ok, Map}
- def purchase(money, payment_method, options \\ [])
-
- def purchase(money, %CreditCard{} = creditcard, options) do
- options = Keyword.put(options, :credit_card, creditcard)
- commit(:post, :purchase, money, options)
- end
-
- def purchase(money, authorization, options) when is_binary(authorization) do
- options = Keyword.put(options, :preauthorization, authorization)
- commit(:post, :purchase, money, options)
- end
-
- @doc """
- Void - A credit card purchase that a seller cancels after it has
- been authorized but before it has been settled.
- A void transaction does not appear on the customer's
- credit card statement, though it might appear in a list
- of pending transactions when the customer checks their
- account online.
- ==== Parameters ======
- identification - The authorization string returned from the
- initial authorization or purchase.
- """
- @spec void(String.t(), Keyword) :: {:ok, Map}
- def void(identification, options \\ []) when is_binary(identification) do
- options = Keyword.put(options, :preauthorization, identification)
- commit(:post, :reversal, nil, options)
- end
-
- @doc """
- Performs a credit.
-
- This transaction indicates that money
- should flow from the merchant to the customer.
- ==== Parameters ====
- money -- The amount to be credited to the customer
- as an Integer value in cents.
- identification -- GuWID
- """
- @spec refund(Float, String.t(), Keyword) :: {:ok, Map}
- def refund(money, identification, options \\ []) when is_binary(identification) do
- options = Keyword.put(options, :preauthorization, identification)
- commit(:post, :bookback, money, options)
- end
-
- @doc """
- Store card - Wirecard supports the notion of "Recurring
- Transactions" by allowing the merchant to provide a reference
- to an earlier transaction (the GuWID) rather than a credit
- card. A reusable reference (GuWID) can be obtained by sending
- a purchase or authorization transaction with the element
- "RECURRING_TRANSACTION/Type" set to "Initial". Subsequent
- transactions can then use the GuWID in place of a credit
- card by setting "RECURRING_TRANSACTION/Type" to "Repeated".
-
- This implementation of card store utilizes a Wirecard
- "Authorization Check" (a Preauthorization that is automatically
- reversed). It defaults to a check amount of "100" (i.e.
- $1.00) but this can be overriden (see below).
-
- IMPORTANT: In order to reuse the stored reference, the
- +authorization+ from the response should be saved by
- your application code.
-
- ==== Options specific to +store+
-
- * :amount -- The amount, in cents, that should be
- "validated" by the Authorization Check. This amount will
- be reserved and then reversed. Default is 100.
-
- Note: This is not the only way to achieve a card store
- operation at Wirecard. Any +purchase+ or +authorize+
- can be sent with +options[:recurring] = 'Initial'+ to make
- the returned authorization/GuWID usable in later transactions
- with +options[:recurring] = 'Repeated'+.
- """
- @spec store(CreditCard.t(), Keyword) :: {:ok, Map}
- def store(%CreditCard{} = creditcard, options \\ []) do
- options =
- options
- |> Keyword.put(:credit_card, creditcard)
- |> Keyword.put(:recurring, "Initial")
-
- money = options[:amount] || @default_amount
- # Amex does not support authorization_check
- case creditcard.brand do
- "american_express" -> commit(:post, :preauthorization, money, options)
- _ -> commit(:post, :authorization_check, money, options)
- end
- end
-
- # =================== Private Methods ===================
-
- # Contact WireCard, make the XML request, and parse the
- # reply into a Response object.
- defp commit(method, action, money, options) do
- # TODO: validate and setup address hash as per AM
- request = build_request(action, money, options)
-
- headers = %{
- "Content-Type" => "text/xml",
- "Authorization" =>
- encoded_credentials(
- options[:config][:login],
- options[:config][:password]
- )
- }
-
- method |> HTTPoison.request(base_url(options), request, headers) |> respond
- end
-
- defp respond({:ok, %{status_code: 200, body: body}}) do
- response = parse(body)
- {:ok, response}
- end
-
- defp respond({:ok, %{body: body, status_code: status_code}}) do
- {:error, "Some Error Occurred: \n #{inspect(body)}"}
- end
-
- # Read the XML message from the gateway and check if it was successful,
- # and also extract required return values from the response
- # TODO: parse XML Response
- defp parse(data) do
- XmlToMap.naive_map(data)
- end
-
- # Generates the complete xml-message, that gets sent to the gateway
- defp build_request(action, money, options) do
- options = Keyword.put(options, :action, action)
-
- request =
- doc(
- element(:WIRECARD_BXML, [
- element(:W_REQUEST, [
- element(:W_JOB, [
- element(:JobID, ""),
- element(:BusinessCaseSignature, options[:config][:signature]),
- add_transaction_data(action, money, options)
- ])
- ])
- ])
- )
-
- request
- end
-
- # Includes the whole transaction data (payment, creditcard, address)
- # TODO: Add order_id to options if not present, see AM
- # TOOD: Clean description before passing it to FunctionID, replace dummy
- defp add_transaction_data(action, money, options) do
- element("FNC_CC_#{atom_to_upcase_string(options[:action])}", [
- element(:FunctionID, "dummy_description"),
- element(
- :CC_TRANSACTION,
- [
- element(:TransactionID, options[:order_id]),
- element(:CommerceType, if(options[:commerce_type], do: options[:commerce_type]))
- ] ++ add_action_data(action, money, options) ++ add_customer_data(options)
- )
- ])
- end
-
- # Includes the IP address of the customer to the transaction-xml
- defp add_customer_data(options) do
- if options[:ip] do
- [
- element(:CONTACT_DATA, [element(:IPAddress, options[:ip])])
- ]
- end
- end
-
- def add_action_data(action, money, options) do
- case options[:action] do
- # returns array of elements
- action when action in [:preauthorization, :purchase, :authorization_check] ->
- create_elems_for_preauth_or_purchase_or_auth_check(money, options)
-
- action when action in [:capture, :bookback] ->
- create_elems_for_capture_or_bookback(money, options)
-
- action when action == :reversal ->
- add_guwid(options[:preauthorization])
- end
- end
-
- # Creates xml request elements if action is capture, bookback
- defp create_elems_for_capture_or_bookback(money, options) do
- add_guwid(options[:preauthorization]) ++ [add_amount(money, options)]
- end
-
- # Creates xml request elements if action is preauth, purchase ir auth_check
- # TODO: handle nil values if array not generated
- defp create_elems_for_preauth_or_purchase_or_auth_check(money, options) do
- # TODO: setup_recurring_flag
- add_invoice(money, options) ++
- element_for_credit_card_or_guwid(options) ++ add_address(options[:billing_address])
- end
-
- defp add_address(address) do
- if address do
- [
- element(:CORPTRUSTCENTER_DATA, [
- element(:ADDRESS, [
- element(:Address1, address[:address1]),
- element(:Address2, if(address[:address2], do: address[:address2])),
- element(:City, address[:city]),
- element(:Zip, address[:zip]),
- add_state(address),
- element(:Country, address[:country]),
- element(
- :Phone,
- if(regex_match(@valid_phone_format, address[:phone]), do: address[:phone])
- ),
- element(:Email, address[:email])
- ])
- ])
- ]
- end
- end
-
- defp add_state(address) do
- if regex_match(~r/[A-Za-z]{2}/, address[:state]) &&
- regex_match(~r/^(us|ca)$/i, address[:country]) do
- element(:State, String.upcase(address[:state]))
- end
- end
-
- defp element_for_credit_card_or_guwid(options) do
- if options[:credit_card] do
- add_creditcard(options[:credit_card])
- else
- add_guwid(options[:preauthorization])
- end
- end
-
- # Includes Guwid data to transaction-xml
- defp add_guwid(preauth) do
- [element(:GuWID, preauth)]
- end
-
- # Includes the credit-card data to the transaction-xml
- # TODO: Format Credit Card month, ref AM
- defp add_creditcard(creditcard) do
- [
- element(:CREDIT_CARD_DATA, [
- element(:CreditCardNumber, creditcard.number),
- element(:CVC2, creditcard.verification_code),
- element(:ExpirationYear, creditcard.year),
- element(:ExpirationMonth, creditcard.month),
- element(:CardHolderName, join_string([creditcard.first_name, creditcard.last_name], " "))
- ])
- ]
- end
-
- # Includes the payment (amount, currency, country) to the transaction-xml
- def add_invoice(money, options) do
- [
- add_amount(money, options),
- element(:Currency, currency(options)),
- element(:CountryCode, options[:billing_address][:country]),
- element(:RECURRING_TRANSACTION, [
- element(:Type, options[:recurring] || "Single")
- ])
- ]
- end
-
- # Include the amount in the transaction-xml
- # TODO: check for localized currency or currency
- # localized_amount(money, options[:currency] || currency(money))
- defp add_amount(money, options), do: element(:Amount, money)
-
- defp atom_to_upcase_string(atom) do
- atom
- |> to_string
- |> String.upcase()
- end
-
- # Encode login and password in Base64 to supply as HTTP header
- # (for http basic authentication)
- defp encoded_credentials(login, password) do
- [login, password]
- |> join_string(":")
- |> Base.encode64()
- |> (&("Basic " <> &1)).()
- end
-
- defp join_string(list_of_words, joiner), do: Enum.join(list_of_words, joiner)
-
- defp regex_match(regex, string), do: Regex.match?(regex, string)
-
- defp base_url(opts), do: if(opts[:test], do: @test_url, else: @live_url)
-
- defp currency(opts), do: opts[:currency] || @default_currency
-end
diff --git a/lib/gringotts/money.ex b/lib/gringotts/money.ex
index 304cc764..5e56a7f4 100644
--- a/lib/gringotts/money.ex
+++ b/lib/gringotts/money.ex
@@ -126,39 +126,6 @@ defprotocol Gringotts.Money do
def to_string(money)
end
-# this implementation is used for dispatch on ex_money (and will also fire for
-# money)
-if Code.ensure_compiled?(Money) do
- defimpl Gringotts.Money, for: Money do
- def currency(money), do: money.currency |> Atom.to_string()
- def value(money), do: money.amount
-
- def to_integer(money) do
- {_, int_value, exponent, _} = Money.to_integer_exp(money)
- {currency(money), int_value, exponent}
- end
-
- def to_string(money) do
- {:ok, currency_data} = Cldr.Currency.currency_for_code(currency(money))
- reduced = Money.reduce(money)
-
- {
- currency(reduced),
- value(reduced)
- |> Decimal.round(currency_data.digits)
- |> Decimal.to_string()
- }
- end
- end
-end
-
-if Code.ensure_compiled?(Monetized.Money) do
- defimpl Gringotts.Money, for: Monetized.Money do
- def currency(money), do: money.currency
- def value(money), do: money.amount
- end
-end
-
# Assumes that the currency is subdivided into 100 units
defimpl Gringotts.Money, for: Any do
def currency(%{currency: currency}), do: currency
diff --git a/mix.exs b/mix.exs
index 589f379c..60056b6c 100644
--- a/mix.exs
+++ b/mix.exs
@@ -4,7 +4,7 @@ defmodule Gringotts.Mixfile do
def project do
[
app: :gringotts,
- version: "1.1.0",
+ version: "1.1.1-rc",
description: description(),
package: [
contributors: ["Aviabird Technologies"],
@@ -12,7 +12,7 @@ defmodule Gringotts.Mixfile do
licenses: ["MIT"],
links: %{github: "https://github.com/aviabird/gringotts"}
],
- elixir: ">= 1.3.0",
+ elixir: ">= 1.5.3",
test_coverage: [
tool: ExCoveralls
],
@@ -42,7 +42,7 @@ defmodule Gringotts.Mixfile do
end
# Specifies which paths to compile per environment.
- defp elixirc_paths(:test), do: ["lib", "test/mocks"]
+ defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]
# Dependencies can be hex.pm packages:
@@ -57,34 +57,32 @@ defmodule Gringotts.Mixfile do
defp deps do
[
{:poison, "~> 3.1.0"},
- {:httpoison, "~> 0.13"},
+ {:httpoison, "~> 1.1"},
{:xml_builder, "~> 2.1"},
{:elixir_xml_to_map, "~> 0.1"},
- # Money related
- {:decimal, "~> 1.0", optional: true},
- # ex_money is just needed for tests.
- {:ex_money, "~> 1.1.0", only: [:dev, :test], optional: true},
+ # money related
+ {:decimal, "~> 1.5"},
# docs and tests
{:ex_doc, "~> 0.18", only: :dev, runtime: false},
{:mock, "~> 0.3.0", only: :test},
{:bypass, "~> 0.8", only: :test},
{:excoveralls, "~> 0.8", only: :test},
+ {:exvcr, "~> 0.10", only: :test},
# various analyses tools
- {:credo, "~> 0.3", only: [:dev, :test]},
+ {:credo, "~> 0.9", only: [:dev, :test]},
{:inch_ex, "~> 0.5", only: :docs},
{:dialyxir, "~> 0.3", only: :dev},
- {:timex, "~> 3.2"},
- {:exvcr, "~> 0.10", only: :test}
+ {:timex, "~> 3.2"}
]
end
defp description do
"""
Gringotts is a payment processing library in Elixir integrating
- various payment gateways, this draws motivation for shopify's
+ various payment gateways, and draws motivation from shopify's
activemerchant ruby gem.
"""
end
diff --git a/mix.lock b/mix.lock
index 83a82360..704128b0 100644
--- a/mix.lock
+++ b/mix.lock
@@ -2,40 +2,41 @@
"abnf2": {:hex, :abnf2, "0.1.2", "6f8792b8ac3288dba5fc889c2bceae9fe78f74e1a7b36bea9726ffaa9d7bef95", [:mix], [], "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"},
+ "certifi": {:hex, :certifi, "2.3.1", "d0f424232390bf47d82da8478022301c561cf6445b5b5fb6a84d49a9e76d2639", [:rebar3], [{:parse_trans, "3.2.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "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"},
- "decimal": {:hex, :decimal, "1.4.1", "ad9e501edf7322f122f7fc151cce7c2a0c9ada96f2b0155b8a09a795c2029770", [:mix], [], "hexpm"},
+ "credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
+ "decimal": {:hex, :decimal, "1.5.0", "b0433a36d0e2430e3d50291b1c65f53c37d56f83665b43d79963684865beab68", [:mix], [], "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"},
+ "earmark": {:hex, :earmark, "1.2.5", "4d21980d5d2862a2e13ec3c49ad9ad783ffc7ca5769cf6ff891a4553fbaae761", [:mix], [], "hexpm"},
+ "elixir_xml_to_map": {:hex, :elixir_xml_to_map, "0.1.2", "e3d1bd2f6562711117ae209657f385a1c1c34c8c720c748eeba2e22815797071", [:mix], [{:erlsom, "~>1.4", [hex: :erlsom, repo: "hexpm", optional: false]}], "hexpm"},
+ "erlsom": {:hex, :erlsom, "1.4.2", "5cddb82fb512f406f61162e511ae86582f824f0dccda788378b18a00d89c1b3f", [:rebar3], [], "hexpm"},
"ex_cldr": {:hex, :ex_cldr, "1.4.4", "654966e8724d607e5cf9ecd5509ffcf66868b17e479bbd22ab2e9123595f9103", [:mix], [{:abnf2, "~> 0.1", [hex: :abnf2, repo: "hexpm", optional: false]}, {:decimal, "~> 1.4", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.13", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}, {:poison, "~> 2.1 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm"},
"ex_cldr_numbers": {:hex, :ex_cldr_numbers, "1.3.1", "50a117654dff8f8ee6958e68a65d0c2835a7e2f1aff94c1ea8f582c04fdf0bd4", [:mix], [{:decimal, "~> 1.4", [hex: :decimal, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 1.4.0", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.1 or ~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.18.3", "f4b0e4a2ec6f333dccf761838a4b253d75e11f714b85ae271c9ae361367897b7", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
"ex_money": {:hex, :ex_money, "1.1.3", "843eed0a5673206de33be47cdc06574401abc3e2d33cbcf6d74e160226791ae4", [:mix], [{:decimal, "~> 1.4", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: true]}, {:ex_cldr, "~> 1.0", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 1.0", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}], "hexpm"},
"exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], [], "hexpm"},
- "excoveralls": {:hex, :excoveralls, "0.8.1", "0bbf67f22c7dbf7503981d21a5eef5db8bbc3cb86e70d3798e8c802c74fa5e27", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
+ "excoveralls": {:hex, :excoveralls, "0.8.2", "b941a08a1842d7aa629e0bbc969186a4cefdd035bad9fe15d43aaaaaeb8fae36", [: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"},
- "exvcr": {:hex, :exvcr, "0.10.0", "5150808404d9f48dbda636f70f7f8fefd93e2433cd39f695f810e73b3a9d1736", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:httpoison, "~> 0.13", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.0", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
+ "exvcr": {:hex, :exvcr, "0.10.2", "a66a0fa86d03153e5c21e38b1320d10b537038d7bc7b10dcc1ab7f0343569822", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
"gettext": {:hex, :gettext, "0.15.0", "40a2b8ce33a80ced7727e36768499fc9286881c43ebafccae6bab731e2b2b8ce", [:mix], [], "hexpm"},
- "hackney": {:hex, :hackney, "1.11.0", "4951ee019df102492dabba66a09e305f61919a8a183a7860236c0fde586134b6", [: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"},
+ "hackney": {:hex, :hackney, "1.12.1", "8bf2d0e11e722e533903fe126e14d6e7e94d9b7983ced595b75f532e04b7fdc7", [:rebar3], [{:certifi, "2.3.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.1", [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, "1.1.1", "96ed7ab79f78a31081bb523eefec205fd2900a02cda6dbc2300e7a1226219566", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
+ "idna": {:hex, :idna, "5.1.1", "cbc3b2fa1645113267cc59c760bafa64b2ea0334635ef06dbac8801e42f7279c", [: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", [: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], [], "hexpm"},
- "mime": {:hex, :mime, "1.2.0", "78adaa84832b3680de06f88f0997e3ead3b451a440d183d688085be2d709b534", [:mix], [], "hexpm"},
+ "mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [: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.5.0", "224b25b4039bedc1eac149fb52ed456770b9678bbf0349cdd810460e1e09195b", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
+ "parse_trans": {:hex, :parse_trans, "3.2.0", "2adfa4daf80c14dc36f522cf190eb5c4ee3e28008fc6394397c16f62a26258c2", [:rebar3], [], "hexpm"},
+ "plug": {:hex, :plug, "1.5.1", "1ff35bdecfb616f1a2b1c935ab5e4c47303f866cb929d2a76f0541e553a58165", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.3", [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", [:rebar3], [], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"},
- "timex": {:hex, :timex, "3.2.1", "639975eac45c4c08c2dbf7fc53033c313ff1f94fad9282af03619a3826493612", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
+ "timex": {:hex, :timex, "3.3.0", "e0695aa0ddb37d460d93a2db34d332c2c95a40c27edf22fbfea22eb8910a9c8d", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
"tzdata": {:hex, :tzdata, "0.5.16", "13424d3afc76c68ff607f2df966c0ab4f3258859bbe3c979c9ed1606135e7352", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"},
"xml_builder": {:hex, :xml_builder, "2.1.0", "c249d5339427c13cae11e9d9d0e8b40d25d228b9ecc54029f24017385e60280b", [:mix], [], "hexpm"},
diff --git a/test/gateways/authorize_net_test.exs b/test/gateways/authorize_net_test.exs
index 66ae689d..4047a1bc 100644
--- a/test/gateways/authorize_net_test.exs
+++ b/test/gateways/authorize_net_test.exs
@@ -1,7 +1,7 @@
defmodule Gringotts.Gateways.AuthorizeNetTest do
use ExUnit.Case, async: false
alias Gringotts.Gateways.AuthorizeNetMock, as: MockResponse
- alias Gringotts.CreditCard
+ alias Gringotts.{CreditCard, FakeMoney}
alias Gringotts.Gateways.AuthorizeNet, as: ANet
import Mock
@@ -21,7 +21,7 @@ defmodule Gringotts.Gateways.AuthorizeNetTest do
verification_code: 123
}
- @amount Money.new("2.99", :USD)
+ @amount FakeMoney.new("2.99", :USD)
@opts [
config: @auth,
@@ -32,17 +32,17 @@ defmodule Gringotts.Gateways.AuthorizeNetTest do
name: "vase",
description: "Cannes logo",
quantity: 18,
- unit_price: Money.mult!(@amount, 18)
+ unit_price: FakeMoney.new("53.82", :USD)
},
- tax: %{name: "VAT", amount: Money.new("0.1", :EUR), description: "Value Added Tax"},
+ tax: %{name: "VAT", amount: FakeMoney.new("0.1", :EUR), description: "Value Added Tax"},
shipping: %{
name: "SAME-DAY-DELIVERY",
- amount: Money.new("0.56", :EUR),
+ amount: FakeMoney.new("0.56", :EUR),
description: "Zen Logistics"
},
duty: %{
name: "import_duty",
- amount: Money.new("0.25", :EUR),
+ amount: FakeMoney.new("0.25", :EUR),
description: "Upon import of goods"
}
]
@@ -132,7 +132,7 @@ defmodule Gringotts.Gateways.AuthorizeNetTest do
post: fn _url, _body, _headers ->
MockResponse.bad_card_purchase_response()
end do
- assert {:error, response} = ANet.purchase(@amount, @bad_card, @opts)
+ assert {:error, _response} = ANet.purchase(@amount, @bad_card, @opts)
end
end
end
@@ -152,7 +152,7 @@ defmodule Gringotts.Gateways.AuthorizeNetTest do
post: fn _url, _body, _headers ->
MockResponse.bad_card_purchase_response()
end do
- assert {:error, response} = ANet.authorize(@amount, @bad_card, @opts)
+ assert {:error, _response} = ANet.authorize(@amount, @bad_card, @opts)
end
end
end
@@ -169,7 +169,7 @@ defmodule Gringotts.Gateways.AuthorizeNetTest do
test "with bad transaction id" do
with_mock HTTPoison, post: fn _url, _body, _headers -> MockResponse.bad_id_capture() end do
- assert {:error, response} = ANet.capture(@capture_invalid_id, @amount, @opts)
+ assert {:error, _response} = ANet.capture(@capture_invalid_id, @amount, @opts)
end
end
end
@@ -186,14 +186,14 @@ defmodule Gringotts.Gateways.AuthorizeNetTest do
test "bad payment params" do
with_mock HTTPoison, post: fn _url, _body, _headers -> MockResponse.bad_card_refund() end do
- assert {:error, response} = ANet.refund(@amount, @refund_id, @opts_refund_bad_payment)
+ assert {:error, _response} = ANet.refund(@amount, @refund_id, @opts_refund_bad_payment)
end
end
test "debit less than refund amount" do
with_mock HTTPoison,
post: fn _url, _body, _headers -> MockResponse.debit_less_than_refund() end do
- assert {:error, response} = ANet.refund(@amount, @refund_id, @opts_refund)
+ assert {:error, _response} = ANet.refund(@amount, @refund_id, @opts_refund)
end
end
end
@@ -208,7 +208,7 @@ defmodule Gringotts.Gateways.AuthorizeNetTest do
test "with bad transaction id" do
with_mock HTTPoison,
post: fn _url, _body, _headers -> MockResponse.void_non_existent_id() end do
- assert {:error, response} = ANet.void(@void_invalid_id, @opts)
+ assert {:error, _response} = ANet.void(@void_invalid_id, @opts)
end
end
end
@@ -233,7 +233,7 @@ defmodule Gringotts.Gateways.AuthorizeNetTest do
post: fn _url, _body, _headers ->
MockResponse.store_without_profile_fields()
end do
- assert {:error, response} = ANet.store(@card, @opts_store_no_profile)
+ assert {:error, _response} = ANet.store(@card, @opts_store_no_profile)
"Error"
end
diff --git a/test/gateways/bogus_test.exs b/test/gateways/bogus_test.exs
index 9235bcf8..611c9696 100644
--- a/test/gateways/bogus_test.exs
+++ b/test/gateways/bogus_test.exs
@@ -5,7 +5,7 @@ defmodule Gringotts.Gateways.BogusTest do
alias Gringotts.Gateways.Bogus, as: Gateway
@some_id "some_arbitrary_id"
- @amount Money.new(5, :USD)
+ @amount Gringotts.FakeMoney.new(5, :USD)
test "authorize" do
{:ok, %Response{id: id, success: success}} = Gateway.authorize(@amount, :card, [])
diff --git a/test/gateways/cams_test.exs b/test/gateways/cams_test.exs
index 43370865..db73b457 100644
--- a/test/gateways/cams_test.exs
+++ b/test/gateways/cams_test.exs
@@ -1,10 +1,7 @@
defmodule Gringotts.Gateways.CamsTest do
use ExUnit.Case, async: false
- alias Gringotts.{
- CreditCard,
- Response
- }
+ alias Gringotts.{CreditCard, Response}
alias Gringotts.Gateways.CamsMock, as: MockResponse
alias Gringotts.Gateways.Cams, as: Gateway
@@ -48,10 +45,10 @@ defmodule Gringotts.Gateways.CamsTest do
config: @auth
]
- @money Money.new(:USD, 100)
- @money_more Money.new(:USD, 101)
- @money_less Money.new(:USD, 99)
- @bad_currency Money.new(:INR, 100)
+ @money Gringotts.FakeMoney.new(100, :USD)
+ @money_more Gringotts.FakeMoney.new(101, :USD)
+ @money_less Gringotts.FakeMoney.new(99, :USD)
+ @bad_currency Gringotts.FakeMoney.new(100, :INR)
@id "some_transaction_id"
@bad_id "some_fake_transaction_id"
diff --git a/test/gateways/global_collect_test.exs b/test/gateways/global_collect_test.exs
index ea0f4742..ae8d504f 100644
--- a/test/gateways/global_collect_test.exs
+++ b/test/gateways/global_collect_test.exs
@@ -3,15 +3,14 @@ defmodule Gringotts.Gateways.GlobalCollectTest do
alias Gringotts.Gateways.GlobalCollectMock, as: MockResponse
alias Gringotts.Gateways.GlobalCollect
- alias Gringotts.{
- CreditCard
- }
+ alias Gringotts.CreditCard
+ alias Gringotts.FakeMoney
import Mock
- @amount Money.new("500", :USD)
+ @amount FakeMoney.new("500", :USD)
- @bad_amount Money.new("50.3", :USD)
+ @bad_amount FakeMoney.new("50.3", :USD)
@shipping_address %{
street: "Desertroad",
@@ -68,13 +67,6 @@ defmodule Gringotts.Gateways.GlobalCollectTest do
@invalid_token 30
- @invalid_config [
- config: %{
- secret_api_key: "some_secret_api_key",
- api_key_id: "some_api_key_id"
- }
- ]
-
@options [
config: %{
secret_api_key: "some_secret_api_key",
diff --git a/test/gateways/monei_test.exs b/test/gateways/monei_test.exs
index e641d91b..0c4f3df3 100644
--- a/test/gateways/monei_test.exs
+++ b/test/gateways/monei_test.exs
@@ -2,15 +2,16 @@ defmodule Gringotts.Gateways.MoneiTest do
use ExUnit.Case, async: true
alias Gringotts.{
- CreditCard
+ CreditCard,
+ FakeMoney
}
alias Gringotts.Gateways.Monei, as: Gateway
alias Plug.{Conn, Parsers}
- @amount42 Money.new(42, :USD)
- @amount3 Money.new(3, :USD)
- @bad_currency Money.new(42, :INR)
+ @amount42 FakeMoney.new(42, :USD)
+ @amount3 FakeMoney.new(3, :USD)
+ @bad_currency FakeMoney.new(42, :INR)
@card %CreditCard{
first_name: "Harry",
diff --git a/test/gateways/paymill_test.exs b/test/gateways/paymill_test.exs
index d830621c..a029f5a4 100644
--- a/test/gateways/paymill_test.exs
+++ b/test/gateways/paymill_test.exs
@@ -17,7 +17,7 @@ defmodule Gringotts.Gateways.PaymillTest do
{:ok, bypass: bypass, opts: opts}
end
- @amount_42 Money.new(42, :EUR)
+ @amount_42 Gringotts.FakeMoney.new(42, :EUR)
@valid_token "tok_d26e611c47d64693a281e8411934"
@invalid_token "tok_d26e611c47d64693a281e841193"
@@ -38,7 +38,7 @@ defmodule Gringotts.Gateways.PaymillTest do
end)
{:ok, response} = Gateway.authorize(@amount_42, @valid_token, config: opts)
- assert response.gateway_code == 20000
+ assert response.gateway_code == 20_000
end
test "when paymill is down or unreachable", %{bypass: bypass, opts: opts} do
@@ -75,7 +75,7 @@ defmodule Gringotts.Gateways.PaymillTest do
end)
{:ok, response} = Gateway.capture(@capture_preauth_id, @amount_42, config: opts)
- assert response.gateway_code == 20000
+ assert response.gateway_code == 20_000
end
test "when preauthorization not found", %{bypass: bypass, opts: opts} do
@@ -121,7 +121,7 @@ defmodule Gringotts.Gateways.PaymillTest do
end)
{:ok, response} = Gateway.purchase(@amount_42, @invalid_token, config: opts)
- assert response.gateway_code == 20000
+ assert response.gateway_code == 20_000
assert response.fraud_review == true
assert response.status_code == 200
end
@@ -156,7 +156,7 @@ defmodule Gringotts.Gateways.PaymillTest do
end)
{:ok, response} = Gateway.refund(@amount_42, @transaction_id, config: opts)
- assert response.gateway_code == 20000
+ assert response.gateway_code == 20_000
assert response.status_code == 200
end
@@ -194,7 +194,7 @@ defmodule Gringotts.Gateways.PaymillTest do
end)
{:ok, response} = Gateway.void(@void_id, config: opts)
- assert response.gateway_code == 50810
+ assert response.gateway_code == 50_810
end
test "when preauthorization used before", %{bypass: bypass, opts: opts} do
diff --git a/test/gateways/trexle_test.exs b/test/gateways/trexle_test.exs
index df74ff7d..65485fe8 100644
--- a/test/gateways/trexle_test.exs
+++ b/test/gateways/trexle_test.exs
@@ -1,13 +1,10 @@
defmodule Gringotts.Gateways.TrexleTest do
use ExUnit.Case, async: false
+
+ alias Gringotts.{Address, CreditCard, FakeMoney}
alias Gringotts.Gateways.TrexleMock, as: MockResponse
alias Gringotts.Gateways.Trexle
- alias Gringotts.{
- CreditCard,
- Address
- }
-
import Mock
@valid_card %CreditCard{
@@ -41,9 +38,9 @@ defmodule Gringotts.Gateways.TrexleTest do
}
# $2.99
- @amount Money.new("2.99", :USD)
+ @amount FakeMoney.new("2.99", :USD)
# 50 US cents, trexle does not work with amount smaller than 50 cents.
- @bad_amount Money.new("0.49", :USD)
+ @bad_amount FakeMoney.new("0.49", :USD)
@valid_token "some_valid_token"
@invalid_token "some_invalid_token"
@@ -63,7 +60,7 @@ defmodule Gringotts.Gateways.TrexleTest do
request: fn _method, _url, _body, _headers, _options ->
MockResponse.test_for_purchase_with_valid_card()
end do
- assert {:ok, response} = Trexle.purchase(@amount, @valid_card, @opts)
+ assert {:ok, _response} = Trexle.purchase(@amount, @valid_card, @opts)
end
end
diff --git a/test/gateways/wire_card_test.exs b/test/gateways/wire_card_test.exs
deleted file mode 100644
index 61633735..00000000
--- a/test/gateways/wire_card_test.exs
+++ /dev/null
@@ -1,18 +0,0 @@
-defmodule Gringotts.Gateways.WireCardTest do
- use ExUnit.Case, async: false
-
- import Mock
-
- setup do
- # TEST_AUTHORIZATION_GUWID = 'C822580121385121429927'
- # TEST_PURCHASE_GUWID = 'C865402121385575982910'
- # TEST_CAPTURE_GUWID = 'C833707121385268439116'
-
- # config = %{credentails: {'user', 'pass'}, default_currency: "EUR"}
- :ok
- end
-
- test "test_successful_authorization" do
- assert 1 + 1 == 2
- end
-end
diff --git a/test/integration/gateways/monei_test.exs b/test/integration/gateways/monei_test.exs
index 59bc9b88..a7a0deda 100644
--- a/test/integration/gateways/monei_test.exs
+++ b/test/integration/gateways/monei_test.exs
@@ -9,8 +9,8 @@ defmodule Gringotts.Integration.Gateways.MoneiTest do
@moduletag :integration
- @amount Money.new(42, :EUR)
- @sub_amount Money.new(21, :EUR)
+ @amount Gringotts.FakeMoney.new(42, :EUR)
+ @sub_amount Gringotts.FakeMoney.new(21, :EUR)
@card %CreditCard{
first_name: "Harry",
diff --git a/test/integration/gateways/paymill_test.exs b/test/integration/gateways/paymill_test.exs
index f7919ac9..3e0db660 100644
--- a/test/integration/gateways/paymill_test.exs
+++ b/test/integration/gateways/paymill_test.exs
@@ -6,7 +6,7 @@ defmodule Gringotts.Integration.Gateways.PaymillTest do
@moduletag integration: true
- @amount Money.new(4200, :EUR)
+ @amount Gringotts.FakeMoney.new(4200, :EUR)
@valid_token1 "tok_784c33eeb9a6adfc2bd3c21f95e6"
@valid_token2 "tok_9e429fb2dc44bcf94bcd4e6e6ec5"
@valid_token3 "tok_55b80f87f44f9328bee99360c4cc"
@@ -31,7 +31,7 @@ defmodule Gringotts.Integration.Gateways.PaymillTest do
test "with valid token and currency" do
use_cassette "paymill/authorize with valid token and currency" do
{:ok, response} = Gringotts.authorize(Gateway, @amount, @valid_token1)
- assert response.gateway_code == 20000
+ assert response.gateway_code == 20_000
assert response.status_code == 200
end
end
@@ -43,7 +43,7 @@ defmodule Gringotts.Integration.Gateways.PaymillTest do
{:ok, response} = Gringotts.authorize(Gateway, @amount, @valid_token2)
payment_id = response.id
{:ok, response_cap} = Gringotts.capture(Gateway, payment_id, @amount)
- assert response_cap.gateway_code == 20000
+ assert response_cap.gateway_code == 20_000
assert response_cap.status_code == 200
end
end
@@ -53,7 +53,7 @@ defmodule Gringotts.Integration.Gateways.PaymillTest do
test "with valid token currency" do
use_cassette "paymill purchase with valid token currency" do
{:ok, response} = Gringotts.purchase(Gateway, @amount, @valid_token3)
- assert response.gateway_code == 20000
+ assert response.gateway_code == 20_000
assert response.status_code == 200
end
end
@@ -65,7 +65,7 @@ defmodule Gringotts.Integration.Gateways.PaymillTest do
{:ok, response} = Gringotts.purchase(Gateway, @amount, @valid_token4)
trans_id = response.id
{:ok, response_ref} = Gringotts.refund(Gateway, @amount, trans_id)
- assert response_ref.gateway_code == 20000
+ assert response_ref.gateway_code == 20_000
assert response_ref.status_code == 200
end
end
@@ -77,7 +77,7 @@ defmodule Gringotts.Integration.Gateways.PaymillTest do
{:ok, response} = Gringotts.authorize(Gateway, @amount, @valid_token5)
auth_id = response.id
{:ok, response_void} = Gringotts.void(Gateway, auth_id)
- assert response_void.gateway_code == 50810
+ assert response_void.gateway_code == 50_810
assert response_void.status_code == 200
end
end
diff --git a/test/integration/gateways/stripe_test.exs b/test/integration/gateways/stripe_test.exs
index e4d3e070..60dbefc5 100644
--- a/test/integration/gateways/stripe_test.exs
+++ b/test/integration/gateways/stripe_test.exs
@@ -1,16 +1,12 @@
defmodule Gringotts.Gateways.StripeTest do
use ExUnit.Case
+ alias Gringotts.{Address, CreditCard}
alias Gringotts.Gateways.Stripe
- alias Gringotts.{
- CreditCard,
- Address
- }
-
@moduletag integration: true
- @amount Money.new(5, :USD)
+ @amount Gringotts.FakeMoney.new(5, :USD)
@card %CreditCard{
first_name: "John",
last_name: "Smith",
diff --git a/test/integration/money.exs b/test/integration/money.exs
deleted file mode 100644
index e4aaa331..00000000
--- a/test/integration/money.exs
+++ /dev/null
@@ -1,61 +0,0 @@
-defmodule Gringotts.Integration.Gateways.MoneyTest do
- use ExUnit.Case, async: true
-
- alias Gringotts.Money, as: MoneyProtocol
-
- @moduletag :integration
-
- @ex_money Money.new(42, :EUR)
- @ex_money_long Money.new("42.126456", :EUR)
- @ex_money_bhd Money.new(42, :BHD)
-
- @any %{value: Decimal.new(42), currency: "EUR"}
- @any_long %{value: Decimal.new("42.126456"), currency: "EUR"}
- @any_bhd %{value: Decimal.new("42"), currency: "BHD"}
-
- describe "ex_money" do
- test "value is a Decimal.t" do
- assert match?(%Decimal{}, MoneyProtocol.value(@ex_money))
- end
-
- test "currency is an upcase String.t" do
- the_currency = MoneyProtocol.currency(@ex_money)
- assert match?(currency when is_binary(currency), the_currency)
- assert the_currency == String.upcase(the_currency)
- end
-
- test "to_integer" do
- assert match?({"EUR", 4200, -2}, MoneyProtocol.to_integer(@ex_money))
- assert match?({"BHD", 42_000, -3}, MoneyProtocol.to_integer(@ex_money_bhd))
- end
-
- test "to_string" do
- assert match?({"EUR", "42.00"}, MoneyProtocol.to_string(@ex_money))
- assert match?({"EUR", "42.13"}, MoneyProtocol.to_string(@ex_money_long))
- assert match?({"BHD", "42.000"}, MoneyProtocol.to_string(@ex_money_bhd))
- end
- end
-
- describe "Any" do
- test "value is a Decimal.t" do
- assert match?(%Decimal{}, MoneyProtocol.value(@any))
- end
-
- test "currency is an upcase String.t" do
- the_currency = MoneyProtocol.currency(@any)
- assert match?(currency when is_binary(currency), the_currency)
- assert the_currency == String.upcase(the_currency)
- end
-
- test "to_integer" do
- assert match?({"EUR", 4200, -2}, MoneyProtocol.to_integer(@any))
- assert match?({"BHD", 4200, -2}, MoneyProtocol.to_integer(@any_bhd))
- end
-
- test "to_string" do
- assert match?({"EUR", "42.00"}, MoneyProtocol.to_string(@any))
- assert match?({"EUR", "42.13"}, MoneyProtocol.to_string(@any_long))
- assert match?({"BHD", "42.00"}, MoneyProtocol.to_string(@any_bhd))
- end
- end
-end
diff --git a/test/support/fake_money.ex b/test/support/fake_money.ex
new file mode 100644
index 00000000..4f2437f1
--- /dev/null
+++ b/test/support/fake_money.ex
@@ -0,0 +1,37 @@
+defmodule Gringotts.FakeMoney do
+ @moduledoc """
+ Defines a Gringotts.Money compliant money struct.
+
+ Implements the gringotts protocol and does nothing else.
+ """
+
+ defstruct [:amount_field, :currency_field]
+
+ def new(amount, currency) when is_atom(currency) do
+ struct(__MODULE__, amount_field: Decimal.new(amount), currency_field: currency)
+ end
+end
+
+defimpl Gringotts.Money, for: Gringotts.FakeMoney do
+ def currency(money_struct), do: money_struct.currency_field
+ def value(money_struct), do: money_struct.amount_field
+
+ def to_integer(money_struct) do
+ {
+ money_struct.currency_field,
+ money_struct.amount_field
+ |> Decimal.mult(100)
+ |> Decimal.to_integer(),
+ -2
+ }
+ end
+
+ def to_string(money_struct) do
+ {
+ Atom.to_string(money_struct.currency_field),
+ money_struct.amount_field
+ |> Decimal.round(2)
+ |> Decimal.to_string()
+ }
+ end
+end
diff --git a/test/mocks/authorize_net_mock.ex b/test/support/mocks/authorize_net_mock.ex
similarity index 100%
rename from test/mocks/authorize_net_mock.ex
rename to test/support/mocks/authorize_net_mock.ex
diff --git a/test/mocks/cams_mock.ex b/test/support/mocks/cams_mock.ex
similarity index 100%
rename from test/mocks/cams_mock.ex
rename to test/support/mocks/cams_mock.ex
diff --git a/test/mocks/global_collect_mock.ex b/test/support/mocks/global_collect_mock.ex
similarity index 100%
rename from test/mocks/global_collect_mock.ex
rename to test/support/mocks/global_collect_mock.ex
diff --git a/test/mocks/paymill_mock.ex b/test/support/mocks/paymill_mock.ex
similarity index 100%
rename from test/mocks/paymill_mock.ex
rename to test/support/mocks/paymill_mock.ex
diff --git a/test/mocks/trexle_mock.ex b/test/support/mocks/trexle_mock.ex
similarity index 100%
rename from test/mocks/trexle_mock.ex
rename to test/support/mocks/trexle_mock.ex