x402 (Beta)
x402 is an HTTP payment standard built into the protocol itself. A client hits your API without paying, gets an HTTP 402 with payment details, pays on-chain, and retries. No accounts, no subscriptions, no API keys for callers — just an XRPL wallet.
DropPay acts as the x402 facilitator: it returns payment details, verifies on-chain transactions, and records every call in your dashboard with full webhook support.
x402 vs checkout API payments
x402 is for agent-native, zero-setup access — the agent discovers payment requirements from the 402 response and pays without any prior arrangement. The checkout API payments flow is different: your server creates a payment request and hands the details to the caller explicitly. Use x402 when you want any XRPL-capable agent to pay your endpoint without being pre-configured for your system.
Beta feature
x402 is in beta. Contact us to get it enabled on your account. Pricing is set in your middleware code.
How it works
Client → GET /your-api/endpoint (no payment)
Your server → POST /x402/payment-required (get 402 details from DropPay)
Your server → HTTP 402 + X-Payment-Required header
Client → Pays XRP on XRPL
Client → GET /your-api/endpoint (retries with X-Payment: <tx_hash>)
Your server → POST /x402/verify-payment (verify tx with DropPay)
Your server → 200 OK + response (access granted)
DropPay → Records call, fires webhook
The payment proof (X-Payment) is the raw XRPL transaction hash. Valid for 5 minutes after the transaction confirms.
Setup
1. Enable x402 on your account
Contact us to get x402 enabled during the beta. Your XRPL wallet address must be configured in the dashboard first.
Your merchant handle is shown at the top of the dashboard - you'll use it in the middleware code.
2. Add middleware to your API
import requests
DROPPAY = "https://droppayxrp.com"
HANDLE = "your-handle"
PRICE = 0.10 # USD per request
def require_payment(f):
from functools import wraps
from flask import request, jsonify
@wraps(f)
def decorated(*args, **kwargs):
proof = request.headers.get("X-Payment")
if not proof:
r = requests.post(f"{DROPPAY}/x402/payment-required",
json={"handle": HANDLE, "price_usd": PRICE,
"resource": request.path})
details = r.json()
resp = jsonify(details)
resp.headers["X-Payment-Required"] = r.text
return resp, 402
r = requests.post(f"{DROPPAY}/x402/verify-payment",
json={"handle": HANDLE, "tx_hash": proof,
"price_usd": PRICE, "resource": request.path})
result = r.json()
if not result.get("valid"):
return jsonify({"error": result.get("reason", "Payment invalid")}), 402
return f(*args, **kwargs)
return decorated
# Usage:
@app.route("/api/translate")
@require_payment
def translate():
return jsonify({"result": "..."})
const DROPPAY = "https://droppayxrp.com";
const HANDLE = "your-handle";
const PRICE = 0.10; // USD per request
export async function requirePayment(req, res, next) {
const proof = req.headers["x-payment"];
if (!proof) {
const r = await fetch(`${DROPPAY}/x402/payment-required`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ handle: HANDLE, price_usd: PRICE,
resource: req.path })
});
const details = await r.json();
res.setHeader("X-Payment-Required", JSON.stringify(details));
return res.status(402).json(details);
}
const r = await fetch(`${DROPPAY}/x402/verify-payment`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ handle: HANDLE, tx_hash: proof,
price_usd: PRICE, resource: req.path })
});
const result = await r.json();
if (!result.valid) {
return res.status(402).json({ error: result.reason ?? "Payment invalid" });
}
next();
}
// Usage:
app.get("/api/translate", requirePayment, (req, res) => {
res.json({ result: "..." });
});
3. Test it
Call your endpoint without a payment header - you should receive an HTTP 402 with payment details. Use the XRPL AI Starter Kit to test with an AI agent, or any XRPL wallet to test manually.
Endpoint reference
POST /x402/payment-required
Returns payment details for an HTTP 402 response. Call this when a request arrives without an X-Payment header.
Request
| Field | Type | Required | Description |
|---|---|---|---|
handle |
string | Yes | Your DropPay merchant handle |
price_usd |
number | Yes | Price in USD for this request |
resource |
string | No | API path being protected (shown in dashboard) |
Response
{
"x402_version": "1",
"pay_to_address": "rYourWalletAddress...",
"destination_tag": 48291,
"amount_xrp": "0.046001",
"amount_usd": "0.1",
"rate": "2.1739",
"network": "xrpl-mainnet",
"expires_at": "2026-07-15T12:05:00Z",
"resource": "/api/translate",
"facilitator": "https://droppayxrp.com",
"verify_url": "https://droppayxrp.com/x402/verify-payment"
}
Return this as the HTTP 402 body and set it as the X-Payment-Required response header. The client uses pay_to_address, destination_tag, and amount_xrp to submit the XRPL payment.
Destination tag
If your wallet is exchange-hosted (e.g. on Bitrue), destination_tag will be set and is required. The payer must include it or the payment will be rejected. Self-custody wallets return null for destination_tag.
POST /x402/verify-payment
Verifies an XRPL transaction as a valid x402 payment. Call this when a request retries with an X-Payment header.
Request
{
"handle": "your-handle",
"tx_hash": "A1B2C3D4...",
"price_usd": 0.10,
"resource": "/api/translate"
}
| Field | Type | Required | Description |
|---|---|---|---|
handle |
string | Yes | Your DropPay merchant handle |
tx_hash |
string | Yes | XRPL transaction hash from the X-Payment header |
price_usd |
number | Yes | The same price used in the payment-required call |
resource |
string | No | API path (recorded in dashboard for this payment) |
Success response
{
"valid": true,
"tx_hash": "A1B2C3D4...",
"amount_xrp": "0.046001",
"paid_at": "2026-07-15T12:00:08Z",
"explorer_url": "https://livenet.xrpl.org/transactions/A1B2C3D4...",
"receipt_url": "https://droppayxrp.com/receipt/dp_x_a1b2c3d4e5"
}
Failed response
When valid is false, return HTTP 402 to the client. Do not grant access.
Verification rules
DropPay checks all of the following before marking a payment valid:
| Check | Rule |
|---|---|
| Destination | Transaction must be sent to your configured wallet address |
| Destination tag | Must match if your wallet requires one |
| Amount | Delivered XRP must be within 5% of the current rate equivalent of price_usd |
| Age | Transaction must have confirmed within the last 5 minutes |
| Replay | Each tx_hash can only be used once across all DropPay merchants |
Tracking
Every verified payment appears in Dashboard > Payments under all transactions. Webhooks fire on every verified payment - the same payment.confirmed event as regular payments. See Webhooks for the payload.
Who can pay
Any caller with an XRPL wallet. No DropPay account required.
- Claude agents via droppay-mcp - install droppay-mcp and the agent handles the full payment flow automatically. Works with Claude Code, Cursor, and any MCP client.
- Other agents - use xrpl-py (Python) or xrpl.js (JavaScript) to submit payments on-chain
- Humans - any XRPL wallet app (Xaman, etc.)
Why DropPay vs native x402
| DropPay x402 | Coinbase x402 | |
|---|---|---|
| Settlement | XRPL - 3-5 seconds | Base - ~2 seconds |
| Fees | Near-zero | ~$0.001 per tx |
| Dashboard | Yes - calls, revenue, top paths | No |
| Webhooks | Yes | No |
| Caller identity | Yes - payer wallet address | No |
| Receipt URL | Yes | No |
| Emerging markets | Yes - XRPL has wide reach | Limited |
| Non-custodial | Yes | Yes |