LatticeStripe.Charge (LatticeStripe v1.7.3)

Copy Markdown View Source

Stripe Charge objects — the result record of a payment attempt.

When a LatticeStripe.PaymentIntent is confirmed, Stripe creates a Charge that captures the settled payment outcome. New integrations should start with LatticeStripe.PaymentIntent for accepting payments; this module is for reading and reconciling those result records after the fact.

When to use this module

  • Connect platform fee reconciliation — walk balance_transaction.fee_details after a destination charge settles to reconcile application fees.
  • Support and audit workflowslist/3, stream!/3, and search/3 to find charges by customer, date, or Stripe search query.
  • Post-hoc metadataupdate/4 to attach or correct metadata and description on an existing charge without re-running payment flows.

When not to use this module

Usage

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

# Retrieve a settled charge by id (expand balance_transaction for fee_details)
{:ok, charge} =
  LatticeStripe.Charge.retrieve(client, "ch_3OoLqrJ...",
    expand: ["balance_transaction"]
  )

# List charges with filters
{:ok, resp} = LatticeStripe.Charge.list(client, %{"limit" => "20", "customer" => "cus_123"})

# Stream all charges lazily (auto-pagination)
client
|> LatticeStripe.Charge.stream!()
|> Stream.take(100)
|> Enum.each(&process_charge/1)

# Search charges (eventual consistency — new charges may lag)
{:ok, resp} = LatticeStripe.Charge.search(client, "status:'succeeded' AND currency:'usd'")

# Update metadata and description on an existing charge
{:ok, charge} =
  LatticeStripe.Charge.update(client, "ch_3OoLqrJ...", %{
    "metadata" => %{"order_id" => "ord_456"},
    "description" => "Order #456"
  })

# Capture an uncaptured legacy direct charge (not PI-initiated)
{:ok, charge} = LatticeStripe.Charge.capture(client, "ch_3OoLqrJ...")

Connect platform fee reconciliation

After a destination charge settles, platforms walk PaymentIntent.latest_charge -> Charge.balance_transaction -> fee_details to reconcile the application fee Stripe transferred into their platform balance. The typed %Charge{} return gives IDE-friendly completion and typespec coverage for that flow without forcing users to drop into LatticeStripe.Client.request/2.

# Walk fee_details to find the application_fee entry (Connect platform fee)
application_fees =
  charge.balance_transaction["fee_details"]
  |> Enum.filter(fn fd -> fd["type"] == "application_fee" end)

SDK surface (intentionally omitted)

There is no create/3 or cancel/3 — this module does not initiate payments. Charges are created as a side effect of PaymentIntent confirmation (or legacy direct-charge flows outside this SDK). See Phase 18 decision D-06 for the payment-initiation rationale.

Security and Inspect

Inspect shows only [id, object, amount, currency, status, captured, paid]. The following fields may contain customer PII and are hidden from inspect output so they do not leak into application logs:

  • billing_details (email, name, phone, address)
  • payment_method_details (card last4, fingerprint, etc.)
  • fraud_details
  • receipt_email, receipt_number, receipt_url
  • customer, payment_method

If you explicitly access those fields on a %Charge{} struct, you own the disclosure decision.

Stripe API Reference

See the Stripe Charge API for the full object reference.

Summary

Types

t()

A Stripe Charge object.

Functions

Captures an uncaptured Charge.

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

Lists Charges with optional filters.

Retrieves a Charge by ID.

Like retrieve/3 but returns the bare %Charge{} on success and raises LatticeStripe.Error on failure.

Searches Charges using Stripe's search query language.

Returns a lazy stream of all Charges matching the search query (auto-pagination).

Returns a lazy stream of all Charges matching the given params (auto-pagination).

Updates a Charge by ID.

Types

t()

@type t() :: %LatticeStripe.Charge{
  amount: integer() | nil,
  amount_captured: integer() | nil,
  amount_refunded: integer() | nil,
  application: String.t() | nil,
  application_fee: String.t() | nil,
  application_fee_amount: integer() | nil,
  balance_transaction: LatticeStripe.BalanceTransaction.t() | String.t() | nil,
  billing_details: map() | nil,
  captured: boolean() | nil,
  created: integer() | nil,
  currency: String.t() | nil,
  customer: LatticeStripe.Customer.t() | String.t() | nil,
  description: String.t() | nil,
  destination: LatticeStripe.Account.t() | String.t() | nil,
  extra: map(),
  failure_code: String.t() | nil,
  failure_message: String.t() | nil,
  fraud_details: map() | nil,
  id: String.t() | nil,
  invoice: LatticeStripe.Invoice.t() | String.t() | nil,
  livemode: boolean() | nil,
  metadata: map() | nil,
  object: String.t(),
  on_behalf_of: String.t() | nil,
  outcome: map() | nil,
  paid: boolean() | nil,
  payment_intent: LatticeStripe.PaymentIntent.t() | String.t() | nil,
  payment_method: LatticeStripe.PaymentMethod.t() | String.t() | nil,
  payment_method_details: map() | nil,
  receipt_email: String.t() | nil,
  receipt_number: String.t() | nil,
  receipt_url: String.t() | nil,
  refunded: boolean() | nil,
  refunds: map() | nil,
  review: String.t() | nil,
  source_transfer: LatticeStripe.Transfer.t() | String.t() | nil,
  statement_descriptor: String.t() | nil,
  statement_descriptor_suffix: String.t() | nil,
  status: atom() | String.t() | nil,
  transfer_data: map() | nil,
  transfer_group: String.t() | nil
}

