# `LatticeStripe.TransferReversal`
[🔗](https://github.com/szTheory/lattice_stripe/blob/v2.2.2/lib/lattice_stripe/transfer_reversal.ex#L1)

Operations on Stripe Transfer Reversal objects — standalone top-level module.

A Transfer Reversal returns some or all of a previously created Transfer
from the destination connected account back to the platform balance. See
the [Stripe Transfer Reversal API](https://docs.stripe.com/api/transfer_reversals).

## Design: standalone module, no `Transfer.reverse/4` delegator

Phase 18 decision D-02 locks this as a top-level module addressed by
`(transfer_id, reversal_id)`, mirroring `/v1/transfers/:transfer/reversals/:id`.
`LatticeStripe.Transfer` deliberately does **not** expose a `reverse/4`
delegator — users reach for `TransferReversal.create/4` directly. This
mirrors the Phase 17 `AccountLink` / `LoginLink` precedent and stripe-java's
own top-level `TransferReversal` class.

## Usage

    client = LatticeStripe.Client.new!(api_key: "sk_live_...", finch: MyApp.Finch)

    # Create a full reversal (no amount = reverse the entire transfer)
    {:ok, reversal} = LatticeStripe.TransferReversal.create(client, "tr_...", %{})

    # Create a partial reversal
    {:ok, reversal} = LatticeStripe.TransferReversal.create(client, "tr_...", %{
      "amount" => 500,
      "description" => "Partial reversal — platform issue"
    })

    # Retrieve
    {:ok, reversal} = LatticeStripe.TransferReversal.retrieve(client, "tr_...", "trr_...")

    # Update metadata
    {:ok, reversal} = LatticeStripe.TransferReversal.update(client, "tr_...", "trr_...", %{
      "metadata" => %{"internal_id" => "rev_42"}
    })

    # List reversals for a transfer
    {:ok, resp} = LatticeStripe.TransferReversal.list(client, "tr_...")

    # Stream lazily (auto-pagination)
    client
    |> LatticeStripe.TransferReversal.stream!("tr_...")
    |> Enum.to_list()

## Pre-network param validation

All public functions validate that `transfer_id` (and `reversal_id` where
present) are non-empty binaries before any HTTP call. Passing `nil` or `""`
raises `ArgumentError` immediately, so tests do not need mock setup to
cover the missing-id path.

## Stripe API Reference

See the [Stripe Transfer Reversal API](https://docs.stripe.com/api/transfer_reversals).

# `t`

```elixir
@type t() :: %LatticeStripe.TransferReversal{
  amount: integer() | nil,
  balance_transaction: LatticeStripe.BalanceTransaction.t() | String.t() | nil,
  created: integer() | nil,
  currency: String.t() | nil,
  destination_payment_refund: LatticeStripe.Refund.t() | String.t() | nil,
  extra: map(),
  id: String.t() | nil,
  metadata: map() | nil,
  object: String.t(),
  source_refund: LatticeStripe.Refund.t() | String.t() | nil,
  transfer: LatticeStripe.Transfer.t() | String.t() | nil
}
```

A Stripe Transfer Reversal object.

See the [Stripe Transfer Reversal API](https://docs.stripe.com/api/transfer_reversals/object)
for field definitions. Unknown fields are preserved in `:extra` (F-001).

# `create`

```elixir
@spec create(LatticeStripe.Client.t(), String.t(), map(), keyword()) ::
  {:ok, t()} | {:error, LatticeStripe.Error.t()}
```

Creates a new TransferReversal for the given transfer.

Sends `POST /v1/transfers/:transfer_id/reversals` with the given params.

Raises `ArgumentError` (pre-network) if `transfer_id` is `nil` or empty.

## Parameters

- `client` - A `%LatticeStripe.Client{}` struct
- `transfer_id` - The Transfer ID (`"tr_..."`) to reverse
- `params` - Optional params: `"amount"`, `"description"`, `"metadata"`,
  `"refund_application_fee"`, `"expand"`
- `opts` - Per-request overrides

# `create!`

```elixir
@spec create!(LatticeStripe.Client.t(), String.t(), map(), keyword()) :: t()
```

Like `create/4` but raises on failure.

# `from_map`

```elixir
@spec from_map(map() | nil) :: t() | nil
```

Converts a decoded Stripe API map to a `%TransferReversal{}` struct.

Maps all known Stripe Transfer Reversal fields explicitly. Any unrecognized
fields are collected into `:extra` (F-001).

`from_map(nil)` returns `nil`.

# `list`

```elixir
@spec list(LatticeStripe.Client.t(), String.t(), map(), keyword()) ::
  {:ok, LatticeStripe.Response.t()} | {:error, LatticeStripe.Error.t()}
```

Lists TransferReversals for the given transfer.

Sends `GET /v1/transfers/:transfer_id/reversals`.

Raises `ArgumentError` (pre-network) if `transfer_id` is `nil` or empty.

# `list!`

```elixir
@spec list!(LatticeStripe.Client.t(), String.t(), map(), keyword()) ::
  LatticeStripe.Response.t()
```

Like `list/4` but raises on failure.

# `retrieve`

```elixir
@spec retrieve(LatticeStripe.Client.t(), String.t(), String.t(), keyword()) ::
  {:ok, t()} | {:error, LatticeStripe.Error.t()}
```

Retrieves a TransferReversal by transfer id and reversal id.

Sends `GET /v1/transfers/:transfer_id/reversals/:reversal_id`.

Raises `ArgumentError` (pre-network) if either id is `nil` or empty.

# `retrieve!`

```elixir
@spec retrieve!(LatticeStripe.Client.t(), String.t(), String.t(), keyword()) :: t()
```

Like `retrieve/4` but raises on failure.

# `stream!`

```elixir
@spec stream!(LatticeStripe.Client.t(), String.t(), map(), keyword()) ::
  Enumerable.t()
```

Returns a lazy stream of all TransferReversals for the given transfer
(auto-pagination).

Raises `ArgumentError` (pre-network) if `transfer_id` is `nil` or empty.

# `update`

```elixir
@spec update(LatticeStripe.Client.t(), String.t(), String.t(), map(), keyword()) ::
  {:ok, t()} | {:error, LatticeStripe.Error.t()}
```

Updates a TransferReversal's metadata.

Sends `POST /v1/transfers/:transfer_id/reversals/:reversal_id`.

Raises `ArgumentError` (pre-network) if either id is `nil` or empty.

# `update!`

```elixir
@spec update!(LatticeStripe.Client.t(), String.t(), String.t(), map(), keyword()) ::
  t()
```

Like `update/5` but raises on failure.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
