@emdash-cms/x402 패키지는 Cloudflare의 모든 Astro 사이트에 x402 결제 프로토콜 지원을 추가합니다. 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: "프리미엄 기사",
});
// 유효한 결제가 없으면 enforce()는 402 Response를 반환합니다.
// 그대로 반환해 클라이언트에 결제 안내를 보냅니다.
if (result instanceof Response) return result;
// 결제 확인됨(또는 botOnly에서 건너뜀). 정산 증명을 위해 응답 헤더를 적용합니다.
x402.applyHeaders(result, Astro.response);
---
<article>
<h1>프리미엄 콘텐츠</h1>
</article>
enforce()는 다음 중 하나를 반환합니다.
Response(402) — 결제 필요. 그대로 반환합니다.EnforceResult— 요청을 계속합니다. 결제됨이거나 botOnly에서 사람으로 건너뜀입니다.
봇 전용 모드
botOnly가 true이면 통합은 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
// result.skipped — botOnly에서 사람으로 건너뛰면 true
// result.payer — 결제자 지갑(결제 시)
---
EmDash로 페이지별 가격
EmDash에서는 컬렉션에 number 필드를 추가해 페이지 가격을 둘 수 있습니다. 별도 스키마 없이 일반 CMS 필드면 됩니다.
---
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>
강제 없이 결제 여부만 확인
hasPayment()는 검증·강제 없이 요청에 결제 헤더가 있는지만 봅니다. 결제자와 비결제자에게 다른 내용을 보여 주는 조건부 렌더링에 유용합니다.
---
const { x402 } = Astro.locals;
const hasPaid = x402.hasPayment(Astro.request);
---
{hasPaid ? (
<p>전체 프리미엄 콘텐츠입니다.</p>
) : (
<p>전체 기사를 보려면 구독하세요.</p>
)}
설정 참조
| 옵션 | 타입 | 기본값 | 설명 |
|---|---|---|---|
payTo | string | 필수 | 수신 지갑 주소 |
network | string | 필수 | CAIP-2 네트워크 ID(예: eip155:8453) |
defaultPrice | Price | — | 기본 가격, 페이지마다 재정의 가능 |
facilitatorUrl | string | https://x402.org/facilitator | 결제 중개 URL |
scheme | string | "exact" | 결제 스킴 |
maxTimeoutSeconds | number | 60 | 결제 서명 최대 타임아웃(초) |
evm | boolean | true | EVM 체인 활성화 |
svm | boolean | false | Solana 활성화(@x402/svm 필요) |
botOnly | boolean | false | 봇에만 결제 강제 |
botScoreThreshold | number | 30 | 봇 점수 임계값(1–99, 낮을수록 봇에 가깝다) |
가격 형식
다음 형식으로 가격을 지정할 수 있습니다.
- 달러 문자열 —
"$0.10"($접두사는 제거되고 값은 그대로 전달) - 숫자 문자열 —
"0.10" - 숫자 —
0.10 - 객체 —
{ amount: "100000", asset: "0x...", extra: {} }로 자산·금액 명시
네트워크 식별자
네트워크는 CAIP-2 형식을 사용합니다.
| 네트워크 | 식별자 |
|---|---|
| Base mainnet | eip155:8453 |
| Base Sepolia | eip155:84532 |
| Ethereum | eip155:1 |
| Solana | solana:mainnet |
enforce 옵션
특정 페이지에서 설정 기본값을 재정의합니다.
await x402.enforce(Astro.request, {
price: "$0.25",
payTo: "0xDifferentWallet",
network: "eip155:1",
description: "기사: x402 작동 방식",
mimeType: "text/html",
});
Solana 지원
Solana는 선택 사항입니다. @x402/svm을 설치하고 설정에서 켭니다.
pnpm add @x402/svm
x402({
payTo: "YourSolanaAddress",
network: "solana:mainnet",
svm: true,
evm: false,
});
동작 방식
x402()통합이 미들웨어를 등록해 적용기를 만들고Astro.locals.x402에 둡니다.- 설정은 Vite 가상 모듈(
virtual:x402/config)로 미들웨어에 전달됩니다. enforce()호출 시 요청의payment-signature헤더를 확인합니다.- 결제 헤더가 없으면
402 Payment Required와PAYMENT-REQUIRED헤더로 응답합니다. - 헤더가 있으면 중개 서비스로 검증·정산합니다.
- 정산 후
applyHeaders()로 응답에PAYMENT-RESPONSE를 설정합니다.
리소스 서버는 첫 요청 시 지연 초기화되며 워커 수명 동안 캐시됩니다.