Accrue.Config (accrue v0.3.0)

Copy Markdown View Source

Runtime configuration schema for Accrue, backed by NimbleOptions.

This module is the single source of truth for supported :accrue application keys. Host code reads validated values via get!/1 or Application.get_env/3; extend behaviour through adapters, not by editing this schema from application code.

Compile-time vs runtime

Adapter atoms (:processor, :mailer, :mailer_adapter, :pdf_adapter, :auth_adapter) are stable per-deploy and fine at compile time via Application.compile_env!/2.

Secrets (:stripe_secret_key) and host-owned fields (:default_currency, :from_email, brand colors) MUST be read at runtime. See CLAUDE.md §Config Boundaries.

Options

  • :repo (atom/0) - Required. Host Ecto.Repo module that Accrue writes to (event ledger, webhook events, billing tables).

  • :processor (atom/0) - Processor adapter implementing Accrue.Processor behaviour. The default value is Accrue.Processor.Fake.

  • :mailer (atom/0) - Mailer pipeline module implementing Accrue.Mailer behaviour. The default value is Accrue.Mailer.Default.

  • :mailer_adapter (atom/0) - Swoosh-backed mailer delivery module. The default value is Accrue.Mailer.Swoosh.

  • :pdf_adapter (atom/0) - PDF adapter implementing Accrue.PDF behaviour. The default value is Accrue.PDF.ChromicPDF.

  • :auth_adapter (atom/0) - Auth adapter implementing Accrue.Auth behaviour. The default value is Accrue.Auth.Default.

  • :storage_adapter (atom/0) - Storage adapter implementing Accrue.Storage behaviour. v1.0 ships Accrue.Storage.Null only; hosts supply a custom adapter (e.g., S3) to enable persisted asset storage. Accrue.Storage.Filesystem ships in v1.1. The default value is Accrue.Storage.Null.

  • :stripe_secret_key (String.t/0) - Runtime Stripe secret key. MUST be read at runtime only; never via Application.compile_env!/2. Validated at boot when processor == Accrue.Processor.Stripe.

  • :stripe_api_version (String.t/0) - Stripe API version pinned by the :lattice_stripe wrapper. The default value is "2026-03-25.dahlia".

  • :emails (keyword/0) - Per-email-type switches. Keys are email type atoms; values are boolean or {Mod, :fun, args} MFA callbacks. The default value is [].

  • :email_overrides (keyword/0) - Per-email-type template module overrides (third rung of the override ladder; see guides/email.md). Keys are email type atoms; values are module names. The default value is [].

  • :attach_invoice_pdf (boolean/0) - Auto-attach invoice PDF to the receipt email. The default value is true.

  • :enforce_immutability (boolean/0) - When true, Accrue.Application boot raises if the current PG role has UPDATE/DELETE on accrue_events. The default value is false.

  • :business_name (String.t/0) - Business name shown in email headers, PDFs, and admin UI. The default value is "Accrue".

  • :business_address (String.t/0) - Business postal address shown in invoice footers. The default value is "".

  • :logo_url (String.t/0) - Absolute URL to the brand logo used in email + PDF headers. The default value is "".

  • :support_email (String.t/0) - Reply-to support email address for transactional mail. The default value is "support@example.com".

  • :from_email (String.t/0) - Default From: address for transactional mail. The default value is "noreply@example.com".

  • :from_name (String.t/0) - Default From: name for transactional mail. The default value is "Accrue".

  • :default_currency (atom/0) - Default currency when one is not explicitly supplied. The default value is :usd.

  • :webhook_signing_secrets (term/0) - Map of processor atom to signing secret(s). Each value is a string or list of strings for rotation. Example: %{stripe: ["whsec_old", "whsec_new"]}. The default value is %{}.

  • :succeeded_retention_days - Number of days to retain :succeeded webhook events before the Pruner deletes them. Set to :infinity to disable pruning. Default: 14. The default value is 14.

  • :dead_retention_days - Number of days to retain :dead webhook events before the Pruner deletes them. Set to :infinity to disable pruning. Default: 90. The default value is 90.

  • :webhook_handlers (list of atom/0) - List of modules implementing Accrue.Webhook.Handler behaviour. Called sequentially after the default handler on each webhook event. Example: [MyApp.BillingHandler, MyApp.AnalyticsHandler]. The default value is [].

  • :expiring_card_thresholds - Strictly-descending list of day thresholds at which the expiring-card reminder email fires ahead of a stored card's expiration. Default: [30, 7, 1] — 30, 7, and 1 days out. The default value is [30, 7, 1].

  • :idempotency_mode - How Accrue.Actor.current_operation_id!/0 behaves when the process dict has no operation_id. :strict raises Accrue.ConfigError; :warn (the default) generates a random UUID and logs a warning. Set to :strict in production to ensure every outbound processor call carries a deterministic idempotency key. The default value is :warn.

  • :succeeded_refund_retention_days (pos_integer/0) - Number of days to retain :succeeded refund records before pruning Default: 90. The default value is 90.

  • :dunning (keyword/0) - Dunning grace-period overlay config. :mode is :stripe_smart_retries or :disabled; :terminal_action is :unpaid or :canceled; :grace_days adds N days past Stripe's last retry before Accrue asks the processor facade to move the subscription to the terminal action. The default value is [mode: :stripe_smart_retries, grace_days: 14, terminal_action: :unpaid, telemetry_prefix: [:accrue, :ops]].

  • :webhook_endpoints (keyword/0) - Map of endpoint name to [secret:, mode:] for multi-endpoint webhooks. Example: [primary: [secret: "whsec_..."], connect: [secret: "whsec_...", mode: :connect]]. The default value is [].

  • :dlq_replay_batch_size (pos_integer/0) - Number of rows per chunk in Accrue.Webhooks.DLQ.requeue_where/2 bulk replay. The default value is 100.

  • :dlq_replay_stagger_ms (non_neg_integer/0) - Milliseconds to sleep between chunks during DLQ bulk replay (protects downstream). Default: 1_000. The default value is 1000.

  • :dlq_replay_max_rows (pos_integer/0) - Hard cap on bulk replay. Returns {:error, :replay_too_large} unless force: true is passed. Default: 10_000. The default value is 10000.

  • :branding (keyword/0) - Branding config. Single source of truth for email + PDF brand. :from_email and :support_email are required for any real deploy. See guides/branding.md. The default value is [].

    • :business_name (String.t/0) - The default value is "Accrue".

    • :from_name (String.t/0) - The default value is "Accrue".

    • :from_email (String.t/0) - Required.

    • :support_email (String.t/0) - Required.

    • :reply_to_email - The default value is nil.

    • :logo_url - The default value is nil.

    • :logo_dark_url - The default value is nil.

    • :accent_color - The default value is "#1F6FEB".

    • :secondary_color - The default value is "#6B7280".

    • :font_stack (String.t/0) - The default value is "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif".

    • :company_address - The default value is nil.

    • :support_url - The default value is nil.

    • :social_links (keyword/0) - The default value is [].

    • :list_unsubscribe_url - The default value is nil.

  • :default_locale (String.t/0) - Application-wide default locale for email + PDF rendering. Third rung of the locale precedence ladder (after assigns[:locale] and customer.preferred_locale). Bad locales fall back to "en". The default value is "en".

  • :default_timezone (String.t/0) - Application-wide default IANA timezone for datetime rendering. Third rung of the timezone precedence ladder (after assigns[:timezone] and customer.preferred_timezone). Bad zones fall back to "Etc/UTC". The default value is "Etc/UTC".

  • :cldr_backend (atom/0) - Cldr backend module used by Accrue.Workers.Mailer.enrich/2 to validate locale strings. Defaults to Accrue.Cldr. The default value is Accrue.Cldr.

  • :connect (keyword/0) - Stripe Connect configuration. :default_stripe_account is the fallback connected account id used when no per-call override or pdict scope is active (three-level precedence chain). :platform_fee configures the default flat-rate fee consumed by Accrue.Connect.platform_fee/2: :percent is a Decimal percentage (e.g. Decimal.new("2.9") for 2.9%), :fixed is an Accrue.Money fee in minor units added after the percentage, and :min/:max optionally clamp the result. The default value is [default_stripe_account: nil, platform_fee: [percent: Decimal.new("2.9"), fixed: nil, min: nil, max: nil]].

