Phase 3 Plan 06 charge / payment intent / setup intent write surface (BILL-20, BILL-21, BILL-22).
Ships three public entry points, all exposed on Accrue.Billing via
defdelegate in Plan 01 Task 4:
charge/3— atomic charge creation with SCA-safe tagged returns. Resolves payment method fromopts[:payment_method]or the customer'sdefault_payment_methodassociation. Returns typed{:error, %Accrue.Error.NoDefaultPaymentMethod{}}when neither is set (BILL-21) — never silently falls back to the first attached PM (Cashier footgun, D3-58).create_payment_intent/2— thin wrapper withIntentResult.wrap/1on the result.create_setup_intent/2— BILL-22 off-session card-on-file parallel.
Every mutation runs inside Accrue.Repo.transact/2 with an
accrue_events row written in the same transaction (EVT-04 invariant).
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}.
Raising variant of create_payment_intent/2.
BILL-22 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()).
Raising variant of create_setup_intent/2.
Functions
@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 (BILL-20){:error, %Accrue.Error.NoDefaultPaymentMethod{}}— no PM resolved (BILL-21 — 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'sdefault_payment_methodvia the schema association.:description— charge description (string).:operation_id— deterministic idempotency seed. Defaults toAccrue.Actor.current_operation_id!/0.
@spec charge!(Accrue.Billing.Customer.t() | struct(), Accrue.Money.t(), keyword()) :: Accrue.Billing.Charge.t()
Raising variant of charge/3. Raises Accrue.ActionRequiredError on
{:ok, :requires_action, pi}, re-raises typed errors otherwise.
@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}.
Raising variant of create_payment_intent/2.
@spec create_setup_intent( Accrue.Billing.Customer.t() | struct(), keyword() ) :: {:ok, map()} | {:ok, :requires_action, map()} | {:error, term()}
BILL-22 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()).
@spec create_setup_intent!( Accrue.Billing.Customer.t() | struct(), keyword() ) :: map()
Raising variant of create_setup_intent/2.