Dev.to · 11 min read

Accept x402 payments on XRPL and Xahau — settle through 402Proof

Accept x402 payments on XRPL and Xahau — settle through 402Proof

You have an API. Your buyers are on the XRP Ledger or on Xahau, and they want to pay per call — XRP or XAH, no accounts, no invoices, settled on-chain. That is what the x402 protocol does: your route answers HTTP 402 with a payment challenge, the buyer's client signs a native Payment transaction, a facilitator verifies and settles it, and your server serves the response. This article walks the kit that wires it, end to end. The facilitator it settles through by default is 402Proof, Script Master Labs' x402 facilitator for the XRPL family. The full kit — Express and FastAPI middleware, the Xahau payer fee-leg builder, and the dated proof — lives on the canonical page: the 402Proof starter kit. One thing is different here from the Base/Arc world, and it is different by design: 402Proof is partner-keyed. On XRPL the facilitator fee accrues per settlement instead of being collected inside it, so keys are issued to known sellers — scoped to your payTo wallet, your resource origin, your asset, an amount ceiling and the invoice ID. Credit goes to known sellers; that scoping is what keeps an accrual rail honest. Getting a key is one email (tim@scriptmasterlabs.com with your service's base URL, the rail, and your receiving wallet — reply within one business day) plus a two-call activation. Build from the kit first; the key slots into one environment variable. The two fee models, stated separately Never merged, because they are genuinely different machines: XRPL (xrpl:0, native XRP): the facilitator fee is $0.0100 USD per completed settlement, accrued on a merchant ledger and not collected at settlement — 402Proof's own pricing publishes automaticCollectionLive: false, "accrued, not collected cash." It is never deducted from your buyer's payment. Loyalty discounts apply on your prior successful settlements: $0.0080 after 100 (20% off), $0.0070 after 1,000 (30%), $0.0050 after 10,000 (50%). Verification, failed settlements and exact replays are free. No subscription. Xahau (xrpl:21337, native XAH): a flat 0.01 XAH per third-party settlement — exactly 10,000 drops — collected inside the settlement as a second signed Payment from the payer to the facilitator fee recipient, carried in the payload as feeTxBlob. Your amount is never reduced by it. No free allowance. For calibration: Script Master Labs' other facilitator, OMNIGATE, settles Base and Arc at 0.005 USDC flat with an open door — different rails, different fee mechanics, different door. (The same 402Proof sidecar also settles Solana at 0.005 USDC flat, collected in-transaction.) Why Xahau, and why now Because as far as we can find, 402Proof is the only Xahau x402 facilitator operating — and we checked, on 2026-11-04, before writing that sentence. The official x402 facilitator list (docs.x402.org, 19 production facilitators at our 2026-10-30 review) contains no Xahau entry at all; XRPL appears once (T54), and T54 settles XRPL only. Our 2026-11-04 searches of the official lists, the public facilitator registries and the web found no other facilitator publishing a Xahau (xrpl:21337) /supported endpoint or discovery index. The rail is still early — which is exactly when the default template gets set. This kit is an attempt to be that default, published with its evidence attached. What you are building One paid route, seven steps: Request a key — email, get a one-time invite token (48 hours). Activate — POST /v1/partners/activate on the sidecar with the token and your acceptance of the fee schedule; the p402_… key it returns is shown once. Store it server-side. Challenge — an unpaid call gets HTTP 402 with your price in drops, your wallet, and a fresh invoiceId, in the JSON body and the PAYMENT-REQUIRED header. Signature — the buyer's x402 v2 client signs a native Payment for your price and retries with the PAYMENT-SIGNATURE header. On Xahau it also signs the 0.01 XAH fee leg (payload.feeTxBlob, sequenced immediately after, memo-bound to the invoice). Verify — your server forwards the payload to 402Proof's POST /verify under your partner key. Free, read-only; a refusal submits nothing. Settle — your server calls POST /settle, at most once per payment. The buyer's signed transaction is submitted: your price lands in your wallet. Serve — deliver only on success: true, with a PAYMENT-RESPONSE header — and your resource is now a row in the sidecar's discovery index (20 resources at the 2026-11-04 live read), which also feeds the public /x402-directory aggregation. The Express middleware This is the whole seller side. Both rails are in the one file — XRPL is the wired example; Xahau is the second requirements object and a one-word switch. // ─── x402 starter kit: Express — XRPL + Xahau via 402Proof ──────────────── // One paid route, settled through 402Proof (Script Master Labs' facilitator // for the XRPL family). Requires: npm i express (Node 18+ for global fetch) // // 402Proof is PARTNER-KEYED by design — the XRPL facilitator fee accrues per // settlement instead of being collected inside it, so keys are issued to // known sellers. Get yours first (reply within one business day): // email tim@scriptmasterlabs.com with (1) your service's base URL, // (2) the rail — XRPL RLUSD, XRPL USDC or Xahau XAH — and // (3) your receiving wallet address. // Then activate the invite token you receive: // POST https://four02proof-x402-v2-xrpl.onrender.com/v1/partners/activate // { "inviteToken": "inv402_…", "acceptFeeUSDCents": 1 } // The p402_… key it returns is shown ONCE — store it server-side only. // // Verified 2026-11-04 against 402Proof's live /supported + /pricing.json on // both hosts, the live discovery-index accepts objects, and live /verify // refusal behavior (see the kit page's proof section). import express from "express"; const FACILITATOR = "https://four02proof.onrender.com"; // 402Proof (standard x402 v2) const PARTNER_KEY = process.env.PROOF402_PARTNER_KEY; // p402_… — server-side only, never ship to a browser const PAY_TO = "rYourReceivingWallet"; // ← your XRPL/Xahau wallet: buyer payments land here directly const PRICE_DROPS = "1000000"; // 1 XRP (or 1 XAH) — drops: 1 XRP = 1,000,000 drops // A fresh invoiceId per challenge — your partner key is scoped to your // payTo, your resource origin, your amount ceiling and the invoiceId. const newInvoiceId = () => "inv-" + Date.now().toString(36) + "-" + Math.random().toString(16).slice(2, 10); // ── Rail 1: XRPL mainnet (xrpl:0), native XRP ────────────────────────────── // Requirements shape copied from 402Proof's live discovery index (2026-11-04). // XRPL carries NO fee leg in the buyer's transaction: the facilitator fee // ($0.0100 per settlement, loyalty-discounted) accrues on a separate merchant // ledger — read yours any time at GET /v1/partners/usage on the sidecar. function xrplRequirements() { return { scheme: "exact", network: "xrpl:0", asset: "XRP", amount: PRICE_DROPS, payTo: PAY_TO, maxTimeoutSeconds: 300, extra: { areFeesSponsored: false, invoiceId: newInvoiceId() }, }; } // ── Rail 2: Xahau (xrpl:21337), native XAH ───────────────────────────────── // Same middleware, one different requirements object. On Xahau the fee IS // collected at settlement: the buyer's client signs a SECOND native Payment // of exactly 10,000 drops (0.01 XAH) to the facilitator fee recipient and // carries it in the payload as feeTxBlob, bound by a memo (see the payer // snippet on the kit page). Your amount is never reduced by the fee. // Payments to the fee recipient itself (SML's own routes) are exempt. const XAHAU_FEE_RECIPIENT = "rEmdJWrAWFtud1vFN8Zm8eWkgK4AVMWPiX"; function xahauRequirements() { return { scheme: "exact", network: "xrpl:21337", asset: "XAH", amount: PRICE_DROPS, payTo: PAY_TO, maxTimeoutSeconds: 300, extra: { areFeesSponsored: false, invoiceId: newInvoiceId(), facilitatorFee: "10000", // drops — flat 0.01 XAH per third-party settlement facilitatorFeeRecipient: XAHAU_FEE_RECIPIENT, }, }; } const REQUIREMENTS_FOR = { xrpl: xrplRequirements, xahau: xahauRequirements }; function challenge(url, description, requirements) { return { x402Version: 2, error: "payment_required", resource: { url, description, mimeType: "application/json" }, accepts: [requirements], }; } async function facilitator(path, paymentPayload, requirements) { const r = await fetch(FACILITATOR + path, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${PARTNER_KEY}`, // your scoped partner key }, body: JSON.stringify({ x402Version: 2, paymentPayload, paymentRequirements: requirements }), }); const text = await r.text(); try { return JSON.parse(text); } catch { // e.g. 401 with an empty body when the key is missing or wrong — a // refusal, not a crash: surface it as an invalid payment (fresh 402). return { isValid: false, success: false, invalidReason: `facilitator_http_${r.status}` }; } } // The buyer's payload is an official x402 v2 envelope: payload.signedTxBlob // is the buyer's own signed XRPL/Xahau Payment (plus payload.feeTxBlob on // Xahau). You forward it exactly as received — your server holds no keys // for settlement and never talks to a chain directly. export function requirePayment(rail, description) { const makeRequirements = REQUIREMENTS_FOR[rail]; return async (req, res, next) => { const url = `${req.protocol}://${req.get("host")}${req.originalUrl}`; const requirements = makeRequirements(); // fresh invoiceId per challenge const body = challenge(url, description, requirements); const header = req.get("PAYMENT-SIGNATURE"); // x402 v2 payment header if (!header) { return res .status(402) .set("PAYMENT-REQUIRED", Buffer.from(JSON.stringify(body)).toString("base64")) .json(body); } let paymentPayload; try { paymentPayload = JSON.parse(Buffer.from(header, "base64").toString("utf8")); } catch { return res.status(402).json(body); } const verify = await facilitator("/verify", paymentPayload, requirements); if (!verify.isValid) { return res.status(402).json({ ...body, error: verify.invalidReason || "invalid_payment" }); } // /settle at most once per payment — deliver ONLY on success:true. const settle = await facilitator("/settle", paymentPayload, requirements); if (!settle.success) { return res.status(402).json({ ...body, error: settle.errorReason || "settlement_failed" }); } res.set("PAYMENT-RESPONSE", Buffer.from(JSON.stringify(settle)).toString("base64")); next(); }; } const app = express(); app.get( "/report", requirePayment("xrpl", "One full report"), // ← or "xahau" for the Xahau rail (req, res) => res.json({ ok: true, report: "your paid payload here" }) ); app.listen(3000, () => console.log("paid API on :3000 — try: curl -i localhost:3000/report")); Two details worth understanding rather than copying blindly: The invoiceId is not decoration. Your partner key is scoped to it, the facilitator binds settlements to it, and on Xahau it is half of the fee leg's binding: the fee Payment carries exactly one memo, MemoData = uppercase hex of invoiceId + ":fee" (if a challenge ever carries no invoiceId, the binding falls back to the main transaction's hash + ":fee"). /verify rejects a fee leg that is missing, mis-addressed, under-amount, wrongly bound, or wrongly sequenced — each failure mode was a design decision, not an accident. Your server never touches a chain. It forwards the buyer's signed payload to /verify and /settle under your key. The facilitator signs nothing and custodies nothing: on XRPL your price moves payer → your payTo because the buyer signed it there; on Xahau the fee leg moves payer → fee recipient the same way. The Xahau fee leg, from the payer side If you are building the buyer client for Xahau, this is the part you cannot guess. The full builder (with the live autofill usage pattern) is on the canonical page; the load-bearing core: const mainTx = { TransactionType: "Payment", Account: wallet.address, Destination: payTo, Amount: String(amountDrops), NetworkID: 21337, Sequence: sequence, LastLedgerSequence: lastLedgerSequence, Fee: String(networkFeeDrops), }; const signedMain = wallet.sign(mainTx); const binding = invoiceId ? `${invoiceId}:fee` : `${signedMain.hash}:fee`; const feeTx = { TransactionType: "Payment", Account: wallet.address, Destination: "rEmdJWrAWFtud1vFN8Zm8eWkgK4AVMWPiX", // facilitator fee recipient Amount: "10000", // exactly 0.01 XAH, in drops NetworkID: 21337, Sequence: sequence + 1, // immediately after the main leg LastLedgerSequence: lastLedgerSequence, Fee: String(networkFeeDrops), Memos: [{ Memo: { MemoData: Buffer.from(binding, "utf8").toString("hex").toUpperCase() } }], }; const signedFee = wallet.sign(feeTx); // payload = { signedTxBlob: signedMain.tx_blob, feeTxBlob: signedFee.tx_blob } We exercised this builder offline on 2026-11-04: both blobs were signed with a throwaway wallet and decoded back — 13 of 13 field checks passed (NetworkID 21337 on both legs, fee exactly 10,000 drops to the published recipient, fee Sequence = main + 1, exactly one memo with the uppercase-hex binding, in both the invoiceId and the hash-fallback forms). Nothing was broadcast; the pattern is proven at the byte level, and the settlement it feeds was proven on-chain by the canary below. The proof, and its limits 402Proof's settlement evidence is public and on-chain. The Xahau canary (2026-11-02): merchant leg B1781F38EE1C1A88BDC106415EC5E0CB507ABE43B30D88D0AA5C9B0AFC636247 (ledger 26986344, tesSUCCESS, merchant +1,000,000 drops exactly) and fee leg 49CFFB98B335CE456DD0ED7A338AC670300BD554675C7B302C51D87597731A5A (ledger 26986346, tesSUCCESS, fee recipient +10,000 drops exactly), with an exact replay returning the recorded result and no second submission. All five of Script Master Labs' facilitator rails — Base, Arc, Solana, XRPL and Xahau — are settlement-proven the same way. And the limit, stated the way the kit page states it: those proofs are SELF-PROOF — the operator's own payer paying the operator's own merchant through the operator's own facilitator. They prove the rails settle end-to-end and charge exactly the published fee. They do not prove outside adoption, and 402Proof's own pricing publishes externalThirdPartySettlementsVerified: false: no external partner has settled end-to-end yet. This kit exists to produce the first one, and the discovery index will show it the day it happens. The full evidence file is the 402Proof attestations page. Get started Read the canonical kit page — Express + FastAPI middleware, the full fee-leg builder, FAQs, and the dated proof table: scriptmasterlabs.com/settle-through-sml-xrpl-starter Email tim@scriptmasterlabs.com with your service's base URL, the rail (XRPL RLUSD, XRPL USDC, or Xahau XAH), and your receiving wallet address. Reply within one business day. Selling on Base or Arc instead? The open-door twin is the OMNIGATE starter kit. The whole seller offer — every rail, every fee — is on /settle-through-sml. FOLLOW X: @TimmyCrypto78 — https://x.com/TimmyCrypto78 Instagram: @scriptmasterlabs — https://www.instagram.com/scriptmasterlabs Threads: @scriptmasterlabs — https://www.threads.net/@scriptmasterlabs Telegram: @x402sml — https://t.me/x402sml LinkedIn: https://www.linkedin.com/in/timothy-walton-23224052 MY LINKS 🛒 All SML products: https://scriptmasterlabs.com/sml-products Website: https://scriptmasterlabs.com Books: https://scriptmasterlabs.com/sml-products#books Shop: https://shop.scriptmasterlabs.com Gumroad: https://timwalton.gumroad.com Whop: https://whop.com/timmy741 Ko-fi: https://ko-fi.com/scriptmaster Discord: https://discord.gg/jGWDutyVAR Verdict Protocol: https://verdict.scriptmasterlabs.com Free VA tools: https://va-ratings.org

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More Crypto & Forex News