# pip install xrpl-py requests
import base64, hashlib, json, math, requests
from xrpl.clients import JsonRpcClient
from xrpl.wallet import Wallet
from xrpl.models.transactions import Payment, Memo
from xrpl.transaction import autofill_and_sign
from xrpl.core.binarycodec import encode
from xrpl.models.requests import Ledger
ENDPOINT = "https://agentnomos.com/xrpl-agentic-payments/api/x402/preflight"
INTENT = {"intent": {"network": "xrpl:0",
"destination": "rhteihAJz1KsY6GpWPEc9Jo1W9qrqg1z1i",
"amount": "5000",
"invoice_id": "A" * 64,
"source_tag": 1}}
# -- 1. free request -> the 402 challenge ------------------------------
r = requests.post(ENDPOINT, json=INTENT, timeout=30)
assert r.status_code == 402, r.status_code
acc = r.json()["accepts"][0]
invoice_id = acc["extra"]["invoiceId"]
# -- 2. bind an XRPL payment to that invoice, sign it locally ---------
# InvoiceID = sha256(invoiceId) ; memo = hex(invoiceId). Both are required.
client = JsonRpcClient("https://xrplcluster.com")
wallet = Wallet.from_seed("sEd…") # your seed. It stays here.
latest = client.request(Ledger(ledger_index="validated")).result["ledger_index"]
tx = Payment(
account=wallet.classic_address,
destination=acc["payTo"],
amount=acc["amount"], # "1000" drops, a string
source_tag=acc["extra"]["sourceTag"], # the facilitator's, not yours
invoice_id=hashlib.sha256(invoice_id.encode()).hexdigest().upper(),
memos=[Memo(memo_data=invoice_id.encode().hex().upper())],
last_ledger_sequence=latest + math.ceil(acc["maxTimeoutSeconds"] / 5) + 2,
)
signed = autofill_and_sign(tx, client, wallet)
blob = encode(signed.to_xrpl())
# -- 3. same request, now with the proof ------------------------------
payload = {"x402Version": 2,
"accepted": {k: acc[k] for k in ("scheme", "network", "amount", "asset",
"payTo", "maxTimeoutSeconds", "extra")},
"payload": {"invoiceId": invoice_id, "signedTxBlob": blob}}
header = base64.b64encode(
json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()).decode()
r = requests.post(ENDPOINT, json=INTENT, headers={"X-PAYMENT": header}, timeout=90)
out = r.json()
# -- 4. the only line that matters ------------------------------------
if out.get("decision") != "ALLOW":
raise SystemExit(f"not signing: {out.get('decision')} {out.get('reason_codes')}")
# … only now build and sign the payment you actually intended to make.