# `Accrue.Billing`
[🔗](https://github.com/szTheory/accrue/blob/accrue-v0.3.1/lib/accrue/billing.ex#L1)

Primary context module for Accrue billing operations.

All write operations for billable entities live here, following
conventional Phoenix context boundaries. Host schemas gain access to
these operations via `use Accrue.Billable`, which injects a
convenience `customer/1` that delegates here.

## Customer lifecycle

  * `customer/1` — lazy fetch-or-create. First call
    auto-creates an `accrue_customers` row via the configured
    processor; subsequent calls return the cached row.
  * `create_customer/1` — explicit create, always hits the processor.
  * `customer!/1` and `create_customer!/1` — raising variants following
    the same `{:ok, _} | {:error, _}` vs `!` naming convention as the rest
    of Accrue.

All writes use `Ecto.Multi` to ensure the customer row and the
corresponding `accrue_events` entry are committed atomically.

# `add_item`

# `add_item!`

# `apply_promotion_code`

# `apply_promotion_code!`

# `attach_payment_method`

# `attach_payment_method!`

# `cancel`

# `cancel!`

# `cancel_at_period_end`

# `cancel_at_period_end!`

# `cancel_schedule`

# `cancel_schedule!`

# `charge`

# `charge!`

# `comp_subscription`

# `comp_subscription!`

# `create_coupon`

# `create_coupon!`

# `create_customer`

```elixir
@spec create_customer(struct()) ::
  {:ok, Accrue.Billing.Customer.t()} | {:error, term()}
```

Explicitly creates a `Customer` for the given billable struct.

Uses `Ecto.Multi` to atomically:

  1. Create the customer on the processor side (Fake or Stripe)
  2. Insert the `accrue_customers` row with the processor-assigned ID
  3. Record a `"customer.created"` event (EVT-04)

Returns `{:ok, %Customer{}}` on success or `{:error, reason}` on
failure. The entire transaction rolls back if any step fails.

## Examples

    {:ok, customer} = Accrue.Billing.create_customer(user)
    customer.processor_id  #=> "cus_fake_00001"

# `create_customer!`

```elixir
@spec create_customer!(struct()) :: Accrue.Billing.Customer.t()
```

Raising variant of `create_customer/1`. Returns the `Customer`
directly or raises on error.

# `create_payment_intent`

# `create_payment_intent!`

# `create_promotion_code`

# `create_promotion_code!`

# `create_refund`

# `create_refund!`

# `create_setup_intent`

# `create_setup_intent!`

# `customer`

```elixir
@spec customer(struct()) :: {:ok, Accrue.Billing.Customer.t()} | {:error, term()}
```

Lazily fetches or creates a `Customer` for the given billable struct.

If a customer row already exists for the billable's `owner_type` and
`owner_id`, returns it. Otherwise, creates one via the configured
processor and persists it atomically with an event record.

## Examples

    {:ok, customer} = Accrue.Billing.customer(user)
    {:ok, ^customer} = Accrue.Billing.customer(user)  # same row

# `customer!`

```elixir
@spec customer!(struct()) :: Accrue.Billing.Customer.t()
```

Raising variant of `customer/1`. Returns the `Customer` directly or
raises on error.

# `detach_payment_method`

# `detach_payment_method!`

# `fetch_invoice_pdf`

# `finalize_invoice`

# `finalize_invoice!`

# `get_subscription`

# `get_subscription!`

# `mark_uncollectible`

# `mark_uncollectible!`

# `patch_data`

```elixir
@spec patch_data(Ecto.Schema.t(), map()) :: {:ok, Ecto.Schema.t()} | {:error, term()}
```

Shallow-merges `partial_data` into the existing `data` column (D2-08).

Used when a partial event carries only a delta. Applies optimistic
locking via `lock_version`.

## Examples

    {:ok, patched} = Accrue.Billing.patch_data(customer, %{"balance" => 100})

# `pause`

# `pause!`

# `pay_invoice`

# `pay_invoice!`

# `preview_upcoming_invoice`

# `preview_upcoming_invoice!`

# `put_data`

```elixir
@spec put_data(Ecto.Schema.t(), map()) :: {:ok, Ecto.Schema.t()} | {:error, term()}
```

Fully replaces the `data` jsonb column on a billing record (D2-08).

Used by webhook reconcile paths that receive the whole object (e.g.
`customer.updated`). Applies optimistic locking via `lock_version`.

## Examples

    {:ok, updated} = Accrue.Billing.put_data(customer, %{"balance" => 0})

# `release_schedule`

# `release_schedule!`

# `remove_item`

# `remove_item!`

# `render_invoice_pdf`

# `report_usage`

# `report_usage!`

# `resume`

# `resume!`

# `send_invoice`

# `send_invoice!`

# `set_default_payment_method`

# `set_default_payment_method!`

# `store_invoice_pdf`

# `subscribe`

# `subscribe!`

# `subscribe_via_schedule`

# `subscribe_via_schedule!`

# `swap_plan`

# `swap_plan!`

# `unpause`

# `unpause!`

# `update_customer`

```elixir
@spec update_customer(
  %Accrue.Billing.Customer{
    __meta__: term(),
    charges: term(),
    data: term(),
    default_payment_method: term(),
    default_payment_method_id: term(),
    email: term(),
    id: term(),
    inserted_at: term(),
    invoices: term(),
    last_stripe_event_id: term(),
    last_stripe_event_ts: term(),
    lock_version: term(),
    metadata: term(),
    name: term(),
    owner_id: term(),
    owner_type: term(),
    payment_methods: term(),
    preferred_locale: term(),
    preferred_timezone: term(),
    processor: term(),
    processor_id: term(),
    subscriptions: term(),
    updated_at: term()
  },
  map()
) :: {:ok, Accrue.Billing.Customer.t()} | {:error, term()}
```

Updates a `Customer` with the given attributes.

Uses `Ecto.Multi` to atomically update the customer and record a
`"customer.updated"` event (EVT-04). Metadata is validated per D2-07
(flat string map, max 50 keys, etc.). Optimistic locking via
`lock_version` prevents torn writes (D2-09).

## Examples

    {:ok, customer} = Accrue.Billing.update_customer(customer, %{metadata: %{"tier" => "pro"}})

# `update_customer_tax_location`

```elixir
@spec update_customer_tax_location(
  %Accrue.Billing.Customer{
    __meta__: term(),
    charges: term(),
    data: term(),
    default_payment_method: term(),
    default_payment_method_id: term(),
    email: term(),
    id: term(),
    inserted_at: term(),
    invoices: term(),
    last_stripe_event_id: term(),
    last_stripe_event_ts: term(),
    lock_version: term(),
    metadata: term(),
    name: term(),
    owner_id: term(),
    owner_type: term(),
    payment_methods: term(),
    preferred_locale: term(),
    preferred_timezone: term(),
    processor: term(),
    processor_id: term(),
    subscriptions: term(),
    updated_at: term()
  },
  map()
) :: {:ok, Accrue.Billing.Customer.t()} | {:error, term()}
```

Updates a processor-backed customer's tax location with immediate validation.

This public path is distinct from `update_customer/2`, which remains a
local-only row update for non-processor customer maintenance.

# `update_customer_tax_location!`

```elixir
@spec update_customer_tax_location!(
  %Accrue.Billing.Customer{
    __meta__: term(),
    charges: term(),
    data: term(),
    default_payment_method: term(),
    default_payment_method_id: term(),
    email: term(),
    id: term(),
    inserted_at: term(),
    invoices: term(),
    last_stripe_event_id: term(),
    last_stripe_event_ts: term(),
    lock_version: term(),
    metadata: term(),
    name: term(),
    owner_id: term(),
    owner_type: term(),
    payment_methods: term(),
    preferred_locale: term(),
    preferred_timezone: term(),
    processor: term(),
    processor_id: term(),
    subscriptions: term(),
    updated_at: term()
  },
  map()
) :: Accrue.Billing.Customer.t()
```

Raising variant of `update_customer_tax_location/2`.

# `update_item_quantity`

# `update_item_quantity!`

# `update_quantity`

# `update_quantity!`

# `update_schedule`

# `update_schedule!`

# `void_invoice`

# `void_invoice!`

---

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