#!/usr/bin/env node // Demonstração x402 da AZPay — leia este arquivo antes de executar. // // Ao vivo: o pedido da página e a resposta HTTP 402. // Reprise: assinatura, liquidação e comprovante de um teste já feito // na Ethereum Sepolia. As transações são as mesmas da página /monetize. // // Este script não pede chave privada, não assina, não envia pagamento // e não usa dinheiro real. // // Uso: node demo.mjs [--no-pause] import { closeSync, openSync, readSync } from "node:fs"; const RESOURCE = "https://x402-demo.azpay.com.br/premium"; const RPC = "https://ethereum-sepolia-rpc.publicnode.com"; const TX_A = "0x14a5eb4d1400c4d07b5c9df418f3a7839db53d0517c2a87fd154f66534ee6dfb"; const TX_B = "0x76a8225747da78cd2054dae003eef225b71d73138c47e60f41bbca98e02b1491"; const PAYER = "0xa409bf1bE7FDeD9154Ae9366bEE43d1a35C41542"; const PAYEE = "0x323d1544D44DBA50d33a8c89acfE3E5d8eDa2570"; const RECORDED_HEADER = "eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQQVlNRU5ULVNJR05BVFVSRSBoZWFkZXIgaXMgcmVxdWlyZWQiLCJyZXNvdXJjZSI6eyJ1cmwiOiJodHRwczovL3g0MDItZGVtby5henBheS5jb20uYnIvcHJlbWl1bSIsIm1pbWVUeXBlIjoiYXBwbGljYXRpb24vanNvbiJ9LCJhY2NlcHRzIjpbeyJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6MTExNTUxMTEiLCJhbW91bnQiOiIxMDAwMCIsImFzc2V0IjoiMHgxYzdENEIxOTZDYjBDN0IwMWQ3NDNGYmM2MTE2YTkwMjM3OUM3MjM4IiwicGF5VG8iOiIweDMyM2QxNTQ0RDQ0REJBNTBkMzNhOGM4OWFjZkUzRTVkOGVEYTI1NzAiLCJtYXhUaW1lb3V0U2Vjb25kcyI6NjAsImV4dHJhIjp7ImFzc2V0VHJhbnNmZXJNZXRob2QiOiJlaXAzMDA5IiwibmFtZSI6IlVTREMiLCJ2ZXJzaW9uIjoiMiJ9fV0sImV4dGVuc2lvbnMiOnt9fQ=="; const args = process.argv.slice(2); if (args.includes("-h") || args.includes("--help")) { console.log("Uso: node demo.mjs [--no-pause]"); console.log("Rede de teste Ethereum Sepolia. Sem dinheiro real."); console.log("Não pede chave privada e não envia pagamento."); process.exit(0); } if (args.some((arg) => arg !== "--no-pause")) { console.error(`Opção desconhecida: ${args.find((arg) => arg !== "--no-pause")}`); process.exit(2); } const noPause = args.includes("--no-pause"); const color = process.stdout.isTTY; const bold = color ? "\u001b[1m" : ""; const dim = color ? "\u001b[2m" : ""; const pink = color ? "\u001b[38;5;197m" : ""; const reset = color ? "\u001b[0m" : ""; function readEnter(fd) { const buf = Buffer.alloc(1); while (true) { const read = readSync(fd, buf, 0, 1, null); if (read <= 0 || buf[0] === 10) return; } } function pause() { if (noPause) return; process.stdout.write("Enter para continuar… "); try { const fd = openSync("/dev/tty", "rs"); readEnter(fd); closeSync(fd); process.stdout.write("\n"); return; } catch { /* o terminal pode não ser o controlador */ } if (process.stdin.isTTY) { try { readEnter(0); process.stdout.write("\n"); return; } catch { /* segue sem pausar */ } } process.stdout.write("\nSem terminal interativo; seguindo. Use --no-pause para ocultar este aviso.\n"); } function formatUsdc(raw) { if (!/^\d+$/.test(String(raw))) return `${raw} USDC`; const text = String(raw); const whole = text.length > 6 ? text.slice(0, -6).replace(/^0+(?=\d)/, "") || "0" : "0"; let frac = text.padStart(6, "0").slice(-6); while (frac.length > 2 && frac.endsWith("0")) frac = frac.slice(0, -1); return `${whole},${frac} USDC`; } function networkLabel(id) { return id === "eip155:11155111" ? "Ethereum Sepolia (rede de teste)" : id; } function decodeHeader(value) { return JSON.parse(Buffer.from(value, "base64").toString("utf8")); } function offerOf(body) { return body?.accepts?.[0] ?? {}; } console.log(`${bold}AZPay Agents — demonstração do x402${reset}`); console.log("Rede de teste Ethereum Sepolia. Sem dinheiro real."); console.log("Este script não pede chave privada, não assina e não envia pagamento."); console.log(""); console.log("Ao vivo: o pedido da página e a resposta 402."); console.log("Reprise: a assinatura, a liquidação e o comprovante de um teste já feito."); console.log(""); console.log(`${pink}Passo 1 de 6 — pedido da página${reset}`); console.log("Vamos pedir a página premium do teste, como um agente pediria um conteúdo."); console.log(`Endereço: ${RESOURCE}`); pause(); let source = "ao vivo"; let status = 0; let header = ""; let body = null; try { const response = await fetch(RESOURCE, { redirect: "manual", signal: AbortSignal.timeout(20000) }); status = response.status; header = response.headers.get("payment-required") || ""; body = await response.json().catch(() => null); } catch { status = 0; } if (status !== 402 || !header) { source = "gravação de uma resposta real, porque a leitura ao vivo falhou"; status = 402; header = RECORDED_HEADER; body = decodeHeader(RECORDED_HEADER); } console.log(`${bold}Resultado (${source})${reset}`); console.log(`HTTP ${status}`); console.log("Nada foi pago neste passo. O site só informou que o acesso é pago."); console.log(""); console.log(`${pink}Passo 2 de 6 — leitura do 402${reset}`); console.log("O cabeçalho PAYMENT-REQUIRED traz o preço, a rede e quem recebe."); console.log("Vamos decodificar esse cabeçalho. Continua sem assinatura e sem pagamento."); pause(); let decoded = body; try { decoded = decodeHeader(header); } catch { decoded = body; } const offer = offerOf(decoded); const price = formatUsdc(offer.amount || "10000"); console.log(`${bold}Resultado (${source})${reset}`); console.log(` Tipo de cobrança: ${offer.scheme || "desconhecido"}`); console.log(` Rede: ${networkLabel(offer.network || "eip155:11155111")}`); console.log(` Identificador da rede: ${offer.network || "eip155:11155111"}`); console.log(` Preço: ${price}`); console.log(` Ativo: ${offer.asset || "desconhecido"}`); console.log(` Recebedor: ${offer.payTo || PAYEE}`); console.log(""); console.log("Cabeçalho decodificado:"); console.log(JSON.stringify(decoded, null, 2)); console.log(""); console.log(`${pink}Passo 3 de 6 — assinatura${reset} ${dim}(reprise)${reset}`); console.log(`No fluxo completo, o agente assinaria: aceito pagar ${price} para o recebedor.`); console.log("A assinatura fica na carteira do agente. Este script não tem carteira."); console.log("Não vamos pedir chave, não vamos assinar e não vamos enviar o pedido de novo."); pause(); console.log(`${bold}Reprise — explicação, não é uma assinatura nova${reset}`); console.log("No teste já liquidado, quem pagou foi:"); console.log(` Pagador do teste: ${PAYER}`); console.log(` Recebedor: ${offer.payTo || PAYEE}`); console.log(` Valor autorizado naquele teste: ${price}`); console.log("A prova iria no cabeçalho PAYMENT-SIGNATURE."); console.log("Aqui ela não existe. Nada foi assinado agora."); console.log(""); console.log(`${pink}Passo 4 de 6 — liquidação${reset} ${dim}(reprise)${reset}`); console.log("No teste real, a AZPay conferiu a autorização e registrou o pagamento na Sepolia."); console.log("Este passo não chama o facilitador e não cria transação nova."); console.log("É a reprise do pagamento que a página /monetize já mostra."); pause(); console.log(`${bold}Reprise — liquidação já feita na rede de teste${reset}`); console.log(` Rede: ${networkLabel(offer.network || "eip155:11155111")}`); console.log(` Valor: ${price}`); console.log(` De: ${PAYER}`); console.log(` Para: ${offer.payTo || PAYEE}`); console.log(` Transação: ${TX_A}`); console.log("Sem dinheiro real. Nenhuma transação nova saiu daqui."); console.log(""); console.log(`${pink}Passo 5 de 6 — conteúdo e comprovante${reset} ${dim}(reprise)${reset}`); console.log("Depois da liquidação, o site respondeu 200 e devolveu o comprovante."); console.log("Não vamos repetir essa chamada: repetir exigiria um pagamento."); console.log("Abaixo está o comprovante gravado daquele teste."); pause(); console.log(`${bold}Reprise — resposta gravada, não é uma chamada nova${reset}`); console.log("HTTP/1.1 200 OK"); console.log("PAYMENT-RESPONSE:"); console.log(JSON.stringify({ success: true, transaction: TX_A, network: "eip155:11155111", payer: PAYER }, null, 2)); console.log("O corpo era o conteúdo premium. Este script não baixa esse conteúdo de novo."); console.log(""); console.log(`${pink}Passo 6 de 6 — transação na Sepolia${reset}`); console.log("Por último, a transação pública do teste. A consulta só lê a rede."); console.log("Ela não envia pagamento e não gasta saldo."); pause(); console.log(`${bold}Consulta pública da transação já gravada${reset}`); console.log(` https://sepolia.etherscan.io/tx/${TX_A}`); console.log("A outra transação do mesmo teste:"); console.log(` https://sepolia.etherscan.io/tx/${TX_B}`); try { const response = await fetch(RPC, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_getTransactionReceipt", params: [TX_A] }), signal: AbortSignal.timeout(15000) }); const payload = await response.json(); const receipt = payload.result || {}; if (receipt.status === "0x1") console.log("A rede de teste confirma: a transação foi incluída com sucesso."); else if (receipt.status) console.log(`A rede devolveu o estado ${receipt.status}. O link acima continua sendo o registro.`); else console.log("A consulta pública não trouxe o estado. O link acima é o registro."); const logs = JSON.stringify(receipt.logs || []); if (logs.includes("0002710")) console.log("O recibo público mostra a transferência de 0,01 USDC do teste."); } catch { console.log("A consulta pública falhou. O link acima continua sendo o registro."); } console.log(""); console.log(`${bold}Fim.${reset} Nenhum pagamento foi feito. Nenhum segredo foi pedido.`); console.log("Rede de teste Ethereum Sepolia. Sem dinheiro real.");