O pacote @emdash-cms/x402 adiciona suporte ao protocolo de pagamento x402 a qualquer site Astro no Cloudflare. Ele é executado como uma integração Astro independente e combina com os campos CMS do EmDash para preços por página quando você usa o EmDash.
x402 é um protocolo de pagamento nativo HTTP. Quando um cliente solicita um recurso pago sem pagamento, o servidor responde com 402 Payment Required e instruções de pagamento legíveis por máquina. Agentes e navegadores que entendem x402 podem completar o pagamento automaticamente e tentar novamente a solicitação.
Quando usar isto
O caso de uso mais comum é o modo somente bots: cobre de agentes de IA e scrapers pelo acesso ao conteúdo enquanto permite que visitantes humanos leiam gratuitamente. Isso usa o Cloudflare Bot Management para distinguir bots de humanos.
Você também pode impor pagamento para todos os visitantes, ou verificar cabeçalhos de pagamento sem impor (renderização condicional).
Instalação
Instale o pacote com seu gerenciador de pacotes:
pnpm
pnpm add @emdash-cms/x402 npm
npm install @emdash-cms/x402 yarn
yarn add @emdash-cms/x402 Configuração
Adicione a integração à sua configuração Astro:
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",
botOnly: true,
botScoreThreshold: 30,
}),
],
});
Adicione a referência de tipo para que o TypeScript conheça Astro.locals.x402:
/// <reference types="@emdash-cms/x402/locals" />
Uso básico
A integração coloca um enforcer em Astro.locals.x402. Chame enforce() no frontmatter da sua página para proteger conteúdo atrás de 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:
- Uma
Response(402) — o cliente precisa pagar. Retorne-a diretamente. - Um
EnforceResult— a solicitação deve prosseguir. O conteúdo foi pago, ou a imposição foi pulada (humano no modo botOnly).
Modo somente bots
Quando botOnly é true, a integração lê request.cf.botManagement.score para classificar solicitações:
- Pontuação abaixo do limiar (padrão 30) -> tratado como bot, pagamento imposto
- Pontuação no limiar ou acima -> tratado como humano, imposição pulada
- Sem dados de gerenciamento de bots (dev local, implantação não-CF) -> tratado como humano
O EnforceResult inclui uma flag 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)
---
Preços por página com EmDash
Ao usar o EmDash, adicione um campo number regular à sua coleção para preços por página e leia-o no momento da solicitação:
---
import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;
const { entry } = await getEmDashEntry("posts", slug);
if (!entry) return Astro.redirect("/404");
const { x402 } = Astro.locals;
// Use the price from the CMS, falling back to a default
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 pagamento sem impor
Use hasPayment() para verificar se uma solicitação inclui cabeçalhos de pagamento sem verificar ou impor. Isso é útil para renderização condicional — mostrar conteúdo diferente para visitantes pagantes vs não pagantes:
---
const { x402 } = Astro.locals;
const hasPaid = x402.hasPayment(Astro.request);
---
{hasPaid ? (
<p>Full premium content here.</p>
) : (
<p>Subscribe for the full article.</p>
)}
Referência de configuração
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
payTo | string | obrigatório | Endereço da carteira de destino |
network | string | obrigatório | Identificador de rede CAIP-2 (ex: eip155:8453) |
defaultPrice | Price | — | Preço padrão, substituível por página |
facilitatorUrl | string | https://x402.org/facilitator | URL do facilitador de pagamento |
scheme | string | "exact" | Esquema de pagamento |
maxTimeoutSeconds | number | 60 | Timeout máximo para assinaturas de pagamento |
evm | boolean | true | Habilitar suporte de cadeia EVM |
svm | boolean | false | Habilitar suporte de cadeia Solana (requer @x402/svm) |
botOnly | boolean | false | Impor pagamento apenas para bots |
botScoreThreshold | number | 30 | Limiar de pontuação de bot (1-99, menor = mais provável ser bot) |
Formato de preço
Os preços podem ser especificados em vários formatos:
- String de dólar —
"$0.10"(o prefixo$é removido, o valor é passado como está) - String numérica —
"0.10" - Número —
0.10 - Objeto —
{ amount: "100000", asset: "0x...", extra: {} }para ativo/valor explícito
Identificadores de rede
As redes usam o formato CAIP-2:
| Rede | Identificador |
|---|---|
| Base mainnet | eip155:8453 |
| Base Sepolia | eip155:84532 |
| Ethereum | eip155:1 |
| Solana | solana:mainnet |
Opções de Enforce
Substitua os padrões de 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
});
Suporte Solana
Solana é opt-in. Instale @x402/svm e habilite-o na configuração:
pnpm add @x402/svm
Defina a rede para um identificador Solana e desabilite EVM se você usar apenas Solana:
x402({
payTo: "YourSolanaAddress",
network: "solana:mainnet",
svm: true,
evm: false, // Disable EVM if only using Solana
});
Como funciona
- A integração
x402()registra middleware que cria um enforcer e o coloca emAstro.locals.x402 - A configuração é passada para o middleware via um módulo virtual Vite (
virtual:x402/config) - Quando
enforce()é chamado, ele verifica um cabeçalhopayment-signaturena solicitação - Se nenhum cabeçalho de pagamento estiver presente, uma resposta
402 Payment Requiredé retornada com instruções de pagamento no cabeçalhoPAYMENT-REQUIRED - Se um cabeçalho de pagamento estiver presente, ele é verificado através do serviço facilitador e liquidado
- Após a liquidação, os cabeçalhos
PAYMENT-RESPONSEsão definidos na resposta viaapplyHeaders()
O servidor de recursos é inicializado preguiçosamente na primeira solicitação e armazenado em cache pela vida útil do worker.