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çã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 (por exemplo, eip155:8453) |
defaultPrice | Price | — | Preço padrão, que pode ser substituído por página |
facilitatorUrl | string | https://x402.org/facilitator | URL do facilitator de pagamento |
scheme | string | "exact" | Esquema de pagamento |
maxTimeoutSeconds | number | 60 | Tempo limite máximo para as assinaturas de pagamento |
evm | boolean | true | Ativar o suporte às redes da Ethereum Virtual Machine |
svm | boolean | false | Ativar o suporte à Solana Virtual Machine (requer @x402/svm) |
botOnly | boolean | false | Exigir pagamento apenas de bots |
botScoreThreshold | number | 30 | Limite 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
- A integração coloca o enforcer de pagamentos em
Astro.locals.x402antes de a página ser executada. enforce()procura um cabeçalhopayment-signature.- Sem um cabeçalho de pagamento,
enforce()retorna uma resposta402 Payment Requiredcujo corpo e cujo cabeçalhoPAYMENT-REQUIREDdescrevem o pagamento aceito. - Com um pagamento correspondente, o facilitator configurado o verifica e o liquida.
enforce()retorna o pagador e o resultado da liquidação. ChameapplyHeaders()para que a resposta da página inclua a provaPAYMENT-RESPONSEdo facilitator.