// Clientul x402 al unui agent AI (2026-09-29, punctele 23, 25): cumpara o resursa HTTP care cere plata (402), scriind INTAI plata in // registrul agentului, apoi cerand portofelului (wallet.mjs) semnatura, apoi reluand cererea cu antetul PAYMENT-SIGNATURE. Numele // antetelor si forma lor sunt ale specificatiei x402 v2 (transportul HTTP): PAYMENT-REQUIRED, PAYMENT-SIGNATURE, PAYMENT-RESPONSE, toate // JSON in base64. Daca politica refuza plata, clientul se opreste inainte de portofel (si portofelul ar refuza oricum). import { x402Action } from './wallet.mjs'; export const b64json = (o) => Buffer.from(JSON.stringify(o), 'utf8').toString('base64'); export function dinB64json(s) { try { return JSON.parse(Buffer.from(String(s), 'base64').toString('utf8')); } catch { return null; } } /** portofelul prin HTTP (serviciul wallet.mjs serve), cu aceeasi forma ca obiectul din createWallet */ export function walletOverHttp(baseUrl, fetchImpl = fetch) { const b = String(baseUrl).replace(/\/+$/, ''); return { async status() { const r = await fetchImpl(b + '/status'); return r.json(); }, async authorize(body) { const r = await fetchImpl(b + '/authorize', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) }); return r.json(); }, }; } /** * @param {object} o * @param {string} o.url resursa * @param {object} o.ledger registrul agentului (openLedger / resumeLedger) * @param {object} o.wallet portofelul (createWallet sau walletOverHttp) * @param {Array} [o.approvals] aprobari umane pentru aceasta plata, cand politica le cere * @param {Function} [o.fetchImpl] * @returns {Promise<{paid:boolean, status:number, reason?:string, body?:string, settlement?:object, entry?:object, receipt?:object, decision?:object}>} */ export async function payWithAgent({ url, ledger, wallet, approvals = [], fetchImpl = fetch }) { const r0 = await fetchImpl(url); if (r0.status !== 402) return { paid: false, status: r0.status, body: await r0.text(), reason: r0.ok ? 'no payment was required' : `the resource answered ${r0.status}` }; const pr = dinB64json(r0.headers.get('payment-required')) || (await r0.json().catch(() => null)); if (!pr || pr.x402Version !== 2 || !Array.isArray(pr.accepts)) return { paid: false, status: 402, reason: 'the 402 response carries no x402 v2 PaymentRequired' }; const w = await wallet.status(); const req = pr.accepts.find((a) => a.scheme === 'exact' && a.network === w.network && String(a.asset).toLowerCase() === String(w.asset).toLowerCase()); if (!req) return { paid: false, status: 402, reason: `no accepted payment is in this wallet's asset on ${w.network}` }; // 1. plata, in registru, inainte de orice semnatura const r = ledger.record(x402Action(w.address, pr.resource || null, req), { approvals }); if (!r.allowed) return { paid: false, status: 402, reason: `the agent's policy refused the payment: ${r.reason}`, entry: r.entry }; // 2. semnatura portofelului, care judeca din nou tot registrul const a = await wallet.authorize({ requirements: req, resource: pr.resource || null, ledger: ledger.export() }); if (!a.ok) return { paid: false, status: 402, reason: `the wallet refused: ${a.error}`, entry: r.entry, equivocation: a.equivocation }; // 3. cererea reluata, cu plata const r1 = await fetchImpl(url, { headers: { 'PAYMENT-SIGNATURE': a.header } }); const settlement = dinB64json(r1.headers.get('payment-response')); const body = await r1.text(); return { paid: r1.ok && !!(settlement && settlement.success), status: r1.status, body, settlement, entry: r.entry, receipt: a.receipt, decision: a.decision, ...(r1.ok ? {} : { reason: `the resource answered ${r1.status} after payment: ${body.slice(0, 160)}` }) }; }