# Routed checkout details

Choose refund funding, bind merchant permission and handle unpaid obligations.

Cleard provider API · 1.4.0 · Base Sepolia test USDC.

Route a purchase for authoritative payment verification, then fund approved refunds using the merchant’s accepted policy: reserve funds or bounded collection from its spendable wallet. The original payment remains final. Use the [quickstart](https://cleard.ai/docs/quickstart) to set up the participants and run the flow.

## Network and amounts

Use Circle test USDC on Base Sepolia (chain ID `84532`). Its token address is `0x036CbD53842c5426634e7929541eC2318f3dCF7e`; verify it against [Circle’s contract list](https://developers.circle.com/stablecoins/usdc-contract-addresses). Obtain test USDC from [Circle’s official faucet](https://faucet.circle.com) and test ETH for wallet gas. These assets have no financial value. Mainnet is unsupported.

API amounts are integer base units: 1 USDC is `1000000`, and 0.02 USDC is `20000`. Use the SDK’s `parseTokenAmount` and `formatTokenAmount` with six decimals. Inputs with excess precision are rejected instead of rounded. Confirm the program’s `deployment_id` and advertised `funding_modes`; its asset, execution contract and reviewer are fixed. Historical `circle-usdc` programs use reserve funding. Credentials and transaction journals cannot cross programs.

The historical `circle-usdc` V3 reserve is `0x6baBBCBCC6517E1b2C6872Ec01f323b98D91eC9d`. For that deployment, use the published `routing-usdc-trust.json` to verify its code, token implementation, metadata and signing key. V4 programs require their separate deployment and `routing-wallet-trust.json`; never reuse the V3 trust file. See [Contracts & trust](https://cleard.ai/docs/onchain) for the complete authorization boundary.

## Sandbox limits

For testing, each program supports up to 20 purchases in total (shared across its account’s programs on the same deployment when successors are enabled), and each merchant wallet can have up to 32 open purchases per reserve onchain. Unpaid checkouts count too. These are sandbox limits, not production capacity.

## Choose the funding mode

- Direct wallet (`mode: 1`) is for an identified merchant whose policy permits unsecured refund exposure. The merchant retains spendable proceeds, with no mandatory reserve or withholding. It must sign the bounded mandate and separately approve token allowance. Spending funds or revoking allowance can leave an approved refund unpaid.
- Reserve (`mode: 0` on V4; historical V3 policies omit `mode`) is for policies requiring more backing, including higher-risk merchants. Configure an upfront minimum, rolling withholding, or both. Available reserve pays approved refunds; agreed later routed payments can recover eligible debt. Partial backing is not a reimbursement guarantee.

Risk tier selects policy requirements; it is not collection authority or proof of KYB. Neither path accesses PayPal-held or other provider-held balances. Payment routing is separate from custody: direct mode forwards the full payment immediately, while reserve mode captures only the agreed amounts. Both preserve original payment finality and use the same buyer claim and designated-reviewer approval flow.

## Purchase and refund flow

Follow the [quickstart](https://cleard.ai/docs/quickstart) for the complete flow and [Integration flow](https://cleard.ai/docs/provider-integration) for participant responsibilities. The sections below define the funding rules accepted before payment.

## Funding semantics

In reserve mode, both rates apply to gross integer base units and round up to one micro-USDC per payment, without fractional carry. Each payment funds the oldest eligible approved debt first, then rolling reserve, then merchant proceeds. Total capture cannot exceed the payment or the cumulative collection ceiling. Recovery transfers do not consume the reserve accumulation cap. Held reserve may also pay debt; funds are never allocated twice.

In `payment_allocation`, `gross = merchant_net + captured`. `captured` is withheld from this payment; `recovered` is all award payments during the transaction, including prior reserve, so it can exceed `captured`. At that transaction, `reserve_after = reserve_before + captured - recovered`. The snapshot’s `reserve_balance` is a later read, not necessarily that transaction’s closing balance. Refresh an older purchase after recovery.

The [Routing OpenAPI](https://cleard.ai/docs/routing-openapi.json) defines every `FundingPolicy` field, unit and bound. In reserve mode, `reserveCap` limits held accumulation and `collectionCap` counts deposits and routed capture. In direct mode, `collectionCap` counts successful wallet collection. Neither counter resets after payouts, withdrawals or retries. `upfront` is the balance required at registration, not per-purchase collateral. Risk tier is provider input, not verified KYB or a guarantee.

Direct collection pays only the fixed beneficiary, which must differ from the merchant, and is capped by the unpaid award, remaining mandate cap, wallet balance and allowance. Set `validUntil` no more than 120 days ahead.

In reserve mode, only explicit deposits and payments deliberately routed through the enrolled contract can fund collection. Arbitrary external wallets, provider-managed balances and other merchant inflows are outside its authority.

## Direct merchant-wallet mode

The merchant keeps its payment proceeds spendable, with no prefunding or withholding. Approved refunds are collected from its wallet under a bounded signed funding mandate and separate USDC allowance. Insufficient funds or revoked allowance leave an unpaid obligation; collection is not guaranteed.

See [API and SDK policy examples](#api-and-sdk-policy-examples) for setup and collection retries.

## API and SDK policy examples

These examples assume client 1.7.1 initialized with the access file, exact deployment trust file, durable journal and approved transaction budget described in its README. Use V4 policies only when that program advertises the corresponding mode. Amounts below are USDC base units. Choose one policy before enrollment; do not switch an enrolled merchant with open purchases.

### Funding policy example

```js
import {routingHash} from './routing-client.mjs';
const common = {
  registrar: client.info.registrar,
  collectionCap: 10000000, purchaseLimit: 1000000,
  exposureLimit: 2000000, decisionWindow: 3600,
  recoveryWindow: 7200, basis: 'gross_round_up',
  releaseRule: 'remaining_exposure',
  validUntil: Math.floor(Date.now() / 1000) + 86400
};
const directTerms = {...common, mode: 1, riskTier: 'trusted',
  upfront: 0, reserveCap: 0, rollingBps: 0, recoveryBps: 0};
const reserveTerms = {...common, mode: 0, riskTier: 'higher_risk',
  upfront: 0, reserveCap: 2000000, rollingBps: 200,
  recoveryBps: 10000};
const terms = directTerms; // Or reserveTerms, before enrollment.
const policy = {...terms, termsHash: routingHash(terms)};
const merchant = await client.merchant('merchant-1', merchantWallet, policy);
if (policy.mode === 1) {
  await client.approveWalletFunding('allowance-1', merchant,
    merchantWallet, 1000000); // Separate consent, capped at 1 USDC.
}
```

The reserve example has zero upfront and 2% rolling capture; it is not fully collateralized. If you select a nonzero `upfront`, approve and deposit that amount before registering purchases. For a historical V3 program, omit `mode` and `validUntil` from its reserve policy and use its original trust file. Do not copy the V4 contract into an old program.

The equivalent terms-creation API is `POST /v1/routing/programs/{program_id}/merchants`, with a scoped bearer credential and stable `Idempotency-Key`. Its body is the following JavaScript object serialized as JSON:

### Funding policy example

```js
const body = {
  merchant_reference: 'merchant-1',
  wallet: merchantWallet.address,
  funding_policy: policy
};
```

Creation alone grants no authority. The response contains the exact enrollment authorization; the merchant must sign it, submit the matching onchain enrollment, and confirm both through `/merchants/{merchant_id}/enrollment`. The SDK `merchant` helper performs these steps with the merchant signer. Keep private keys with their owner. See the Routing OpenAPI for complete schemas.

After the same purchase, buyer claim and designated-reviewer decision steps in either mode:

### Funding policy example

```js
await client.refund('refund-attempt-1', purchase);
const result = await client.snapshot(purchase);
console.log({approved: result.approved, paid: result.paid,
  outstanding: result.outstanding});
```

Retry an uncertain first response with `refund-attempt-1`, not a new ID. After a confirmed completed attempt, check remaining debt and authority. An explicitly requested direct-wallet retry then calls `await client.refund('refund-attempt-2', purchase)` with a new ID. Available balance, allowance and unexpired deadlines still govern payment. For reserve recovery, a later eligible routed purchase follows the accepted withholding policy; a transfer into an unrelated external wallet does nothing.

## Deadlines, withdrawal and exit

Accepted purchases freeze their beneficiary, amount, policy version, terms and deadlines. Policy replacement is refused while purchases remain open. Programs support two merchants for a provider or one for a direct merchant; see [Sandbox limits](#sandbox-limits) for purchase capacity.

Reserve withdrawals preserve remaining exposure. Stopping new purchases preserves already registered purchases. Purchase creation fixes the deadline upper bounds: claim deadline, then `decisionWindow`, then `recoveryWindow`. A timely claim preserves authority through the fixed decision deadline; it does not restart the clock. For V4, the first accepted reviewer decision’s expiry can shorten the collection deadline. Approved debt can be collected only through that accepted deadline and the mandate expiry. The contract method `releaseExpired(merchant, purchaseId)` releases expired authority and exposure but does not erase an approved unpaid amount. It requires a transaction; waiting alone does not close the purchase. An expired award does not revive on a later deposit.

## Requests, signatures and retries

Use `https://sandbox.cleard.ai/v1/routing`. [Access & authentication](https://cleard.ai/docs/authentication) describes account, service, wallet and reviewer permissions. [API reference](https://cleard.ai/docs/api) lists operations; [Routing OpenAPI](https://cleard.ai/docs/routing-openapi.json) defines exact schemas and required scopes.

Writes use `Content-Type: application/json`. Program and wallet writes require an 8–100 character `Idempotency-Key`. After an uncertain response, retry the exact body and key. Access renewal revokes the previous credential; notification receivers deduplicate signed event IDs. Keep service credentials and private keys out of URLs, source files and logs.

Wallet personal-sign bytes are `cleard.routing.v1.<kind>\n` plus canonical JSON. Ed25519 record, decision and notification bytes use `cleard.routing.v1.<kind>\0` plus canonical JSON. Canonical JSON sorts object keys recursively, preserves array order and supports finite JSON values. Reviewer private keys stay with the reviewer; only signatures reach the API. Decisions support positive approvals; rejection and review case management are not implemented.

`recoverPurchase(merchant, purchaseId)` is permissionless but can only collect an already-approved award for that purchase. It cannot authorize a new award or change the beneficiary.

Receipt accounting deduplicates by transaction hash and log index, including all recoveries within one payment. Out-of-order cumulative amounts require earlier receipts to be reconciled first. Changed canonical block hashes freeze the program for operator review. See [Errors & retries](https://cleard.ai/docs/retries) for interrupted wallet submissions, durable journals and recovery steps.

## Token failures and notifications

An unknown token implementation or metadata change halts new execution. Circle pause or blacklist controls can prevent transfers. In reserve mode, reverted, false-return, missing-return and inexact transfers roll back the whole onchain action. Direct mode rolls back only its isolated transfer attempt and retains the award without consuming collection capacity; a failed refund is never recorded as paid. A blocked refund recipient can also halt a payment that would recover its debt. Read-only records remain available. Preflight checks cannot prevent a token upgrade between verification and mining.

Notifications go only to `https://sandbox.cleard.ai/v1/routing/notification-receiver`. Signed `refund.collection_attempted` events report collection attempts; `refund.paid` reports payment. Delivery is caller-driven and deduplicated by signed event ID. No external provider webhook or scheduler is enabled. See [Refunds & recovery](https://cleard.ai/docs/api-refunds#notifications) for dispatch and retry testing.

## Continue an integration

Use the same program, deployment trust and durable journal across browser and SDK activity. [Access & authentication](https://cleard.ai/docs/authentication) owns account discovery, renewal, successor limits and reviewer continuity. The client README covers explicit service-history reconciliation, offline reviewer signing and notification verification. See [Errors & retries](https://cleard.ai/docs/retries) before retrying an uncertain or failed operation.
