From 831d9221d00fa4f8c8a12194d423a5f8ea38aadc Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 07:34:00 +0000 Subject: [PATCH] docs(sdk): add forwardToArbiter page and document named address constants - New page for forwardToArbiter() with payload shape, error handling, and address re-exports - Updated installation page to show named constant imports alongside getChainConfig - Updated helpers card description in SDK overview - Added forwardToArbiter link to refundable() next steps Generated-By: mintlify-agent --- docs.json | 1 + sdk/helpers/forward-to-arbiter.mdx | 140 +++++++++++++++++++++++++++++ sdk/helpers/refundable.mdx | 3 + sdk/installation.mdx | 43 +++++++-- sdk/overview.mdx | 2 +- 5 files changed, 179 insertions(+), 10 deletions(-) create mode 100644 sdk/helpers/forward-to-arbiter.mdx diff --git a/docs.json b/docs.json index 3ae8129..a3858d0 100644 --- a/docs.json +++ b/docs.json @@ -102,6 +102,7 @@ "pages": [ "sdk/merchant/getting-started", "sdk/helpers/refundable", + "sdk/helpers/forward-to-arbiter", "sdk/merchant/quickstart", "sdk/merchant/refund-handling" ] diff --git a/sdk/helpers/forward-to-arbiter.mdx b/sdk/helpers/forward-to-arbiter.mdx new file mode 100644 index 0000000..deb2dcc --- /dev/null +++ b/sdk/helpers/forward-to-arbiter.mdx @@ -0,0 +1,140 @@ +--- +title: "forwardToArbiter()" +description: "Forward settlement data to an arbiter service for quality evaluation" +icon: "arrow-right" +--- + +The `forwardToArbiter()` function creates an `onAfterSettle` hook that forwards the response body and payment payload to an arbiter service. It runs fire-and-forget so it never blocks the response to the client. + +- Only fires for successful **commerce** scheme settlements +- POSTs `{ responseBody, transaction, paymentPayload }` to `{arbiterUrl}/verify` +- Errors are silently caught so an unavailable arbiter cannot break the payment flow + +## Usage + +```typescript +import { forwardToArbiter } from '@x402r/helpers'; + +const resourceServer = new x402ResourceServer(facilitatorClient) + .register(networkId, new EscrowServerScheme()) + .onAfterSettle( + forwardToArbiter('http://arbiter:3001') + ); +``` + +## Function signature + +```typescript +function forwardToArbiter( + arbiterUrl: string, + options?: ForwardToArbiterOptions +): (context: SettleResultContext) => Promise +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `arbiterUrl` | `string` | Base URL of your arbiter service (e.g. `http://arbiter:3001`) | +| `options` | `ForwardToArbiterOptions` | Optional configuration (see below) | + +### Options + +```typescript +interface ForwardToArbiterOptions { + /** Custom error handler. Defaults to `console.warn`. */ + onError?: (error: unknown) => void; +} +``` + +## Payload shape + +When a commerce settlement succeeds, the hook POSTs the following JSON to `{arbiterUrl}/verify`: + +```typescript +{ + responseBody: string; // UTF-8 encoded response body + transaction: string; // Settlement transaction hash + paymentPayload: { + x402Version: number; + accepted: { + scheme: string; // e.g. "commerce" + network: string; // e.g. "eip155:84532" + // ...other accepted fields + }; + payload: { + paymentInfo: { + operator: string; + payer: string; + receiver: string; + // ...full PaymentInfo + }; + }; + }; +} +``` + + +Arbiters that need `paymentInfo` for `release()` can read it directly from `paymentPayload.payload.paymentInfo` — no extra RPC call needed. + + +## Error handling + +By default, fetch errors are logged with `console.warn`. You can override this with a custom handler: + +```typescript +import { forwardToArbiter } from '@x402r/helpers'; + +const resourceServer = new x402ResourceServer(facilitatorClient) + .register(networkId, new EscrowServerScheme()) + .onAfterSettle( + forwardToArbiter('http://arbiter:3001', { + onError: (err) => sentry.captureException(err), + }) + ); +``` + +Errors are wrapped in an `X402rError` with the arbiter URL and request details for easier debugging. + +## Skipped scenarios + +The hook silently returns without making a request when: + +- The settlement was not successful (`context.result.success === false`) +- The scheme is not `commerce` (e.g. direct or other custom schemes) +- No response body is available in the transport context + +## Address re-exports + +The `@x402r/helpers` package re-exports chain-invariant address constants from `@x402r/core` for convenience: + +```typescript +import { + authCaptureEscrow, + tokenCollector, + protocolFeeConfig, + arbiterRegistry, + receiverRefundCollector, + usdcTvlLimit, + factories, + conditions, + getChainConfig, + supportedChainIds, +} from '@x402r/helpers'; +``` + +These are the same CREATE3 addresses available from `@x402r/core`. You can import from either package depending on which you already have installed. + +## Next steps + + + + Configure escrow options and fee bounds. + + + Build an arbiter that processes forwarded settlements. + + + See working merchant server examples. + + diff --git a/sdk/helpers/refundable.mdx b/sdk/helpers/refundable.mdx index 1d5fbfe..7e67163 100644 --- a/sdk/helpers/refundable.mdx +++ b/sdk/helpers/refundable.mdx @@ -155,6 +155,9 @@ app.get('/api/resource', (req, res) => { ## Next Steps + + Forward settlement data to an arbiter for evaluation. + Deploy a PaymentOperator to use with refundable(). diff --git a/sdk/installation.mdx b/sdk/installation.mdx index 7485080..f78b6f6 100644 --- a/sdk/installation.mdx +++ b/sdk/installation.mdx @@ -37,18 +37,43 @@ Create `publicClient` and `walletClient` using [viem](https://viem.sh/docs/clien ## Contract Addresses -Get the deployed contract addresses from the network config: +All protocol contracts use CREATE3 addresses that are the same on every supported chain. You can import them directly as named constants or look them up from a chain config: -```typescript -import { getNetworkConfig } from '@x402r/core'; + + + ```typescript + import { + authCaptureEscrow, + tokenCollector, + protocolFeeConfig, + arbiterRegistry, + receiverRefundCollector, + usdcTvlLimit, + factories, + conditions, + } from '@x402r/core'; + + console.log(authCaptureEscrow); // 0xBC15… + console.log(factories.paymentOperator); // 0x3Cd5… + console.log(conditions.payer); // 0x808b… + ``` + + + ```typescript + import { getChainConfig } from '@x402r/core'; -const config = getNetworkConfig('eip155:84532'); // Base Sepolia + const config = getChainConfig(84532); // Base Sepolia + + console.log(config.authCaptureEscrow); // Escrow contract + console.log(config.arbiterRegistry); // ArbiterRegistry contract + console.log(config.usdc); // USDC token address (chain-specific) + ``` + + -console.log(config.authCaptureEscrow); // Escrow contract -console.log(config.refundRequest); // RefundRequest contract -console.log(config.arbiterRegistry); // ArbiterRegistry contract -console.log(config.usdc); // USDC token address -``` + +The named constants and `getChainConfig()` return identical addresses. Use named constants when you don't need the chain-specific USDC address. + Network identifiers use the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) format: `eip155:`. For Base Sepolia, use `'eip155:84532'`. For Base Mainnet, use `'eip155:8453'`. diff --git a/sdk/overview.mdx b/sdk/overview.mdx index c0e9ceb..ba1510c 100644 --- a/sdk/overview.mdx +++ b/sdk/overview.mdx @@ -28,7 +28,7 @@ The SDK is organized into packages designed for specific roles in the payment ec SDK for arbiters to resolve disputes and manage refund decisions. - Framework-agnostic helper to mark x402 payment options as refundable with escrow configuration. + Framework-agnostic helpers: `refundable()` for escrow configuration, `forwardToArbiter()` for arbiter integration, and re-exported address constants.