> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metalntwx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authorized payment

> Collect a signed MIP20 payment or accept an agent's exact x402 payment through a facilitator

> [!NOTE]
>
> **What you'll be able to do**
>
> * Obtain the sender's signed authorization for a one-time payment.
> * Collect the payment by submitting that authorization from the recipient's account.
> * Accept an agent's payment for an HTTP resource through a facilitator.
> * Verify settlement before completing delivery and recover uncertain outcomes.

An authorized payment lets a sender sign a transfer authorization while another
account submits the transaction. The submitting signing account pays the
transaction fee from its balance in the gas token. The authorization fixes one
recipient and amount and can be used once within its validity window. An agent
authorized to sign for the sender can use this flow to pay for an HTTP
resource through a facilitator. A merchant can also collect a customer's signed
payment directly. In both cases, the sender can pay without holding the gas
token.

Choose the path that matches who submits the payment:

| Path | Submission and fee responsibility | Reader task |
| - | - | - |
| [Recipient-submitted collection](#prepare-the-payment) | Recipient calls `receiveWithAuthorization` and pays the transaction fee | Collect the sender's payment and verify the transfer |
| [Facilitator-backed exact x402 payment](#use-a-facilitator) | Facilitator calls `transferWithAuthorization` and pays the transaction fee | Charge for an HTTP resource and establish payment before completing delivery |

The recipient receives the payment in both paths. In the API flow, the seller
operates the HTTP service, the agent signs for the sender and the facilitator
submits settlement. The sender signs `ReceiveWithAuthorization` for
recipient-submitted collection and
`TransferWithAuthorization` for ordinary facilitator submission. Those types
have different signing digests, so their signatures cannot be substituted.

The first procedure collects from the recipient's account. For the HTTP
workflow, go to [Use a facilitator](#use-a-facilitator). An agent authorized
through a mandate uses the account-specific `pull` path, described in
[Pay under spending limits](https://docs.metalntwx.com/agents/spending-limits.md).

## Prepare the payment

Use `senderWallet` to authorize the payment and `recipientWallet` to submit it.
The sender supplies the payment tokens, and the recipient pays gas.

Install the public dependencies with `pnpm add @metalntwx/metal-ts@0.3.2
viem@2.57.7`. Resolve the trusted `token` and its `name` and `decimals` using
[Read metadata](https://docs.metalntwx.com/build/payments/direct-payment.md#read-metadata). Convert the
order's decimal amount to the positive base-unit `amount` using
[Convert a payment amount](https://docs.metalntwx.com/build/payments/direct-payment.md#convert-a-payment-amount).
Check the sender's spendable balance before requesting a signature.

Check the current deployment and the credit from sender to recipient with
[Receive policy](https://docs.metalntwx.com/reference/compliance/receive-policy.md). The documented screening and
quarantine behavior requires compatible deployed token and ReceivePolicy code.
Stop if that prerequisite is missing.

Save an immutable order record containing the chain ID, token, sender, recipient,
amount and the application order ID. An authorization nonce is a random
32-byte value, separate from the submitter's transaction nonce.

```typescript theme={null}
import { randomBytes } from "node:crypto"
import { parseAbi, type Hex } from "viem"

const sender = senderWallet.account
const recipient = recipientWallet.account
if (!sender || !recipient) throw new Error("Configure each wallet client with its signing account")
const chainId = await client.getChainId()
if (chainId !== 63825) throw new Error("Wrong network")
const block = await client.getBlock({ blockTag: "latest" })
const authorization = {
  from: sender.address,
  to: recipient.address,
  value: amount,
  validAfter: block.timestamp,
  validBefore: block.timestamp + 300n,
  nonce: `0x${randomBytes(32).toString("hex")}` as Hex,
}
const domain = { name, version: "1", chainId, verifyingContract: token }
const types = {
  ReceiveWithAuthorization: [
    { name: "from", type: "address" },
    { name: "to", type: "address" },
    { name: "value", type: "uint256" },
    { name: "validAfter", type: "uint256" },
    { name: "validBefore", type: "uint256" },
    { name: "nonce", type: "bytes32" },
  ],
} as const
```

The timestamp window is strict: `validAfter < block.timestamp < validBefore`.
Wait for chain time to advance beyond `validAfter` before simulation. Choose a
window that allows the sender to review the payment and the recipient to submit
it. If it expires before submission, close that attempt before requesting a new
authorization.

## Obtain and check the signature

Have the sender or its authorized agent check the chain ID, token, recipient,
amount and expiry against the approved order. The wallet client signs the
complete authorization using the sender's signing account and the token's
EIP-712 domain.

```typescript theme={null}
import { verifyTypedData } from "viem"

const signature = await senderWallet.signTypedData({
  account: sender,
  domain,
  types,
  primaryType: "ReceiveWithAuthorization",
  message: authorization,
})
if (!await verifyTypedData({
  address: sender.address, domain, types,
  primaryType: "ReceiveWithAuthorization", message: authorization, signature,
})) throw new Error("Invalid sender signature")
```

This verification uses a sender account controlled by a private key. For
signatures validated through ERC-1271, simulate the token call so it checks the
signature against the sender's code.
See the [authorization signature rules](https://docs.metalntwx.com/reference/network-fundamentals/signing.md#mip20-erc-3009).
Store the exact authorization and signature with the order before submitting.

## Submit from the recipient

Only `authorization.to` can call `receiveWithAuthorization`. The outer
transaction must therefore be signed for the recipient's account. An agent
collecting on the recipient's behalf must be authorized to sign for that account.
A relayer cannot submit this variant for an ordinary recipient account.

```typescript theme={null}
const authorizationAbi = parseAbi([
  "function receiveWithAuthorization(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, bytes signature)",
  "function authorizationState(address authorizer, bytes32 nonce) view returns (bool)",
  "event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce)",
  "event AuthorizationCanceled(address indexed authorizer, bytes32 indexed nonce)",
])
const used = await client.readContract({
  address: token, abi: authorizationAbi, functionName: "authorizationState",
  args: [authorization.from, authorization.nonce],
})
if (used) throw new Error("Authorization used or canceled: reconcile its outcome")
const call = {
  address: token,
  abi: authorizationAbi,
  functionName: "receiveWithAuthorization",
  args: [authorization.from, authorization.to, authorization.value,
    authorization.validAfter, authorization.validBefore,
    authorization.nonce, signature],
} as const
await client.simulateContract({ ...call, account: recipient })
```

Estimate this call and check the recipient signing account's balance in the gas
token using [Estimate and record fees](https://docs.metalntwx.com/build/prepare/estimate-and-record-fees.md). Use
the recipient as `sender`, with this call's `gasLimit`, and the quoted `price` as
`maxFeePerGas`. Check the sender's
balance in the token being transferred separately from the recipient signing
account's balance in the gas token.

```typescript theme={null}
import { signMip20Contract } from "@metalntwx/metal-ts"

const signed = await signMip20Contract({
  publicClient: client, walletClient: recipientWallet, ...call,
  gasToken: "0x000000000000000000000000000000000000b5d5",
  gasLimit, gasFallback: gasLimit, maxFeePerGas,
})
```

[Save the signed
attempt](https://docs.metalntwx.com/build/payments/verify-and-reconcile-payments.md#save-outgoing-attempts-before-submission)
before calling `await signed.send()`. Retain the signed transaction hash and
bytes alongside the authorization nonce. Serialize transactions from the
recipient account so concurrent collections cannot select the same transaction nonce.

## Verify and record collection

Load the transaction receipt by the saved transaction hash and require
successful execution. Use [Verify a finalized
transfer](https://docs.metalntwx.com/build/payments/verify-and-reconcile-payments.md#verify-a-finalized-transfer)
for the exact token, sender, recipient and amount, including the canonical block
and finality checks. Match that transfer to `AuthorizationUsed` from the same
token with this sender and authorization nonce. Scan forward from that event,
skipping the token's transfers involving its own address that settle yield, and
require the next transfer to match the sender, recipient and amount. Stop if
another authorization event intervenes. Two unrelated matching events somewhere
in a transaction receipt do not establish that this authorization paid the
order. Read `authorizationState` and require `true` after the transaction.

Credit the order once using the verified transfer's event identity. A consumed
nonce alone does not identify the order's recipient or amount. An authorization
can consume its nonce while the payment is quarantined. In that case, verify
the token's transfer from the sender to ReceivePolicy and matching quarantine receipt,
save the full recovery record, and leave the order unpaid. Do not request a new
authorization as an automatic retry.

Routing requires a call to the smart contract function that forwards the funds.
If this collection is part of that call, also verify its required downstream transfer with
[Route incoming payments](https://docs.metalntwx.com/build/payments/route-incoming-payments.md).

## Recover or reject an attempt

An uncertain send retains both identities: the saved transaction hash and
`(sender, authorization nonce)`. Follow [Recover an uncertain
submission](https://docs.metalntwx.com/build/payments/verify-and-reconcile-payments.md#recover-an-uncertain-submission).
Check the original transaction receipt before resending the same signed bytes.
If another submission consumed the authorization, locate its `AuthorizationUsed`
or `AuthorizationCanceled` event.

A reverted transaction rolls back authorization consumption and token transfers.
Record its gas charge, inspect the error, and retry only when the original
authorization remains unused and valid. A policy refusal in `REVERT`
mode rolls back consumption. In `QUARANTINE` mode, the authorization is consumed
and funds are in custody even though the intended recipient has not been paid.

Before enabling collection, test these cases using simulation or controlled
fixtures:

| Attempt | Required result |
| - | - |
| Submit from an account other than the recipient | Rejection with no token transfer |
| Change recipient, amount, expiry or domain after signing | Invalid signature |
| Submit at or after `validBefore` | Expired authorization rejected |
| Reuse an authorization after successful collection | Nonce reuse rejected |
| Refuse the credit in `REVERT` mode | Revert leaves the authorization unused and payment balances unchanged |
| Refuse the credit in `QUARANTINE` mode | Nonce consumed, funds in ReceivePolicy quarantine, quarantine receipt saved, order unpaid |
| Cancel the authorization | State reports consumed, cancellation recorded, order unpaid |
| Lose the send response | Recover the saved transaction without a second credit |

## Use a facilitator

Use `exact` x402 payments to let an agent pay for an HTTP resource. For an
ordinary sender account, the agent needs a wallet client authorized to sign for
that account, and the account needs the payment token. The seller needs a
controlled recipient address, a price, an HTTP service and a facilitator that
advertises `exact` for the target network through `/supported`. The facilitator's
signing account pays the transaction fee from its balance in the gas token.

Implement the middleware using the [seller quickstart](https://docs.metalntwx.com/agents/quickstart-accept.md).
The [facilitator reference](https://docs.metalntwx.com/agents/facilitator.md) describes its requests, results
and refusal reasons. The payment and delivery stages are:

1. **Offer a price.** The seller answers the agent's unpaid request with HTTP `402` and
   `PAYMENT-REQUIRED`, naming the scheme, network, token, recipient and amount.
   Associate that offer with a durable application request ID and resource.
2. **Obtain the signed payment.** The agent checks the offer against its payment
   rights and retries with `PAYMENT-SIGNATURE`. For an ordinary account, this carries a
   `TransferWithAuthorization` signature binding the exact payment and validity
   window. Check that it answers the seller's offer.
3. **Verify.** The seller passes the payment and offer to `/verify`. For `exact`,
   the facilitator checks the authorization and simulates the transfer.
   Verification says the payment could succeed against the checked state.
   It neither transfers funds nor reserves the sender's balance.
4. **Settle.** The seller submits the same payment and offer to `/settle`.
   The facilitator rechecks them, submits `transferWithAuthorization` and
   returns the transaction outcome. A mandate-backed payment instead calls
   the smart account's `pull`, which enforces the mandate before calling the
   token.
5. **Verify delivery conditions.** Match the resulting token transfer to the
   authorization and expected recipient, amount and network. Apply the
   [collection checks](#verify-and-record-collection), including finality and
   quarantine handling, before marking the order paid or completing an
   irreversible delivery.

The Express middleware runs the route handler after verification and before
settlement. Keep work in that handler reversible. Stage the result and complete
delivery only after the payment checks succeed. Use the successful settlement's
`extra.settlement.id` as a durable deduplication key. `first: true` identifies
the request that claimed the payment, but your application still needs to save
the delivery state and recover interrupted work. Return an already completed
result on a retry without charging or delivering again.

Save the request, offer and signed payment before settlement. An
`onAfterSettle` notification cannot block the middleware response if its work
fails. For irreversible operations, return only a pending request ID from the
protected handler, then expose the completed result through an authenticated
status route after durable recipient-credit and finality checks. The
[seller quickstart](https://docs.metalntwx.com/agents/quickstart-accept.md#2-receive-a-paid-request) explains
that delivery boundary.

Retain the request ID, resource and price, full offer and signed payment,
authorization nonce, facilitator payment ID, settlement transaction hash when
present, verified event identity and delivery state. Use
[Verify and reconcile payments](https://docs.metalntwx.com/build/payments/verify-and-reconcile-payments.md)
for durable records and [uncertain-submission recovery](https://docs.metalntwx.com/build/payments/verify-and-reconcile-payments.md#recover-an-uncertain-submission).
Use the facilitator's response or listing to locate the payment, then verify
the finalized transfer to establish the recipient credit.

If settlement times out, keep the request pending. Retry `/settle` with the
same signed payment and investigate the returned transaction or
[`/settlements`](https://docs.metalntwx.com/agents/facilitator#get-settlements). Do not request a second
authorization from the agent while the original is unresolved. `already_settled`
requires reconciling the original payment and delivery record. A quarantined
payment leaves the recipient unpaid and needs recovery even though its
authorization is consumed.

For frequent API charges through a deposit, use
[Offer Pay-per-Call Clearing](https://docs.metalntwx.com/agents/clearing.md).
That flow uses signed Clearing receipts and epoch finalization.
An `exact` verification response carries no Clearing preconfirmation.

MIP20 exposes no `WithPolicy` variants or transfer callbacks. If collection
requires additional application evidence, authenticate its issuer, exact payment
binding, expiry and replay protection before submission, or enforce those checks
in a send policy contract before it requests the transfer.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.