# `X402.AuthCapture`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/auth_capture.ex#L1)

The `auth-capture` payment scheme: flows, operations, deadlines, lifecycle
payloads, and reason strings.

`auth-capture` adds refundable, two-phase payments to x402 on top of
Base's commerce-payments escrow. The client signs one token
authorization; how it settles follows `extra.paymentFlow`:

* `"escrow"` (default) — the collect is an `authorize` that holds the
  funds; the resource server later relays `capture`, `void`, or
  `refund` lifecycle payloads through the facilitator.
* `"authorization"` — the collect is a terminal `charge` the server
  completes with an amount, fee, and authorizer signature; only `refund`
  follows.

This module is chain-agnostic in shape but the only binding today is EVM
(`X402.AuthCapture.EVM`, `X402.Scheme.AuthCaptureEVM`,
`X402.Verify.AuthCaptureEVM`), so the
lifecycle builders here produce the EVM binding's wire payloads.

## Lifecycle payloads

A resource server authors lifecycle settles from the stored `PaymentInfo`
and client `saltNonce`:

    {:ok, capture} =
      X402.AuthCapture.capture_payload(requirements, payment_info,
        salt_nonce: salt_nonce,
        amount: "750000",
        expected_capturable_amount: "1000000",
        expected_refundable_amount: "0",
        authorizer: receiver_authorizer_signer
      )

These builders do not broadcast or persist the operation. Submit the
resulting envelope through an auth-capture-capable facilitator only
after authenticating the request and establishing any refund funding
agreement. An explicit `:authorizer` signer is required; implicit
receiver-authorizer delegation is not supported.

## Reason strings

Every reason this scheme defines is namespaced
`invalid_auth_capture_evm_*`; standard x402 reasons keep their canonical
names. See `reason_string/1`.

# `capture_mode`

```elixir
@type capture_mode() :: :sync | :deferred
```

When the post-resource finalize of an escrow route runs.

# `operation`

```elixir
@type operation() :: :authorize | :charge | :capture | :void | :refund
```

The escrow call a payload settles as.

# `operator_type`

```elixir
@type operator_type() :: :delegated | :custom | :policy
```

The kind of `extra.captureAuthorizer`.

# `payment_flow`

```elixir
@type payment_flow() :: :escrow | :authorization
```

The settlement lifecycle a requirements entry selects.

# `reason`

```elixir
@type reason() :: atom()
```

A reason atom this scheme reports (see `reason_string/1`).

# `check_authorization_window`
*since 0.9.0* 

```elixir
@spec check_authorization_window(term(), term(), term(), keyword()) ::
  :ok
  | {:error,
     :authorization_expired
     | :authorization_not_yet_valid
     | :deadline_ordering
     | :payload_format
     | {:invalid_options, String.t()}}
```

Checks a client authorization's time window against the capture deadline:
`validAfter <= now`, `validBefore > now + skew`, and
`validBefore <= captureDeadline`.

`valid_after` is `nil` for Permit2 (which has no lower bound). The
`:now` and `:skew_seconds` options must be non-negative integers;
the skew is never lowered below six seconds.

## Examples

    iex> X402.AuthCapture.check_authorization_window("0", "1600", 2_000, now: 1_000)
    :ok

    iex> X402.AuthCapture.check_authorization_window("0", "1005", 2_000, now: 1_000)
    {:error, :authorization_expired}

    iex> X402.AuthCapture.check_authorization_window("0", "2500", 2_000, now: 1_000)
    {:error, :deadline_ordering}

    iex> X402.AuthCapture.check_authorization_window("1100", "1600", 2_000, now: 1_000)
    {:error, :authorization_not_yet_valid}

# `check_deadlines`
*since 0.9.0* 

```elixir
@spec check_deadlines(
  map(),
  keyword()
) ::
  :ok
  | {:error,
     :deadline_ordering
     | :capture_deadline_expired
     | :extra
     | {:invalid_options, String.t()}}
```

Checks the requirements' deadline ordering:
`now + maxTimeoutSeconds <= captureDeadline <= refundDeadline` and
`captureDeadline > now + skew`.

## Options

* `:now` — Unix seconds (default: current time)
* `:skew_seconds` — clock-skew floor (default `6`)

## Examples

    iex> requirements = %{
    ...>   "maxTimeoutSeconds" => 600,
    ...>   "extra" => %{"captureDeadline" => 2_000, "refundDeadline" => 3_000}
    ...> }
    iex> X402.AuthCapture.check_deadlines(requirements, now: 1_000)
    :ok

    iex> X402.AuthCapture.check_deadlines(
    ...>   %{"maxTimeoutSeconds" => 600, "extra" => %{"captureDeadline" => 3_000, "refundDeadline" => 2_000}},
    ...>   now: 1_000
    ...> )
    {:error, :deadline_ordering}

    iex> X402.AuthCapture.check_deadlines(
    ...>   %{"maxTimeoutSeconds" => 1_500, "extra" => %{"captureDeadline" => 2_000, "refundDeadline" => 3_000}},
    ...>   now: 1_000
    ...> )
    {:error, :deadline_ordering}

    iex> X402.AuthCapture.check_deadlines(
    ...>   %{"maxTimeoutSeconds" => 0, "extra" => %{"captureDeadline" => 1_005, "refundDeadline" => 3_000}},
    ...>   now: 1_000
    ...> )
    {:error, :capture_deadline_expired}

