# Braintree
[![Build Status](https://travis-ci.org/sorentwo/braintree-elixir.svg?branch=master)](https://travis-ci.org/sorentwo/braintree-elixir)
[![Hex version](https://img.shields.io/hexpm/v/braintree.svg "Hex version")](https://hex.pm/packages/braintree)
[![Hex downloads](https://img.shields.io/hexpm/dt/braintree.svg "Hex downloads")](https://hex.pm/packages/braintree)
[![Inline docs](https://inch-ci.org/github/sorentwo/braintree-elixir.svg)](https://inch-ci.org/github/sorentwo/braintree-elixir)
A native [Braintree][braintree] client library for Elixir.
[braintree]: https://www.braintreepayments.com
## Installation
Add braintree to your list of dependencies in `mix.exs`:
```elixir
def deps do
[{:braintree, "~> 0.13"}]
end
```
Once that is configured you are all set. Braintree is a library, not an
application, but it does rely on `hackney`, which must be started. For Elixir
versions < 1.4 you'll need to include it in the list of applications:
```elixir
def application do
[applications: [:braintree]]
end
```
Within your application you will need to configure the merchant id and
authorization keys. You do *not* want to put this information in your
`config.exs` file! Either put it in a `{prod,dev,test}.secret.exs` file which is
sourced by `config.exs`, or read the values in from the environment:
```elixir
config :braintree,
environment: :sandbox,
master_merchant_id: {:system, "BRAINTREE_MASTER_MERCHANT_ID"},
merchant_id: {:system, "BRAINTREE_MERCHANT_ID"},
public_key: {:system, "BRAINTREE_PUBLIC_KEY"},
private_key: {:system, "BRAINTREE_PRIVATE_KEY"}
```
Furthermore, the environment defaults to `:sandbox`, so you'll want to configure
it with `:production` in `prod.exs`.
You may optionally pass directly those configuration keys to all functions
performing an API call. In that case, those keys will be used to perform the
call.
You can optionally [configure Hackney options][opts] with:
```elixir
config :braintree,
http_options: [
timeout: 30_000, # default, in milliseconds
recv_timeout: 5000 # default, in milliseconds
]
```
[opts]: https://github.com/benoitc/hackney/blob/master/doc/hackney.md#request5
## Usage
The online [documentation][doc] for Ruby/Java/Python etc. will give you a
general idea of the modules and available functionality. Where possible the
namespacing has been preserved.
The CRUD functions for each action module break down like this:
```elixir
alias Braintree.Customer
alias Braintree.ErrorResponse, as: Error
case Customer.create(%{company: "Whale Corp"}) do
{:ok, %Customer{} = customer} -> do_stuff_with_customer(customer)
{:error, %Error{} = error} -> do_stuff_with_error(error)
end
```
### Searching
Search params are constructed with a fairly complex structure of maps. There
isn't a DSL provided, so queries must be constructed by hand. For example, to
search for a customer:
```elixir
search_params = %{
first_name: %{is: "Jenna"},
last_name: %{
starts_with: "Smith",
contains: "ith",
is_not: "Smithsonian"
},
email: %{ends_with: "gmail.com"}
}
{:ok, customers} = Braintree.Customer.search(search_params)
```
Or, to search for pending credit card verifications within a particular dollar
amount:
```elixir
search_params = %{
amount: %{
min: "10.0",
max: "15.0"
},
status: ["approved", "pending"]
}
{:ok, verifications} = Braintree.CreditCardVerification.search(search_params)
```
[doc]: https://developers.braintreepayments.com/
### Telemetry
If the `telemetry` application is running, the library will emit telemetry events.
Immediately before the HTTP request is fired, a start event will be fired with the following shape:
```
event name: [:braintree, :request, :start]
measurements: %{system_time: System.system_time()}
meta data: %{method: method, path: path}
```
Once the HTTP call completes, a stop event will be fired with the following shape:
```
event name: [:braintree, :request, :stop]
measurements: %{duration: duration}
meta data: %{method: method, path: path, http_status: status}
```
If Hackney returns an error, an error event will be fired with the following shape:
```
event name: [:braintree, :request, :error]
measurements: %{duration: duration}
meta data: %{method: method, path: path, error: error_reason}
```
If an exception is raised during the Hackney call, an exception event will be fired with the following shape:
```
event name: [:braintree, :request, :exception]
measurements: %{duration: duration}
meta data: %{method: method, path: path, kind: error_type, reason: error_message, stacktrace: stacktrace}
```
## Testing
You'll need a Braintree sandbox account to run the integration tests. Also, be
sure that your account has [Duplicate Transaction Checking][dtc] disabled.
### Merchant Account Features
In order to test the merchant account features, your sandbox account needs to
have a master merchant account and it needs to be added to your environment
variables (only needed in test).
Your environment needs to have the following:
* Add-ons with ids: "bronze", "silver" and "gold"
* Plans with ids: "starter", "business"
* "business" plan needs to include the following add-ons: "bronze" and "silver"
### PayPal Account Testing
PayPal testing uses the mocked API flow, which requires linking a sandbox PayPal
account. You can accomplish that by following the directions for [linked paypal
testing][plp].
[dtc]: https://articles.braintreepayments.com/control-panel/transactions/duplicate-checking
[plp]: https://developers.braintreepayments.com/guides/paypal/testing-go-live/php#linked-paypal-testing
### Testing Using Only `localhost`
You can optionally configure the sandbox endpoint url to point towards a local url and
port for testing which does not need to call out to the Braintree sandbox API.
For example, in your `config.exs`
```elixir
config :braintree, :sandbox_endpoint, "localhost:4001"
```
In conjuction with a libary such as [`Bypass`](https://github.com/PSPDFKit-labs/bypass)
you can use this config to define test-specific returns from `Braintree` calls without
hitting the Braintree sandbox API.
## License
MIT License, see [LICENSE.txt](LICENSE.txt) for details.