> ## Documentation Index
> Fetch the complete documentation index at: https://daehan-base.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# API 및 서비스 결제 (x402)

> x402 작동 방식, 에이전트의 결제 처리 방법, 지원 네트워크 및 토큰

export const X402PayDemo = () => {
  const mono = "ui-monospace,'Cascadia Code','Source Code Pro',Menlo,Monaco,Consolas,monospace";
  const col = {
    dim: "#3f3f46",
    muted: "#52525b",
    active: "#60a5fa",
    success: "#34d399",
    code: "#d4d4d8"
  };
  const steps = [{
    delay: 350,
    left: [{
      t: "> GET api.coingecko.com/api/v3/simple/price",
      c: "active"
    }],
    right: [{
      t: "── Request 1 ───────────────────────────",
      c: "dim"
    }, {
      t: "GET /api/v3/simple/price?ids=ethereum",
      c: "code"
    }, {
      t: "Host: api.coingecko.com",
      c: "muted"
    }]
  }, {
    delay: 650,
    left: [{
      t: "  ← 402 Payment Required",
      c: "muted"
    }],
    right: [{
      t: "",
      c: "dim"
    }, {
      t: "── Response 1 ──────────────────────────",
      c: "dim"
    }, {
      t: "HTTP/1.1 402 Payment Required",
      c: "muted"
    }, {
      t: "X-Payment-Required: {",
      c: "muted"
    }, {
      t: '  "amount": "0.001",',
      c: "code"
    }, {
      t: '  "asset":  "USDC",',
      c: "code"
    }, {
      t: '  "network": "base"',
      c: "code"
    }, {
      t: "}",
      c: "muted"
    }]
  }, {
    delay: 500,
    left: [{
      t: "  paying 0.001 USDC via wallet...",
      c: "muted"
    }],
    right: []
  }, {
    delay: 700,
    left: [{
      t: "  ✓ tx confirmed   0x1a9f...c4e2",
      c: "success"
    }],
    right: []
  }, {
    delay: 450,
    left: [{
      t: "  → retrying with payment signature",
      c: "muted"
    }],
    right: [{
      t: "",
      c: "dim"
    }, {
      t: "── Request 2 ───────────────────────────",
      c: "dim"
    }, {
      t: "GET /api/v3/simple/price?ids=ethereum",
      c: "code"
    }, {
      t: "X-Payment-Sig: 0x1a9f...c4e2",
      c: "success"
    }]
  }, {
    delay: 600,
    left: [{
      t: "  ← 200 OK",
      c: "success"
    }],
    right: [{
      t: "",
      c: "dim"
    }, {
      t: "── Response 2 ──────────────────────────",
      c: "dim"
    }, {
      t: "HTTP/1.1 200 OK",
      c: "success"
    }, {
      t: '{"ethereum":{"usd":2847.32}}',
      c: "code"
    }]
  }, {
    delay: 300,
    left: [{
      t: "  ETH  $2,847.32  ↑ 2.3%",
      c: "code",
      bold: true
    }],
    right: []
  }];
  const [leftLines, setLeftLines] = useState([]);
  const [rightLines, setRightLines] = useState([]);
  const [running, setRunning] = useState(false);
  const [done, setDone] = useState(false);
  const [blink, setBlink] = useState(true);
  const leftRef = useRef(null);
  const rightRef = useRef(null);
  useEffect(() => {
    const t = setInterval(() => setBlink(b => !b), 530);
    return () => clearInterval(t);
  }, []);
  useEffect(() => {
    if (leftRef.current) leftRef.current.scrollTop = leftRef.current.scrollHeight;
  }, [leftLines]);
  const play = () => {
    setLeftLines([]);
    setRightLines([]);
    setRunning(true);
    setDone(false);
    let i = 0;
    const next = () => {
      if (i >= steps.length) {
        setRunning(false);
        setDone(true);
        return;
      }
      const s = steps[i];
      setTimeout(() => {
        if (s.left && s.left.length) setLeftLines(prev => [...prev, ...s.left]);
        if (s.right && s.right.length) setRightLines(prev => [...prev, ...s.right]);
        i++;
        next();
      }, s.delay);
    };
    next();
  };
  useEffect(() => {
    setTimeout(play, 350);
  }, []);
  const renderLine = (item, i) => {
    if (!item.t) return <div key={i} style={{
      height: 6
    }} />;
    return <div key={i} style={{
      fontFamily: mono,
      fontSize: 12,
      lineHeight: "20px",
      color: col[item.c] || col.code,
      fontWeight: item.bold ? 600 : 400,
      whiteSpace: "pre"
    }}>{item.t}</div>;
  };
  return <div style={{
    margin: "28px 0",
    borderRadius: 12,
    overflow: "hidden",
    border: "1px solid #27272a",
    background: "#09090b"
  }}>
      <div style={{
    display: "flex",
    alignItems: "center",
    padding: "8px 12px",
    background: "#111113",
    borderBottom: "1px solid #27272a"
  }}>
        <div style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    width: 28,
    height: 24,
    borderRadius: 6,
    background: "#1e1e20",
    border: "1px solid #27272a",
    marginRight: 10,
    flexShrink: 0
  }}>
          <span style={{
    fontFamily: mono,
    fontSize: 11,
    color: "#71717a",
    userSelect: "none"
  }}>{">"}_</span>
        </div>
        <span style={{
    fontFamily: mono,
    fontSize: 12,
    color: "#52525b"
  }}>x402 Pay Flow</span>
        <div style={{
    flex: 1
  }} />
        <button onClick={play} title="Reset" style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    width: 28,
    height: 24,
    borderRadius: 6,
    background: "transparent",
    border: "1px solid transparent",
    cursor: "pointer",
    color: "#3f3f46",
    fontSize: 15,
    lineHeight: 1
  }} onMouseEnter={e => {
    e.currentTarget.style.color = "#a1a1aa";
    e.currentTarget.style.background = "#1e1e20";
    e.currentTarget.style.borderColor = "#27272a";
  }} onMouseLeave={e => {
    e.currentTarget.style.color = "#3f3f46";
    e.currentTarget.style.background = "transparent";
    e.currentTarget.style.borderColor = "transparent";
  }}>
          {"\u21ba"}
        </button>
      </div>

      <div style={{
    height: 250,
    borderBottom: "1px solid #27272a",
    overflow: "hidden",
    display: "flex",
    flexDirection: "column"
  }}>
        <div style={{
    padding: "7px 16px 5px",
    borderBottom: "1px solid #1c1c1e"
  }}>
          <span style={{
    fontFamily: mono,
    fontSize: 10,
    fontWeight: 600,
    letterSpacing: "0.1em",
    textTransform: "uppercase",
    color: "#3f3f46"
  }}>Agent</span>
        </div>
        <div ref={leftRef} style={{
    flex: 1,
    overflowY: "hidden",
    padding: "12px 16px"
  }}>
          {leftLines.map(renderLine)}
          {running && <div style={{
    fontFamily: mono,
    fontSize: 12,
    lineHeight: "20px",
    color: "#60a5fa",
    opacity: blink ? 1 : 0
  }}>{"▋"}</div>}
        </div>
      </div>

      <div style={{
    padding: "8px 16px",
    display: "flex",
    justifyContent: "center",
    minHeight: 37,
    alignItems: "center"
  }}>
        {done && <button onClick={play} style={{
    fontFamily: mono,
    fontSize: 11,
    color: "#52525b",
    background: "none",
    border: "none",
    cursor: "pointer",
    padding: "4px 10px",
    borderRadius: 4
  }} onMouseEnter={e => {
    e.currentTarget.style.color = "#a1a1aa";
    e.currentTarget.style.background = "#18181b";
  }} onMouseLeave={e => {
    e.currentTarget.style.color = "#52525b";
    e.currentTarget.style.background = "none";
  }}>
            {"\u21ba"} Play again
          </button>}
      </div>
    </div>;
};

