Accrue.Billing.ChargeActions (accrue v1.0.0)

Copy Markdown View Source

Charge / payment intent / setup intent write surface.

Ships three public entry points, all exposed on Accrue.Billing via defdelegate:

  • charge/3 — atomic charge creation with SCA-safe tagged returns. Resolves payment method from opts[:payment_method] or the customer's default_payment_method association. Returns a typed {:error, %Accrue.Error.NoDefaultPaymentMethod{}} when neither is set — Accrue never silently falls back to the first attached payment method, because doing so can charge the wrong card when a customer has multiple payment methods on file.
  • create_payment_intent/2 — thin wrapper with IntentResult.wrap/1 on the result.
  • create_setup_intent/2 — off-session card-on-file setup.

Every mutation runs inside Accrue.Repo.transact/2 with an accrue_events row written in the same transaction.

Summary

Functions

Charges a customer a fixed amount of %Accrue.Money{}. Returns intent_result(Charge.t())

Raising variant of charge/3. Raises Accrue.ActionRequiredError on {:ok, :requires_action, pi}, re-raises typed errors otherwise.

Thin wrapper over Processor.create_payment_intent/2. Returns intent_result(map()) so SCA paths surface via {:ok, :requires_action, pi}.

Off-session card-on-file parallel. Creates a SetupIntent with usage: "off_session" so the resulting PaymentMethod can be charged off-session later (e.g. subscription renewals with SCA pre-authorized). Returns intent_result(map()).

Functions

charge(billable_or_customer, amount, opts \\ [])

@spec charge(Accrue.Billing.Customer.t() | struct(), Accrue.Money.t(), keyword()) ::
  {:ok, Accrue.Billing.Charge.t()}
  | {:ok, :requires_action, map()}
  | {:error, term()}

Charges a customer a fixed amount of %Accrue.Money{}. Returns intent_result(Charge.t()):

  • {:ok, %Charge{}} — happy path
  • {:ok, :requires_action, pi} — SCA / 3DS required
  • {:error, %Accrue.Error.NoDefaultPaymentMethod{}} — no PM resolved (typed, loud, pattern-matchable; never silently falls back to "first attached PM")
  • {:error, other} — anything else

Options

  • :payment_method — processor-side payment method id (e.g. "pm_..."). If absent, resolves to the customer's default_payment_method via the schema association.
  • :description — charge description (string).
  • :operation_id — deterministic idempotency seed. Defaults to Accrue.Actor.current_operation_id!/0.

charge!(billable_or_customer, amount, opts \\ [])

Raising variant of charge/3. Raises Accrue.ActionRequiredError on {:ok, :requires_action, pi}, re-raises typed errors otherwise.

create_payment_intent(params, opts \\ [])

@spec create_payment_intent(
  map(),
  keyword()
) :: {:ok, map()} | {:ok, :requires_action, map()} | {:error, term()}

Thin wrapper over Processor.create_payment_intent/2. Returns intent_result(map()) so SCA paths surface via {:ok, :requires_action, pi}.

create_payment_intent!(params, opts \\ [])

@spec create_payment_intent!(
  map(),
  keyword()
) :: map()

Raising variant of create_payment_intent/2.

create_setup_intent(customer_or_billable, opts \\ [])

@spec create_setup_intent(
  Accrue.Billing.Customer.t() | struct(),
  keyword()
) :: {:ok, map()} | {:ok, :requires_action, map()} | {:error, term()}

Off-session card-on-file parallel. Creates a SetupIntent with usage: "off_session" so the resulting PaymentMethod can be charged off-session later (e.g. subscription renewals with SCA pre-authorized). Returns intent_result(map()).

create_setup_intent!(customer_or_billable, opts \\ [])

@spec create_setup_intent!(
  Accrue.Billing.Customer.t() | struct(),
  keyword()
) :: map()

Raising variant of create_setup_intent/2.