SettleLab
전체 코스
LESSON 07Labs and Capstone

x402 결제 서버 랩

심화1시간근거 4

학습 결과

  • HTTP 결제 요구와 settlement proof 저장 흐름을 구현한다.
  • agent별 한도와 idempotency를 테스트한다.

선행 조건

  • x402 HTTP 스테이블코인 결제

완료 기준

  • payment required response가 설계됐다.
  • settlement proof를 저장한다.
  • agent 한도 테스트가 있다.

x402 결제 서버 랩

도입

x402는 HTTP 요청 흐름 안에서 결제 요구, 결제 payload 제출, 검증, settlement를 연결하는 모델이다. 클라이언트가 paid resource를 요청하면 서버는 HTTP 402로 결제 조건을 알려주고, 클라이언트는 결제 payload를 만들어 다시 요청한다. 검증과 settlement가 끝나면 서버는 원래 요청한 resource 접근을 허용한다.

이 랩은 실제 HTTP 서버 대신 Solidity mock server로 그 구조를 축소한다. X402PaymentServer는 paid resource를 등록하고, paymentRequirement로 402 응답에 들어갈 정보를 만들고, accessWithAuthorization에서 ERC-3009 authorization을 사용해 merchant에게 token을 보낸 뒤 receipt를 저장한다.

개념 강의인 x402 HTTP 스테이블코인 결제는 network, asset, recipient, amount를 어떤 요구사항으로 표현할지 다룬다. 이 랩은 그 요구사항을 code path, idempotency, receipt 저장으로 검증한다.

학습 목표

  • HTTP 결제 요구와 settlement proof 저장 흐름을 구현한다.
  • agent별 한도와 idempotency를 테스트한다.

개념 설명

판단 트리가로 스크롤 · 크게 보기 지원
x402 결제 서버 랩 판단 트리이 시각화는 랩 산출물을 캡스톤 설계로 옮길 때 진행, 보류, 재설계 결정을 가르는 질문이 무엇인지를 보여주며, 'x402 결제 서버 랩'에서 남겨야 할 설계 증거를 좁힌다.
시작 질문

x402 결제 서버 랩을 제품 설계에 넣어도 되는가?

진행

랩 산출물이 캡스톤의 어떤 장과 테스트 증거로 재사용되는가

x402 server flow 구현
보류

실패 로그가 남는가

가정과 실패 상태를 보강한다.
차단

운영 대시보드까지 닫히는가

캡스톤 risk register에 차단 사유를 남긴다.
크게 보기
시작 질문

x402 결제 서버 랩을 제품 설계에 넣어도 되는가?

진행

랩 산출물이 캡스톤의 어떤 장과 테스트 증거로 재사용되는가

x402 server flow 구현
보류

실패 로그가 남는가

가정과 실패 상태를 보강한다.
차단

운영 대시보드까지 닫히는가

캡스톤 risk register에 차단 사유를 남긴다.

대상 코드는 08-실습/mock-stablecoin-lab/src/X402PaymentServer.sol이고, 테스트는 test/X402PaymentServer.t.sol이다. 이 구현은 학습용이므로 실제 header encoding이나 facilitator API를 구현하지 않는다. 대신 x402에서 필요한 핵심 판단을 contract state로 드러낸다.

표 자료가로 스크롤 · 크게 보기 지원
x402 개념랩의 대응 요소확인할 질문
paid resourceResourceresourceId어떤 endpoint가 얼마를 요구하는가
402 responsepaymentRequirement 반환값payee, token, amount, network, scheme이 충분한가
payment payloadERC-3009 authorizationpayer가 정확한 payee, amount, valid window에 서명했는가
verificationtoken의 transferWithAuthorizationsignature, recipient, amount, nonce가 맞는가
settlement proofReceiptResourceAccessPaid event같은 payer와 nonce로 중복 접근을 막는가
크게 보기
x402 개념랩의 대응 요소확인할 질문
paid resourceResourceresourceId어떤 endpoint가 얼마를 요구하는가
402 responsepaymentRequirement 반환값payee, token, amount, network, scheme이 충분한가
payment payloadERC-3009 authorizationpayer가 정확한 payee, amount, valid window에 서명했는가
verificationtoken의 transferWithAuthorizationsignature, recipient, amount, nonce가 맞는가
settlement proofReceiptResourceAccessPaid event같은 payer와 nonce로 중복 접근을 막는가

