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

Real-world processor adapter delegating to `:lattice_stripe`.

**This is the ONLY module in the codebase allowed to alias, import, or
reference `LatticeStripe`** (D-07). All raw Stripe errors cross this
facade and are translated to `Accrue.Error` subtypes via
`Accrue.Processor.Stripe.ErrorMapper` — downstream Billing code never
sees raw `LatticeStripe.Error` shapes. A CI-enforced facade-lockdown test
in `test/accrue/processor/stripe_test.exs` walks `lib/accrue/**/*.ex`
and fails if `LatticeStripe` appears anywhere except `stripe.ex` and
`stripe/error_mapper.ex`.

## Config keys (READ-ONLY)

This module reads (never writes) the following Phase 1 keys that
`Accrue.Config` already defines:

- `:stripe_secret_key` — runtime only (CLAUDE.md §Config Boundaries). An
  unset key raises `Accrue.ConfigError` at call time rather than at
  `Application.compile_env!/2` load time so secrets never leak into
  compiled release artifacts.
- `:stripe_api_version` — runtime only, defaults to `"2026-03-25.dahlia"`.

## PII discipline

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

- **Does not log `processor_error` verbatim** — T-PROC-01 mitigation.
- **Does not auto-inject params or responses into telemetry metadata**
  — only `%{adapter: :stripe, operation: ...}` at this layer.
- **Converts `LatticeStripe.Customer` structs to plain maps** via
  `customer_to_map/1` so downstream code never pattern-matches on
  `%LatticeStripe.Customer{}`.

## Phase 1 scope

Only the three customer callbacks are implemented (PROC-01, PROC-03,
PROC-07). Wire-level integration tests against Stripe test mode are
deferred to Phase 3 (PROC-02) — Phase 1 only proves the behaviour
conformance, the error-mapping contract, and the facade lockdown.

# `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 (D2-11). The seed resolution chain is (D2-12):

  1. `opts[:operation_id]` (explicit)
  2. `Accrue.Actor.current_operation_id/0` (process dict)
  3. Random UUID + `Logger.warning` (non-deterministic fallback)

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 (D2-14):

  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 (D5-01):

  1. `opts[:stripe_account]` (explicit per-call override)
  2. `Process.get(:accrue_connected_account_id)` (via `Accrue.Connect.with_account/2`
     — scoped pdict key; the `Accrue.Connect` module itself lands in Plan 05-02)
  3. `Accrue.Config.connect/0` `[:default_stripe_account]` (config fallback)

Returns `nil` when no connected-account context is set, which preserves
platform-scoped behavior: `lattice_stripe` omits the `Stripe-Account`
header when the client is built with `stripe_account: nil`.

---

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