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
| Option | Type | Par défaut | Description |
|---|---|---|---|
payTo | string | requis | Adresse de wallet de destination |
network | string | requis | Identifiant de réseau CAIP-2 (ex. eip155:8453) |
defaultPrice | Price | — | Prix par défaut, remplaçable par page |
facilitatorUrl | string | https://x402.org/facilitator | URL du facilitateur de paiement |
scheme | string | "exact" | Schéma de paiement |
maxTimeoutSeconds | number | 60 | Délai maximum pour les signatures de paiement |
evm | boolean | true | Activer la prise en charge de la chaîne EVM |
svm | boolean | false | Activer la prise en charge de la chaîne Solana (nécessite @x402/svm) |
botOnly | boolean | false | Imposer le paiement uniquement pour les bots |
botScoreThreshold | number | 30 | Seuil 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" - Nombre —
0.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éseau | Identifiant |
|---|---|
| Base mainnet | eip155:8453 |
| Base Sepolia | eip155:84532 |
| Ethereum | eip155:1 |
| Solana | solana: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
- L’intégration
x402()enregistre un middleware qui crée un enforcer et le place surAstro.locals.x402 - La configuration est passée au middleware via un module virtuel Vite (
virtual:x402/config) - Lorsque
enforce()est appelé, il vérifie la présence d’un en-têtepayment-signaturesur la requête - Si aucun en-tête de paiement n’est présent, une réponse
402 Payment Requiredest retournée avec des instructions de paiement dans l’en-têtePAYMENT-REQUIRED - Si un en-tête de paiement est présent, il est vérifié via le service facilitateur et réglé
- Après le règlement, les en-têtes
PAYMENT-RESPONSEsont définis sur la réponse viaapplyHeaders()
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.