공식 문서 기준으로 x402는 402 응답에서 결제 조건을 알리고, 클라이언트가 결제 payload를 포함해 재요청하며, 서버나 facilitator가 verify/settle을 처리한다. 이 랩의 SCHEME = "exact"는 고정 가격 endpoint를 가정한다. usage-based billing을 다룰 때는 최대 지불액과 실제 정산액을 분리하는 설계를 별도 문서로 남긴다.

흐름도가로 스크롤 · 크게 보기 지원
강의 흐름도상태, 책임, 검증 지점을 순서대로 읽기 위한 다이어그램이다.
크게 보기

코드로 확인하기

앞에서 만든 설계를 실습 코드로 연결한다. 예시는 그대로 외우는 대상이 아니라, 구현 파일에서 어떤 줄을 읽고 어떤 테스트를 붙일지 정하는 기준이다.

컨트랙트paymentRequirement — 402 응답 생성

클라이언트가 paid resource를 요청하면 서버가 반환할 페이로드를 컨트랙트가 그대로 만든다. HTTP status, payee, token, amount, network, scheme이 모두 한 곳에서 결정된다.

컨트랙트paymentRequirement 본문 파일: 08-실습/mock-stablecoin-lab/src/X402PaymentServer.sol (라인 76-98)

402 응답 6개 필드가 어디서 오는지 본다.

CODE SURFACEsolidity
    function paymentRequirement(bytes32 resourceId)        external        view        returns (            uint16 httpStatus,            address payee,            address token,            uint256 amount,            string memory network,            string memory scheme        )    {        Resource storage resource = _resource(resourceId);        return (            PAYMENT_REQUIRED,            resource.payee,            resource.token,            resource.amount,            resource.network,            SCHEME        );    }

컨트랙트accessWithAuthorization — 결제 검증 + receipt 저장

ERC-3009 서명을 token에게 위임 검증하고, 성공하면 (resourceId, payer, nonce)로 receipt를 박는다. 같은 키로 두 번째 호출은 ReceiptAlreadyExists로 차단된다.

컨트랙트accessWithAuthorization 본문 파일: 08-실습/mock-stablecoin-lab/src/X402PaymentServer.sol (라인 100-124)

결제 정산과 receipt 인덱스의 동시 트랜잭션 처리.

CODE SURFACEsolidity
    function accessWithAuthorization(        bytes32 resourceId,        address payer,        uint256 validAfter,        uint256 validBefore,        bytes32 nonce,        uint8 v,        bytes32 r,        bytes32 s    ) external {        Resource storage resource = _resource(resourceId);        if (payer == address(0)) revert InvalidAddress();        if (receipts[resourceId][payer][nonce].exists) {            revert ReceiptAlreadyExists(resourceId, payer, nonce);        }        IX402AuthorizationToken(resource.token).transferWithAuthorization(            payer, resource.payee, resource.amount, validAfter, validBefore, nonce, v, r, s        );        receipts[resourceId][payer][nonce] =            Receipt({ payer: payer, nonce: nonce, paidAt: block.timestamp, exists: true });        emit ResourceAccessPaid(resourceId, payer, nonce);    }

백엔드HTTP 402 응답 헬퍼 (Next.js / Node)

컨트랙트가 반환한 6개 필드를 그대로 표준 x402 응답 JSON으로 직렬화하는 백엔드 라우트.

