Accrue.Processor.Stripe (accrue v1.0.0)

Copy Markdown View Source

Production Stripe adapter — wires all Accrue.Processor callbacks to :lattice_stripe.

This is the adapter you configure in production. Every Accrue.Billing operation flows through here when :processor is set to Accrue.Processor.Stripe. All raw Stripe errors are translated to Accrue.Error subtypes via Accrue.Processor.Stripe.ErrorMapper so nothing upstream ever sees a raw LatticeStripe.Error.

Facade boundary: This is the only module in the Accrue codebase allowed to reference LatticeStripe directly. A CI test enforces this by scanning lib/accrue/**/*.ex and failing if LatticeStripe appears anywhere except stripe.ex and stripe/error_mapper.ex.

When you reach for this module

  • Configuring Stripe credentials — set :stripe_secret_key and optionally :stripe_api_version in config/runtime.exs.
  • Debugging a Stripe-specific error — check Accrue.Processor.Stripe.ErrorMapper for how Stripe error codes map to Accrue.Error subtypes.
  • Overriding the API version per-call — use opts[:api_version] or scope a block with Accrue.Stripe.with_api_version/2.

Configuration

# config/runtime.exs
config :accrue,
  processor: Accrue.Processor.Stripe,
  stripe_secret_key: System.fetch_env!("STRIPE_SECRET_KEY"),
  stripe_api_version: "2026-03-25.dahlia"   # optional; this is the default

Both keys are runtime-only — never set them at compile time or they will be baked into your release artifact.

An unset or empty :stripe_secret_key raises Accrue.ConfigError at the first call, not at boot.

API version precedence

The resolved API version for each call follows this order:

  1. opts[:api_version] (explicit per-call override)
  2. Process.get(:accrue_stripe_api_version) (scoped via Accrue.Stripe.with_api_version/2)
  3. Accrue.Config.stripe_api_version/0 (application config default)

PII discipline

Raw Stripe responses can contain PII in fields like email, name, address, phone, and shipping. This adapter:

  • Does not log processor errors verbatim.
  • Emits only %{adapter: :stripe, operation: ...} in telemetry metadata — never raw params or response bodies.
  • Converts LatticeStripe structs to plain maps so downstream code never pattern-matches on library-internal struct types.

Summary

Functions

Computes a deterministic idempotency key from the operation, subject ID, and a seed. The seed resolution chain is

Resolves the Stripe API version using three-level precedence

Resolves the Stripe-Account header using three-level precedence

Functions

compute_idempotency_key(op, subject_id, opts \\ [])

@spec compute_idempotency_key(atom(), String.t(), keyword()) :: String.t()

Computes a deterministic idempotency key from the operation, subject ID, and a seed. The seed resolution chain is:

  1. opts[:operation_id] (explicit per-call value)
  2. Accrue.Actor.current_operation_id/0 (process dictionary)
  3. Random UUID + Logger.warning (non-deterministic fallback — retries will NOT be idempotent when this path is taken)

Returns a string like "accr_<22 url-safe base64 chars>".

resolve_api_version(opts \\ [])

@spec resolve_api_version(keyword()) :: String.t()

Resolves the Stripe API version using three-level precedence:

  1. opts[:api_version] (explicit per-call override)
  2. Process.get(:accrue_stripe_api_version) (scoped via Accrue.Stripe.with_api_version/2)
  3. Accrue.Config.stripe_api_version/0 (application config default)

resolve_stripe_account(opts \\ [])

@spec resolve_stripe_account(keyword()) :: String.t() | nil

Resolves the Stripe-Account header using three-level precedence:

  1. opts[:stripe_account] (explicit per-call override)
  2. Process.get(:accrue_connected_account_id) (scoped via Accrue.Connect.with_account/2)
  3. Accrue.Config.connect/0 [:default_stripe_account] (config fallback)

Returns nil when no connected-account context is set, which causes lattice_stripe to omit the Stripe-Account header — preserving platform-scoped behaviour for calls that must run as the platform.