LangGraph 결제 통합.
LangGraph 패키지가 필요 없습니다. 일반 노드는 실제 blockchain0x 클라이언트를 호출하고, interrupt()는 결제 웹훅이 도착할 때까지 일시 중지되며, 그래프는 재개됩니다 - 실행 간에 지속적입니다.
LangGraph 전용 패키지는 없으며, 필요하지도 않습니다. 의 일반 노드가 실제 클라이언트를 호출하고, 가 payment webhook이 도착할 때까지 graph를 일시 중단한 뒤, handler가 LangGraph 자체의 + 로 다시 이어갑니다. 결제는 Base에서 정산됩니다.
내구성 있는 실행은 결제 후 재개 패턴을 자연스럽게 만듭니다.
에이전트 주도 결제의 어려운 부분은 비동기 간격입니다: 결제를 요청하고, 구매자가 몇 분 후(또는 결제를 하지 않음) 결제하며, 작업을 재개해야 하지만 현재 상태를 잃지 않아야 합니다. 대부분의 에이전트 프레임워크는 이 간격을 메우기 위해 작업 큐 + 수동 상태 저장소에 의존합니다. LangGraph는 이미 기본 기능을 내장하고 있습니다: interrupt()는 노드에서 그래프를 중단하고 체크포인터는 상태를 디스크에 지속합니다. 재개는 한 번의 호출로 이루어집니다.
이 레시피는 별도 장치 없이 이 흐름을 그대로 활용합니다. 일반 pay node는 blockchain0x.payments.create를 호출합니다. await_settlement node는 interrupt()를 호출해 중단하고, checkpointer가 상태를 저장합니다. webhook handler는 서명을 검증하고, 일치하는 thread에 graph.updateState로 settled를 설정한 뒤, graph.invoke를 호출해 interrupt 지점부터 계속합니다. graph는 이전 node의 LLM context까지 포함해 정확히 중단한 지점에서 이어집니다 - 모두 LangGraph의 자체 API만 사용합니다.
LangGraph 및 핵심 SDK를 설치하세요. 추가 패키지 없음.
Node 18+ 및 @langchain/langgraph 0.2+와 실제 @blockchain0x/node 클라이언트. 두 언어 모두에 추가할 LangGraph 어댑터가 없습니다 - Python LangGraph 사용자는 langgraph와 blockchain0x Python 클라이언트를 설치하고 동일한 레시피를 작성하며, Python 측에서 interrupt() 및 Command(resume=...)를 사용합니다.
npm install @langchain/langgraph @blockchain0x/node
export BLOCKCHAIN0X_API_KEY=sk_test_... # sk_test_ = Base Sepolia, sk_live_ = Base mainnet export BLOCKCHAIN0X_WEBHOOK_SECRET=... # for the webhook handler
BLOCKCHAIN0X_API_KEY는 dashboard의 sk_test_ testnet 또는 sk_live_ mainnet 키이며, client는 이를 environment에서 읽습니다. BLOCKCHAIN0X_WEBHOOK_SECRET (webhook을 생성하거나 회전할 때 한 번 반환됨)는 webhooks를 처리하는 process에서 필요합니다. graph가 한 process에서 실행되고 handler가 다른 process에서 실행된다면, handler가 suspended thread를 찾을 수 있도록 둘 다 동일한 checkpointer(예: shared Postgres)가 필요합니다.
세 개의 노드 그래프: 지불, 일시 중지, 배달.
아래는 typed State, 실제 blockchain0x.payments.create를 호출하는 pay node, interrupt 기반 await_settlement node, 그리고 chain이 payment 이동을 확인한 뒤 종속 작업을 실행하는 deliver node를 포함한 완전한 LangGraph workflow입니다.
import { StateGraph, START, END, MemorySaver, interrupt, Annotation } from "@langchain/langgraph"; import { createClient } from "@blockchain0x/node"; const blockchain0x = createClient({ apiKey: process.env.BLOCKCHAIN0X_API_KEY! }); const State = Annotation.Root({ agentId: Annotation<string>(), to: Annotation<string>(), amountWei: Annotation<string>(), // USDC base units: "10000" = 0.01 USDC settled: Annotation<boolean>(), }); // A plain node that calls the real SDK. No dedicated package needed. async function pay(state: typeof State.State) { await blockchain0x.payments.create({ agentId: state.agentId, to: state.to, amountWei: state.amountWei, }); return {}; } // Suspend the graph until the payment.sent webhook resumes this thread. function awaitSettlement() { interrupt("awaiting on-chain settlement"); return {}; } async function deliver() { // Funds have moved - do the work that depended on the payment. return {}; } const graph = new StateGraph(State) .addNode("pay", pay) .addNode("await_settlement", awaitSettlement) .addNode("deliver", deliver) .addEdge(START, "pay") .addEdge("pay", "await_settlement") .addConditionalEdges("await_settlement", (s) => (s.settled ? "deliver" : END)) .addEdge("deliver", END) .compile({ checkpointer: new MemorySaver() });
When the graph runs, the pay node submits the USDC transfer through the SDK. The await_settlement node calls interrupt(), which suspends the graph and persists state via the checkpointer. The conditional edge waits to be told settled=true before routing to deliver; if settlement never lands, a timeout sweep routes it to END. amountWei is base units, so 0.01 USDC is "10000".
웹훅을 검증한 후 스레드를 재개하십시오.
핸들러는 Node SDK의 webhooks.verify로 서명을 확인하고, 귀하의 매핑에서 중단된 LangGraph 스레드를 조회하며, graph.updateState로 settled를 설정하고, graph.invoke(null, config)를 호출하여 중단에서 계속합니다. 특별한 도우미는 없습니다 - 그것들은 LangGraph의 자체 API입니다.
import express from "express"; import { webhooks } from "@blockchain0x/node"; import { graph } from "./graph"; const app = express(); // Capture the RAW body. The HMAC is over the exact bytes on the wire. app.use(express.raw({ type: "application/json" })); app.post("/webhooks/payment", async (req, res) => { const result = webhooks.verify({ headers: req.headers, rawBody: req.body, // Buffer, raw bytes secret: process.env.BLOCKCHAIN0X_WEBHOOK_SECRET!, }); if (!result.ok) return res.status(400).json({ code: result.code }); if (result.eventType === "payment.sent") { // You map the event to the suspended graph thread (your own store). const config = { configurable: { thread_id: threadFor(result.eventId) } }; await graph.updateState(config, { settled: true }); await graph.invoke(null, config); // resume from the interrupt } res.status(200).send("ok"); });
webhooks.verify는 원본 본문에 대해 상수 시간 내에 HMAC-SHA256을 수행하고 구별된 유니온을 반환합니다 - result.ok에서 분기하며, try/catch는 없습니다. result.eventId를 일시 중지한 스레드에 매핑한 후, graph.updateState + graph.invoke(null, config)가 지속된 체크포인트에서 재개되고 그래프가 배달을 실행합니다. 전송된 이벤트는 payment.received, payment.sent, wallet.deployed 및 webhook.test입니다.
래핑하고 있는 클라이언트는 공개되어 있습니다. 읽어보십시오.
복제할 LangGraph starter package는 없습니다 - 위의 recipe가 바로 integration입니다. blockchain0x SDK는 GitHub에서 open source로 제공됩니다. 이 recipe는 Python SDK(blockchain0x-python)를 감싸며, 전체 method surface는 docs에 있습니다. node body와 webhook verify 참고용으로 읽으세요.
github.com/tosh-labs/blockchain0x-python전체 SDK 메서드 표면과 webhook 서명 방식은 the docs에 문서화되어 있습니다. Base Sepolia에서 sk_test_ 키로 시작한 다음, graph가 기대한 대로 재개되면 sk_live_로 전환하세요.
LangGraph에 특화된 피해야 할 다섯 가지 함정.
LangGraph의 내구성 실행 모델은 강력하지만 체크포인트, 인터럽트 및 상태 업데이트와 관련하여 자체적인 위험 요소가 있습니다.
LangGraph 패키지가 없습니다 - 일반 노드가 SDK를 호출합니다.
Blockchain0x는 LangChain 및 CrewAI와 MCP 서버를 위한 어댑터를 제공합니다. npm이나 pip에는 전용 LangGraph 패키지가 없습니다. 위의 레시피는 경로입니다: 일반 그래프 노드가 실제 @blockchain0x/node 클라이언트를 호출하고, LangGraph의 graph.updateState + graph.invoke로 이어집니다. 특별한 payment-node 클래스도 없고 가져올 resume helper도 없습니다 - 그런 것은 존재하지 않았습니다.
체크포인터 누락
LangGraph의 interrupt()는 그래프에 체크포인터가 있을 때만 작동합니다. 체크포인터가 없으면 프로세스가 종료될 때 상태가 사라지고 웹후크 핸들러는 재개할 스레드가 없습니다. 개발에는 MemorySaver를 사용하고, 프로덕션에서는 SqliteSaver 또는 PostgresSaver를 사용하세요. 그래프와 웹후크 핸들러가 서로 다른 프로세스에서 실행되는 경우, 동일한 체크포인터(예: 하나의 Postgres)를 공유해야 핸들러가 중단된 스레드를 찾을 수 있습니다.
웹훅을 올바른 스레드에 매핑하는 것은 귀하의 작업입니다.
웹훅은 결제가 정산되었음을 알려줍니다; LangGraph thread_id를 알지 못합니다. 자신의 매핑을 유지하십시오 - 그래프를 시작할 때, 에이전트, 수신자 또는 설정한 idempotency 키로 키를 지정하여 thread_id를 저장한 후, 핸들러에서 이를 조회하십시오. 위의 threadFor() 호출이 그 조회입니다. 이를 잘못 처리하면 graph.invoke가 잘못된 스레드로 재개되거나 아예 재개되지 않습니다.
정산되지 않는 경로를 처리하거나 영구적으로 중단합니다.
await_settlement에서의 조건부 엣지는 settled가 true일 때만 전달하도록 라우팅됩니다. 결제가 결제되지 않으면 스레드는 무기한으로 중단됩니다. 타임아웃을 추가하십시오: settled=false인 오래된 스레드를 재개하는 예약된 스윕이 있어 엣지가 END로 라우팅되거나, payment.sent와 마감 기한을 확인합니다. 중단된 스레드는 비용이 들지 않지만 스스로 완료되지도 않습니다.
금액은 문자열로 된 USDC 기본 단위입니다.
payments.create takes amountWei: a string of USDC base units (6 decimals), so 0.01 USDC is "10000" and 5 USDC is "5000000". It also does not retry by default and can answer 503 until the chain adapter is wired for your network. Handle that in the pay node - route to END or a retry node on error rather than letting the graph wedge.