CODE SURFACEtypescript
import { NextResponse } from "next/server";export async function GET(_req: Request, ctx: { params: { resourceId: string } }) {  const [httpStatus, payee, token, amount, network, scheme] =    await x402Server.paymentRequirement(ctx.params.resourceId);  if (httpStatus !== 402) {    return NextResponse.json({ error: "resource not found" }, { status: 404 });  }  return NextResponse.json(    {      x402Version: 1,      accepts: [        {          scheme,          network,          maxAmountRequired: amount.toString(),          asset: token,          payTo: payee,          resource: ctx.params.resourceId        }      ]    },    { status: 402, headers: { "Accept-Payment": "x402/1" } }  );}

클라이언트agent 결제 한도 정책

agent가 정책 외 결제를 자동 실행하지 않도록 spending limit·allowlist를 감싸는 클라이언트 측 가드.

CODE SURFACEtypescript
type AgentPolicy = {  endpointAllowlist: RegExp[];  perRequestCapUsd: number;  dailyCapUsd: number;  requireManualApprovalAboveUsd: number;};export function shouldAutoPay(  policy: AgentPolicy,  request: { url: string; amountUsd: number; todaySpentUsd: number }) {  if (!policy.endpointAllowlist.some((re) => re.test(request.url))) return "blocked:allowlist";  if (request.amountUsd > policy.perRequestCapUsd) return "blocked:per-request";  if (request.todaySpentUsd + request.amountUsd > policy.dailyCapUsd) return "blocked:daily";  if (request.amountUsd > policy.requireManualApprovalAboveUsd) return "needs-approval";  return "auto-pay";}

강의 포인트

표 자료가로 스크롤 · 크게 보기 지원
관점확인할 질문증거로 남길 것
requirementresource별 token, amount, network, scheme이 명확한가payment requirement 예시
authorizationpayer가 merchant와 금액에 정확히 서명했는가signed payload 필드 표
idempotency같은 resource, payer, nonce가 재사용될 때 서버 경계에서 막히는가receipt 재사용 실패 로그
agent 한도agent가 하루 또는 요청당 얼마까지 결제할 수 있는가spending limit 정책
settlement proof결제 완료 후 어떤 receipt를 사용자와 운영자가 조회하는가receipt schema
크게 보기
관점확인할 질문증거로 남길 것
requirementresource별 token, amount, network, scheme이 명확한가payment requirement 예시
authorizationpayer가 merchant와 금액에 정확히 서명했는가signed payload 필드 표
idempotency같은 resource, payer, nonce가 재사용될 때 서버 경계에서 막히는가receipt 재사용 실패 로그
agent 한도agent가 하루 또는 요청당 얼마까지 결제할 수 있는가spending limit 정책
settlement proof결제 완료 후 어떤 receipt를 사용자와 운영자가 조회하는가receipt schema

실무 예시

백엔드[CLIENT] agent가 유료 API endpoint를 호출한다고 가정한다. 첫 요청에는 결제가 없기 때문에 서버가 payment requirement를 반환한다. agent는 지갑으로 authorization을 만들고 재요청한다. 서버는 authorization을 token contract로 실행해 merchant에게 지불하고 receipt를 저장한다.

표 자료가로 스크롤 · 크게 보기 지원
실패 상황사용자 또는 agent 안내운영 증거
missing resource이 endpoint는 결제 요구사항을 찾을 수 없다ResourceNotFound(resourceId)
wrong recipient서명된 payee가 등록된 merchant와 다르다InvalidSignature
wrong amount서명 금액이 resource price와 맞지 않다InvalidSignature 또는 amount mismatch
expired authorization결제 payload가 만료됐다AuthorizationExpired(validBefore)
reused nonce이미 처리된 결제 payload다ReceiptAlreadyExists(resourceId, payer, nonce)
크게 보기
실패 상황사용자 또는 agent 안내운영 증거
missing resource이 endpoint는 결제 요구사항을 찾을 수 없다ResourceNotFound(resourceId)
wrong recipient서명된 payee가 등록된 merchant와 다르다InvalidSignature
wrong amount서명 금액이 resource price와 맞지 않다InvalidSignature 또는 amount mismatch
expired authorization결제 payload가 만료됐다AuthorizationExpired(validBefore)
reused nonce이미 처리된 결제 payload다ReceiptAlreadyExists(resourceId, payer, nonce)

