Skip to content

pull in latest upstream#2

Open
TtheBC01 wants to merge 779 commits into
1Shot-API:mainfrom
x402-foundation:main
Open

pull in latest upstream#2
TtheBC01 wants to merge 779 commits into
1Shot-API:mainfrom
x402-foundation:main

Conversation

@TtheBC01

Copy link
Copy Markdown

Description

Tests

Checklist

  • I have formatted and linted my code
  • All new and existing tests pass
  • My commits are signed (required for merge) -- you may need to rebase if you initially pushed unsigned commits
  • I added a changelog fragment for user-facing changes (docs-only changes can skip)

fabianbormann and others added 18 commits April 22, 2026 19:17
* Create scheme_exact_cardano.md

* Update scheme_exact_cardano.md

* Update and rename scheme_exact_cardano.md to scheme_exact_cardano-masumi.md

* Added Address-To-Address on Cardano Specs

* Update scheme_exact_cardano.md

* feat: add optional session management to final response

* chore: add instructions for script flavor

* feat: update cardano schema according to x402 v2 migration guide

* chore: update mermaid diagram for x402 v2

* chore: reapply v2 changes that might got lost in the merging process

* chore: address x402 schema feedback around nonce, legacy v1 fields, flavor and session tokens

* chore: add warning for mempool status, rewrite facilitator verification rules

---------

Co-authored-by: Patrick Tobler <129864078+PatrickTobler@users.noreply.github.com>
Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
* feat(site): make x402.org agent-ready for isitagentready.com

Add all 13 isitagentready.com checks:
- robots.txt with AI crawler rules and Content Signals
- sitemap.xml via Next.js metadata API
- Link response headers (api-catalog, service-doc, payment-required)
- Markdown content negotiation (Accept: text/markdown)
- API catalog at /.well-known/api-catalog (RFC 9727)
- OAuth/OIDC discovery at /.well-known/openid-configuration
- OAuth Protected Resource at /.well-known/oauth-protected-resource
- MCP Server Card at /.well-known/mcp/server-card.json
- Agent Skills index at /.well-known/agent-skills/index.json
- WebMCP tool registration (get-x402-info, navigate-x402)
- x402 demo endpoint returning HTTP 402 with PAYMENT-REQUIRED header

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(site): add JSDoc descriptions to agent-ready route handlers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(site): address PR review feedback

- Remove hollow OAuth stubs (openid-configuration, oauth-protected-resource)
  that declared endpoints which don't exist
