Home›Documentation›x402 Payments
x402 v2 · XRPL

Charge for your own API

Accept XRP or RLUSD for your APIs, data, and services. No XRPay account, merchant signup, API key, or purchase from an XRPay merchant is required.

Choose the integration you need

Standalone payments: use x402Middleware to charge for your own endpoints, or X402Client to pay compatible exact XRPL x402 endpoints. The public facilitator at https://api.xrpay.it/api/x402 needs no account or API key. Payments go directly to your chosen XRPL recipient.

Hosted digital commerce: sellers need an XRPay merchant account to list products and use managed orders, receipts, and private file delivery. Buyers do not need an XRPay account. Read the optional commerce guide.

The SDK is MIT licensed. Payments require XRPL wallets and network fees. This integration supports the exact XRPL x402 scheme for XRP and RLUSD.

Run a paid API in minutes

Use Node.js 20 or later. In an empty project, install the SDK and Express:

BASH
npm init -y
npm install @xrpay/x402 express

Save this complete server as paid-api.mjs. It charges 0.001 Testnet XRP per request:

JAVASCRIPT
// npm install @xrpay/x402 express
// Uses XRP Testnet. No XRPay account, API key, or product listing is required.
import express from "express";
import { x402Middleware } from "@xrpay/x402";

const payTo = process.env.XRPL_RECIPIENT_ADDRESS;
const challengeSecret = process.env.X402_CHALLENGE_SECRET;
if (!payTo || !challengeSecret) {
  throw new Error("Set XRPL_RECIPIENT_ADDRESS and X402_CHALLENGE_SECRET (at least 32 characters).");
}

const app = express();
app.get("/api/hello", x402Middleware({
  payTo,
  amount: "0.001", // XRP per request, paid directly to your wallet.
  currency: "XRP",
  testnet: true,
  challengeSecret,
  facilitatorUrl: "https://api.xrpay.it/api/x402", // Public; no API key.
}), (_req, res) => res.json({ message: "Hello from your paid API!" }));

const port = Number(process.env.PORT ?? 3000);
app.listen(port, "127.0.0.1", () => {
  console.log(`Paid API: http://127.0.0.1:${port}/api/hello (0.001 Testnet XRP)`);
});

Generate a receiving wallet with the XRPL Testnet faucet, selecting Testnet. The server needs its classic address, not its seed. Configure the address and a locally generated challenge secret:

BASH
export XRPL_RECIPIENT_ADDRESS="rYOUR_FUNDED_TESTNET_ADDRESS"
export X402_CHALLENGE_SECRET="$(openssl rand -hex 32)"
node paid-api.mjs

X402_CHALLENGE_SECRET authenticates the payment challenges your API issues. It is not an XRPay API key. Keep it private, at least 32 characters long, and stable across processes and restarts. The same example is included at node_modules/@xrpay/x402/examples/paid-api.mjs.

In another terminal, request the endpoint without payment:

BASH
curl -i http://127.0.0.1:3000/api/hello

You receive HTTP 402 with a PAYMENT-REQUIRED header and the exact terms. A paying agent signs those terms and retries with PAYMENT-SIGNATURE. After validated settlement, your API returns HTTP 200, its JSON result, and a PAYMENT-RESPONSE receipt.

You host the API and control its response. No XRPay merchant or product record is created. Standalone payments do not create hosted storefront orders or file-delivery entitlements.

Let an agent pay your API

Install xrpl in the agent project. Use a separate funded Testnet payer wallet, keep its seed in AGENT_TESTNET_SEED, and set XRPL_RECIPIENT_ADDRESS to your API's receiving address.

JAVASCRIPT
import { Client, Wallet } from 'xrpl';
import { X402Client } from '@xrpay/x402/client';

const ledgerClient = new Client('wss://s.altnet.rippletest.net:51233');
await ledgerClient.connect();
try {
  const agent = new X402Client({
    wallet: Wallet.fromSeed(process.env.AGENT_TESTNET_SEED),
    ledgerClient,
    network: 'xrpl:1', currency: 'XRP',
    maxAmount: '0.001', maxTotalAmount: '0.001',
    allowedRecipients: [process.env.XRPL_RECIPIENT_ADDRESS],
  });
  const result = await agent.fetch('http://127.0.0.1:3000/api/hello');
  if (!result.ok) throw new Error('Resource request failed');
  console.log(await result.json());
} finally {
  await ledgerClient.disconnect();
}

Signing happens locally. The facilitator receives a signed transaction, never the wallet seed. Payment and total authorization limits are required. If the payment outcome is uncertain, reconcile the transaction and API result before authorizing another payment.

Deploy your own service

The example binds to localhost and uses Testnet. Serve the deployed API over HTTPS and set the middleware's resource to its public endpoint URL when needed behind a proxy. Keep your challenge secret stable. Switch to testnet: false with Mainnet wallets deliberately for real XRP payments.

For RLUSD, set currency: 'RLUSD' and the exact issuer address on the chosen network. Payer and recipient need matching trust lines. You can also supply your own XrplFacilitator instance through the middleware's facilitator option.

Public facilitator and optional merchant routes

GET /api/x402/supported returns supported mechanisms. POST /api/x402/verify checks a signed authorization without submitting it. POST /api/x402/settle deduplicates, submits, and waits for validated delivery. These public routes need no XRPay credentials.

JAVASCRIPT
{ x402Version: 2, paymentPayload, paymentRequirements }

The optional /api/v1/x402 merchant routes require an XRPay API key and bind terms to the configured merchant wallet and network. Existing merchants can use server-side facilitatorHeaders for that integration.

A successful HTTP response is not always a successful payment

When calling settlement directly, check the response's success field before granting access. HTTP 200 alone does not indicate payment. The middleware in this example performs that check for you.