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.reversalsbecomes[%LatticeStripe.TransferReversal{}, ...](a plain Elixir list, not a%LatticeStripe.List{}struct)- The wrapper metadata (
has_more,url,total_count) is preserved undertransfer.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
Functions
Creates a new Transfer.
Like create/3 but raises on failure.
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).
Like update/4 but raises on failure.
Types
@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
@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{}}).
@spec create!(LatticeStripe.Client.t(), map(), keyword()) :: t()
Like create/3 but raises on failure.
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"].
@spec list(LatticeStripe.Client.t(), map(), keyword()) :: {:ok, LatticeStripe.Response.t()} | {:error, LatticeStripe.Error.t()}
Lists Transfers with optional filters.
@spec list!(LatticeStripe.Client.t(), map(), keyword()) :: LatticeStripe.Response.t()
Like list/3 but raises on failure.
@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.
@spec retrieve!(LatticeStripe.Client.t(), String.t(), keyword()) :: t()
Like retrieve/3 but raises on failure.
@spec stream!(LatticeStripe.Client.t(), map(), keyword()) :: Enumerable.t()
Returns a lazy stream of all Transfers (auto-pagination).
@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.
@spec update!(LatticeStripe.Client.t(), String.t(), map(), keyword()) :: t()
Like update/4 but raises on failure.