# `Accrue.Processor.Stripe`
[🔗](https://github.com/szTheory/accrue/blob/accrue-v1.0.0/lib/accrue/processor/stripe.ex#L1)

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.

# `compute_idempotency_key`

```elixir
@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`

```elixir
@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`

```elixir
@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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
