LangGraph支付集成。
不需要LangGraph包。一个普通节点调用真实的blockchain0x客户端,interrupt()挂起,直到支付webhook到达,然后图形恢复 - 在运行之间持久。
没有 LangGraph package,而且你也不需要。你的 中的普通 node 调用真实的 client, 会暂停 graph,直到 payment webhook 到达,而 handler 会用 LangGraph 自己的 + 恢复它。支付在 Base 上结算。
持久执行使得支付后恢复模式变得自然。
代理驱动支付的难点在于异步间隙:您请求支付,买方在几分钟后(或从未)支付,而您必须在不丢失您所在状态的情况下恢复工作。大多数代理框架依赖于作业队列 + 手动状态存储来弥补这一差距。LangGraph已经内置了原语:interrupt()在节点处挂起图形,检查点将状态持久化到磁盘。恢复只需一次调用。
这个 recipe 并不依赖特殊机制。一个普通的 pay node 调用 blockchain0x.payments.create。await_settlement node 调用 interrupt() 进入挂起状态,而 checkpointer 会持久化 state。webhook handler 验证签名,在匹配的 thread 上通过 graph.updateState 将其设为 settled,然后调用 graph.invoke 从 interrupt 继续执行。graph 会精确从上次中断的位置恢复,包括 earlier nodes 中的任何 LLM context - 全部使用 LangGraph 自身的 APIs。
安装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 key;client 会从环境变量读取它。处理 webhooks 的进程还需要 BLOCKCHAIN0X_WEBHOOK_SECRET(在创建或轮换 webhook 时仅返回一次)。如果你的 graph 运行在一个进程中,而 handler 运行在另一个进程中,那么两者都需要相同的 checkpointer(例如共享的 Postgres),这样 handler 才能找到被挂起的 thread。
一个三节点图:支付、暂停、交付。
下面是一个完整的 LangGraph workflow,包含 typed State、一个调用真实 blockchain0x.payments.create 的 pay node、一个基于 interrupt 的 await_settlement node,以及一个在链上确认付款已转移后执行依赖工作的 deliver node。
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".
验证 webhook,然后恢复线程。
处理程序使用 Node SDK 的 webhooks.verify 验证签名,从您自己的映射中查找挂起的 LangGraph 线程,使用 graph.updateState 设置已结算,并调用 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 就是集成方式。blockchain0x SDK 在 GitHub 上开源;这个 recipe 封装了 Python SDK (blockchain0x-python),完整的方法表面已记录在 docs 中。可将其作为 node bodies 和 webhook verify 的参考。
github.com/tosh-labs/blockchain0x-python完整的 SDK method surface 和 webhook signature scheme 已记录在 the docs 中。先使用 sk_test_ key 对接 Base Sepolia 进行测试,然后在 graph 按预期恢复后切换到 sk_live_。
五个需要避免的 LangGraph 特定陷阱。
LangGraph的持久执行模型强大,但在检查点、中断和状态更新方面有其自身的风险。
没有 LangGraph 包 - 一个普通节点调用 SDK
Blockchain0x 为 LangChain 和 CrewAI 以及 MCP 服务器提供适配器;没有专门的 LangGraph 包,无论是在 npm 还是 pip 中。上面的配方是路径:一个普通的图节点调用真实的 @blockchain0x/node 客户端,然后你继续使用 LangGraph 自己的 graph.updateState + graph.invoke。没有特殊的支付节点类,也没有需要导入的恢复助手 - 这些从未存在过。
缺少检查点
LangGraph的interrupt()仅在图形具有检查点时有效。没有检查点时,当您的进程退出时状态会消失,Webhook处理程序没有线程可以恢复。开发时使用MemorySaver,生产中使用SqliteSaver或PostgresSaver。如果您的图形和Webhook处理程序在不同的进程中运行,它们必须共享相同的检查点(例如一个Postgres),以便处理程序可以找到挂起的线程。
将webhook映射到正确的线程是你的工作
Webhook 告诉您支付已结算;它不知道您的 LangGraph thread_id。保持您自己的映射 - 当您启动图形时,存储由代理、接收者或您设置的幂等密钥键入的 thread_id,然后在处理程序中查找它。上面的 threadFor() 调用就是该查找。如果弄错了,graph.invoke 将恢复错误的线程或根本没有线程。
处理无结算路径,或永久暂停
来自 await_settlement 的条件边缘仅在 settled 为 true 时路由以进行交付。如果支付从未到达,线程将无限期挂起。添加超时:一个计划的清理,恢复 stale 线程,settled=false,以便边缘路由到 END,或者进行 payment.sent-versus-deadline 检查。挂起的线程不花费任何费用,但它也永远不会自行完成。
金额是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.