Accrue ships with Stripe and the Fake Processor, but the processor boundary is an explicit extension point. A custom adapter should implement the documented behaviour and return the same shape of {:ok, map} or {:error, exception} results that the billing context already expects.

Do not use a custom processor to fake undocumented parity with every Stripe feature. The contract is the public Accrue.Processor behaviour, and host apps should rely on the documented billing facade rather than adapter internals.

Behaviour contract

A custom adapter starts with the behaviour declaration:

defmodule MyApp.Billing.AcmePay do
  @behaviour Accrue.Processor

  @impl Accrue.Processor
  def create_customer(params, opts), do: {:ok, %{id: "cus_custom_123", params: params, opts: opts}}

  @impl Accrue.Processor
  def retrieve_customer(id, _opts), do: {:ok, %{id: id}}

  @impl Accrue.Processor
  def update_customer(id, params, _opts), do: {:ok, %{id: id, params: params}}

  # Implement the rest of the documented callbacks your billing flow uses.
end

The important part is the contract marker:

@behaviour Accrue.Processor

Match the existing callback names and keep return values plain. Billing context functions wrap higher-level behavior such as intent branches, telemetry, and event recording around those adapter calls.

Wiring your adapter

Set the processor module in config:

config :accrue, :processor, MyApp.Billing.AcmePay

That keeps all host-facing calls on the usual entrypoint:

Accrue.Processor.create_customer(%{email: "user@example.test"})

Use runtime config rather than hard-coding a module alias in the application. That is the same dispatch model the built-in adapters use.

Keep the fake path first

Accrue.Processor.Fake remains the baseline for most host-app tests even if a production deployment uses a custom adapter. The Fake gives deterministic ids, clock control, and webhook synthesis without any network dependency.

Treat your custom processor tests as adapter-contract tests and keep the main billing suite on the Fake unless the external processor itself is the thing under test.

Test with Accrue.Test

For normal billing flows, keep the primary assertions on the host billing context and use the built-in fake surface:

use Accrue.Test

setup do
  Accrue.Test.setup_fake_processor()
  :ok
end

Accrue.Processor.Fake is the primary test surface because it proves the flow through the same reducer, event, mailer, and PDF paths that the host app uses. Once that is green, add a narrower suite around the custom adapter module to prove callback conformance and error mapping.