x402는 HTTP 상태 코드 `402 Payment Required`를 기반으로 구축된 결제 프로토콜입니다 — 1990년대부터 HTTP 스펙에 있었던 코드가 마침내 실제로 활용되고 있습니다. 에이전트가 구독이나 API 키 없이 요청별로 스테이블코인으로 API 접근 비용을 지불할 수 있게 합니다.

## 모의 데모

<X402PayDemo />

## 결제 흐름

<Steps>
  <Step title="에이전트가 요청을 전송">
    에이전트는 다른 API 호출과 마찬가지로 API 엔드포인트에 표준 HTTP 요청을 전송합니다.
  </Step>

  <Step title="서버가 402 결제 필요로 응답">
    데이터를 반환하는 대신 서버는 `402` 상태 코드와 함께 `PAYMENT-REQUIRED` 헤더에 결제 요건을 포함하여 응답합니다: 비용, 토큰 종류, 네트워크.
  </Step>

  <Step title="에이전트가 결제">
    에이전트의 지갑이 서명된 결제 페이로드를 구성하고 `PAYMENT-SIGNATURE` 헤더와 함께 요청을 재전송합니다. 사람의 승인이 필요하지 않습니다.
  </Step>

  <Step title="서버가 데이터를 전달">
    서버는 퍼실리테이터를 통해 결제를 검증하고 온체인에서 정산한 후 요청된 데이터를 반환합니다. 전체 흐름이 몇 초 내에 완료됩니다.
  </Step>
</Steps>

자금이 있는 지갑을 가진 에이전트는 사전 관계나 계정 없이 x402 지원 API를 이용할 수 있습니다.

