Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

종량제 API 구축

이 가이드는 x402를 사용하여 API 엔드포인트를 수익화하는 방법을 안내합니다. 서버는 지불 핸들러를 추가하고, 클라이언트는 요청당 지불하며, 결제는 HTTP 수명 주기 내에서 이루어집니다.

구축할 내용

서버가 402 Payment Required로 응답하고, 클라이언트가 요청당 지불하며, 촉진자가 HTTP 수명 주기 내에서 온체인 USDT0을 결제하는 유료 HTTP API.

데모

step 1. Client: GET /weather (지불 없음)
        Server: 402 Payment Required
                PAYMENT-REQUIRED: { amount: "1000", asset: USDT0, network: eip155:988 }

step 2. 클라이언트가 ERC-3009 승인에 서명합니다.

step 3. Client: GET /weather + PAYMENT-SIGNATURE 헤더
        Server: 촉진자로 포워딩 → transferWithAuthorization 온체인 결제
                (~700ms 블록 확인)
        Server: 200 OK { weather: "sunny", temperature: 70 }
                PAYMENT-SETTLE-RESPONSE: { txHash: "0x8f3a...", paid: "0.001 USDT0" }

step 4. Stablescan에서 결제 확인
        https://stablescan.xyz/tx/0x8f3a...

개요

판매자 (서버):
// --- 서버 ---
app.use(paymentMiddleware({
  "GET /weather": {
    price: { amount: "1000", asset: USDT0 },
    payTo: sellerAddress,
  },
  "POST /inference": {
    price: { amount: "50000", asset: USDT0 },
    payTo: sellerAddress,
  },
}, resourceServer));
 
// 구성에 나열되지 않은 경로는 게이트되지 않습니다.
구매자 (클라이언트):
// --- 클라이언트 ---
account = new WalletAccountEvm(seedPhrase, { provider: RPC });
client = new x402Client();
fetchWithPayment = wrapFetchWithPayment(fetch, client);
 
weatherResponse = fetchWithPayment("https://api.example.com/weather");
inferenceResponse = fetchWithPayment("https://api.example.com/inference", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ prompt: "Hello" }),
});
 
// 각 유료 요청에 대해:
// 1. 초기 요청은 PAYMENT-REQUIRED 헤더와 함께 402를 반환합니다.
// 2. 클라이언트는 지갑으로 ERC-3009 승인에 서명합니다.
// 3. 클라이언트는 PAYMENT-SIGNATURE 헤더로 재시도합니다.
// 4. 촉진자가 온체인으로 결제하고, 서버는 응답을 반환합니다.

판매자: 유료 엔드포인트 설정

판매자는 x402 미들웨어를 추가하여 지불이 필요한 경로를 정의합니다. 지불 없이 요청이 도착하면 미들웨어는 402 Payment Required와 지불 조건을 응답합니다. 유효한 지불 헤더가 있으면 미들웨어는 서명을 확인하고 온체인에서 지불을 결제하는 촉진자에게 전달합니다. 판매자는 가격과 수신 주소만 구성하고, 촉진자가 확인 및 결제를 처리합니다.

npm install express @x402/express @x402/evm @x402/core

가격 책정

각 경로는 USDT0 기본 단위(6 소수점)로 지불 금액, 네트워크 및 자금을 수신할 주소를 지정합니다. 예를 들어, "1000"$0.001과 같고 "50000"$0.05와 같습니다.

price: {
  amount: "1000",                                      // 기본 단위 (6 소수점)
  asset: USDT0_STABLE,                                 // USDT0 계약 주소
  extra: { name: "USDT0", version: "1", decimals: 6 }, // EIP-712 도메인 정보
}

extra 필드(name, version, decimals)는 구매자의 클라이언트가 EIP-712 서명 구성을 위해 사용하며 온체인 USDT0 계약과 일치해야 합니다.

경로 구성

