Tích hợp thanh toán LangGraph.
Không cần gói LangGraph. Một nút đơn giản gọi đến khách hàng blockchain0x thực, interrupt() tạm dừng cho đến khi webhook thanh toán đến, và đồ thị tiếp tục - bền bỉ qua các lần chạy.
Không có package riêng cho LangGraph, và bạn cũng không cần một package như vậy. Một node bình thường trong của bạn gọi client thực sự, một sẽ tạm dừng graph cho đến khi webhook thanh toán đến, và handler sẽ tiếp tục nó bằng + riêng của LangGraph. Thanh toán được chốt trên Base.
Thực thi bền vững làm cho mẫu tiếp tục sau thanh toán trở nên tự nhiên.
Phần khó khăn của thanh toán do tác nhân điều khiển là khoảng cách không đồng bộ: bạn yêu cầu thanh toán, người mua thanh toán vài phút sau (hoặc không bao giờ), và bạn phải tiếp tục công việc mà không làm mất trạng thái của nơi bạn đã ở. Hầu hết các khung tác nhân dựa vào hàng đợi công việc + lưu trữ trạng thái thủ công để cầu nối khoảng cách này. LangGraph đã có nguyên thủy được tích hợp sẵn: interrupt() tạm ngưng một đồ thị tại một nút và một checkpointer lưu trữ trạng thái vào đĩa. Việc tiếp tục là một cuộc gọi.
Recipe này tận dụng điều đó mà không cần cơ chế đặc biệt nào. Một pay node đơn giản gọi blockchain0x.payments.create. Một await_settlement node gọi interrupt() để tạm dừng, và checkpointer sẽ lưu trạng thái. Webhook handler xác minh chữ ký, đặt settled cho thread tương ứng bằng graph.updateState, rồi gọi graph.invoke để tiếp tục từ điểm interrupt. Graph sẽ tiếp tục đúng tại nơi nó dừng lại, bao gồm cả mọi ngữ cảnh LLM từ các node trước - tất cả đều dùng chính API của LangGraph.
Cài đặt LangGraph và SDK cốt lõi. Không có gói bổ sung.
Node 18+ và @langchain/langgraph 0.2+ cộng với khách hàng thực @blockchain0x/node. Không có bộ điều hợp LangGraph nào để thêm vào cả hai ngôn ngữ - người dùng Python LangGraph cài đặt langgraph và khách hàng blockchain0x Python và viết cùng một công thức, với interrupt() và Command(resume=...) ở phía 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 là một khóa sk_test_ testnet hoặc sk_live_ mainnet từ dashboard của bạn; client đọc nó từ môi trường. BLOCKCHAIN0X_WEBHOOK_SECRET (được trả về một lần khi bạn tạo hoặc xoay vòng webhook) là cần thiết trong tiến trình xử lý webhook. Nếu graph của bạn chạy trong một tiến trình và handler chạy ở tiến trình khác, cả hai cần cùng một checkpointer (ví dụ Postgres dùng chung) để handler có thể tìm thấy thread đang bị tạm dừng.
Một đồ thị ba nút: thanh toán, tạm dừng, giao hàng.
Bên dưới là một workflow LangGraph hoàn chỉnh với một State có kiểu, một pay node gọi blockchain0x.payments.create thật, một await_settlement node dựa trên interrupt, và một deliver node chạy phần việc phụ thuộc khi chain xác nhận khoản thanh toán đã được chuyển.
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".
Xác minh webhook, sau đó tiếp tục luồng.
Trình xử lý xác minh chữ ký với webhooks.verify từ Node SDK, tra cứu luồng LangGraph bị treo từ ánh xạ của riêng bạn, đặt settled với graph.updateState và gọi graph.invoke(null, config) để tiếp tục từ điểm ngắt. Không có trợ giúp đặc biệt - đó là các API của 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 thực hiện HMAC-SHA256 trong thời gian không đổi trên thân thô và trả về một union phân biệt - phân nhánh trên result.ok, không có try/catch. Ánh xạ result.eventId đến luồng bạn đã tạm dừng, sau đó graph.updateState + graph.invoke(null, config) tiếp tục từ điểm kiểm tra đã lưu và đồ thị chạy giao hàng. Các sự kiện đã gửi là payment.received, payment.sent, wallet.deployed, và webhook.test.
Khách hàng mà bạn đang bọc là mở. Đọc nó.
Không có starter package của LangGraph để clone - recipe ở trên chính là phần tích hợp. Các SDK của blockchain0x là mã nguồn mở trên GitHub; recipe này bọc Python SDK (blockchain0x-python), với toàn bộ bề mặt phương thức nằm trong docs. Hãy đọc để tham khảo phần thân của node và phần xác minh webhook.
github.com/tosh-labs/blockchain0x-pythonToàn bộ bề mặt phương thức của SDK và cơ chế chữ ký webhook được tài liệu hóa tại the docs. Hãy bắt đầu với một key sk_test_ trên Base Sepolia, rồi chuyển sang sk_live_ khi graph tiếp tục đúng theo cách bạn mong đợi.
Năm cạm bẫy cụ thể về LangGraph cần tránh.
Mô hình thực thi bền vững của LangGraph rất mạnh mẽ nhưng có những cạm bẫy riêng liên quan đến các điểm kiểm tra, ngắt và cập nhật trạng thái.
Không có gói LangGraph - một nút đơn giản gọi SDK
Blockchain0x cung cấp các bộ chuyển đổi cho LangChain và CrewAI cùng với máy chủ MCP; không có gói LangGraph chuyên dụng, trong npm hoặc pip. Công thức ở trên là con đường: một nút đồ thị thông thường gọi đến khách hàng thực @blockchain0x/node, và bạn tiếp tục với graph.updateState + graph.invoke của LangGraph. Không có lớp nút thanh toán đặc biệt nào và không có trợ giúp tiếp tục để nhập - những điều đó chưa bao giờ tồn tại.
Thiếu checkpointer
Hàm interrupt() của LangGraph chỉ hoạt động nếu đồ thị có một checkpointer. Nếu không có, trạng thái sẽ biến mất khi quy trình của bạn thoát và bộ xử lý webhook không có luồng nào để tiếp tục. Sử dụng MemorySaver cho phát triển và SqliteSaver hoặc PostgresSaver trong sản xuất. Nếu đồ thị và bộ xử lý webhook của bạn chạy trong các quy trình khác nhau, chúng phải chia sẻ cùng một checkpointer (ví dụ: một Postgres) để bộ xử lý có thể tìm thấy luồng bị tạm dừng.
Việc ánh xạ webhook đến luồng đúng là nhiệm vụ của bạn
Webhook cho bạn biết một khoản thanh toán đã được giải quyết; nó không biết thread_id LangGraph của bạn. Giữ ánh xạ của riêng bạn - khi bạn bắt đầu một đồ thị, lưu trữ thread_id được khóa bởi tác nhân, người nhận hoặc một khóa idempotency mà bạn đã đặt, sau đó tra cứu nó trong trình xử lý. Cuộc gọi threadFor() ở trên là tra cứu đó. Làm sai điều này và graph.invoke sẽ tiếp tục luồng sai hoặc không có luồng nào cả.
Xử lý đường không thanh toán, hoặc tạm dừng mãi mãi
Cạnh điều kiện từ await_settlement định tuyến để giao hàng chỉ khi đã được thanh toán là đúng. Nếu một khoản thanh toán không bao giờ đến nơi, luồng sẽ ở trạng thái treo vô thời hạn. Thêm một thời gian chờ: một cuộc quét theo lịch phục hồi các luồng cũ với settled=false để cạnh định tuyến đến KẾT THÚC, hoặc kiểm tra payment.sent-so với deadline. Một luồng bị treo không tốn gì, nhưng nó cũng không bao giờ hoàn thành một mình.
Các số lượng là đơn vị cơ sở USDC, dưới dạng chuỗi
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.