Helper for asynchronous meter-event state transitions driven by Stripe webhooks.
Kept separate from Accrue.Billing.MeterEventActions so the webhook
path (Accrue.Webhook.DefaultHandler) doesn't pull the outbox/
NimbleOptions surface into its dependency graph.
Centralizes pending → failed transitions with guarded updates
and [:accrue, :ops, :meter_reporting_failed] so retries and duplicate
deliveries do not inflate ops counters.
Summary
Types
Origin of a terminal failure for meter_reporting_failed metadata.
Functions
Looks up the meter-event row by identifier and flips it to failed
with the Stripe error-report object sanitized into stripe_error.
Atomically flips a single meter-event row whose stripe_status is in
:from_statuses (default ["pending"]) to failed, persisting a sanitized
stripe_error derived from err.
Types
@type failure_source() :: :sync | :reconciler | :webhook
Origin of a terminal failure for meter_reporting_failed metadata.
:sync— synchronousreport_usage/3processor error:reconciler—MeterEventsReconcilerretry exhausted path:webhook— Stripebilling.meter.error_report_triggered(or v1) path
Functions
@spec mark_failed_by_identifier(String.t() | nil, map(), String.t() | nil) :: {:ok, Accrue.Billing.MeterEvent.t()} | {:error, :not_found}
Looks up the meter-event row by identifier and flips it to failed
with the Stripe error-report object sanitized into stripe_error.
Uses the same guarded transition as the sync path so duplicate webhook
deliveries do not emit a second meter_reporting_failed for an already
terminal row.
webhook_event_id is optional metadata for ops telemetry (attach the
Stripe event id when known).
@spec mark_failed_with_telemetry( Accrue.Billing.MeterEvent.t(), term(), failure_source(), keyword() ) :: {:ok, :transitioned, Accrue.Billing.MeterEvent.t()} | {:ok, :noop, Accrue.Billing.MeterEvent.t()} | {:error, :not_found}
Atomically flips a single meter-event row whose stripe_status is in
:from_statuses (default ["pending"]) to failed, persisting a sanitized
stripe_error derived from err.
Emits [:accrue, :ops, :meter_reporting_failed] only when the guarded
update affects one row (count == 1). If no row matched (already terminal,
wrong status, or deleted), no telemetry is emitted and {:ok, :noop, row}
returns the current row when it still exists.
Returns
{:ok, :transitioned, %MeterEvent{}}— this invocation performed the durablepending→failedtransition (telemetry fired).{:ok, :noop, %MeterEvent{}}— no qualifying row was updated;rowis the latest state for the same primary key (e.g. idempotent replay or race).{:error, :not_found}— the row id no longer exists.
source is attached to ops telemetry metadata (:sync, :reconciler, or :webhook).
Options
:from_statuses— list ofstripe_statusvalues that may transition tofailedin this update (default["pending"]for sync/reconciler). Stripe meter error webhooks use["pending", "reported"]because Stripe may reject usage after an initialreportedacknowledgement.