- Remove redundant /api/x402/demo route, point to /protected instead
- Remove non-existent /mcp endpoint from MCP server card
- Fix sitemap to use static dates instead of new Date()
- Fix SKILL.md to use CAIP-2 network identifiers (eip155:8453)
- Change WebMCP script strategy from beforeInteractive to afterInteractive

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ctly (#2109)

* feat(ts): allow ResourceServerExtension to assign x402ResourceServer hooks directly

* feat: improve enforcement/security of what hooks can and cannot modify

* feat: pr review

* feat: try/catching hooks

* feat: pr feedback
* Update docs/extensions/overview.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/extensions/overview.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/extensions/overview.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/advanced-concepts/lifecycle-hooks.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

---------

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
* feat(hedera): add @x402/hedera package with exact payment scheme

  - New @x402/hedera package implementing the x402 v2 exact scheme
  - Uses @hiero-ledger/sdk for signing and submission
  - Onchain preflight transfer check before settlement
  - Settlement receipt validation (USDC + HBAR coverage)
  - HEDERA_ACCOUNT_ID / HEDERA_PRIVATE_KEY / HEDERA_NETWORK env vars
  - Integrated with advanced clients (axios, fetch), facilitators, and HTTP adapters (hono, fastify, next)
  - E2E tests for Hono, Next.js, and Fastify servers
  - Testnet faucet docs and token association notes

Signed-off-by: Piotr Swierzy <piotr.swierzy@blockydevs.com>

* refactor: use convertToAtomicAmount function from @x402/core/utils

Signed-off-by: Piotr Swierzy <piotr.swierzy@blockydevs.com>

---------

Signed-off-by: Piotr Swierzy <piotr.swierzy@blockydevs.com>
…ion (#1902)

* feat: add auth-hints extension to streamline scheme-level authentication

- Described general auth scheme for x402, mainly on HTTP transports
- Initial support for two auth method types: oauth2 (Bearer/DPoP) and sign-in-with-x
- Defined scheme and acceptIndex hints.

* feat: settle on acceptIndexes, schemeAuth->authRequirements, remove references to scheme-based hints
…DC 6 (#1926)

* getDisplayAmount でトークンの decimals を動的取得し USDC ハードコードを修正

* Amount 表示で末尾の0を削除し動的 decimals を適用

* decimals を [0, 100] にクランプし toFixed の RangeError を防ぐ

* x402ResourceServer.ts の prettier フォーマットを修正
* checkIfBazaarNeeded を @x402/core に統合し、4パッケージの重複を排除

* 重複した checkIfBazaarNeeded 定義を削除

* 各HTTPパッケージの vi.mock に checkIfBazaarNeeded を追加
* fix(paywall): use dynamic token decimals instead of hardcoding 6

The EVM paywall displays incorrect amounts for any token that does not
use 6 decimal places. An 18-decimal token priced at 0.001 raw units
renders as $1,000,000,000 because the code divides by 10^6 everywhere.

Root cause: the paywall was built assuming USDC (6 decimals) is the
only payment token. This assumption appears in three places:

1. Server-side HTML generation (index.ts) divides the raw amount by
   a hardcoded 10^6 to produce the display value injected into the
   template.

2. Client-side balance display (EvmPaywall.tsx) calls formatUnits
   with a hardcoded 6 and reads the token address from a 3-chain
   USDC lookup table that returns 0 for any other chain.

3. The balance utility (utils.ts) only knows USDC addresses on
   Ethereum, Base, and Base Sepolia — every other chain silently
   shows a zero balance.

Fix:

Server-side (index.ts): look up the token's decimal precision from a
known-decimals map aligned with DEFAULT_STABLECOINS. Only non-6-decimal
chains need entries; everything else falls back to 6. This keeps the
server-side path simple with no RPC calls.

Client-side (EvmPaywall.tsx): read the token contract address from
the payment requirement's `asset` field (which the server already
populates) and query the standard ERC-20 decimals() function on-chain.
This works for any compliant token without maintaining a lookup table.

utils.ts: replace the USDC-specific getUSDCBalance (internal helper,
not part of the public API) with generic getTokenBalance and
getTokenDecimals functions that accept a token address parameter.
The function signature changes from 2 args to 3, but no external
consumer can import it — it is only used by the bundled React
component inside the paywall HTML template.

**Breaking change note:** the internal helper getUSDCBalance in utils.ts
is removed and replaced by getTokenBalance (different signature: takes
an explicit token address instead of looking it up by chain ID). This
function is not exported from the package's public API — it is only
consumed by the bundled EvmPaywall React component inside the paywall
HTML template. No server operator code calls it directly. However,
anyone who has forked the paywall and imports from the file path
(bypassing package exports) would need to update their import and
call site.

Regenerated Go, Python, and TS paywall templates.

**AI disclosure:** This PR was prepared with the assistance of a coding agent and
reviewed by Ryan R. Fox (an actual human) before submission.

* style: ruff format paywall templates

* fix(paywall): use DEFAULT_STABLECOINS lookup + BigInt formatUnits

Removes the parallel KNOWN_DECIMALS map in @x402/paywall/src/evm/index.ts
that mirrored DEFAULT_STABLECOINS from @x402/evm. The paywall now resolves
token decimals through the same registry that scheme implementations and
@x402/core's inline scheme dispatch already read from.

evmPaywall.generateHtml changes:
  * getDefaultTokenDecimals helper looks up requirement.network directly in
    @x402/evm's DEFAULT_STABLECOINS, with a 6 (USDC default) fallback when
    the network is unknown.
  * Atomic-to-display conversion uses Number(formatUnits(BigInt(amount),
    decimals)) instead of parseFloat(amount) / 10**decimals. parseFloat
    silently rounds sub-cent precision on real 18-decimal amounts before
    the divide; BigInt + formatUnits preserves precision through the
    conversion. BigInt also rejects non-integer atomic strings, which
    matches the spec's atomic-integer contract.

@x402/paywall package.json: @x402/evm moves from devDependencies to
dependencies because the new server-side import of DEFAULT_STABLECOINS is
on the runtime path emitted into dist/.

@x402/evm now publicly re-exports DEFAULT_STABLECOINS from
./shared/defaultAssets so consumers can read the canonical default-asset
registry directly. Both @x402/evm and @x402/paywall get a minor bump
for the additive public API.

Adds 6 unit tests:
  * getDefaultTokenDecimals returns 18 for Mezo Testnet (registry path)
  * getDefaultTokenDecimals returns 6 for Base mainnet, asserted alongside
    DEFAULT_STABLECOINS so an empty registry would fail the test
  * getDefaultTokenDecimals falls back to 6 for unknown networks
  * evmPaywall.generateHtml renders 1e15-atomic Mezo mUSD as
    'amount: 0.001,' end-to-end (regression test for the /1e6 bug)
  * evmPaywall.generateHtml renders 1e6-atomic Base USDC as 'amount: 1,'
  * evmPaywall.generateHtml throws on a non-integer atomic ('1.5'),
    pinning the BigInt strictness so a future revert to parseFloat fails

Closes #1979.

* refactor(paywall): generate evm decimals at build time, drop @x402/evm runtime dep

Addresses @phdargen's review: rather than promoting @x402/evm to a
runtime dependency of @x402/paywall, keep it a devDependency and
emit a codegen'd `src/evm/gen/decimals.ts` that mirrors just the
per-network `decimals` field of `DEFAULT_STABLECOINS`.

Pattern-consistent with the existing `src/evm/gen/template.ts` codegen
(HTML bundle) — same build step (`src/evm/build.ts`), same "AUTO-GENERATED
DO NOT EDIT" banner, same commit-the-generated-file workflow. The CI
drift check queued in #2054 (`check_paywall_template.yml`) will pick up
`decimals.ts` under the existing `typescript/packages/http/paywall/**`
trigger glob once that PR merges; in the meantime an in-process drift
invariant test in `network-handlers.test.ts` asserts `NETWORK_DECIMALS`
matches `DEFAULT_STABLECOINS` key-for-key.

Runtime behaviour is unchanged. `getDefaultTokenDecimals` now reads the
generated `NETWORK_DECIMALS` map instead of reading `DEFAULT_STABLECOINS`
at runtime; the same Mezo-18, Base-6, unknown→6 cases still hold, and
the existing 6 regression tests in `network-handlers.test.ts` pass
without modification. A seventh test pins the drift invariant described
above.

package.json: `@x402/evm` moved from `dependencies` to `devDependencies`.
Runtime module graph for `@x402/paywall` no longer pulls `@x402/evm`;
the only surviving `@x402/evm` references in paywall src are:
  * `browserAdapter.ts` — `import type { ClientEvmSigner }` (type-only,
    erased at build time)
  * `EvmPaywall.tsx` — `ExactEvmScheme` (bundled into `gen/template.ts`
    by esbuild, not part of the server-side runtime path)
  * `build.ts` and `network-handlers.test.ts` — build-time / test-time
    consumers; devDep is available in both.

Both the root `typescript/pnpm-lock.yaml` and `examples/typescript/pnpm-lock.yaml`
are updated to reflect the dep move — both workspaces reference the
paywall package manifest via `workspace:~`.
- Add /protected to sitemap so scanners can discover the 402 endpoint
- Add X-X402-Supported header on homepage to signal x402 support

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The committed @x402/paywall bundle on main was last generated against an
older viem than the lockfile currently pins. pnpm install --frozen-lockfile
resolves viem 2.47.12 (via #2013), but the bundled src/evm/gen/template.ts
(and its Python + Go siblings) were built against an earlier resolution
that pre-dated chain definitions for Mezo, MegaETH, Stable, Radius (each
with a testnet variant), and 33 others. EvmPaywall.tsx throws
"Unsupported chain ID" at init for any eip155:<id> whose chain isn't in
the bundled viem/chains.

This PR:
  1. Regenerates all nine auto-generated template files (TypeScript,
     Python, and Go for EVM/SVM/AVM) against the current lockfile. No
     source changes on any runtime path.
  2. Teaches build:paywall to emit output matching the repo's formatters
     (prettier / ruff), so generated files stay clean under pnpm run
     format + uvx ruff format.
  3. Adds a PR-time drift check
     (.github/workflows/check_paywall_template.yml) so a future lockfile
     or @x402/evm change that invalidates the bundle fails loudly
     instead of shipping silently.

Scope is limited to @x402/paywall because it is the only package in this
repo that bakes viem chain definitions into a built artifact.

Verified on a fork-internal test PR running on ubuntu-latest
(Node 24, pnpm 10.7.0): check_format, check_lint, check_go, check_python,
check_package_lock, and the new check_paywall_template drift check all
pass. Additional local evidence:

  - Chain-presence in regenerated bundle: Mezo 31612/31611, MegaETH
    4326/6343, Stable 988/2201, Radius 723487/72344, Base/Arbitrum/
    Polygon all present (mainnet/testnet ordering).
  - Cross-platform determinism: a clean-slate rebuild on macOS produces
    byte-identical output to ubuntu-latest CI.
  - Tarball install into a throwaway consumer: all expected chain IDs
    present in the published dist/esm/evm/index.js.
  - Browser smoke test (Chrome, served from a local HTTP server):
    EvmPaywall mounts without Unsupported-chain throw for
    eip155:31611 (Mezo Testnet) and eip155:723487 (Radius); DOM
    renders the expected token/chain labels.

Fixes #1971.
…2152)

The isitagentready.com scanner cannot detect x402 support through
the existing /protected page (tried sitemap inclusion, Link headers,
X-X402-Supported header). Add a dedicated API endpoint at /api/x402/demo
that returns a clean HTTP 402 with PAYMENT-REQUIRED header using the
same payment config as /protected.
* Add codeowners per-network

* Update CODEOWNERS
…h, and Python alignment (#2094)

* update withBazaar

* Fix bazaar MCP discovery parity across TS/Python/Go; tighten e2e validation; update docs/examples.

* python format

* remove formatting-only changes

* remove formatting-only changes

* reinstate old comments

* clean up response shape

* revert pagination to object instead of top level fields

* address review comments
phdargen and others added 30 commits July 3, 2026 10:47
* fix #2701

* pr feedback

* pr feedback 2
* spec(xrpl): add exact scheme

* spec(xrpl): address exact scheme review

* spec(xrpl): add assetTransferMethod to select sequence or ticketSequence

Per review feedback, account sequencing is now negotiable via
extra.assetTransferMethod ("sequence" | "ticketSequence"), mirroring the
EVM exact scheme pattern. Documents the tradeoffs (ticket reserve and
TicketCreate preflight vs. the sequence settlement race) and splits the
facilitator sequencing checks per method.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Update docs/schemes/exact.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/schemes/exact.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

---------

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
* mcp interop fixes

* add changeset
)

facilitator.x402.org has no DNS record (NXDOMAIN), so every quickstart
that copies these snippets fails at the first request. The rest of the
repo (99 references) already uses https://x402.org/facilitator, which
is live and serving /supported.


Claude-Session: https://claude.ai/code/session_018p8jtKC5g7uUMrjGCWksqu

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… fresh clone (#2791)

* examples/go: add missing .env-example for echo server

* examples/go: simplify payment response handling in http client

The call site duplicated the v2/v1 header lookup that extractPaymentResponse already performs, and parse errors were silently dropped. Use the helper directly and surface parse failures.

* examples/go: tidy modules in echo server and http client examples

SDK dependency bumps were not propagated to the example modules, so neither example builds from a fresh clone (missing go.sum entries). Regenerated with go mod tidy under go1.24.1, the toolchain pinned in go.mod.
* deprecate website

* small fixes

* bring back protected

* fix fmt
* add ts/go readmes

* core + EVM mechanism plumbing

* add extension

* add examples

* fix siwx
* Update docs/sdk-features.md

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/extensions/overview.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/extensions/builder-code.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/extensions/builder-code.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/extensions/builder-code.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/extensions/builder-code.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

---------

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
…2810)

* fix(python/flask): use sync server in payment_middleware_from_config

The Flask factory instantiated the async x402ResourceServer and handed it
to PaymentMiddleware, which builds x402HTTPResourceServerSync. The sync
HTTP server rejects a server whose verify_payment is a coroutine, so the
factory raised TypeError('requires a sync server') at construction — it
failed 100% of the time and was unusable.

Switch to x402ResourceServerSync, mirroring how the FastAPI factory uses
the async x402ResourceServer for its async server. Regression test added.

* chore(python): changelog fragment for #2810
Add USDC on Igra Network (chain ID 38833) as a default stablecoin
across TypeScript, Go, and Python SDKs. Uses Permit2 transfer method
(no EIP-3009 or EIP-2612 support on the token contract).

Token: 0xA5b8BF902b2844dA17d4506cc827F7F1681735E7 (USDC, 6 decimals)
* docs(specs): add exact scheme spec for Casper Network

Implements PR 1 ("Specification Only") of the "Adding a New Chain
Family" workflow in CONTRIBUTING.md.

The scheme is built on CEP-3009, Casper's CEP-18 + EIP-712 adaptation
of EIP-3009: the payer signs an off-chain EIP-712 authorization for
a single fixed-amount CEP-18 transfer; the facilitator relays it on
chain via transfer_with_authorization and pays the gas. The token
contract enforces signature validity, strict validity window, and
nonce uniqueness. speculative_exec is referenced as an optional
verification step since it is not enabled on most Casper nodes.

No SDK changes; reference implementation will follow in PR 2.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(specs): address review feedback on Casper exact scheme spec

Fix validBefore/maxTimeoutSeconds bound direction, require canonical
low-s secp256k1 signatures, make supported CAIP-2 networks explicit,
rename PAYMENT-RESPONSE section to SettlementResponse, and update the
stale Casper JSON-RPC docs link.

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix flask 3xx response bug

* changeset
#2622)

* fix(python/fastapi): log + return 402 with PAYMENT-RESPONSE on unexpected settlement errors

* style(python/fastapi): satisfy ruff lint

- move logger below the extension imports (bazaar imports were E402)
- drop unused 'as error' on the settlement catch-all (F841)
- ruff format (one-line logger.exception, sorted test imports)
* docs(facilitators): add Solvador to facilitators list

* docs(facilitators:solvador): add NEAR support
…t error (#2721)

* fix(python/flask): return 500 (and log) on unexpected settlement error

* chore(changelog): name flask settlement fragment by PR number

* fix(python/flask): surface unexpected settlement error as 402 + PAYMENT-RESPONSE (mirror #2622)

* fix(python/flask): use canonical unexpected_settle_error reason (parity with #2622)

* style(python): apply ruff format to test_flask.py

* chore(python): restore uv.lock to main (no dependency changes in this PR)

* chore(python): leave uv.lock untouched (no dependency changes)
* verify aptos sign

* verify aptos sign
* feat(xrpl): add exact scheme TypeScript reference implementation

Implements specs/schemes/exact/scheme_exact_xrpl.md as @x402/xrpl with
client, resource-server, and facilitator schemes for payer-signed XRPL
Payment transactions:

- sequence and ticketSequence asset transfer methods with the spec's
  resolution order, per-method verification (current account Sequence,
  ticket availability), and a createTickets utility
- extra.areFeesSponsored=false advertised via facilitator getExtra and
  enforced in envelope verification
- offline signature validation, NetworkID binding, InvoiceID invoice
  binding (Memos rejected), Delegate/Paths/DeliverMin/partial-payment
  rejection, fee caps, LastLedgerSequence expiry policy, and simulation
  (signature fields stripped: the XRPL simulate API only accepts
  unsigned transactions)
- settlement re-verifies, submits, and requires validated tesSUCCESS
- XRP drops and issued-currency decimal value amounts compared with
  exact decimal arithmetic (no extra.decimals field)

The server scheme keeps enhancePaymentRequirements deterministic
(requirements are rebuilt per request), so invoice binding activates
only when the resource configuration provides extra.invoiceId.

Adds unit and integration tests, publish workflows, release-script and
changeset fixed-group wiring, and the root test:integration filter.

* feat(e2e): wire XRPL into the e2e harness and participants

Adds the xrpl protocol family to the harness networks/types/env
plumbing, registers @x402/xrpl in the axios and fetch clients, the
express/fastify/hono/next servers (GET /exact/xrpl routes priced in
XRP drops), the TypeScript facilitator (keyless: the payer signs and
pays XRPL fees), and the mock facilitator supported response.

Verified locally against XRPL Testnet (xrpl:1) with funded faucet
accounts: pnpm test --testnet --families=xrpl --versions=2 passes all
10 scenarios (axios/fetch x express/fastify/hono/next incl. withX402).

* feat(examples): add XRPL to the advanced all_networks examples

Registers @x402/xrpl in the client, server, and facilitator
all_networks examples (alphabetical by network prefix), with
XRPL_SEED/XRPL_ADDRESS/XRPL_NETWORK/XRPL_WS_URL configuration.

* docs(examples): add XRPL setup to the advanced example READMEs

Document the XRPL environment variables, testnet faucet steps, and
reserve/trust line requirements in the advanced client, server, and
facilitator example READMEs, and add the matching .env-local entries.
The facilitator README also notes the keyless XRPL scheme (enabled via
XRPL_NETWORK), its /supported kind with areFeesSponsored=false, and the
xrpl:0 / xrpl:1 network identifiers.

* fix(typescript): regenerate pnpm-lock.yaml after rebase onto main

The website deprecation (#2794) removed the jiti-suffixed peer-dependency
variants that the @x402/xrpl importer entries resolved to. Re-resolve the
lockfile against the current workspace so frozen installs succeed.

* fix(xrpl): bind signatures to account authorization and harden verification

- Verify the embedded SigningPubKey is authorized for Account (master key
  pair unless disabled, or the configured regular key) via a validated
  account_info lookup. Offline signature validation only proves the blob
  was signed by its embedded key, and simulation strips signature fields,
  so an unauthorized signer previously passed /verify and failed at
  /settle with tefBAD_AUTH or tefMASTER_DISABLED.
- Reject single-signed blobs that carry a Signers array; the single
  signature does not cover Signers, so hybrid blobs still pass
  verifySignature.
- Fail closed on malformed extra.destinationTag in the facilitator,
  resource server, and client instead of silently skipping the tx-level
  binding.
- Correct the changeset wording: the server scheme passes through
  configured invoice ids, it does not generate them.
- Cover the new checks and previously untested spec rules in unit tests:
  envelope mismatches, NetworkID branches, XRP/IOU amount and SendMax
  variants, LastLedgerSequence bounds, InvoiceID mismatch, sequence 0,
  settle outcomes, and client-side NetworkID handling.

* test(xrpl): add env-gated live settlement integration test

Mirror the NEAR exact-near.live.test.ts pattern: when XRPL_PAYER_SEED and
XRPL_LIVE_PAYTO (or the e2e CLIENT_XRPL_SEED / SERVER_XRPL_ADDRESS names)
are set, drive the reference client, resource server, and keyless
facilitator end to end against a real network and assert the payTo XRP
balance increases by exactly the requested drops. Skipped when the
accounts are not configured, so it stays inert in CI.

Verified against XRPL Testnet: settled tx
6AF0A4F630F2B43CA44544D5246B58260A01860A3CCBB331625229CD78696791.

* fix(xrpl): mirror rippled signer-key ordering and require canonical signing keys

- Authorize the configured regular key before the master-disabled check,
  matching rippled's fixMasterKeyAsRegularKey ordering so legacy accounts
  whose RegularKey equals their own address are not wrongly rejected.
- Reject non-canonical SigningPubKey encodings (only 33-byte compressed
  secp256k1 or ed25519 keys) before signature validation; rippled refuses
  such keys at preflight, so accepting them would let verification pass
  for a transaction that can never settle.

* feat(xrpl): create tickets automatically for ticketSequence payments

When a ticketSequence payment finds no available ticket, the client now
creates tickets through the ClientXrplSigner abstraction instead of
throwing and requiring a manual createTickets() call. The new
ticketCreateCount option controls the policy: it defaults to 1, allows
up to 250 tickets per TicketCreate, and 0 restores the previous
fail-fast behavior for pre-provisioned setups. Available tickets are
still reused first and skip the creation transaction entirely.

- Widen ClientXrplSigner.sign from Payment to SubmittableTransaction so
  the same signer can sign TicketCreate; createTickets accepts the
  signer abstraction instead of an xrpl.js Wallet.
- Cover the default, configured, and disabled policies plus invalid
  counts in unit tests.

* fix(xrpl): return a stable reason code for unexpected verification failures

The verify() catch-all embedded the raw error message in the
invalidReason string (invalid_exact_xrpl_payload_malformed: <message>),
so integrators could not match on a stable code. Return
invalid_exact_xrpl_facilitator_error and carry the raw diagnostic text
separately in invalidMessage. Settlement submission errors keep the
existing transaction_failed: <diagnostic> reason.

* fix(xrpl): fail closed on missing or invalid IOU issuers

Issued-currency requirements and accepted payloads must both carry a
valid classic issuer address before the issuer comparison runs. Two
absent issuers previously compared equal and passed envelope
validation, and a single absent issuer surfaced as a confusing
mismatch. Missing or malformed issuers now return
invalid_exact_xrpl_iou_issuer_missing.

* fix(xrpl): set the custom-network NetworkID when building payments

The client validated that prepared transactions carried the correct
NetworkID on custom networks (id > 1024) but relied on the preparer to
add the field. Insert it while building the payment so the default and
custom preparers both start from a correct transaction; validation
still rejects preparers that remove or replace it.

* refactor(xrpl): reuse parseMoneyString from @x402/core

Replace the server scheme's hand-rolled money-string parsing with the
shared parseMoneyString helper used by the other mechanism packages,
aligning string-price edge-case handling with the rest of the SDK.

* test(e2e): cover both XRPL asset-transfer methods on every server

Replace the single /exact/xrpl route with explicit sequence and
ticketSequence endpoints on Express, Fastify, Hono, and Next (both the
proxy middleware and withX402), pinning extra.assetTransferMethod and
the issuer for issued-currency pricing in each route config. Teach the
harness that asset transfer methods are not EVM-only metadata: xrpl
endpoints resolve their declared method and default to sequence, and
the server proxy forwards the configured issuer.

* test(e2e): add a self-managed XRPL Testnet IOU fixture

pnpm xrpl:iou:setup creates or reuses dedicated Testnet issuer, payer,
and payee accounts, funds missing ones through the faucet, enables
DefaultRipple on the issuer, establishes the trust lines, and issues
test USD to the payer, replenishing it on re-runs. Credentials are
persisted only in the git-ignored e2e/.env with 0600 permissions,
seeds are never logged, and the script refuses to run unless the
connected ledger reports Testnet network id 1. Mask seed values
alongside private keys in the harness debug output.

* fix(xrpl): reject duplicate settlements with a settlement cache

Concurrent /settle calls carrying the same signed blob each passed
verification, and because XRPL submission is idempotent on the
transaction hash, submitAndWait resolved the same validated tesSUCCESS
for every caller. The facilitator therefore returned success to all of
them, letting a client access the resource N times while paying once.

Add an in-memory SettlementCache (mirroring the SVM one) keyed on the
signed transaction hash. settle() checks and inserts the key
synchronously between re-verification and submission, so concurrent
duplicates are serialized: the first proceeds, the rest return a
duplicate_settlement error.

Unlike Solana's protocol-fixed blockhash lifetime, an XRPL transaction
stays landable until its LastLedgerSequence, which the scheme derives
from maxTimeoutSeconds. Each entry's retention is sized to that window
(plus a margin) rather than a flat interval, so an entry cannot be
evicted while a slow-to-validate duplicate could still pass
re-verification. Document the mitigation as REQUIRED in the spec,
including the per-process scope and the shared-store requirement for
horizontally scaled facilitators.
Follow-up to #2801, implementing the maintainer suggestion to add XRPL
as an optional network on the x402.org testnet facilitator.

Unlike the other optional networks, XRPL is gated on an explicit
FACILITATOR_XRPL_ENABLED=true flag rather than a private key: in the
XRPL exact scheme the payer signs the transaction and pays its fee, so
the facilitator needs no key or funds — it only verifies and submits
the pre-signed blob. An optional FACILITATOR_XRPL_TESTNET_WS_URL
override is supported for deployments that prefer their own node over
the default public testnet endpoint, mirroring the e2e facilitator's
XRPL_WS_URL option.
* Update docs/sdk-features.md

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/sdk-features.md

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/schemes/exact.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

---------

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
…2856)

* docs: add XRPL to buyer quickstart, network lists, and facilitators

Complements #2854 (XRPL Setup in exact.mdx + sdk-features tables) with
the remaining pages, mirroring how NEAR was documented in #2771:

- quickstart-for-buyers: install line, XRPL signer section, multi-scheme
  registration example, and the summary package list
- schemes/overview: add XRPL to the network implementations sentence
- network-and-token-support: add the xrpl:<networkId> CAIP-2 identifier
- dev-tools/facilitators: list the T54 XRPL facilitator (mainnet and
  testnet), which previously appeared on the ecosystem page removed in
  #2794

* docs: add XRPL to the token support table

The Token Support ecosystem table and its intro sentence stopped at
Concordium; add the XRPL row (XRP or any issued currency, transferred
via a payer-signed Payment transaction) alongside the CAIP-2 identifier
added in the previous commit.

* docs: add XRPL to the network support summary bullet

The Summary list at the bottom of network-and-token-support.mdx parallels
the intro sentence updated in the previous commit; bring it in line so
both lists end with the same network set.
* Update docs/core-concepts/network-and-token-support.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/core-concepts/network-and-token-support.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

---------

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
* SIWX configured-origin fix

* py+go

* updated docs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.