Summary

Functions

Returns the branding config keyword list.

Returns a single branding key. Raises if the key is unknown.

Returns the configured Cldr backend module used by Accrue.Workers.Mailer.enrich/2 to validate locale strings.

Returns the Connect config keyword list.

Returns the number of days to retain :dead webhook events.

Returns the application default locale string.

Returns the application default IANA timezone string.

Returns the list of deprecated flat branding keys. Consumed by Accrue.Application.warn_deprecated_branding/0 and by the internal flat-key shim in branding/0.

Returns the DLQ bulk-replay chunk size.

Returns the hard cap on DLQ bulk-replay rows.

Returns the DLQ bulk-replay inter-chunk stagger in milliseconds.

Returns the dunning grace-period overlay config.

Reads a config key from Application.get_env/3, falling back to the schema default. Raises Accrue.ConfigError if the key is not in the schema at all (prevents silent typos in downstream code).

Returns the NimbleOptions schema keyword list. Used by boot-time validation to iterate keys.

Returns the configured Stripe API version string.

Returns the number of days to retain :succeeded webhook events.

Validates a keyword list against the Accrue config schema and returns the normalized form. Raises NimbleOptions.ValidationError on failure.

Reads the current :accrue application env at boot time, filters it to the schema-known keys, and validates via NimbleOptions.validate!/2.

NimbleOptions :custom validator for :expiring_card_thresholds.

NimbleOptions :custom validator for :branding.accent_color / :branding.secondary_color. Accepts #rgb, #rrggbb, and #rrggbbaa hex color strings; rejects anything else.

