Workspace IndexDev Notes › HTTP 402 is the easy part — settlement owns the retry

#28PoC

HTTP 402 is the easy part — settlement owns the retry

x402 turns payment into HTTP middleware, but the product boundary is what happens when verification succeeds, settlement times out, and the client retries the paid request.

Protect one idempotent API route with @x402/express, inject a timeout after each stage — verification, on-chain submission, settlement, resource generation — then retry the identical signed request and assert the two invariants: at most one settled payment, exactly one durable entitlement per settled payment.

Why

The facilitator removes blockchain plumbing from the seller; it does not remove distributed-systems semantics. A paid API needs an idempotency key and a durable payment-to-resource state machine.

x402 makes stablecoin payment look like ordinary HTTP middleware: the server returns payment requirements, the client signs, and a facilitator verifies and settles. The difficult boundary appears when one stage succeeds and the next response is lost.

How it works

Put one idempotent API behind @x402/express, inject failures between verify, settle, and fulfillment, then prove that one signed payment produces at most one charge and one response entitlement.

PoC

Protect one idempotent API route with @x402/express. Inject a timeout after verification, after on-chain submission, after settlement, and after resource generation. Retry the identical signed request and assert two invariants: at most one payment settles, and one settled payment always maps to one durable entitlement.

What it proves

A facilitator removes node operation and settlement submission from the seller, but it cannot make a distributed workflow atomic. The application still needs a payment identifier, durable state, replay handling, and reconciliation between facilitator results and canonical receipts.

References: x402 facilitator, x402 v2 SDK.

Review clarification

The demo ends where the product begins

Returning HTTP 402 with payment requirements is an hour of work; the middleware makes the demo almost free. What the middleware cannot give you is the thing that makes it a product: the moment money moves in one system (the chain) and the good is delivered in another (your API), you have a distributed transaction, and distributed transactions cannot be made atomic. The real work sits exactly where the demo ends — the retry.

The failure window is between the stages

One "paid request" is four stages — verify → submit → settle → fulfill — each crossing a network boundary that can eat the response. That is why the PoC injects a timeout after each stage rather than failing the stages: every stage succeeded, only the response was lost, and the client does the only rational thing and retries the identical signed request.

Window What is true What the retry must do
After verify Nothing settled, no money moved Re-verify and continue — the cheapest window
After submit The dangerous one: did the transfer land? The authorization nonce makes the chain refuse a second settle — so read "already used" as this payment already succeeded, never as an error to show the user
After settle Money moved, no product yet The debt exists only in your database — it must have been recorded durably before the crash
After fulfill Resource exists, delivery lost Return the same resource, never generate again

Misclassifying the second window is how a paying customer gets an error screen for a payment that worked.

The two invariants are the whole product

At most one settled payment per signed request; exactly one durable entitlement per settled payment. Everything else — middleware, facilitator, SDK — is replaceable plumbing. This is exactly the Web2 payments playbook: Stripe's Idempotency-Key and a durable state machine (received → verified → submitted → settled → fulfilled) keyed by the payment nonce. x402 changes the rail, not the semantics. "Settlement owns the retry" means retry handling cannot live in generic HTTP retry logic; a retry is a lookup about an existing payment first, and a new attempt only after that lookup says so.

Vigilance is not the deliverable

You cannot monitor your way out of a double charge — by the time you see it, the money moved twice. The guarantee has to be structural: assume every response can be lost, treat the retry as the normal path, and ask in design review, for each arrow in the flow, "what happens if the response after this arrow is lost and the same request arrives again?" If any arrow answers "we charge again" or "we deliver nothing," that arrow is the bug — before any code is written. The reconciliation line at the end is receipt-is-not-settlement again: the facilitator saying "settled" is one service's claim; the canonical receipt on chain is the authority. And the reason x402 matters at all is agent commerce — pay-per-request with no account and no API key is the rail an AI agent would use.

← All Dev Notes · Workspace Index · Top ↑

HTTP 402 는 쉬운 부분이다 — 재시도는 정산의 책임이다

x402 는 결제를 HTTP 미들웨어로 만들지만, 제품의 경계는 검증이 성공하고 정산이 타임아웃된 뒤 클라이언트가 이미 결제한 요청을 재시도할 때 드러납니다.

@x402/express 로 멱등한 API 경로 하나를 보호하고, 단계마다 — 검증, 온체인 제출, 정산, 리소스 생성 직후 — 타임아웃을 주입합니다. 동일한 서명 요청을 재시도하며 두 불변식을 확인합니다: 정산은 최대 한 번, 정산된 결제 하나에 지속성 있는 사용 권한 하나.

Facilitator 는 판매자에게서 블록체인 배관을 없애 주지만, 분산 시스템의 의미론까지 없애 주지는 않습니다. 유료 API 에는 멱등성 키와 결제→리소스의 지속성 있는 상태 머신이 필요합니다.

x402 는 스테이블코인 결제를 평범한 HTTP 미들웨어처럼 보이게 합니다: 서버가 결제 조건을 돌려주고, 클라이언트가 서명하고, facilitator 가 검증하고 정산합니다. 어려운 경계는 한 단계가 성공한 뒤 다음 응답이 사라질 때 나타납니다.

