x402 Payments

이 페이지

@emdash-cms/x402 패키지는 Cloudflare의 모든 Astro 사이트에 x402 결제 프로토콜 지원을 추가합니다. 독립적인 Astro 통합으로 실행되며, EmDash를 사용할 때 페이지별 가격 책정을 위해 EmDash의 CMS 필드와 결합됩니다.

x402는 HTTP 네이티브 결제 프로토콜입니다. 클라이언트가 결제 없이 유료 리소스를 요청하면 서버는 402 Payment Required와 기계가 읽을 수 있는 결제 지침으로 응답합니다. x402를 이해하는 에이전트와 브라우저는 자동으로 결제를 완료하고 요청을 재시도할 수 있습니다.

언제 사용할까요

가장 일반적인 사용 사례는 봇 전용 모드입니다: AI 에이전트와 스크레이퍼에게 콘텐츠 액세스 비용을 청구하면서 인간 방문자는 무료로 읽을 수 있습니다. 이는 Cloudflare Bot Management를 사용하여 봇과 인간을 구별합니다.

모든 방문자에게 결제를 강제하거나, 강제하지 않고 결제 헤더를 확인(조건부 렌더링)할 수도 있습니다.

설치

패키지 매니저로 패키지를 설치합니다:

pnpm

pnpm add @emdash-cms/x402

npm

npm install @emdash-cms/x402

yarn

yarn add @emdash-cms/x402

설정

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

TypeScript가 Astro.locals.x402를 인식하도록 타입 참조를 추가합니다:

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

기본 사용법

통합은 Astro.locals.x402에 enforcer를 배치합니다. 페이지 프론트매터에서 enforce()를 호출하여 결제 뒤에 콘텐츠를 보호합니다:

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

enforce() 메서드는 다음 중 하나를 반환합니다:

  • Response (402) — 클라이언트가 결제해야 합니다. 직접 반환합니다.
  • EnforceResult — 요청이 계속되어야 합니다. 콘텐츠가 결제되었거나 강제가 건너뛰어졌습니다(botOnly 모드의 인간).

봇 전용 모드

botOnlytrue일 때, 통합은 request.cf.botManagement.score를 읽어 요청을 분류합니다:

  • 임계값 이하의 점수(기본값 30) -> 봇으로 처리되어 결제 강제
  • 임계값 이상의 점수 -> 인간으로 처리되어 강제 건너뜀
  • 봇 관리 데이터 없음(로컬 개발, 비CF 배포) -> 인간으로 처리

EnforceResult에는 skipped 플래그가 포함되어 “결제할 필요가 없었음”과 “결제함”을 구별할 수 있습니다:

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

EmDash로 페이지별 가격 책정

EmDash를 사용할 때 페이지별 가격 책정을 위해 컬렉션에 일반 number 필드를 추가하고 요청 시 읽습니다:

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

강제하지 않고 결제 확인

hasPayment()를 사용하여 요청에 결제 헤더가 포함되어 있는지 확인하거나 강제하지 않고 확인합니다. 이는 조건부 렌더링에 유용합니다 — 결제한 방문자와 결제하지 않은 방문자에게 다른 콘텐츠를 표시합니다:

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

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

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

구성 참조

옵션타입기본값설명
payTostring필수대상 지갑 주소
networkstring필수CAIP-2 네트워크 식별자 (예: eip155:8453)
defaultPricePrice기본 가격, 페이지별로 재정의 가능
facilitatorUrlstringhttps://x402.org/facilitator결제 촉진자 URL
schemestring"exact"결제 스킴
maxTimeoutSecondsnumber60결제 서명 최대 타임아웃
evmbooleantrueEVM 체인 지원 활성화
svmbooleanfalseSolana 체인 지원 활성화 (@x402/svm 필요)
botOnlybooleanfalse봇에게만 결제 강제
botScoreThresholdnumber30봇 점수 임계값 (1-99, 낮을수록 봇일 가능성 높음)

가격 형식

가격은 여러 형식으로 지정할 수 있습니다:

  • 달러 문자열"$0.10" ($ 접두사가 제거되고 값이 그대로 전달됨)
  • 숫자 문자열"0.10"
  • 숫자0.10
  • 객체{ amount: "100000", asset: "0x...", extra: {} } 명시적 자산/금액

네트워크 식별자

네트워크는 CAIP-2 형식을 사용합니다:

네트워크식별자
Base mainneteip155:8453
Base Sepoliaeip155:84532
Ethereumeip155:1
Solanasolana:mainnet

Enforce 옵션

특정 페이지의 구성 기본값을 재정의합니다:

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

Solana 지원

Solana는 옵트인입니다. @x402/svm을 설치하고 구성에서 활성화합니다:

pnpm add @x402/svm

네트워크를 Solana 식별자로 설정하고 Solana만 사용하는 경우 EVM을 비활성화합니다:

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

작동 방식

  1. x402() 통합은 enforcer를 생성하고 Astro.locals.x402에 배치하는 미들웨어를 등록합니다
  2. 구성은 Vite 가상 모듈(virtual:x402/config)을 통해 미들웨어에 전달됩니다
  3. enforce()가 호출되면 요청의 payment-signature 헤더를 확인합니다
  4. 결제 헤더가 없으면 PAYMENT-REQUIRED 헤더에 결제 지침이 포함된 402 Payment Required 응답이 반환됩니다
  5. 결제 헤더가 있으면 촉진자 서비스를 통해 확인되고 정산됩니다
  6. 정산 후 applyHeaders()를 통해 응답에 PAYMENT-RESPONSE 헤더가 설정됩니다

리소스 서버는 첫 요청 시 지연 초기화되고 워커 수명 동안 캐시됩니다.