경로는 METHOD /path 형식을 사용하여 매핑됩니다. 각 경로는 허용되는 지불 방식, 네트워크, 가격 및 자금을 수신할 주소(payTo)를 지정합니다. descriptionmimeType 필드는 구매자와 AI 에이전트가 엔드포인트가 제공하는 것을 발견하는 데 도움이 됩니다. 구성에 나열되지 않은 경로는 게이트되지 않으며 일반 Express 경로처럼 작동합니다.

// server.ts
import express from "express";
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
 
const PAY_TO = process.env.PAY_TO_ADDRESS as `0x${string}`;
const FACILITATOR_URL = "https://x402.semanticpay.io/";
const STABLE_NETWORK = "eip155:988"; // Stable Mainnet CAIP-2 ID
const USDT0_STABLE = "0x779Ded0c9e1022225f8E0630b35a9b54bE713736";
 
const facilitatorClient = new HTTPFacilitatorClient({ url: FACILITATOR_URL });
const resourceServer = new x402ResourceServer(facilitatorClient)
  .register(STABLE_NETWORK, new ExactEvmScheme());
 
const app = express();
 
app.use(
  paymentMiddleware(
    {
      // 예제 1: 유료 GET 경로 구성
      "GET /weather": {
        accepts: [
          {
            scheme: "exact",
            network: STABLE_NETWORK,
            price: {
              amount: "1000", // $0.001
              asset: USDT0_STABLE,
              extra: { name: "USDT0", version: "1", decimals: 6 },
            },
            payTo: PAY_TO,
          },
        ],
        description: "날씨 데이터",
        mimeType: "application/json",
      },
      // 예제 2: 유료 POST 경로 구성
      "POST /inference": {
        accepts: [
          {
            scheme: "exact",
            network: STABLE_NETWORK,
            price: {
              amount: "50000", // $0.05
              asset: USDT0_STABLE,
              extra: { name: "USDT0", version: "1", decimals: 6 },
            },
            payTo: PAY_TO,
          },
        ],
        description: "AI 추론 엔드포인트",
        mimeType: "application/json",
      },
    },
    resourceServer,
  ),
);
 
app.get("/weather", (req, res) => {
  res.json({ weather: "sunny", temperature: 70 });
});
 
app.post("/inference", (req, res) => {
  const { prompt } = req.body;
  res.json({ result: `Inference result for: ${prompt}` });
});
 
// 구성에 나열되지 않아 지불이 필요하지 않습니다.
app.get("/health", (req, res) => {
  res.json({ status: "ok", payTo: PAY_TO });
});
 
const PORT = process.env.PORT || 4021;
app.listen(PORT, () => {
  console.log(`서버가 http://localhost:${PORT}에서 수신 중입니다.`);
  console.log(`GET  /health    - 무료`);
  console.log(`GET  /weather   - 요청당 $0.001`);
  console.log(`POST /inference - 요청당 $0.05`);
});

구매자: 유료 요청하기

구매자는 수동 결제 흐름을 거치지 않고 유료 엔드포인트에 액세스합니다. 구매자는 가스비를 지불하지 않습니다. 촉진자가 온체인으로 결제하며, 구매자는 결제 요구 사항에 명시된 정확한 금액만 지불합니다.

npm install @x402/fetch @x402/evm @tetherto/wdk-wallet-evm

지갑 생성 및 잔액 확인

// client.ts
import WalletManagerEvm from "@tetherto/wdk-wallet-evm";
 
const account = await new WalletManagerEvm(process.env.SEED_PHRASE!, {
  provider: "https://rpc.stable.xyz",
}).getAccount(0);
 
console.log("구매자 주소:", account.address);
 
// USDT0은 6 소수점을 사용합니다. 1000000의 잔액은 1.00 USDT0과 같습니다.
const USDT0_STABLE = "0x779Ded0c9e1022225f8E0630b35a9b54bE713736";
const balance = await account.getTokenBalance(USDT0_STABLE);
console.log("USDT0 잔액:", Number(balance) / 1e6, "USDT0");

x402에 연결하고 유료 요청하기

WalletAccountEvm은 x402가 예상하는 서명자 인터페이스를 만족하므로 x402 클라이언트의 서명자로 직접 등록할 수 있습니다. 등록되면 x402 지원 클라이언트를 통해 전송된 요청은 402 지불 흐름을 자동으로 처리합니다.