동작 방식

멱등한 API 하나를 @x402/express 뒤에 두고, 검증·정산·이행 사이에 실패를 주입한 뒤, 서명된 결제 하나가 최대 한 번의 청구와 하나의 응답 권한만 만든다는 것을 증명합니다.

PoC

@x402/express 로 멱등한 API 경로 하나를 보호합니다. 검증 직후, 온체인 제출 직후, 정산 직후, 리소스 생성 직후에 각각 타임아웃을 주입합니다. 동일한 서명 요청을 다시 보내면서 두 불변식을 확인합니다: 결제는 최대 한 번만 정산되고, 정산된 결제 하나는 항상 지속성 있는 사용 권한 하나에 연결됩니다.

무엇을 증명하나

Facilitator 는 판매자가 노드를 운영하고 정산 트랜잭션을 제출하는 일을 없애 주지만, 분산 워크플로를 원자적으로 만들지는 못합니다. 애플리케이션에는 여전히 결제 식별자, 지속성 있는 상태, 재생 처리, facilitator 결과와 정규 체인 receipt 사이의 대사가 필요합니다.

참고: x402 facilitator, x402 v2 SDK.

검토 후 보완

데모가 끝나는 곳에서 제품이 시작된다

HTTP 402 와 결제 조건을 돌려주는 것은 한 시간짜리 일이고, 미들웨어는 데모를 거의 공짜로 만들어 줍니다. 미들웨어가 못 주는 것이 이것을 제품으로 만드는 부분입니다: 돈이 한 시스템(체인)에서 움직이고 상품이 다른 시스템(내 API)에서 전달되는 순간 분산 트랜잭션이 생기고, 분산 트랜잭션은 원자적으로 만들 수 없습니다. 진짜 일은 정확히 데모가 끝나는 자리 — 재시도 — 에 있습니다.

실패의 창은 단계 사이에 있다

"유료 요청" 하나는 네 단계 — verify → submit → settle → fulfill — 이고, 각 단계 사이의 네트워크 경계가 응답을 삼킬 수 있습니다. 그래서 PoC 는 단계를 실패시키는 게 아니라 각 단계 직후에 타임아웃을 주입합니다: 단계는 전부 성공했고 응답만 사라졌으며, 클라이언트는 합리적으로 동일한 서명 요청을 재시도합니다.

무엇이 사실인가 재시도가 해야 할 일
검증 직후 정산 없음, 돈 안 움직임 다시 검증하고 진행 — 가장 싼 창
제출 직후 위험한 창: 전송이 실렸나? 인가의 논스 덕에 체인이 두 번째 정산을 거부한다 — 그러니 "already used" 를 이 결제는 이미 성공했다로 읽어야지, 사용자에게 보여줄 에러로 읽으면 안 된다
정산 직후 돈은 움직였는데 상품이 아직 없음 그 부채는 내 DB 에만 존재한다 — 크래시 전에 지속성 있게 기록돼 있어야 한다
이행 직후 리소스는 있는데 전달이 유실됨 같은 리소스를 돌려준다, 절대 다시 생성하지 않는다

두 번째 창을 오분류하는 것이, 결제가 성공한 고객이 에러 화면을 보는 경로입니다.

두 불변식이 제품의 전부다

서명 요청당 정산은 최대 한 번; 정산된 결제당 지속성 있는 사용 권한 정확히 하나. 나머지 — 미들웨어, facilitator, SDK — 는 교체 가능한 배관입니다. 이것은 정확히 Web2 결제의 교과서입니다: Stripe 의 Idempotency-Key 와, 결제 논스를 키로 삼는 지속성 있는 상태 머신(received → verified → submitted → settled → fulfilled). x402 가 바꾸는 것은 레일이지 의미론이 아닙니다. "재시도는 정산의 책임"이라는 말은 재시도 처리가 일반 HTTP 재시도 로직에 살 수 없다는 뜻입니다 — 재시도는 먼저 기존 결제에 대한 조회이고, 그 조회가 허락할 때에만 새 시도입니다.

주시가 산출물이 아니다

이중 청구는 지켜봐서 막을 수 없습니다 — 눈에 보였을 때는 이미 돈이 두 번 움직인 뒤입니다. 보장은 구조적이어야 합니다: 모든 응답은 사라질 수 있다고 가정하고, 재시도를 정상 경로로 취급하고, 설계 리뷰에서 플로우의 화살표 하나하나에 "이 화살표 직후의 응답이 사라지고 같은 요청이 다시 오면 무슨 일이 생기는가?"를 묻습니다. 어느 화살표든 답이 "또 청구된다"거나 "아무것도 전달 안 된다"면, 코드를 쓰기 전에 그 화살표가 버그입니다. 마지막의 대사(reconciliation) 줄은 receipt-is-not-settlement 의 반복입니다: facilitator 의 "정산됨"은 한 서비스의 주장이고, 권위는 체인 위의 정규 receipt 에 있습니다. 그리고 x402 가 애초에 중요한 이유는 에이전트 커머스입니다 — 계정도 API 키도 없는 요청당 결제는 AI 에이전트가 쓸 레일입니다.

← 전체 개발 노트 · 워크스페이스 인덱스 · 맨 위 ↑