# `capture_payload`
*since 0.9.0* 

```elixir
@spec capture_payload(map(), map(), keyword()) :: {:ok, map()} | {:error, term()}
```

Builds a `capture` lifecycle payload (v2 envelope) for the facilitator.

## Options

* `:salt_nonce` (`t:String.t/0`) - Required. The client's `saltNonce` (32-byte `0x` hex) that reopens the salt binding.

* `:amount` - Atomic amount to capture or refund (required for those operations).

* `:fee` - Submitted fee for `capture`: `feeAmount` (atomic units) on v1.1 or
  `feeBps` on v1.0. Defaults to the deployment's minimum
  (`amount * minFeeBps / 10000`, or `minFeeBps`).

* `:fee_receiver` (`t:String.t/0`) - Submitted fee receiver for `capture`. Defaults to `paymentInfo.feeReceiver`.

* `:expected_capturable_amount` - The `capturableAmount` the authorizer expects to find (capture and refund).

* `:expected_refundable_amount` - The `refundableAmount` the authorizer expects to find (capture and refund).

* `:authorizer` (`t:term/0`) - Required. The receiver authorizer's `X402.Signer`, required for explicit consent.

* `:void_remainder` (`t:boolean/0`) - For `capture`: also sign a `Void` so the same settle releases the
  remaining hold (sync partial close-out). Requires `:authorizer`. The default value is `false`.

`:amount`, `:expected_capturable_amount`, and `:expected_refundable_amount`
are required.

# `collector`
*since 0.9.0* 

```elixir
@spec collector(X402.AuthCapture.EVM.deployment(), :eip3009 | :permit2) :: String.t()
```

The token collector a deployment uses for an asset transfer method.

## Examples

    iex> deployment = X402.AuthCapture.EVM.deployment(:v1_1)
    iex> X402.AuthCapture.collector(deployment, :permit2)
    "0xD69831Aed5bfe262067ec4c751f4F830EcdD446e"

# `complete_charge`
*since 0.9.0* 

```elixir
@spec complete_charge(
  map(),
  keyword()
) :: {:ok, map()} | {:error, term()}
```

Completes a client payment payload for a `charge` settle.

Appends `amount`, the deployment's fee field, `feeReceiver`, and an
explicit `authorizerSignature` over exactly those values, leaving the
client's fields untouched. `envelope` is the v2 envelope whose
`accepted` requirements select the deployment.

## Options

* `:amount` - Amount to charge, at most `requirements.amount` (the default).

* `:fee` - Submitted fee (`feeAmount` on v1.1, `feeBps` on v1.0); defaults to the minimum.

* `:fee_receiver` (`t:String.t/0`) - Submitted fee receiver; defaults to `extra.feeRecipient`.

* `:authorizer` (`t:term/0`) - Required. The receiver authorizer's `X402.Signer`, required for explicit consent.

# `refund_payload`
*since 0.9.0* 

```elixir
@spec refund_payload(map(), map(), keyword()) :: {:ok, map()} | {:error, term()}
```

Builds a `refund` lifecycle payload (v2 envelope) for the facilitator.

Requires `:salt_nonce`, `:amount`, `:expected_capturable_amount`, and
`:expected_refundable_amount`; the token collector is always the
deployment's operator refund collector and is not carried on the wire.

# `void_payload`
*since 0.9.0* 

```elixir
@spec void_payload(map(), map(), keyword()) :: {:ok, map()} | {:error, term()}
```

Builds a `void` lifecycle payload (v2 envelope) for the facilitator.

Accepts `:salt_nonce` and `:authorizer` from the lifecycle options.

# `reason_string`
*since 0.9.0* 

```elixir
@spec reason_string(reason()) :: String.t()
```

Converts a reason atom to its canonical wire string.

Standard x402 reasons keep their name; everything else is namespaced
`invalid_auth_capture_evm_*`.

## Examples

    iex> X402.AuthCapture.reason_string(:nonce_mismatch)
    "invalid_auth_capture_evm_nonce_mismatch"

    iex> X402.AuthCapture.reason_string(:invalid_network)
    "invalid_network"

# `reasons`
*since 0.9.0* 

```elixir
@spec reasons() :: [reason()]
```

Every reason atom this scheme reports, standard ones first.

# `asset_transfer_method`
*since 0.9.0* 

```elixir
@spec asset_transfer_method(map()) ::
  {:ok, :eip3009 | :permit2} | {:error, :unsupported_asset_transfer_method}
```

Resolves `extra.assetTransferMethod` (default `"eip3009"`).

