Packages
stripity_stripe
3.1.1
3.3.2
3.3.1
3.2.0
3.1.1
3.1.0
3.0.0
2.17.3
2.17.2
2.17.1
2.17.0
2.16.0
2.15.1
2.15.0
2.14.1
2.14.0
2.13.0
2.12.1
2.12.0
2.11.0
2.10.0
2.9.0
2.8.0
2.7.2
2.7.1
2.7.0
2.6.0
2.5.0
2.4.0
2.3.0
2.2.3
2.2.2
2.2.1
2.2.0
2.1.0
2.0.1
2.0.0
2.0.0-alpha.11
2.0.0-alpha.10
2.0.0-alpha.9
2.0.0-alpha.8
2.0.0-alpha.7
2.0.0-alpha.6
2.0.0-alpha.5
2.0.0-alpha.4
2.0.0-alpha.3
2.0.0-alpha.2
2.0.0-alpha.1
1.6.2
1.6.1
1.6.0
1.4.0
1.3.0
1.2.0
1.1.0
0.5.0
0.4.0
0.3.0
0.2.0
A Stripe client for Elixir.
Current section
Files
Jump to
Current section
Files
lib/stripe/error.ex
defmodule Stripe.Error do
@moduledoc """
A struct which represents an error which occurred during a Stripe API call.
This struct is designed to provide all the information needed to effectively log and maybe respond
to an error.
It contains the following fields:
- `:source` – this is one of
* `:internal` – the error occurred within the library. This is usually caused by an unexpected
or missing parameter.
* `:network` – the error occurred while making the network request (i.e. `:hackney.request/5`
returned an error.) In this case, `:code` will always be `:network_error`. The
`:hackney_reason` field in the `:extra` map contains the actual error reason received from
hackney.
* `:stripe` – an error response was received from Stripe.
- `:code` – an atom indicating the particular error. See "Error Codes" for more detail.
- `:request_id` – if `:source` is `:stripe`, this will contain the
[request ID](https://stripe.com/docs/api#request_ids) for logging and troubleshooting.
Otherwise, this field is `nil`.
- `:message` – a loggable message describing the error. This should not be shown to your users
but is intended for logging and troubleshooting.
- `:user_message` – if Stripe has provided a user-facing message (e.g. when a card is declined),
this field will contain it. Otherwise it is `nil`.
- `:extra` - a map which may contain some additional information about the error. See "Extra
Fields" for details.
## Extra Fields
The `:extra` field contains a map of miscellaneous information about the error which may be
useful. The fields are not present if not relevant. The possible fields are:
- `:card_code` – when `:code` is `:card_error`, contains one of Stripe's
[decline reasons](https://stripe.com/docs/api#errors).
- `:decline_code` – an optional short string provided by the bank when a card is declined.
- `:param` – for errors where a particular parameter was the cause, indicates which parameter
was invalid.
- `:charge_id` – when a Charge was declined, indicates the ID of the failed Charge which was
created.
- `:http_status` – for `:stripe` errors, the HTTP status returned with the error.
- `:raw_error` – the raw error map received from Stripe.
- `:hackney_reason` – for `:network` errors, contains the error reason received from hackney.
## Error Codes
The `:code` fields may be one of the following:
- `:api_connection_error`, `:api_error`, `:authentication_error`, `:card_error`,
`:invalid_request_error`, `:rate_limit_error`, `:validation_error` – as per the
[Stripe docs](https://stripe.com/docs/api#errors)
- `:bad_request`, `:unauthorized`, `:request_failed`, `:not_found`, `:conflict`,
`:too_many_requests`, `:server_error`, `:unknown_error` – these only occur if Stripe did not
send an explicit `type` for the error. They have the meaning as defined in [Stripe's HTTP status
code summary](https://stripe.com/docs/api#errors)
- `:network_code` – used only when `:source` is `:network`. Indicates an error occurred while
making the request.
- `:valid_keys_failed`, `:required_keys_failed`, `:endpoint_fun_invalid_result`,
`:invalid_endpoint` – used when `:source` is `:internal`. See `Stripe.Request` for details.
"""
@type error_source :: :internal | :network | :stripe
@type error_status ::
:bad_request
| :unauthorized
| :request_failed
| :not_found
| :conflict
| :too_many_requests
| :server_error
| :unknown_error
@type stripe_error_type ::
:api_connection_error
| :api_error
| :authentication_error
| :card_error
| :invalid_request_error
| :rate_limit_error
| :validation_error
@type card_error_code ::
:invalid_number
| :invalid_expiry_month
| :invalid_expiry_year
| :invalid_cvc
| :invalid_swipe_data
| :incorrect_number
| :expired_card
| :incorrect_cvc
| :incorrect_zip
| :card_declined
| :missing
| :processing_error
| :bank_account_verification_failed
| :idempotency_key_in_use
@type t :: %__MODULE__{
source: error_source,
code: error_status | stripe_error_type | Stripe.Request.error_code() | :network_error,
request_id: String.t() | nil,
message: String.t(),
user_message: String.t() | nil,
extra: %{
optional(:card_code) => card_error_code,
optional(:decline_code) => String.t(),
optional(:param) => String.t(),
optional(:charge_id) => Stripe.id(),
optional(:http_status) => 400..599,
optional(:raw_error) => map,
optional(:hackney_reason) => any
}
}
@enforce_keys [:source, :code, :message]
defstruct [:source, :code, :request_id, :extra, :message, :user_message]
@doc false
@spec new(Keyword.t()) :: t
def new(fields) do
struct!(__MODULE__, fields)
end
@doc false
@spec from_hackney_error(any) :: t
def from_hackney_error(reason) do
%__MODULE__{
source: :network,
code: :network_error,
message:
"An error occurred while making the network request. The HTTP client returned the following reason: #{inspect(reason)}",
extra: %{
hackney_reason: reason
}
}
end
@doc false
@spec from_stripe_error(400..599, nil, String.t() | nil) :: t
def from_stripe_error(status, nil, request_id) do
%__MODULE__{
source: :stripe,
code: code_from_status(status),
request_id: request_id,
extra: %{http_status: status},
message: status |> message_from_status()
}
end
@spec from_stripe_error(400..599, map, String.t()) :: t
def from_stripe_error(status, error_data, request_id) do
case error_data |> Map.get("type") |> maybe_to_atom() do
nil ->
from_stripe_error(status, nil, request_id)
type ->
stripe_message = error_data |> Map.get("message")
user_message =
case type do
:card_error -> stripe_message
_ -> nil
end
message = stripe_message || message_from_type(type)
extra =
%{raw_error: error_data, http_status: status}
|> maybe_put(:card_code, error_data |> Map.get("code") |> maybe_to_atom())
|> maybe_put(:decline_code, error_data |> Map.get("decline_code"))
|> maybe_put(:param, Map.get(error_data, "param"))
|> maybe_put(:charge_id, error_data |> Map.get("charge"))
%__MODULE__{
source: :stripe,
code: type,
request_id: request_id,
message: message,
user_message: user_message,
extra: extra
}
end
end
defp code_from_status(400), do: :bad_request
defp code_from_status(401), do: :unauthorized
defp code_from_status(402), do: :request_failed
defp code_from_status(404), do: :not_found
defp code_from_status(409), do: :conflict
defp code_from_status(429), do: :too_many_requests
defp code_from_status(s) when s in [500, 502, 503, 504], do: :server_error
defp code_from_status(_), do: :unknown_error
defp message_from_status(400),
do: "The request was unacceptable, often due to missing a required parameter."
defp message_from_status(401), do: "No valid API key provided."
defp message_from_status(402), do: "The parameters were valid but the request failed."
defp message_from_status(404), do: "The requested resource doesn't exist."
defp message_from_status(409),
do:
"The request conflicts with another request (perhaps due to using the same idempotent key)."
defp message_from_status(429),
do:
"Too many requests hit the API too quickly. We recommend an exponential backoff of your requests."
defp message_from_status(s) when s in [500, 502, 503, 504],
do: "Something went wrong on Stripe's end."
defp message_from_status(s), do: "An unknown HTTP code of #{s} was received."
defp message_from_type(:api_connection_error), do: "The connection to Stripe's API failed."
defp message_from_type(:api_error),
do: "An internal Stripe error occurred. This is usually temporary."
defp message_from_type(:authentication_error),
do: "You failed to properly authenticate yourself in the request."
defp message_from_type(:card_error), do: "The card could not be charged for some reason."
defp message_from_type(:invalid_request_error), do: "Your request had invalid parameters."
defp message_from_type(:rate_limit_error),
do:
"Too many requests hit the API too quickly. We recommend an exponential backoff of your requests."
defp message_from_type(:validation_error),
do: "A client-side library failed to validate a field."
defp maybe_put(map, _key, nil), do: map
defp maybe_put(map, key, value), do: map |> Map.put(key, value)
defp maybe_to_atom(nil), do: nil
defp maybe_to_atom(string) when is_binary(string), do: string |> String.to_atom()
end