Skip to content

xrpl-x402-middleware

PyPI version Source directory

Install:

pip install xrpl-x402-middleware

Optional upstream x402 interop:

pip install "xrpl-x402-middleware[x402]"

Public API

  • PaymentMiddlewareASGI
  • require_payment(...)
  • XRPLFacilitatorClient

Quickstart With The Example Merchant

Install the package:

pip install xrpl-x402-middleware

Point the example merchant at your facilitator and merchant wallet:

export FACILITATOR_URL=http://127.0.0.1:8000
export FACILITATOR_TOKEN=replace-with-your-facilitator-token
export MERCHANT_XRPL_ADDRESS=rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe
export XRPL_NETWORK=xrpl:1
export PRICE_DROPS=1000

Run the repo example:

uvicorn examples.merchant_fastapi.app:app --reload --port 8010

Then request GET /premium. The first request will return 402 Payment Required, and a buyer that retries with a valid payment will receive 200.

The example route handler reads the verified payment from request.state.x402_payment.

That object includes the resolved invoice_id, so seller code can log or persist the exact request/payment binding that the facilitator accepted.

Example:

from fastapi import FastAPI, Request


@app.get("/premium")
async def premium(request: Request) -> dict[str, str]:
    payment = request.state.x402_payment
    return {
        "payer": payment.payer,
        "invoice_id": payment.invoice_id,
        "tx_hash": payment.tx_hash,
    }

Minimal FastAPI Integration

from fastapi import FastAPI

from xrpl_x402_middleware import PaymentMiddlewareASGI, require_payment

app = FastAPI()
app.add_middleware(
    PaymentMiddlewareASGI,
    route_configs={
        "GET /premium": require_payment(
            facilitator_url="http://127.0.0.1:8000",
            bearer_token="replace-with-your-token",
            pay_to="rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
            network="xrpl:1",
            xrp_drops=1000,
        )
    },
)

Price XRP Or Issued Assets

XRP pricing uses xrp_drops:

require_payment(
    facilitator_url="http://127.0.0.1:8000",
    bearer_token="replace-with-your-token",
    pay_to="rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
    network="xrpl:1",
    xrp_drops=1000,
)

Issued-asset pricing uses amount, asset_code, and asset_issuer:

require_payment(
    facilitator_url="http://127.0.0.1:8000",
    bearer_token="replace-with-your-token",
    pay_to="rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
    network="xrpl:1",
    amount="1.25",
    asset_code="RLUSD",
    asset_issuer="rQhWct2fv4Vc4KRjRgMrxa8xPN9Zx9iLKV",
)

The repo example also supports env-driven issued-asset pricing:

export PRICE_ASSET_CODE=RLUSD
export PRICE_ASSET_ISSUER=rQhWct2fv4Vc4KRjRgMrxa8xPN9Zx9iLKV
export PRICE_ASSET_AMOUNT=1.25

Invoice ID Behavior

The middleware does not generate invoice IDs on its own. It forwards the buyer-provided invoice_id to the facilitator during verify and settle, then exposes the facilitator-resolved value back to the app.

That means:

  • if the buyer signs a payment with XRPL InvoiceID, your route can trust request.state.x402_payment.invoice_id
  • if the buyer omits it, your route still gets a stable fallback value derived by the facilitator
  • if the buyer sends a mismatched payload invoice_id, the facilitator rejects the payment before your route runs

For the full asset flows, continue to the RLUSD guide or USDC guide.

Header And Adapter Details

The middleware emits a Base64-encoded PAYMENT-REQUIRED challenge header on 402, accepts a Base64-encoded PAYMENT-SIGNATURE header on retry, and adds a Base64-encoded PAYMENT-RESPONSE header to successful responses.

See Header Contract for the exact shapes, Replay And Freshness for the facilitator-side replay rules that sit behind settlement, and Coinbase x402 Adapters if you want to plug the middleware into the upstream Python x402 server abstraction.