Packages

Gringotts is a payment processing library in Elixir integrating various payment gateways, and draws motivation from shopify's activemerchant ruby gem.

Current section

Files

Jump to
gringotts lib gringotts.ex
Raw

lib/gringotts.ex

defmodule Gringotts do
@moduledoc ~S"""
Gringotts is a payment gateway integration library supporting many gateway integrations.
## Configuration
The configuration for `Gringotts` must be in your application environment,
usually defined in your `config/config.exs` and are **mandatory**:
**Global Configuration**
The global configuration sets the library level configurations to interact with the gateway.
If the mode is not set then by 'default' the sandbox account is selected.
To integrate with the sandbox account set.
config :gringotts, :global_config,
mode: :test
To integrate with the live account set.
config :gringotts, :global_config,
mode: :prod
**Gateway Configuration**
The gateway level configurations are for fields related to a specific gateway.
config :Gringotts, Gringotts.Gateways.Stripe,
adapter: Gringotts.Gateways.Stripe,
api_key: "sk_test_vIX41hC0sdfBKrPWQerLuOMld",
default_currency: "USD"
`Key` for the configuration and the adapter value should be the same, we could have
chosen to pick adapter and used it as the key but we have chosen to be explicit rather
than implicit.
## Standard Arguments
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
This option specifies which payment gateway this request should be called for.
Since `Gringotts` supports multiple payment gateway integrations at the same time
so this information get's critical.
### Amount
eg: 5000
Amount is the money an application wants to deduct in cents on the card.
### Card Info
eg:
%CreditCard {
name: "John Doe",
number: "4242424242424242",
expiration: {2018, 12},
cvc: "123",
street1: "123 Main",
street2: "Suite 100",
city: "New York",
region: "NY",
country: "US",
postal_code: "11111"
}
This stores all the credit card info of the customer along with some address info etc.
### Other options
eg: [currency: "usd"]
This is a keyword list of all the other options/information which the payment gateway
needs apart from the above mentioned options.
> This is passed as is to the gateway and not modified, usually it comes back in the
response object intact.
"""
import GenServer, only: [call: 2]
@doc """
This is the bare minimum API for a gateway to support, and consists of a single call:
@payment %{
name: "John Doe",
number: "4242424242424242",
expiration: {2018, 12},
cvc: "123",
street1: "123 Main",
street2: "Suite 100",
city: "New York",
region: "NY",
country: "US",
postal_code: "11111"
}
@options [currency: "usd"]
Gringotts.purchase(:payment_worker, 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
validate_config(gateway)
call(worker, {:purchase, gateway, amount, card, opts})
end
@doc """
Authorize should authorize funds on a payment instrument that will
not be settled without a following call to `capture` within some finite
period of time. When implementing this API, authorize and capture are
both required.
@payment %{
name: "John Doe",
number: "4242424242424242",
expiration: {2018, 12},
cvc: "123",
street1: "123 Main",
street2: "Suite 100",
city: "New York",
region: "NY",
country: "US",
postal_code: "11111"
}
@options [currency: "usd"]
Gringotts.authorize(:payment_worker, Gringotts.Gateways.Stripe, 5, @payment, @options)
"""
def authorize(worker, gateway, amount, card, opts \\ []) do
validate_config(gateway)
call(worker, {:authorize, gateway, amount, card, opts})
end
@doc """
Captures deducts an amount from the card, this happens once the card is authorised.
Partial captures, if supported by the gateway, are achieved by passing an amount.
Not passing an amount to capture should always cause the full amount of the initial
authorization to be captured.
If the gateway does not support partial captures, calling `capture` with an amount
other than nil should raise an error indicating partial capture is not supported.
@payment %{
name: "John Doe",
number: "4242424242424242",
expiration: {2018, 12},
cvc: "123",
street1: "123 Main",
street2: "Suite 100",
city: "New York",
region: "NY",
country: "US",
postal_code: "11111"
}
@options [currency: "usd"]
id = "ch_1BYvGkBImdnrXiZwet3aKkQE"
Gringotts.capture(:payment_worker, Gringotts.Gateways.Stripe, id, 5)
"""
def capture(worker, gateway, id, amount, opts \\ []) do
validate_config(gateway)
call(worker, {:capture, gateway, id, amount, opts})
end
@doc """
Void is an optional (but highly recommended) supplement to `authorise` & `capture`
API that should immediately cancel an authorized charge, clearing it off of the
underlying payment instrument without waiting for expiration.
@payment %{
name: "John Doe",
number: "4242424242424242",
expiration: {2018, 12},
cvc: "123",
street1: "123 Main",
street2: "Suite 100",
city: "New York",
region: "NY",
country: "US",
postal_code: "11111"
}
@options [currency: "usd"]
id = "ch_1BYvGkBImdnrXiZwet3aKkQE"
Gringotts.void(:payment_worker, Gringotts.Gateways.Stripe, id)
"""
def void(worker, gateway, id, opts \\ []) do
validate_config(gateway)
call(worker, {:void, gateway, id, opts})
end
@doc """
Cancels settlement or returns funds as appropriate for a referenced prior
`purchase` or `capture`.
@payment %{
name: "John Doe",
number: "4242424242424242",
expiration: {2018, 12},
cvc: "123",
street1: "123 Main",
street2: "Suite 100",
city: "New York",
region: "NY",
country: "US",
postal_code: "11111"
}
id = "ch_1BYvGkBImdnrXiZwet3aKkQE"
Gringotts.refund(:payment_worker, Gringotts.Gateways.Stripe, 5, id)
"""
def refund(worker, gateway, amount, id, opts \\ []) do
validate_config(gateway)
call(worker, {:refund, gateway, amount, id, opts})
end
@doc """
Tokenizes a supported payment method in the gateway's vault. If the gateway
conflates tokenization with customer management, `Gringotts` should hide all
customer management and any customer identifier(s) within the token returned.
It's certainly legitimate to have a library that interacts with all the features
in a gateway's vault, but `Gringotts` is not the right place for it.
It's critical that `store` returns a token that can be used against `purchase`
and `authorize`. Currently the standard is to return the token in the
`%Response{...}` `authorization` field.
@payment %{
name: "John Doe",
number: "4242424242424242",
expiration: {2018, 12},
cvc: "123",
street1: "123 Main",
street2: "Suite 100",
city: "New York",
region: "NY",
country: "US",
postal_code: "11111"
}
id = "ch_1BYvGkBImdnrXiZwet3aKkQE"
Gringotts.store(:payment_worker, Gringotts.Gateways.Stripe, @payment)
"""
def store(worker, gateway, card, opts \\ []) do
validate_config(gateway)
call(worker, {:store, gateway, card, opts})
end
@doc """
Removes the token from the payment gateway, once `unstore` request is fired the
token which could enable `authorise` & `capture` would not work with this token.
This should be done once the payment capture is done and you don't wish to make any
further deductions for the same card.
customer_id = "random_customer"
Gringotts.unstore(:payment_worker, Gringotts.Gateways.Stripe, customer_id)
"""
def unstore(worker, gateway, customer_id, opts \\ []) do
validate_config(gateway)
call(worker, {:unstore, gateway, customer_id, opts})
end
# TODO: This is runtime error reporting fix this so that it does compile
# time error reporting.
defp validate_config(gateway) do
# Keep the key name and adapter the same in the config in application
config = Application.get_env(:gringotts, gateway)
gateway.validate_config(config)
end
end