Accrue.Processor.Stripe (accrue v0.3.0)

Copy Markdown View Source

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.

Summary

Functions

Computes a deterministic idempotency key from the operation, subject ID, and a seed (D2-11). The seed resolution chain is (D2-12)

Resolves the Stripe API version using three-level precedence (D2-14)

Resolves the Stripe-Account header using three-level precedence (D5-01)

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 (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(opts \\ [])

@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(opts \\ [])

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