// Un server de resurse x402 v2, minimal (2026-09-29): raspunde 402 cu PAYMENT-REQUIRED, iar la reluare trimite plata facilitatorului // (POST /verify, apoi POST /settle) si serveste resursa numai dupa o decontare reusita, cu PAYMENT-RESPONSE. E piesa pe care o are // orice vanzator x402; aici e ca probele agentilor sa plateasca cap la cap printr-un facilitator adevarat (cel de pe testnetul 28001) // sau printr-unul local. Nu are voie sa accepte o plata pentru alta cerinta decat a emis-o: `accepted` trebuie sa fie chiar ea. import http from 'node:http'; import { b64json, dinB64json } from './client.mjs'; /** * @param {object} o { requirement:{scheme,network,amount,asset,payTo,maxTimeoutSeconds,extra}, facilitator: URL, path, content, description } */ export function createResourceServer({ requirement, facilitator, path: cale = '/premium', content = 'the paid content', description = 'a paid resource', fetchImpl = fetch }) { const fac = String(facilitator).replace(/\/+$/, ''); const post = async (p, body) => { const r = await fetchImpl(fac + p, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) }); return r.json(); }; const cheie = (x) => JSON.stringify(['scheme', 'network', 'amount', 'asset', 'payTo', 'maxTimeoutSeconds'].map((k) => String(x && x[k]).toLowerCase())); const decontari = [], primite = []; const srv = http.createServer(async (req, res) => { const url = (req.url || '/').split('?')[0]; if (url !== cale) { res.writeHead(404); return res.end('not found'); } const resource = { url: `http://${req.headers.host}${cale}`, description, mimeType: 'text/plain' }; const cere = (error) => { const pr = { x402Version: 2, error, resource, accepts: [requirement] }; res.writeHead(402, { 'content-type': 'application/json', 'PAYMENT-REQUIRED': b64json(pr) }); res.end(JSON.stringify(pr)); }; const h = req.headers['payment-signature']; if (!h) return cere('payment required'); const payload = dinB64json(h); if (!payload || payload.x402Version !== 2 || !payload.payload) return cere('the PAYMENT-SIGNATURE header is not an x402 v2 payment payload'); if (cheie(payload.accepted) !== cheie(requirement)) return cere('the payment is for another requirement than this resource issued'); primite.push(payload); try { const body = { x402Version: 2, paymentPayload: payload, paymentRequirements: requirement }; const v = await post('/verify', body); if (!v.isValid) return cere(`payment not valid: ${v.invalidReason}`); const s = await post('/settle', body); decontari.push(s); if (!s.success) return cere(`settlement failed: ${String(s.errorReason).slice(0, 120)}`); res.writeHead(200, { 'content-type': 'text/plain', 'PAYMENT-RESPONSE': b64json(s) }); res.end(content); } catch (e) { res.writeHead(502, { 'content-type': 'text/plain' }); res.end('facilitator unreachable: ' + String(e.message).slice(0, 80)); } }); return { server: srv, settlements: decontari, lastPayloads: primite, listen: (port = 0, host = '127.0.0.1') => new Promise((r) => srv.listen(port, host, () => r(`http://${host}:${srv.address().port}${cale}`))) }; }