x402 Payments

Nesta página

O pacote @emdash-cms/x402 adiciona suporte ao protocolo de pagamento x402 a sites Astro renderizados no servidor. Ele é uma integração do Astro independente, portanto a cobrança dos pagamentos não exige o EmDash. Quando o site também usa o EmDash, um campo de conteúdo pode fornecer o preço de cada entrada.

O x402 é um protocolo de pagamento nativo de HTTP. Quando um cliente solicita um recurso pago sem ter pagado, o servidor responde com 402 Payment Required e com instruções de pagamento legíveis por máquina. Agentes e navegadores que entendem o x402 podem concluir o pagamento automaticamente e repetir a requisição.

Escolher um modo de aplicação

Use a aplicação normal quando toda requisição à rota precisar apresentar um pagamento válido. Esse modo não depende de metadados de requisição específicos da Cloudflare.

Use o modo somente bots quando o site for executado no Cloudflare Workers com o Bot Management ativado e apenas as requisições com pontuação baixa devam pagar. O pacote lê request.cf.botManagement.score; quando esse valor está ausente, ele trata a requisição como humana e ignora a aplicação. Não use o modo somente bots quando a ausência de dados de bots precisar falhar de forma fechada (fail closed).

hasPayment() oferece um terceiro comportamento, apenas para apresentação. Ele informa se a requisição tem um cabeçalho de pagamento, mas não verifica nem liquida o pagamento.

Instalar o pacote

Instale o pacote com o seu gerenciador de pacotes:

pnpm

pnpm add @emdash-cms/x402

npm

npm install @emdash-cms/x402

yarn

yarn add @emdash-cms/x402

Configurar a integração

Escolha uma carteira, uma rede e um facilitator que funcionem em conjunto. A integração oferece suporte por padrão às redes da Ethereum Virtual Machine (EVM), mas o facilitator também precisa oferecer suporte à rede e ao ativo configurados. O exemplo a seguir usa a mainnet da Base e exige o pagamento em toda requisição:

import { defineConfig } from "astro/config";
import { x402 } from "@emdash-cms/x402";

export default defineConfig({
	integrations: [
		x402({
			payTo: "0xYourWalletAddress",
			network: "eip155:8453", // Base mainnet
			defaultPrice: "$0.01",
		}),
	],
});

Adicione a referência de tipos para que o TypeScript conheça Astro.locals.x402:

/// <reference types="@emdash-cms/x402/locals" />

Exigir pagamento em uma rota

A integração coloca um enforcer em Astro.locals.x402. Chame enforce() no frontmatter da sua página para colocar o conteúdo atrás de um pagamento:

---
const { x402 } = Astro.locals;

const result = await x402.enforce(Astro.request, {
  price: "$0.05",
  description: "Premium article",
});

// If the request has no valid payment, enforce() returns a 402 Response.
// Return it directly to send payment instructions to the client.
if (result instanceof Response) return result;

// Payment verified (or skipped in botOnly mode). Apply response headers
// so the client gets settlement proof.
x402.applyHeaders(result, Astro.response);
---

<article>
  <h1>Premium content</h1>
</article>

O método enforce() retorna um destes dois valores:

  • Uma Response (402) — o cliente precisa pagar. Retorne-a diretamente.
  • Um EnforceResult — a requisição deve prosseguir. O conteúdo foi pago ou a aplicação foi ignorada (humano no modo botOnly).

Ativar o modo somente bots

Ative botOnly na configuração da integração:

x402({
	payTo: "0xYourWalletAddress",
	network: "eip155:8453",
	defaultPrice: "$0.01",
	botOnly: true,
	botScoreThreshold: 30,
});

A integração lê request.cf.botManagement.score da Cloudflare para classificar as requisições:

  • Pontuação abaixo do limite (padrão 30) -> tratada como bot, pagamento exigido
  • Pontuação igual ou acima do limite -> tratada como humana, aplicação ignorada
  • Sem dados do Bot Management (desenvolvimento local, implantação fora da Cloudflare) -> tratada como humana

O EnforceResult inclui um sinalizador skipped, para que você possa distinguir «não precisou pagar» de «pagou»:

---
const result = await x402.enforce(Astro.request, { price: "$0.01" });
if (result instanceof Response) return result;

x402.applyHeaders(result, Astro.response);

// result.paid    — true if payment was verified
// result.skipped — true if enforcement was skipped (human in botOnly mode)
// result.payer   — wallet address of payer (if paid)
---

