#!/usr/bin/env bash # 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: # bash demo.sh # bash demo.sh --no-pause # curl -fsSL https://agents.azpay.com.br/monetize/demo.sh | bash # curl -fsSL https://agents.azpay.com.br/monetize/demo.sh | bash -s -- --no-pause set -u RESOURCE="https://x402-demo.azpay.com.br/premium" RPC="https://ethereum-sepolia-rpc.publicnode.com" TX_A="0x14a5eb4d1400c4d07b5c9df418f3a7839db53d0517c2a87fd154f66534ee6dfb" TX_B="0x76a8225747da78cd2054dae003eef225b71d73138c47e60f41bbca98e02b1491" PAYER="0xa409bf1bE7FDeD9154Ae9366bEE43d1a35C41542" PAYEE="0x323d1544D44DBA50d33a8c89acfE3E5d8eDa2570" RECORDED_HEADER="eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQQVlNRU5ULVNJR05BVFVSRSBoZWFkZXIgaXMgcmVxdWlyZWQiLCJyZXNvdXJjZSI6eyJ1cmwiOiJodHRwczovL3g0MDItZGVtby5henBheS5jb20uYnIvcHJlbWl1bSIsIm1pbWVUeXBlIjoiYXBwbGljYXRpb24vanNvbiJ9LCJhY2NlcHRzIjpbeyJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6MTExNTUxMTEiLCJhbW91bnQiOiIxMDAwMCIsImFzc2V0IjoiMHgxYzdENEIxOTZDYjBDN0IwMWQ3NDNGYmM2MTE2YTkwMjM3OUM3MjM4IiwicGF5VG8iOiIweDMyM2QxNTQ0RDQ0REJBNTBkMzNhOGM4OWFjZkUzRTVkOGVEYTI1NzAiLCJtYXhUaW1lb3V0U2Vjb25kcyI6NjAsImV4dHJhIjp7ImFzc2V0VHJhbnNmZXJNZXRob2QiOiJlaXAzMDA5IiwibmFtZSI6IlVTREMiLCJ2ZXJzaW9uIjoiMiJ9fV0sImV4dGVuc2lvbnMiOnt9fQ==" NO_PAUSE=0 for arg in "$@"; do case "$arg" in --no-pause) NO_PAUSE=1 ;; -h|--help) printf '%s\n' "Uso: demo.sh [--no-pause]" \ "Rede de teste Ethereum Sepolia. Sem dinheiro real." \ "Não pede chave privada e não envia pagamento." exit 0 ;; *) printf 'Opção desconhecida: %s\n' "$arg" >&2 exit 2 ;; esac done if [[ -t 1 ]]; then BOLD=$'\033[1m'; DIM=$'\033[2m'; PINK=$'\033[38;5;197m'; RESET=$'\033[0m' else BOLD=""; DIM=""; PINK=""; RESET="" fi pause() { if [[ "$NO_PAUSE" -eq 1 ]]; then return 0 fi printf '%s' "Enter para continuar… " if IFS= read -r _ /dev/null; then printf '\n' return 0 fi if [[ -t 0 ]] && IFS= read -r _; then printf '\n' return 0 fi printf '\n%s\n' "Sem terminal interativo; seguindo. Use --no-pause para ocultar este aviso." } say() { printf '%s\n' "$1"; } blank() { printf '\n'; } header_value() { local name="$1" file="$2" line key value while IFS= read -r line || [[ -n "$line" ]]; do line=${line%$'\r'} key=${line%%:*} key=$(printf '%s' "$key" | tr '[:upper:]' '[:lower:]') if [[ "$key" == "$name" ]]; then value=${line#*:} value=${value#"${value%%[![:space:]]*}"} printf '%s' "$value" return 0 fi done < "$file" } json_string() { local key="$1" file="$2" sed -n "s/.*\"${key}\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p" "$file" | head -n 1 } format_usdc() { local raw="$1" whole frac if [[ ! "$raw" =~ ^[0-9]+$ ]]; then printf '%s USDC' "$raw" return fi if ((${#raw} <= 6)); then frac=$(printf '%06d' "$((10#$raw))") whole=0 else whole=${raw:0:${#raw}-6} frac=${raw:${#raw}-6} whole=${whole#0} [[ -n "$whole" ]] || whole=0 fi while [[ ${#frac} -gt 2 && ${frac: -1} == 0 ]]; do frac=${frac:0:${#frac}-1} done printf '%s,%s USDC' "$whole" "$frac" } network_label() { if [[ "$1" == "eip155:11155111" ]]; then printf '%s' "Ethereum Sepolia (rede de teste)" else printf '%s' "$1" fi } decode_b64() { local value="$1" out out=$(printf '%s' "$value" | openssl base64 -d -A 2>/dev/null || true) if [[ -z "$out" ]]; then out=$(printf '%s' "$value" | base64 -d 2>/dev/null || printf '%s' "$value" | base64 -D 2>/dev/null || true) fi printf '%s' "$out" } show_json_file() { if command -v python3 >/dev/null 2>&1; then python3 -m json.tool "$1" 2>/dev/null && return 0 fi cat "$1" printf '\n' } command -v curl >/dev/null 2>&1 || { printf '%s\n' "Este script precisa do curl." >&2; exit 1; } WORKDIR=$(mktemp -d) trap 'rm -rf "$WORKDIR"' EXIT say "${BOLD}AZPay Agents — demonstração do x402${RESET}" say "Rede de teste Ethereum Sepolia. Sem dinheiro real." say "Este script não pede chave privada, não assina e não envia pagamento." blank say "Ao vivo: o pedido da página e a resposta 402." say "Reprise: a assinatura, a liquidação e o comprovante de um teste já feito." blank say "${PINK}Passo 1 de 6 — pedido da página${RESET}" say "Vamos pedir a página premium do teste, como um agente pediria um conteúdo." say "Endereço: $RESOURCE" pause HTTP_CODE=$(curl -sS --max-time 20 --proto '=https' -D "$WORKDIR/headers" -o "$WORKDIR/body" -w '%{http_code}' "$RESOURCE" || printf '000') SOURCE="ao vivo" HEADER=$(header_value payment-required "$WORKDIR/headers") if [[ "$HTTP_CODE" != "402" || -z "$HEADER" ]]; then SOURCE="gravação de uma resposta real, porque a leitura ao vivo falhou" HTTP_CODE="402" HEADER="$RECORDED_HEADER" decode_b64 "$RECORDED_HEADER" > "$WORKDIR/body" printf '%s\n' "HTTP/1.1 402 Payment Required" "payment-required: $RECORDED_HEADER" > "$WORKDIR/headers" fi say "${BOLD}Resultado (${SOURCE})${RESET}" awk 'NR==1 {print}' "$WORKDIR/headers" say "Nada foi pago neste passo. O site só informou que o acesso é pago." blank say "${PINK}Passo 2 de 6 — leitura do 402${RESET}" say "O cabeçalho PAYMENT-REQUIRED traz o preço, a rede e quem recebe." say "Vamos decodificar esse cabeçalho. Continua sem assinatura e sem pagamento." pause decode_b64 "$HEADER" > "$WORKDIR/decoded" SCHEME=$(json_string scheme "$WORKDIR/decoded") NETWORK=$(json_string network "$WORKDIR/decoded") AMOUNT=$(json_string amount "$WORKDIR/decoded") ASSET=$(json_string asset "$WORKDIR/decoded") PAY_TO=$(json_string payTo "$WORKDIR/decoded") [[ -n "$SCHEME" ]] || SCHEME=$(json_string scheme "$WORKDIR/body") [[ -n "$NETWORK" ]] || NETWORK=$(json_string network "$WORKDIR/body") [[ -n "$AMOUNT" ]] || AMOUNT=$(json_string amount "$WORKDIR/body") [[ -n "$ASSET" ]] || ASSET=$(json_string asset "$WORKDIR/body") [[ -n "$PAY_TO" ]] || PAY_TO=$(json_string payTo "$WORKDIR/body") PRICE=$(format_usdc "${AMOUNT:-10000}") say "${BOLD}Resultado (${SOURCE})${RESET}" printf ' Tipo de cobrança: %s\n' "${SCHEME:-desconhecido}" printf ' Rede: %s\n' "$(network_label "${NETWORK:-eip155:11155111}")" printf ' Identificador da rede: %s\n' "${NETWORK:-eip155:11155111}" printf ' Preço: %s\n' "$PRICE" printf ' Ativo: %s\n' "${ASSET:-desconhecido}" printf ' Recebedor: %s\n' "${PAY_TO:-$PAYEE}" blank say "Cabeçalho decodificado:" if [[ -s "$WORKDIR/decoded" ]]; then cp "$WORKDIR/decoded" "$WORKDIR/pretty.json" show_json_file "$WORKDIR/pretty.json" else show_json_file "$WORKDIR/body" fi blank say "${PINK}Passo 3 de 6 — assinatura${RESET} ${DIM}(reprise)${RESET}" say "No fluxo completo, o agente assinaria: aceito pagar ${PRICE} para o recebedor." say "A assinatura fica na carteira do agente. Este script não tem carteira." say "Não vamos pedir chave, não vamos assinar e não vamos enviar o pedido de novo." pause say "${BOLD}Reprise — explicação, não é uma assinatura nova${RESET}" say "No teste já liquidado, quem pagou foi:" printf ' Pagador do teste: %s\n' "$PAYER" printf ' Recebedor: %s\n' "${PAY_TO:-$PAYEE}" printf ' Valor autorizado naquele teste: %s\n' "$PRICE" say "A prova iria no cabeçalho PAYMENT-SIGNATURE." say "Aqui ela não existe. Nada foi assinado agora." blank say "${PINK}Passo 4 de 6 — liquidação${RESET} ${DIM}(reprise)${RESET}" say "No teste real, a AZPay conferiu a autorização e registrou o pagamento na Sepolia." say "Este passo não chama o facilitador e não cria transação nova." say "É a reprise do pagamento que a página /monetize já mostra." pause say "${BOLD}Reprise — liquidação já feita na rede de teste${RESET}" printf ' Rede: %s\n' "$(network_label "${NETWORK:-eip155:11155111}")" printf ' Valor: %s\n' "$PRICE" printf ' De: %s\n' "$PAYER" printf ' Para: %s\n' "${PAY_TO:-$PAYEE}" printf ' Transação: %s\n' "$TX_A" say "Sem dinheiro real. Nenhuma transação nova saiu daqui." blank say "${PINK}Passo 5 de 6 — conteúdo e comprovante${RESET} ${DIM}(reprise)${RESET}" say "Depois da liquidação, o site respondeu 200 e devolveu o comprovante." say "Não vamos repetir essa chamada: repetir exigiria um pagamento." say "Abaixo está o comprovante gravado daquele teste." pause say "${BOLD}Reprise — resposta gravada, não é uma chamada nova${RESET}" say "HTTP/1.1 200 OK" say "PAYMENT-RESPONSE:" cat > "$WORKDIR/response.json" <