x402 Payments

Sur cette page

Le package @emdash-cms/x402 ajoute la prise en charge du protocole de paiement x402 à n’importe quel site Astro sur Cloudflare. Il fonctionne comme une intégration Astro autonome et se combine avec les champs CMS d’EmDash pour la tarification par page lorsque vous utilisez EmDash.

x402 est un protocole de paiement natif HTTP. Lorsqu’un client demande une ressource payante sans paiement, le serveur répond avec 402 Payment Required et des instructions de paiement lisibles par machine. Les agents et navigateurs qui comprennent x402 peuvent effectuer le paiement automatiquement et réessayer la requête.

Quand utiliser ceci

Le cas d’usage le plus courant est le mode bots uniquement : facturez les agents IA et les scrapers pour l’accès au contenu tout en laissant les visiteurs humains lire gratuitement. Cela utilise Cloudflare Bot Management pour distinguer les bots des humains.

Vous pouvez également imposer le paiement pour tous les visiteurs, ou vérifier les en-têtes de paiement sans imposer (rendu conditionnel).

Installation

Installez le package avec votre gestionnaire de packages :

pnpm

pnpm add @emdash-cms/x402

npm

npm install @emdash-cms/x402

yarn

yarn add @emdash-cms/x402

Configuration

Ajoutez l’intégration à votre configuration 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,
		}),
	],
});

Ajoutez la référence de type pour que TypeScript connaisse Astro.locals.x402 :

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

Utilisation de base

L’intégration place un enforcer sur Astro.locals.x402. Appelez enforce() dans le frontmatter de votre page pour protéger le contenu derrière un paiement :

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

La méthode enforce() retourne soit :

  • Une Response (402) — le client doit payer. Retournez-la directement.
  • Un EnforceResult — la requête doit continuer. Le contenu a été payé, ou l’application a été ignorée (humain en mode botOnly).

Mode bots uniquement

Lorsque botOnly est true, l’intégration lit request.cf.botManagement.score pour classifier les requêtes :

  • Score en dessous du seuil (par défaut 30) -> traité comme bot, paiement imposé
  • Score au seuil ou au-dessus -> traité comme humain, application ignorée
  • Pas de données de gestion des bots (développement local, déploiement non-CF) -> traité comme humain

Le EnforceResult inclut un flag skipped pour que vous puissiez distinguer “n’a pas eu besoin de payer” de “a payé” :

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

Tarification par page avec EmDash

Lors de l’utilisation d’EmDash, ajoutez un champ number régulier à votre collection pour la tarification par page et lisez-le au moment de la requête :

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

Vérifier le paiement sans imposer

Utilisez hasPayment() pour vérifier si une requête inclut des en-têtes de paiement sans vérifier ou imposer. Ceci est utile pour le rendu conditionnel — afficher un contenu différent aux visiteurs payants vs non-payants :

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

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

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

Référence de configuration

OptionTypePar défautDescription
payTostringrequisAdresse de wallet de destination
networkstringrequisIdentifiant de réseau CAIP-2 (ex. eip155:8453)
defaultPricePricePrix par défaut, remplaçable par page
facilitatorUrlstringhttps://x402.org/facilitatorURL du facilitateur de paiement
schemestring"exact"Schéma de paiement
maxTimeoutSecondsnumber60Délai maximum pour les signatures de paiement
evmbooleantrueActiver la prise en charge de la chaîne EVM
svmbooleanfalseActiver la prise en charge de la chaîne Solana (nécessite @x402/svm)
botOnlybooleanfalseImposer le paiement uniquement pour les bots
botScoreThresholdnumber30Seuil de score de bot (1-99, plus bas = plus probable qu’il s’agisse d’un bot)

Format de prix

Les prix peuvent être spécifiés dans plusieurs formats :

  • Chaîne dollar"$0.10" (le préfixe $ est supprimé, la valeur est passée telle quelle)
  • Chaîne numérique"0.10"
  • Nombre0.10
  • Objet{ amount: "100000", asset: "0x...", extra: {} } pour un actif/montant explicite

Identifiants de réseau

Les réseaux utilisent le format CAIP-2 :

RéseauIdentifiant
Base mainneteip155:8453
Base Sepoliaeip155:84532
Ethereumeip155:1
Solanasolana:mainnet

Options d’Enforce

Remplacez les valeurs par défaut de configuration pour une page spécifique :

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

Prise en charge de Solana

Solana est opt-in. Installez @x402/svm et activez-le dans la configuration :

pnpm add @x402/svm

Définissez le réseau sur un identifiant Solana et désactivez EVM si vous utilisez uniquement Solana :

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

Comment ça fonctionne

  1. L’intégration x402() enregistre un middleware qui crée un enforcer et le place sur Astro.locals.x402
  2. La configuration est passée au middleware via un module virtuel Vite (virtual:x402/config)
  3. Lorsque enforce() est appelé, il vérifie la présence d’un en-tête payment-signature sur la requête
  4. Si aucun en-tête de paiement n’est présent, une réponse 402 Payment Required est retournée avec des instructions de paiement dans l’en-tête PAYMENT-REQUIRED
  5. Si un en-tête de paiement est présent, il est vérifié via le service facilitateur et réglé
  6. Après le règlement, les en-têtes PAYMENT-RESPONSE sont définis sur la réponse via applyHeaders()

Le serveur de ressources est initialisé de manière lazy à la première requête et mis en cache pendant la durée de vie du worker.