A Stripe Charge object.

See the Stripe Charge API for field definitions. Unknown fields are preserved in :extra (F-001).

Functions

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

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

Captures an uncaptured Charge.

Sends POST /v1/charges/:id/capture with optional params and returns {:ok, %Charge{}}. Only applicable to Charges created via legacy direct-charge flows that were not yet captured.

For charges created by PaymentIntent confirmation, use LatticeStripe.PaymentIntent.capture/4 instead — capturing a PI-initiated charge through this function is not the supported path.

Parameters

  • client - A %LatticeStripe.Client{} struct
  • id - The Charge ID string
  • params - Optional capture params (e.g., %{"amount" => 1500})
  • opts - Per-request overrides

Returns

  • {:ok, %Charge{}} on success
  • {:error, %LatticeStripe.Error{}} on failure

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

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

Like capture/4 but raises LatticeStripe.Error on failure.

from_map(map)

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

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

Maps every known Stripe Charge field explicitly. Any unrecognized fields are collected into :extra (F-001) so no data is silently lost when Stripe adds new fields.

from_map(nil) returns nil for use with optional/nullable charge payloads.

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

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

Lists Charges with optional filters.

Sends GET /v1/charges and returns {:ok, %Response{data: %List{}}} with typed %Charge{} items.

Parameters

  • client - A %LatticeStripe.Client{} struct
  • params - Filter params (e.g., %{"limit" => "10", "customer" => "cus_123"})
  • opts - Per-request overrides

Returns

  • {:ok, %Response{data: %List{data: [%Charge{}, ...]}}} on success
  • {:error, %LatticeStripe.Error{}} on failure

Example

{:ok, resp} = LatticeStripe.Charge.list(client, %{"limit" => "20"})
Enum.each(resp.data.data, &IO.inspect/1)

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

Like list/3 but raises LatticeStripe.Error on failure.

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

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

Retrieves a Charge by ID.

Sends GET /v1/charges/:id and returns {:ok, %Charge{}}.

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

Parameters

  • client - A %LatticeStripe.Client{} struct
  • id - The Charge ID string (e.g., "ch_3OoLqrJ...")
  • opts - Per-request overrides. Supports expand: ["balance_transaction", ...] to inline expanded child objects.

Returns

  • {:ok, %Charge{}} on success
  • {:error, %LatticeStripe.Error{}} on failure

Example

{:ok, charge} =
  LatticeStripe.Charge.retrieve(client, "ch_3OoLqrJ...",
    expand: ["balance_transaction"]
  )

application_fees =
  charge.balance_transaction["fee_details"]
  |> Enum.filter(fn fd -> fd["type"] == "application_fee" end)

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

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

Like retrieve/3 but returns the bare %Charge{} on success and raises LatticeStripe.Error on failure.

Also raises ArgumentError (pre-network) when id is nil or empty.

search(client, query, opts \\ [])

Searches Charges using Stripe's search query language.

Sends GET /v1/charges/search with the query string and returns typed results. Note: search results have eventual consistency — newly created Charges may not appear immediately.

Parameters

  • client - A %LatticeStripe.Client{} struct
  • query - Stripe search query string (e.g., "status:'succeeded' AND currency:'usd'")
  • opts - Per-request overrides

Returns

  • {:ok, %Response{data: %List{data: [%Charge{}, ...]}}} on success
  • {:error, %LatticeStripe.Error{}} on failure

Example

{:ok, resp} = LatticeStripe.Charge.search(client, "status:'succeeded'")

search!(client, query, opts \\ [])

Like search/3 but raises LatticeStripe.Error on failure.

search_stream!(client, query, opts \\ [])

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

Returns a lazy stream of all Charges matching the search query (auto-pagination).

Emits individual %Charge{} structs, fetching additional search pages as needed. Raises LatticeStripe.Error if any page fetch fails.

Parameters

  • client - A %LatticeStripe.Client{} struct
  • query - Stripe search query string
  • opts - Per-request overrides

Returns

An Enumerable.t() of %Charge{} structs.

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

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

Returns a lazy stream of all Charges matching the given params (auto-pagination).

Emits individual %Charge{} structs, fetching additional pages as needed. Raises LatticeStripe.Error if any page fetch fails.

Parameters

  • client - A %LatticeStripe.Client{} struct
  • params - Filter params (e.g., %{"limit" => "100"})
  • opts - Per-request overrides

Returns

An Enumerable.t() of %Charge{} structs.

Example

client
|> LatticeStripe.Charge.stream!()
|> Stream.take(500)
|> Enum.to_list()

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

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

Updates a Charge by ID.

Sends POST /v1/charges/:id with the given params and returns {:ok, %Charge{}}. Note: the Stripe API only supports updating the metadata and description fields on a Charge.

Parameters

  • client - A %LatticeStripe.Client{} struct
  • id - The Charge ID string
  • params - Map of fields to update (only "metadata" and "description" are accepted by Stripe)
  • opts - Per-request overrides

Returns

  • {:ok, %Charge{}} on success
  • {:error, %LatticeStripe.Error{}} on failure

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

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

Like update/4 but raises LatticeStripe.Error on failure.