## Examples

    iex> X402.AuthCapture.asset_transfer_method(%{"extra" => %{}})
    {:ok, :eip3009}

    iex> X402.AuthCapture.asset_transfer_method(%{"extra" => %{"assetTransferMethod" => "permit2"}})
    {:ok, :permit2}

    iex> X402.AuthCapture.asset_transfer_method(%{"extra" => %{"assetTransferMethod" => "eip2612"}})
    {:error, :unsupported_asset_transfer_method}

# `capture_mode`
*since 0.9.0* 

```elixir
@spec capture_mode(map()) :: {:ok, capture_mode()} | {:error, :extra}
```

Resolves `extra.captureMode` (default `"sync"`), which is only meaningful
under the escrow flow.

## Examples

    iex> X402.AuthCapture.capture_mode(%{"extra" => %{}})
    {:ok, :sync}

    iex> X402.AuthCapture.capture_mode(%{"extra" => %{"captureMode" => "deferred"}})
    {:ok, :deferred}

    iex> X402.AuthCapture.capture_mode(%{
    ...>   "extra" => %{"paymentFlow" => "authorization", "captureMode" => "sync"}
    ...> })
    {:error, :extra}

# `lifecycle?`
*since 0.9.0* 

```elixir
@spec lifecycle?(map()) :: boolean()
```

Whether the payload is a lifecycle payload (carries `payload.type`).

## Examples

    iex> X402.AuthCapture.lifecycle?(%{"payload" => %{"type" => "capture"}})
    true

    iex> X402.AuthCapture.lifecycle?(%{"payload" => %{"authorization" => %{}}})
    false

# `operation`
*since 0.9.0* 

```elixir
@spec operation(map(), map()) ::
  {:ok, operation()} | {:error, :payload_type | :unsupported_payment_flow}
```

Resolves the escrow operation a payload settles as.

Client payloads settle as `:authorize` (escrow flow) or `:charge`
(authorization flow); lifecycle payloads name their `payload.type`, with
`capture` and `void` admitted only under the escrow flow.

## Examples

    iex> X402.AuthCapture.operation(%{"payload" => %{"authorization" => %{}}}, %{"extra" => %{}})
    {:ok, :authorize}

    iex> X402.AuthCapture.operation(
    ...>   %{"payload" => %{"authorization" => %{}}},
    ...>   %{"extra" => %{"paymentFlow" => "authorization"}}
    ...> )
    {:ok, :charge}

    iex> X402.AuthCapture.operation(%{"payload" => %{"type" => "refund"}}, %{"extra" => %{}})
    {:ok, :refund}

    iex> X402.AuthCapture.operation(
    ...>   %{"payload" => %{"type" => "capture"}},
    ...>   %{"extra" => %{"paymentFlow" => "authorization"}}
    ...> )
    {:error, :payload_type}

    iex> X402.AuthCapture.operation(%{"payload" => %{"type" => "settle"}}, %{"extra" => %{}})
    {:error, :payload_type}

# `operator_type`
*since 0.9.0* 

```elixir
@spec operator_type(map()) ::
  {:ok, operator_type()} | {:error, :unsupported_operator_type}
```

Resolves `extra.operatorType` (default `"delegated"`).

## Examples

    iex> X402.AuthCapture.operator_type(%{"extra" => %{}})
    {:ok, :delegated}

    iex> X402.AuthCapture.operator_type(%{"extra" => %{"operatorType" => "custom"}})
    {:ok, :custom}

    iex> X402.AuthCapture.operator_type(%{"extra" => %{"operatorType" => "dao"}})
    {:error, :unsupported_operator_type}

# `payment_flow`
*since 0.9.0* 

```elixir
@spec payment_flow(map()) ::
  {:ok, payment_flow()} | {:error, :unsupported_payment_flow}
```

Resolves `extra.paymentFlow` (default `"escrow"`).

The removed v1.0 `autoCapture: true` is rejected as unsupported.

## Examples

    iex> X402.AuthCapture.payment_flow(%{"extra" => %{}})
    {:ok, :escrow}

    iex> X402.AuthCapture.payment_flow(%{"extra" => %{"paymentFlow" => "authorization"}})
    {:ok, :authorization}

    iex> X402.AuthCapture.payment_flow(%{"extra" => %{"autoCapture" => true}})
    {:error, :unsupported_payment_flow}

    iex> X402.AuthCapture.payment_flow(%{"extra" => %{"paymentFlow" => "instant"}})
    {:error, :unsupported_payment_flow}

# `scheme`
*since 0.9.0* 

```elixir
@spec scheme() :: String.t()
```

The scheme identifier.

## Examples

    iex> X402.AuthCapture.scheme()
    "auth-capture"

# `skew_seconds`
*since 0.9.0* 

```elixir
@spec skew_seconds() :: pos_integer()
```

The clock-skew floor (seconds) the deadline checks apply.

## Examples

    iex> X402.AuthCapture.skew_seconds()
    6

---

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