LatticeStripe.Transfer (LatticeStripe v1.7.4)

Copy Markdown View Source

Operations on Stripe Transfer objects — the Connect separate-charge-and-transfer primitive.

A Transfer moves funds from your platform balance to a connected account. See the Stripe Transfer API.

Usage

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

# Create a transfer
{:ok, transfer} = LatticeStripe.Transfer.create(client, %{
  "amount" => 1000,
  "currency" => "usd",
  "destination" => "acct_1Nv0FGQ9RKHgCVdK"
})

# Retrieve
{:ok, transfer} = LatticeStripe.Transfer.retrieve(client, "tr_...")

# Update metadata
{:ok, transfer} = LatticeStripe.Transfer.update(client, "tr_...", %{
  "metadata" => %{"internal_id" => "xfer_42"}
})

# List / stream
{:ok, resp} = LatticeStripe.Transfer.list(client, %{"destination" => "acct_..."})

client
|> LatticeStripe.Transfer.stream!()
|> Enum.take(500)

Reversing a transfer: use LatticeStripe.TransferReversal

This module deliberately does not define reverse/3 or reverse/4. Phase 18 decision D-02 locks TransferReversal as a standalone top-level module addressed by (transfer_id, reversal_id). To reverse a transfer:

{:ok, reversal} = LatticeStripe.TransferReversal.create(client, "tr_...", %{})

Mirrors stripe-java's own top-level TransferReversal class and the Phase 17 AccountLink / LoginLink precedent.

Embedded reversals sublist decoding

The Stripe API returns an embedded (non-paginated) reversals sublist on every Transfer:

{
  "reversals": {
    "object": "list",
    "data": [ {...}, {...} ],
    "has_more": false,
    "url": "/v1/transfers/tr_.../reversals",
    "total_count": 2
  }
}

Transfer.from_map/1 decodes this specially:

  • transfer.reversals becomes [%LatticeStripe.TransferReversal{}, ...] (a plain Elixir list, not a %LatticeStripe.List{} struct)
  • The wrapper metadata (has_more, url, total_count) is preserved under transfer.extra["reversals_meta"] so no data is lost

Idempotency for double-execution safety

Transfer.create/3 is a money-moving operation. LatticeStripe.Client.request/2 already auto-generates idempotency keys for mutating requests and reuses them across retries (Phase 2 RTRY-03). For at-least-once safety in your own failure recovery loops, pass an explicit key:

{:ok, transfer} =
  LatticeStripe.Transfer.create(client, params, idempotency_key: "my-xfer-42")

No client-side validation

Per Phase 15 D5 / Phase 18 D-04, Transfer.create/3 does not pre-validate amount, currency, or destination beyond the standard CRUDL shape. Stripe's own 400 errors surface as {:error, %LatticeStripe.Error{type: :invalid_request_error}}.

Stripe API Reference

See the Stripe Transfer API.

Summary

Types

t()

A Stripe Transfer object.

Functions

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

Lists Transfers with optional filters.

Like list/3 but raises on failure.

Retrieves a Transfer by ID.

Like retrieve/3 but raises on failure.

Returns a lazy stream of all Transfers (auto-pagination).

Updates a Transfer (typically only metadata / description).

Types

t()

@type t() :: %LatticeStripe.Transfer{
  amount: integer() | nil,
  amount_reversed: integer() | nil,
  balance_transaction: LatticeStripe.BalanceTransaction.t() | String.t() | nil,
  created: integer() | nil,
  currency: String.t() | nil,
  description: String.t() | nil,
  destination: LatticeStripe.Account.t() | String.t() | nil,
  destination_payment: LatticeStripe.Charge.t() | String.t() | nil,
  extra: map(),
  id: String.t() | nil,
  livemode: boolean() | nil,
  metadata: map() | nil,
  object: String.t(),
  reversals: [LatticeStripe.TransferReversal.t()],
  reversed: boolean() | nil,
  source_transaction: LatticeStripe.Charge.t() | String.t() | nil,
  source_type: String.t() | nil,
  transfer_group: String.t() | nil
}

A Stripe Transfer object.

reversals is a plain list of %LatticeStripe.TransferReversal{} structs (NOT a %LatticeStripe.List{}). The original sublist wrapper metadata (has_more, url, total_count) is stashed under extra["reversals_meta"] by from_map/1.

See the Stripe Transfer API.

Functions

create(client, params \\ %{}, opts \\ [])

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

Creates a new Transfer.

Sends POST /v1/transfers. No client-side validation of params (Stripe's 400 surfaces as {:error, %Error{}}).

create!(client, params \\ %{}, opts \\ [])

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

Like create/3 but raises on failure.

from_map(map)

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

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

Specially decodes the embedded reversals sublist into [%TransferReversal{}] (a plain list, not a %List{}), and preserves the wrapper metadata under extra["reversals_meta"].

list(client, params \\ %{}, opts \\ [])

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

Lists Transfers with optional filters.

list!(client, params \\ %{}, opts \\ [])

Like list/3 but raises on failure.

retrieve(client, id, opts \\ [])

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

Retrieves a Transfer by ID.

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

retrieve!(client, id, opts \\ [])

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

Like retrieve/3 but raises on failure.

stream!(client, params \\ %{}, opts \\ [])

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

Returns a lazy stream of all Transfers (auto-pagination).

update(client, id, params, opts \\ [])

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

Updates a Transfer (typically only metadata / description).

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

update!(client, id, params, opts \\ [])

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

Like update/4 but raises on failure.