Ler um preço do EmDash

Ao usar o EmDash, adicione um campo number comum à sua coleção para definir preços por página e leia-o no momento da requisição:

---
import { getEmDashEntry } from "emdash";

const { slug } = Astro.params;
const { entry } = await getEmDashEntry("posts", slug);

if (!entry) return Astro.rewrite("/404");

const { x402 } = Astro.locals;

// Use the price from the CMS, falling back only when it is absent.
const result = await x402.enforce(Astro.request, {
  price: entry.data.price ?? "$0.01",
  description: entry.data.title,
});
if (result instanceof Response) return result;

x402.applyHeaders(result, Astro.response);
---

<article>
  <h1>{entry.data.title}</h1>
</article>

Verificar um cabeçalho de pagamento sem exigir o pagamento

Use hasPayment() para verificar se uma requisição inclui um cabeçalho de pagamento sem validá-lo nem aplicá-lo. Isso pode alterar um aviso ou uma mensagem de login, mas nunca deve desbloquear conteúdo protegido:

---
const { x402 } = Astro.locals;

const hasPaymentHeader = x402.hasPayment(Astro.request);
---

{hasPaymentHeader ? (
  <p>Payment supplied. Verification is still required.</p>
) : (
  <p>This page requires payment.</p>
)}

Referência de configuração

OpçãoTipoPadrãoDescrição
payTostringobrigatórioEndereço da carteira de destino
networkstringobrigatórioIdentificador de rede CAIP-2 (por exemplo, eip155:8453)
defaultPricePrice—Preço padrão, que pode ser substituído por página
facilitatorUrlstringhttps://x402.org/facilitatorURL do facilitator de pagamento
schemestring"exact"Esquema de pagamento
maxTimeoutSecondsnumber60Tempo limite máximo para as assinaturas de pagamento
evmbooleantrueAtivar o suporte às redes da Ethereum Virtual Machine
svmbooleanfalseAtivar o suporte à Solana Virtual Machine (requer @x402/svm)
botOnlybooleanfalseExigir pagamento apenas de bots
botScoreThresholdnumber30Limite da pontuação de bot (1-99; quanto menor, mais provável que seja um bot)

Formato de preço

Os preços podem ser especificados em vários formatos:

  • String em dólares — "$0.10" (o prefixo $ é removido e o valor é repassado como está)
  • String numérica — "0.10"
  • Número — 0.10
  • Objeto — { amount: "100000", asset: "0x...", extra: {} } para informar explicitamente o ativo e o valor

Identificadores de rede

O CAIP-2 atribui identificadores inequívocos às redes blockchain, como eip155:8453 para a mainnet da Base. Use o identificador exato anunciado pelo facilitator. O pacote aceita a família eip155:* quando o suporte a EVM está ativado e a família solana:* quando o suporte à Solana Virtual Machine (SVM) está ativado; isso não garante que um facilitator aceite todas as redes dessa família.

Substituir uma única requisição

Substitua os padrões da configuração para uma página específica:

await x402.enforce(Astro.request, {
	price: "$0.25", // Override price
	payTo: "0xDifferentWallet", // Override wallet
	network: "eip155:1", // Override network
	description: "Article: How x402 Works", // Resource description
	mimeType: "text/html", // MIME type hint
});

Ativar o suporte à Solana

A Solana é opcional. Instale @x402/svm e ative-a na configuração:

pnpm add @x402/svm

Defina svm: true, use o identificador CAIP-2 da Solana compatível com o facilitator e desative a EVM se o site aceitar apenas pagamentos em Solana. Por exemplo, o identificador da mainnet da Solana usado pelo @x402/svm 2.8 é solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:

x402({
	payTo: "YourSolanaAddress",
	network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
	svm: true,
	evm: false, // Disable EVM if only using Solana
});

Fluxo da requisição

  1. A integração coloca o enforcer de pagamentos em Astro.locals.x402 antes de a página ser executada.
  2. enforce() procura um cabeçalho payment-signature.
  3. Sem um cabeçalho de pagamento, enforce() retorna uma resposta 402 Payment Required cujo corpo e cujo cabeçalho PAYMENT-REQUIRED descrevem o pagamento aceito.
  4. Com um pagamento correspondente, o facilitator configurado o verifica e o liquida.
  5. enforce() retorna o pagador e o resultado da liquidação. Chame applyHeaders() para que a resposta da página inclua a prova PAYMENT-RESPONSE do facilitator.