Returns the multi-endpoint webhook config.

Returns the list of user-registered webhook handler modules.

Returns the signing secret(s) for the given processor.

Functions

branding()

@spec branding() :: keyword()

Returns the branding config keyword list.

Falls back to building a keyword list from deprecated top-level flat branding keys (:business_name, :logo_url, :from_email, :from_name, :support_email, :business_address) when the nested :branding key is unset or empty. Nested :branding always takes precedence. See Accrue.Application.warn_deprecated_branding/0 for the boot-time Logger.warning when flat keys are still in use.

branding(key)

@spec branding(atom()) :: term()

Returns a single branding key. Raises if the key is unknown.

cldr_backend()

@spec cldr_backend() :: module()

Returns the configured Cldr backend module used by Accrue.Workers.Mailer.enrich/2 to validate locale strings.

connect()

@spec connect() :: keyword()

Returns the Connect config keyword list.

Shape: `[default_stripe_account: String.t() | nil,

      platform_fee: [percent: Decimal.t(), fixed: Accrue.Money.t() | nil,
                     min: Accrue.Money.t() | nil, max: Accrue.Money.t() | nil]]`.

dead_retention_days()

@spec dead_retention_days() :: pos_integer() | :infinity

Returns the number of days to retain :dead webhook events.

default_locale()

@spec default_locale() :: String.t()

Returns the application default locale string.

default_timezone()

@spec default_timezone() :: String.t()

Returns the application default IANA timezone string.

deprecated_flat_branding_keys()

@spec deprecated_flat_branding_keys() :: [atom()]

Returns the list of deprecated flat branding keys. Consumed by Accrue.Application.warn_deprecated_branding/0 and by the internal flat-key shim in branding/0.

dlq_replay_batch_size()

@spec dlq_replay_batch_size() :: pos_integer()

Returns the DLQ bulk-replay chunk size.

dlq_replay_max_rows()

@spec dlq_replay_max_rows() :: pos_integer()

Returns the hard cap on DLQ bulk-replay rows.

dlq_replay_stagger_ms()

@spec dlq_replay_stagger_ms() :: non_neg_integer()

Returns the DLQ bulk-replay inter-chunk stagger in milliseconds.

dunning()

@spec dunning() :: keyword()

Returns the dunning grace-period overlay config.

get!(key)

@spec get!(atom()) :: term()

Reads a config key from Application.get_env/3, falling back to the schema default. Raises Accrue.ConfigError if the key is not in the schema at all (prevents silent typos in downstream code).

schema()

@spec schema() :: keyword()

Returns the NimbleOptions schema keyword list. Used by boot-time validation to iterate keys.

stripe_api_version()

@spec stripe_api_version() :: String.t()

Returns the configured Stripe API version string.

succeeded_retention_days()

@spec succeeded_retention_days() :: pos_integer() | :infinity

Returns the number of days to retain :succeeded webhook events.

validate!(opts)

@spec validate!(keyword()) :: keyword()

Validates a keyword list against the Accrue config schema and returns the normalized form. Raises NimbleOptions.ValidationError on failure.

validate_at_boot!()

@spec validate_at_boot!() :: :ok

Reads the current :accrue application env at boot time, filters it to the schema-known keys, and validates via NimbleOptions.validate!/2.

Called by Accrue.Application.start/2 before the supervision tree starts. Raises NimbleOptions.ValidationError on misconfig — fail loud rather than limp into production with silently-broken config.

Only schema-known keys are validated. Extra keys in the :accrue env (e.g., per-module adapter configs like Accrue.Mailer.Swoosh) are ignored here — they belong to their own libraries and would otherwise produce spurious unknown option errors.

validate_descending(list)

@spec validate_descending(term()) :: {:ok, [pos_integer()]} | {:error, String.t()}

NimbleOptions :custom validator for :expiring_card_thresholds.

Accepts a non-empty list of positive integers that is strictly descending (each element strictly less than the previous). Returns {:ok, list} on success, {:error, message} on failure.

validate_hex(full)

@spec validate_hex(term()) :: {:ok, String.t()} | {:error, String.t()}

NimbleOptions :custom validator for :branding.accent_color / :branding.secondary_color. Accepts #rgb, #rrggbb, and #rrggbbaa hex color strings; rejects anything else.

webhook_endpoints()

@spec webhook_endpoints() :: keyword()

Returns the multi-endpoint webhook config.

webhook_handlers()

@spec webhook_handlers() :: [module()]

Returns the list of user-registered webhook handler modules.

webhook_signing_secrets(processor)

@spec webhook_signing_secrets(atom()) :: String.t() | [String.t()]

Returns the signing secret(s) for the given processor.

Looks up webhook_signing_secrets in the :accrue application env and extracts the value for the given processor atom. Returns a list of strings (for multi-secret rotation support). Raises Accrue.ConfigError if no secrets are configured for the processor.