import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
 
const client = new x402Client();
registerExactEvmScheme(client, { signer: account });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
 
const response = await fetchWithPayment("http://localhost:4021/weather");
const data = await response.json();
console.log("응답:", data);

내부적으로 fetchWithPayment는 402 응답을 가로채고, 지불 요구 사항(금액, 토큰, 네트워크, 수신자)을 구문 분석하고, WDK 지갑으로 ERC-3009 transferWithAuthorization에 서명하고, PAYMENT-SIGNATURE 헤더로 요청을 재시도합니다.

결제 흐름 테스트

서버를 시작하고 유료 및 무료 경로를 모두 확인하십시오.

1. 402 응답 확인

curl -i http://localhost:4021/weather

응답은 가격, 자산 및 네트워크를 포함하는 PAYMENT-REQUIRED 헤더와 함께 402 Payment Required여야 합니다.

2. 클라이언트 실행

npx tsx client.ts

클라이언트는 전체 주기를 처리합니다. 402를 수신하고, 승인에 서명하고, 지불로 재시도하고, 응답을 인쇄합니다.

3. 영수증 읽기

성공적인 유료 요청 후 구매자는 서버 응답에서 PAYMENT-SETTLE-RESPONSE 헤더를 읽고 결제 영수증을 구문 분석할 수 있습니다.

// (계속) client.ts
import { x402HTTPClient } from "@x402/fetch";
 
const httpClient = new x402HTTPClient(client);
const receipt = httpClient.getPaymentSettleResponse(
  (name) => response.headers.get(name),
);
console.log("결제 영수증:", JSON.stringify(receipt, null, 2));

라이브 촉진자 없이 테스트

시맨틱 촉진자는 메인넷 전용이므로 현재 테스트넷 촉진자에 서버를 연결할 수 없습니다. 실제 결제를 하지 않고 서버 로직, 경로 핸들러 및 미들웨어 동작을 반복하기 위해 촉진자 클라이언트를 스텁 처리합니다.

// server.test.ts
import { x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
 
// 스텁 촉진자: 모든 서명을 수락하고 가짜 결제를 반환합니다.
const stubFacilitatorClient = {
  verify: async () => ({ isValid: true, payer: "0xMockPayer" }),
  settle: async () => ({
    success: true,
    txHash: "0xMOCK000000000000000000000000000000000000000000000000000000000001",
    networkId: "eip155:988",
  }),
};
 
export const testResourceServer = new x402ResourceServer(stubFacilitatorClient as any)
  .register("eip155:988", new ExactEvmScheme());

스텁에 대해 단위 테스트를 실행하여 다음을 검증하십시오.

  • 402 응답에는 올바른 PAYMENT-REQUIRED 페이로드가 포함됩니다.
  • 유효한 PAYMENT-SIGNATURE 헤더가 있는 요청은 핸들러에 도달합니다.
  • 누락되거나 잘못된 헤더가 있는 요청은 핸들러가 실행되기 전에 거부됩니다.

실제 결제를 실행할 준비가 되면 HTTPFacilitatorClient로 다시 전환하고 소액으로 메인넷에서 실행하십시오.

고급: 수명 주기 훅

x402는 흐름의 주요 지점에서 결제 처리를 가로채고 사용자 정의할 수 있는 훅을 제공합니다. 예를 들어, 서버는 확인 전에 논리를 실행하여 (예: API 키 또는 구독자 상태 확인) 승인된 요청에 대한 결제를 우회할 수 있으며, 클라이언트는 서명하기 전에 지출 한도를 적용할 수 있습니다.

전체 훅 참조 및 예제는 x402 수명 주기 훅을 참조하십시오.

다음 권장 사항

  • x402 개념: 프로토콜과 적합한 위치를 이해합니다.
  • ERC-3009: x402가 사용하는 결제 표준을 검토합니다.
  • MCP 서버로 결제: 이 API를 MCP 도구로 랩핑하여 AI 클라이언트가 프롬프트를 통해 호출할 수 있도록 합니다.