[x402 더 알아보기 →](https://docs.cdp.coinbase.com/x402/docs/client-server-model)

## x402 요청 처리

### CDP Agentic Wallet

[CDP Agentic Wallet](https://docs.cdp.coinbase.com/agentic-wallet/) 스킬이 설치된 경우 에이전트가 x402 결제를 자동으로 처리합니다:

```bash Terminal theme={null}
npx skills add coinbase/agentic-wallet-skills
```

[`search-for-service`](https://docs.cdp.coinbase.com/agentic-wallet/skills/search-for-service) 스킬로 **이용 가능한 서비스를 검색**합니다:

```bash Terminal theme={null}
npx awal@latest x402 bazaar search "weather forecast"
```

[`pay-for-service`](https://docs.cdp.coinbase.com/agentic-wallet/skills/pay-for-service) 스킬로 **유료 서비스를 호출**합니다:

```bash Terminal theme={null}
npx awal@latest x402 pay https://example.com/api/weather \
  -X POST \
  -d '{"query": "New York"}'
```

또는 에이전트에게 직접 프롬프트합니다:

```text theme={null}
Find APIs for sentiment analysis
Call that weather API and get the forecast for New York
```

[`search-for-service`](https://docs.cdp.coinbase.com/agentic-wallet/skills/search-for-service)와 [`pay-for-service`](https://docs.cdp.coinbase.com/agentic-wallet/skills/pay-for-service) 스킬이 검색, 결제, 재시도를 처리합니다.

[CDP Agentic Wallet 스킬 →](https://docs.cdp.coinbase.com/agentic-wallet/skills)

### Sponge Wallet

[Sponge Wallet](https://www.paysponge.com)에는 서비스를 검색하고 결제를 자동으로 처리하는 내장 x402 프록시가 있습니다:

**1단계 — 서비스 검색:**

```bash Terminal theme={null}
curl "https://api.wallet.paysponge.com/api/discover?query=weather+forecast" \
  -H "Authorization: Bearer $SPONGE_API_KEY" \
  -H "Sponge-Version: 0.2.1"
```

**2단계 — 서비스 세부 정보 조회** (필수 — 건너뛰지 마세요):

```bash Terminal theme={null}
curl "https://api.wallet.paysponge.com/api/discover/{serviceId}" \
  -H "Authorization: Bearer $SPONGE_API_KEY" \
  -H "Sponge-Version: 0.2.1"
```

`baseUrl`, 엔드포인트 경로, 파라미터, 가격이 반환됩니다.

**3단계 — 서비스 호출** (결제 자동 처리):

```bash Terminal theme={null}
curl -X POST "https://api.wallet.paysponge.com/api/x402/fetch" \
  -H "Authorization: Bearer $SPONGE_API_KEY" \
  -H "Sponge-Version: 0.2.1" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://{baseUrl}/{endpointPath}",
    "method": "POST",
    "body": { "query": "New York" },
    "preferred_chain": "base"
  }'
```

[Sponge Wallet](https://www.paysponge.com)이 `402`를 감지하고 지갑에서 USDC로 결제한 후 API 응답을 반환합니다.

[Sponge Wallet 문서 →](https://wallet.paysponge.com/skill.md)

## 오류 처리 및 재시도

| 상태                        | 의미       | 처리 방법                          |
| ------------------------- | -------- | ------------------------------ |
| `402`                     | 결제 필요    | `PAYMENT-REQUIRED` 헤더를 파싱하고 결제 |
| `402` + `X-Payment-Error` | 결제 거부    | 지갑 잔액과 토큰 승인 확인                |
| `408`                     | 결제 시간 초과 | 동일하거나 더 높은 결제 금액으로 재시도         |
| `429`                     | 속도 제한    | 지정된 지연 후 백오프 및 재시도             |

[CDP Agentic Wallet](https://docs.cdp.coinbase.com/agentic-wallet/) [`pay-for-service`](https://docs.cdp.coinbase.com/agentic-wallet/skills/pay-for-service) 스킬과 [Sponge Wallet의](https://www.paysponge.com) x402 프록시가 재시도를 자동으로 처리합니다. 커스텀 클라이언트를 구축하는 경우 `PAYMENT-REQUIRED` 헤더를 파싱하고, 서명된 페이로드를 구성하고, `PAYMENT-SIGNATURE` 헤더와 함께 재시도합니다.

<Note>
  [Bankr](https://bankr.bot)을 사용하는 경우 별도 설정이 필요하지 않습니다. 직접 프롬프트하면 됩니다:

  ```text theme={null}
  Get the current ETH price from a paid data source
  Find a weather API and get the forecast for New York
  Get the token balances for 0xYourAddress on Base
  ```

  Bankr이 서비스 검색, 결제, 데이터 추출을 자동으로 처리합니다.
</Note>

## 영상 튜토리얼

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/b5Wx2NAeY2E?si=r1FumIsbGt6nAt29" title="에이전트를 위한 결제와 트랜잭션" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

## 관련 항목

<CardGroup cols={2}>
  <Card title="결제 수락" icon="money-bill" href="/ai-agents/payments/accepting-payments">
    자신의 엔드포인트를 게이팅하고 다른 에이전트에게 요청별 요금을 청구합니다.
  </Card>

  <Card title="x402 클라이언트-서버 모델" icon="file-contract" href="https://docs.cdp.coinbase.com/x402/docs/client-server-model">
    퍼실리테이터 엔드포인트와 프로토콜 주소입니다.
  </Card>
</CardGroup>