agent 결제에서는 "지불할 수 있다"와 "지불해도 된다"를 분리해야 한다. 지갑이 결제 가능하더라도 policy layer가 endpoint allowlist, per-request cap, daily cap, retry cap, manual approval threshold를 확인해야 한다.

흔한 오해와 실패 시나리오

표 자료가로 스크롤 · 크게 보기 지원
오해실제로 확인할 것
HTTP 402만 반환하면 결제 서버가 완성됐다고 본다.requirement, payment payload, verify, settle, receipt가 연결되어야 한다.
receipt 저장을 optional로 본다.receipt가 없으면 재시도, 중복 청구, 고객 지원, agent audit을 설명할 수 없다.
agent는 사람이 아니므로 spending limit이 덜 중요하다고 본다.agent는 반복 호출을 빠르게 만들 수 있으므로 한도와 idempotency가 더 중요하다.
exact scheme과 usage-based billing을 같은 정책으로 처리한다.고정 가격과 최대 승인액 기반 정산은 다른 사용자 안내와 settlement 증거가 필요하다.
wrong recipient와 wrong amount를 단순 signature 실패로만 본다.운영 로그에는 어떤 requirement 필드가 payload와 달랐는지 남겨야 한다.
크게 보기
오해실제로 확인할 것
HTTP 402만 반환하면 결제 서버가 완성됐다고 본다.requirement, payment payload, verify, settle, receipt가 연결되어야 한다.
receipt 저장을 optional로 본다.receipt가 없으면 재시도, 중복 청구, 고객 지원, agent audit을 설명할 수 없다.
agent는 사람이 아니므로 spending limit이 덜 중요하다고 본다.agent는 반복 호출을 빠르게 만들 수 있으므로 한도와 idempotency가 더 중요하다.
exact scheme과 usage-based billing을 같은 정책으로 처리한다.고정 가격과 최대 승인액 기반 정산은 다른 사용자 안내와 settlement 증거가 필요하다.
wrong recipient와 wrong amount를 단순 signature 실패로만 본다.운영 로그에는 어떤 requirement 필드가 payload와 달랐는지 남겨야 한다.

실습 과제

  1. 백엔드[CONTRACT] x402 server flow 구현: createResource, paymentRequirement, accessWithAuthorization, hasReceipt를 request-response 흐름으로 설명한다.
  2. 클라이언트Agent 한도 설계: endpoint allowlist, per-request cap, daily cap, retry cap, manual approval threshold를 표로 작성한다.
  3. 백엔드idempotency 정책 작성: resourceId + payer + nonce 외에 request body hash나 payment identifier가 필요한 상황을 정리한다.
  4. 인덱서[OPS] settlement proof 설계: receipt에 payer, resourceId, nonce, amount, token, network, paidAt, settlement tx를 어떻게 남길지 capstone 형식으로 쓴다.

완료 기준

  1. payment required response가 설계됐다.
  2. settlement proof를 저장한다.
  3. agent 한도 테스트가 있다.

근거 자료

Final checkpoint

읽기를 마쳤다면 여기서 기록한다

아래 버튼은 읽기 진도를 저장한다. 체크리스트, 과제, 랩 산출물은 위 Workbook에서 따로 관리한다.

  • payment required response가 설계됐다.
  • settlement proof를 저장한다.
  • agent 한도 테스트가 있다.

학습 자료 근거

x402 결제 서버 랩
이 LMS 레슨의 개념, 예시, 과제 구성을 잡는 데 사용한 근거 문서.
내부 참고 문서
x402 How It Works
https://docs.cdp.coinbase.com/x402/core-concepts/how-it-works
x402 Quickstart for Sellers
https://docs.cdp.coinbase.com/x402/quickstart-for-sellers
x402 FAQ
https://docs.cdp.coinbase.com/x402/support/faq