x402 Payments

En esta página

El paquete @emdash-cms/x402 agrega soporte para el protocolo de pago x402 a cualquier sitio Astro en Cloudflare. Se ejecuta como una integración Astro independiente y se combina con los campos CMS de EmDash para precios por página cuando usas EmDash.

x402 es un protocolo de pago nativo de HTTP. Cuando un cliente solicita un recurso de pago sin pago, el servidor responde con 402 Payment Required e instrucciones de pago legibles por máquina. Los agentes y navegadores que entienden x402 pueden completar el pago automáticamente y reintentar la solicitud.

Cuándo usar esto

El caso de uso más común es el modo solo bots: cobra a los agentes de IA y scrapers por el acceso al contenido mientras permites que los visitantes humanos lean gratis. Esto usa Cloudflare Bot Management para distinguir bots de humanos.

También puedes forzar el pago para todos los visitantes, o verificar encabezados de pago sin forzar (renderizado condicional).

Instalación

Instala el paquete con tu gestor de paquetes:

pnpm

pnpm add @emdash-cms/x402

npm

npm install @emdash-cms/x402

yarn

yarn add @emdash-cms/x402

Configuración

Agrega la integración a tu configuración de 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,
		}),
	],
});

Agrega la referencia de tipo para que TypeScript conozca Astro.locals.x402:

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

Uso básico

La integración coloca un enforcer en Astro.locals.x402. Llama a enforce() en el frontmatter de tu página para proteger contenido detrás de pago:

---
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>

El método enforce() devuelve:

  • Una Response (402) — el cliente necesita pagar. Devuélvela directamente.
  • Un EnforceResult — la solicitud debe continuar. El contenido fue pagado, o la aplicación fue omitida (humano en modo botOnly).

Modo solo bots

Cuando botOnly es true, la integración lee request.cf.botManagement.score para clasificar solicitudes:

  • Puntuación por debajo del umbral (predeterminado 30) -> tratado como bot, pago forzado
  • Puntuación en o por encima del umbral -> tratado como humano, aplicación omitida
  • Sin datos de gestión de bots (desarrollo local, despliegue no-CF) -> tratado como humano

El EnforceResult incluye un flag skipped para que puedas distinguir entre “no necesitó pagar” y “pagó”:

---
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)
---

Precios por página con EmDash

Al usar EmDash, agrega un campo regular number a tu colección para precios por página y léelo en tiempo de solicitud:

---
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 pago sin forzar

Usa hasPayment() para verificar si una solicitud incluye encabezados de pago sin verificar o forzar. Esto es útil para renderizado condicional — mostrar contenido diferente a visitantes que pagaron vs. los que no:

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

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

{hasPaid ? (
  <p>Full premium content here.</p>
) : (
  <p>Subscribe for the full article.</p>
)}

Referencia de configuración

OpciónTipoPredeterminadoDescripción
payTostringrequeridoDirección de wallet de destino
networkstringrequeridoIdentificador de red CAIP-2 (ej. eip155:8453)
defaultPricePricePrecio predeterminado, sobrescribible por página
facilitatorUrlstringhttps://x402.org/facilitatorURL del facilitador de pago
schemestring"exact"Esquema de pago
maxTimeoutSecondsnumber60Tiempo de espera máximo para firmas de pago
evmbooleantrueHabilitar soporte de cadena EVM
svmbooleanfalseHabilitar soporte de cadena Solana (requiere @x402/svm)
botOnlybooleanfalseSolo forzar pago para bots
botScoreThresholdnumber30Umbral de puntuación de bot (1-99, menor = más probable que sea bot)

Formato de precio

Los precios se pueden especificar en varios formatos:

  • String de dólar"$0.10" (el prefijo $ se elimina, el valor se pasa tal cual)
  • String numérico"0.10"
  • Número0.10
  • Objeto{ amount: "100000", asset: "0x...", extra: {} } para cantidad/activo explícito

Identificadores de red

Las redes usan formato CAIP-2:

RedIdentificador
Base mainneteip155:8453
Base Sepoliaeip155:84532
Ethereumeip155:1
Solanasolana:mainnet

Opciones de Enforce

Sobrescribe los valores predeterminados de configuración para una 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
});

Soporte de Solana

Solana es opt-in. Instala @x402/svm y habilítalo en la configuración:

pnpm add @x402/svm

Establece la red a un identificador de Solana y deshabilita EVM si solo usas Solana:

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

Cómo funciona

  1. La integración x402() registra middleware que crea un enforcer y lo coloca en Astro.locals.x402
  2. La configuración se pasa al middleware a través de un módulo virtual de Vite (virtual:x402/config)
  3. Cuando se llama a enforce(), verifica un encabezado payment-signature en la solicitud
  4. Si no hay encabezado de pago presente, se devuelve una respuesta 402 Payment Required con instrucciones de pago en el encabezado PAYMENT-REQUIRED
  5. Si hay un encabezado de pago presente, se verifica a través del servicio facilitador y se liquida
  6. Después de la liquidación, los encabezados PAYMENT-RESPONSE se establecen en la respuesta a través de applyHeaders()

El servidor de recursos se inicializa de forma lazy en la primera solicitud y se almacena en caché durante la vida útil del worker.