Accrue.Billing.MeterEvents (accrue v1.0.0)

Copy Markdown View Source

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

failure_source()

@type failure_source() :: :sync | :reconciler | :webhook

Origin of a terminal failure for meter_reporting_failed metadata.

  • :sync — synchronous report_usage/3 processor error
  • :reconcilerMeterEventsReconciler retry exhausted path
  • :webhook — Stripe billing.meter.error_report_triggered (or v1) path

Functions

mark_failed_by_identifier(identifier, stripe_obj, webhook_event_id \\ nil)

@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).

mark_failed_with_telemetry(row, err, source, opts \\ [])

@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 durable pendingfailed transition (telemetry fired).
  • {:ok, :noop, %MeterEvent{}} — no qualifying row was updated; row is 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 of stripe_status values that may transition to failed in this update (default ["pending"] for sync/reconciler). Stripe meter error webhooks use ["pending", "reported"] because Stripe may reject usage after an initial reported acknowledgement.