Integración de pagos de LangGraph.
No se necesita ningún paquete LangGraph. Un nodo simple llama al cliente real de blockchain0x, interrupt() se suspende hasta que el webhook de pago llegue, y el gráfico se reanuda - duradero a través de ejecuciones.
No existe un paquete de LangGraph, y no lo necesitas. Un nodo simple en tu llama al cliente real de , un suspende el grafo hasta que llega el webhook de pago, y el handler lo reanuda con los propios + de LangGraph. Los pagos se liquidan en Base.
La ejecución duradera hace que el patrón de reanudación después del pago sea natural.
La parte difícil de los pagos impulsados por agentes es la brecha asíncrona: solicitas el pago, el comprador paga minutos después (o nunca), y tienes que reanudar el trabajo sin perder el estado de dónde estabas. La mayoría de los marcos de agentes dependen de una cola de trabajos + un almacén de estado manual para cerrar esta brecha. LangGraph ya tiene el primitivo incorporado: interrupt() suspende un gráfico en un nodo y un checkpointer persiste el estado en el disco. Reanudar es una llamada.
La receta se basa en esto sin maquinaria especial. Un nodo de pago simple llama a blockchain0x.payments.create. Un nodo await_settlement llama a interrupt() para suspender, y el checkpointer persiste el estado. El manejador de webhook verifica la firma, establece settled en el hilo correspondiente con graph.updateState, y llama a graph.invoke para continuar desde la interrupción. El gráfico retoma exactamente donde lo dejó, incluyendo cualquier contexto LLM de nodos anteriores - todo usando las propias APIs de LangGraph.
Instala LangGraph y el SDK principal. Sin paquete adicional.
Node 18+ y @langchain/langgraph 0.2+ más el cliente real @blockchain0x/node. No hay un adaptador LangGraph para agregar en ningún idioma - los usuarios de Python LangGraph instalan langgraph y el cliente Python de blockchain0x y escriben la misma receta, con interrupt() y Command(resume=...) en el lado de Python.
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 es una clave sk_test_ testnet o sk_live_ mainnet de tu panel de control; el cliente la lee del entorno. BLOCKCHAIN0X_WEBHOOK_SECRET (devuelto una vez cuando creas o rotas un webhook) es necesario en el proceso que maneja los webhooks. Si tu gráfico se ejecuta en un proceso y el manejador en otro, ambos necesitan el mismo checkpointer (por ejemplo, Postgres compartido) para que el manejador pueda encontrar el hilo suspendido.
Un gráfico de tres nodos: pagar, suspender, entregar.
A continuación se muestra un flujo de trabajo completo de LangGraph con un Estado tipado, un nodo de pago que llama a la verdadera blockchain0x.payments.create, un nodo await_settlement basado en interrupciones, y un nodo de entrega que ejecuta el trabajo dependiente una vez que la cadena confirma que el pago se movió.
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".
Verifica el webhook, luego reanuda el hilo.
El controlador verifica la firma con webhooks.verify del SDK de Node, busca el hilo suspendido de LangGraph de tu propio mapeo, establece settled con graph.updateState y llama a graph.invoke(null, config) para continuar desde la interrupción. No hay ayudante especial - esas son las propias APIs de LangGraph.
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 hace HMAC-SHA256 en tiempo constante sobre el cuerpo raw y devuelve una unión discriminada - ramifica en result.ok, sin try/catch. Mapea result.eventId al hilo que suspendiste, luego graph.updateState + graph.invoke(null, config) reanuda desde el punto de control persistido y el gráfico ejecuta entregas. Los eventos enviados son payment.received, payment.sent, wallet.deployed, y webhook.test.
El cliente que estás envolviendo es abierto. Léelo.
No hay un paquete de inicio de LangGraph para clonar - la receta anterior es la integración. Los SDK de blockchain0x son de código abierto en GitHub; esta receta envuelve el SDK de Python (blockchain0x-python), con la superficie completa de métodos en la documentación. Léelo como referencia para los cuerpos de los nodos y la verificación del webhook.
github.com/tosh-labs/blockchain0x-pythonLa superficie completa de métodos del SDK y el esquema de firma de webhook están documentados en la documentación. Comienza con una clave sk_test_ contra Base Sepolia, luego cambia a sk_live_ una vez que el gráfico reanude como esperas.
Cinco trampas específicas de LangGraph a evitar.
El modelo de ejecución duradera de LangGraph es poderoso, pero tiene sus propias trampas en torno a puntos de control, interrupciones y actualizaciones de estado.
No hay paquete LangGraph - un nodo simple llama al SDK
Blockchain0x envía adaptadores para LangChain y CrewAI además del servidor MCP; no hay un paquete dedicado de LangGraph, en npm o pip. La receta anterior es el camino: un nodo de gráfico ordinario llama al cliente real de @blockchain0x/node, y usted continúa con el propio graph.updateState + graph.invoke de LangGraph. No hay una clase especial de nodo de pago ni un ayudante de reanudación para importar - esos nunca existieron.
Falta el checkpointer
El interrupt() de LangGraph solo funciona si el gráfico tiene un checkpointer. Sin uno, el estado se evapora cuando tu proceso sale y el controlador de webhook no tiene hilo para reanudar. Usa MemorySaver para desarrollo y SqliteSaver o PostgresSaver en producción. Si tu gráfico y el controlador de webhook se ejecutan en diferentes procesos, deben compartir el mismo checkpointer (por ejemplo, uno Postgres) para que el controlador pueda encontrar el hilo suspendido.
Mapear el webhook al hilo correcto es tu trabajo
El webhook te informa que un pago se liquidó; no conoce tu thread_id de LangGraph. Mantén tu propio mapeo - cuando inicies un gráfico, almacena thread_id claveado por el agente, el destinatario o una clave de idempotencia que establezcas, luego búscala en el controlador. La llamada threadFor() anterior es esa búsqueda. Si te equivocas, graph.invoke reanuda el hilo incorrecto o ninguno en absoluto.
Maneja el camino sin liquidación, o suspende para siempre
El borde condicional de await_settlement se dirige a entregar solo cuando settled es verdadero. Si un pago nunca llega, el hilo permanece suspendido indefinidamente. Agrega un tiempo de espera: una barrida programada que reanuda hilos obsoletos con settled=false para que el borde se dirija a END, o una verificación de payment.sent-versus-deadline. Un hilo suspendido no cuesta nada, pero tampoco termina por sí mismo.
Las cantidades son unidades base de USDC, como cadenas
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.