defmodule Railsr do
@moduledoc """
Production-grade Elixir client for the **Railsr Embedded Finance API**.
## Overview
`railsr` provides a complete, idiomatic Elixir interface to every Railsr v1/v2
endpoint group: Endusers, Ledgers, Transactions, Beneficiaries, Cards, Direct
Debit / Mandates, Compliance Firewall, Webhooks, and Customer management.
Key design goals:
- **Zero external secrets in code** — all configuration via `Application.get_env/3`
with `nimble_options` validation at startup.
- **Automatic OAuth 2.0 token management** — tokens are fetched once, cached in
an ETS-backed `GenServer`, and silently refreshed before expiry.
- **Full-jitter exponential backoff** on transient failures (429, 5xx).
- **Idempotency keys** generated automatically on all mutating requests.
- **Telemetry** — every HTTP call emits `[:railsr, :request, :start/stop/exception]`
events compatible with `Telemetry.Metrics`.
- **Typed returns** — all public functions return `{:ok, struct}` or
`{:error, %Railsr.Error{}}`.
## Installation
```elixir
def deps do
[{:railsr, "~> 0.1"}]
end
```
## Configuration
```elixir
# config/config.exs
config :railsr,
client_id: System.get_env("RAILSR_CLIENT_ID"),
client_secret: System.get_env("RAILSR_CLIENT_SECRET"),
environment: :live, # :play | :play_live | :live
timeout: 30_000, # ms
max_retries: 3,
base_backoff_ms: 200,
telemetry_prefix: [:railsr]
```
## Quick Start
```elixir
{:ok, enduser} = Railsr.Endusers.create(%{
person: %{
name: %{family_name: "Smith", given_name: "Alice"},
email: "alice@example.com",
date_of_birth: "1990-01-15",
nationality: "GB",
country_of_residence: ["GB"],
address: %{
address_number: "14",
address_street: "High Street",
address_city: "London",
address_postal_code: "EC1A 1BB",
address_iso_country: "GB"
}
}
})
{:ok, _kyc_check} = Railsr.Endusers.create_kyc_check(enduser.enduser_id)
{:ok, ledger} = Railsr.Ledgers.create(%{
holder_id: enduser.enduser_id,
ledger_type: "standard-gbp",
asset_class: "currency",
asset_type: "gbp"
})
{:ok, _tx} = Railsr.Transactions.send_money(%{
ledger_id: ledger.ledger_id,
beneficiary_id: "ben_xxxxx",
amount: 1000,
currency: "GBP",
payment_type: "faster-payment",
reason: "Invoice #42"
})
```
"""
alias Railsr.Error
alias Railsr.Resources.Cards
alias Railsr.Resources.Endusers
alias Railsr.Resources.Ledgers
alias Railsr.Resources.Transactions
# Delegate convenience functions to resource modules so callers can use
# either `Railsr.Resources.Endusers.create/1` or `Railsr.create_enduser/1`.
# Note: defdelegate does not support default arguments; list_endusers uses
# an explicit wrapper below.
defdelegate create_enduser(params), to: Endusers, as: :create
defdelegate get_enduser(id), to: Endusers, as: :get
@doc "List endusers — delegates to `Railsr.Resources.Endusers.list/2`."
@spec list_endusers(map(), keyword()) :: {:ok, list()} | {:error, Error.t()}
def list_endusers(query \\ %{}, opts \\ []) do
Endusers.list(query, opts)
end
defdelegate create_ledger(params), to: Ledgers, as: :create
defdelegate get_ledger(id), to: Ledgers, as: :get
defdelegate send_money(params), to: Transactions, as: :send_money
defdelegate get_transaction(id), to: Transactions, as: :get
defdelegate create_card(params), to: Cards, as: :create
defdelegate get_card(id), to: Cards, as: :get
@doc "Returns the current package version."
@spec version() :: String.t()
def version, do: to_string(Application.spec(:railsr, :vsn) || "0.1.0")
end