x402-Zahlungen

Auf dieser Seite

Das Paket @emdash-cms/x402 ergänzt jede Astro-Site auf Cloudflare um Unterstützung für das x402-Zahlungsprotokoll. Es funktioniert eigenständig — ohne Abhängigkeit vom EmDash-Kern — und lässt sich gut mit CMS-Feldern von EmDash für preisliche Seiten kombinieren.

x402 ist ein HTTP-natives Zahlungsprotokoll. Fordert ein Client eine kostenpflichtige Ressource ohne Zahlung an, antwortet der Server mit 402 Payment Required und maschinenlesbaren Zahlungsanweisungen. Clients, die x402 verstehen (Agenten, Browser), können die Zahlung automatisch ausführen und die Anfrage wiederholen.

Wann einsetzen

Der häufigste Fall ist der Bot-only-Modus: KI-Agenten und Scraper zahlen für den Zugriff, menschliche Besucher lesen kostenlos. Dafür nutzt man Cloudflare Bot Management zur Unterscheidung.

Sie können Zahlungen auch für alle Besucher erzwingen oder nur prüfen, ob Zahlungs-Header vorhanden sind (bedingtes Rendering).

Installation

pnpm

pnpm add @emdash-cms/x402

npm

npm install @emdash-cms/x402

yarn

yarn add @emdash-cms/x402

Einrichtung

Fügen Sie die Integration in Ihre Astro-Konfiguration ein:

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

Ergänzen Sie die Typreferenz, damit TypeScript Astro.locals.x402 kennt:

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

Grundlegende Nutzung

Die Integration stellt einen Enforcer unter Astro.locals.x402 bereit. Rufen Sie enforce() im Frontmatter Ihrer Seite auf, um Inhalte hinter Zahlung zu schützen:

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

const result = await x402.enforce(Astro.request, {
  price: "$0.05",
  description: "Premium-Artikel",
});

// Ohne gültige Zahlung liefert enforce() eine 402-Response.
// Direkt zurückgeben, um dem Client Zahlungsanweisungen zu senden.
if (result instanceof Response) return result;

// Zahlung verifiziert (oder im botOnly-Modus übersprungen). Response-Header setzen
// für den Nachweis der Abwicklung.
x402.applyHeaders(result, Astro.response);
---

<article>
  <h1>Premium-Inhalt</h1>
</article>

enforce() liefert entweder:

  • eine Response (402) — der Client muss zahlen; direkt zurückgeben.
  • ein EnforceResult — die Anfrage soll fortgesetzt werden. Der Inhalt wurde bezahlt oder die Prüfung wurde übersprungen (Mensch im botOnly-Modus).

Bot-only-Modus

Ist botOnly true, liest die Integration request.cf.botManagement.score:

  • Score unter dem Schwellenwert (Standard 30) → als Bot behandelt, Zahlung erzwungen
  • Score ab Schwellenwert → als Mensch behandelt, Prüfung übersprungen
  • Keine Bot-Management-Daten (lokale Entwicklung, kein CF) → als Mensch behandelt

EnforceResult enthält ein Flag skipped, um „musste nicht zahlen“ von „hat gezahlt“ zu unterscheiden:

---
const result = await x402.enforce(Astro.request, { price: "$0.01" });
if (result instanceof Response) return result;

x402.applyHeaders(result, Astro.response);

// result.paid    — true, wenn Zahlung verifiziert
// result.skipped — true, wenn Prüfung übersprungen (Mensch im botOnly-Modus)
// result.payer   — Wallet-Adresse des Zahlers (falls gezahlt)
---

Preise pro Seite mit EmDash

Mit EmDash können Sie ein number-Feld in der Sammlung für den Seitenpreis nutzen. Kein spezielles Schema nötig — ein normales CMS-Feld:

---
import { getEmDashEntry } from "emdash";

const { slug } = Astro.params;
const { entry } = await getEmDashEntry("posts", slug);

if (!entry) return Astro.redirect("/404");

const { x402 } = Astro.locals;

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>

Zahlung prüfen ohne Erzwingen

Mit hasPayment() prüfen Sie, ob die Anfrage Zahlungs-Header enthält, ohne zu verifizieren oder zu erzwingen. Nützlich für bedingtes Rendering — unterschiedliche Inhalte für zahlende und nicht zahlende Besucher:

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

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

{hasPaid ? (
  <p>Hier der vollständige Premium-Inhalt.</p>
) : (
  <p>Abonnieren Sie für den vollen Artikel.</p>
)}

Konfigurationsreferenz

OptionTypStandardBeschreibung
payTostringerforderlichZiel-Wallet-Adresse
networkstringerforderlichCAIP-2-Netzwerk-ID (z. B. eip155:8453)
defaultPricePriceStandardpreis, pro Seite überschreibbar
facilitatorUrlstringhttps://x402.org/facilitatorURL des Payment-Facilitators
schemestring"exact"Zahlungsschema
maxTimeoutSecondsnumber60Maximale Gültigkeit der Zahlungssignatur
evmbooleantrueEVM-Ketten aktivieren
svmbooleanfalseSolana aktivieren (benötigt @x402/svm)
botOnlybooleanfalseZahlung nur für Bots erzwingen
botScoreThresholdnumber30Bot-Score-Schwelle (1–99, niedriger = eher Bot)

Preisformate

Preise können auf verschiedene Weise angegeben werden:

  • Dollar-String"$0.10" (Präfix $ wird entfernt, Wert wird weitergegeben)
  • Numerischer String"0.10"
  • Zahl0.10
  • Objekt{ amount: "100000", asset: "0x...", extra: {} } für explizites Asset/Betrag

Netzwerk-IDs

Netzwerke nutzen das CAIP-2-Format:

NetzwerkKennung
Base Mainneteip155:8453
Base Sepoliaeip155:84532
Ethereumeip155:1
Solanasolana:mainnet

Optionen für enforce

Konfigurations-Defaults pro Seite überschreiben:

await x402.enforce(Astro.request, {
	price: "$0.25",
	payTo: "0xDifferentWallet",
	network: "eip155:1",
	description: "Artikel: Wie x402 funktioniert",
	mimeType: "text/html",
});

Solana-Unterstützung

Solana ist optional. Installieren Sie @x402/svm und aktivieren Sie es in der Konfiguration:

pnpm add @x402/svm
x402({
	payTo: "YourSolanaAddress",
	network: "solana:mainnet",
	svm: true,
	evm: false,
});

Funktionsweise

  1. Die Integration x402() registriert Middleware, die einen Enforcer erzeugt und unter Astro.locals.x402 ablegt.
  2. Die Konfiguration wird per Vite-Virtual-Modul (virtual:x402/config) an die Middleware übergeben.
  3. Bei enforce() wird auf einen payment-signature-Header der Anfrage geprüft.
  4. Fehlt der Header, wird eine 402 Payment Required-Response mit Anweisungen im Header PAYMENT-REQUIRED zurückgegeben.
  5. Ist der Header vorhanden, wird er über den Facilitator verifiziert und abgewickelt.
  6. Nach der Abwicklung setzt applyHeaders() die Header PAYMENT-RESPONSE auf der Response.

Der Ressourcen-Server wird beim ersten Request lazy initialisiert und für die Worker-Laufzeit gecacht.