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
LatticeStripedirectly. A CI test enforces this by scanninglib/accrue/**/*.exand failing ifLatticeStripeappears anywhere exceptstripe.exandstripe/error_mapper.ex.
When you reach for this module
- Configuring Stripe credentials — set
:stripe_secret_keyand optionally:stripe_api_versioninconfig/runtime.exs. - Debugging a Stripe-specific error — check
Accrue.Processor.Stripe.ErrorMapperfor how Stripe error codes map toAccrue.Errorsubtypes. - Overriding the API version per-call — use
opts[:api_version]or scope a block withAccrue.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 defaultBoth 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:
opts[:api_version](explicit per-call override)Process.get(:accrue_stripe_api_version)(scoped viaAccrue.Stripe.with_api_version/2)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
LatticeStripestructs 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
Computes a deterministic idempotency key from the operation, subject ID, and a seed. The seed resolution chain is:
opts[:operation_id](explicit per-call value)Accrue.Actor.current_operation_id/0(process dictionary)- 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>".
Resolves the Stripe API version using three-level precedence:
opts[:api_version](explicit per-call override)Process.get(:accrue_stripe_api_version)(scoped viaAccrue.Stripe.with_api_version/2)Accrue.Config.stripe_api_version/0(application config default)
Resolves the Stripe-Account header using three-level precedence:
opts[:stripe_account](explicit per-call override)Process.get(:accrue_connected_account_id)(scoped viaAccrue.Connect.with_account/2)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.