LangGraph Integration
The @shade402/langgraph package provides payment nodes for LangGraph workflows, enabling agents to make X402 payments as part of their execution graph.
Installation
pnpm add @shade402/langgraph @shade402/client @shade402/core @langchain/langgraphQuick Start
import { StateGraph } from '@langchain/langgraph';
import { fetchWithPaymentNode } from '@shade402/langgraph';
import { Keypair } from '@solana/web3.js';
import { PaymentState } from '@shade402/langgraph';
const workflow = new StateGraph<PaymentState>({
channels: {
api_url: { reducer: (x, y) => y ?? x },
api_response: { reducer: (x, y) => y ?? x },
wallet_keypair: { reducer: (x, y) => y ?? x },
},
});
workflow.addNode('fetch_api', fetchWithPaymentNode);
workflow.setEntryPoint('fetch_api');
const app = workflow.compile();
const result = await app.invoke({
api_url: 'https://api.example.com/premium-data',
wallet_keypair: Keypair.generate(),
});Payment Nodes
fetchWithPaymentNode
fetchWithPaymentNodeCombined node that fetches API and handles payment automatically:
import { fetchWithPaymentNode } from '@shade402/langgraph';
workflow.addNode('fetch_api', fetchWithPaymentNode);This node:
Makes HTTP request to
state.api_urlIf 402 received, automatically creates payment
Retries request with payment
Returns API response in
state.api_response
paymentNode
paymentNodeSeparate payment node for more control:
import { paymentNode } from '@shade402/langgraph';
workflow.addNode('make_payment', paymentNode);This node:
Expects
state.payment_requiredto be trueMakes payment for
state.api_urlUpdates
state.api_responseandstate.payment_completed
Payment State
The PaymentState interface defines the state structure:
interface PaymentState {
wallet_keypair?: Keypair; // Wallet for payments
api_url?: string; // URL to fetch
api_response?: string; // API response (JSON string)
payment_completed?: boolean; // Payment completion status
payment_error?: string | null; // Payment error message
payment_required?: boolean; // Whether payment is required
max_payment_amount?: string; // Maximum payment amount
http_method?: string; // HTTP method (default: 'GET')
allow_local?: boolean; // Allow localhost URLs
rpc_url?: string; // Solana RPC URL
[key: string]: any; // Additional state fields
}Workflow Patterns
Simple Payment Flow
import { StateGraph } from '@langchain/langgraph';
import { fetchWithPaymentNode } from '@shade402/langgraph';
import { PaymentState } from '@shade402/langgraph';
const workflow = new StateGraph<PaymentState>({
channels: {
api_url: { reducer: (x, y) => y ?? x },
api_response: { reducer: (x, y) => y ?? x },
wallet_keypair: { reducer: (x, y) => y ?? x },
max_payment_amount: { reducer: (x, y) => y ?? x },
},
});
workflow.addNode('fetch_api', fetchWithPaymentNode);
workflow.addNode('process', async (state: PaymentState) => {
const data = JSON.parse(state.api_response || '{}');
return { ...state, processed: data };
});
workflow.setEntryPoint('fetch_api');
workflow.addEdge('fetch_api', 'process');
const app = workflow.compile();Conditional Payment Flow
import { StateGraph } from '@langchain/langgraph';
import { paymentNode, checkPaymentRequired, checkPaymentCompleted } from '@shade402/langgraph';
import { PaymentState } from '@shade402/langgraph';
const workflow = new StateGraph<PaymentState>({
channels: {
api_url: { reducer: (x, y) => y ?? x },
api_response: { reducer: (x, y) => y ?? x },
wallet_keypair: { reducer: (x, y) => y ?? x },
payment_required: { reducer: (x, y) => y ?? x },
payment_completed: { reducer: (x, y) => y ?? x },
},
});
// Initial fetch node
workflow.addNode('fetch_api', async (state: PaymentState) => {
// Make initial request
const response = await fetch(state.api_url!);
if (response.status === 402) {
return { ...state, payment_required: true };
}
const data = await response.json();
return { ...state, api_response: JSON.stringify(data) };
});
// Payment node
workflow.addNode('make_payment', paymentNode);
// Process node
workflow.addNode('process', async (state: PaymentState) => {
const data = JSON.parse(state.api_response || '{}');
return { ...state, processed: data };
});
// Error handler
workflow.addNode('error_handler', async (state: PaymentState) => {
return { ...state, error: state.payment_error };
});
// Edges
workflow.setEntryPoint('fetch_api');
workflow.addConditionalEdges(
'fetch_api',
checkPaymentRequired,
{
payment_required: 'make_payment',
success: 'process',
error: 'error_handler',
}
);
workflow.addConditionalEdges(
'make_payment',
checkPaymentCompleted,
{
completed: 'process',
failed: 'error_handler',
}
);
const app = workflow.compile();Multiple API Calls
const workflow = new StateGraph<PaymentState>({
channels: {
api_urls: { reducer: (x, y) => y ?? x },
api_responses: { reducer: (x, y) => y ?? x },
wallet_keypair: { reducer: (x, y) => y ?? x },
},
});
// Fetch multiple APIs
workflow.addNode('fetch_apis', async (state: PaymentState) => {
const urls = state.api_urls || [];
const responses: string[] = [];
for (const url of urls) {
const nodeState = {
...state,
api_url: url,
};
const result = await fetchWithPaymentNode(nodeState);
responses.push(result.api_response || '');
}
return { ...state, api_responses: responses };
});
workflow.setEntryPoint('fetch_apis');Conditional Functions
checkPaymentRequired
checkPaymentRequiredRoutes based on whether payment is required:
import { checkPaymentRequired } from '@shade402/langgraph';
workflow.addConditionalEdges(
'fetch_api',
checkPaymentRequired,
{
payment_required: 'make_payment',
success: 'process',
error: 'error_handler',
}
);Returns:
'payment_required': Ifstate.payment_requiredis true'success': Ifstate.api_responseexists'error': Otherwise
checkPaymentCompleted
checkPaymentCompletedRoutes based on payment completion:
import { checkPaymentCompleted } from '@shade402/langgraph';
workflow.addConditionalEdges(
'make_payment',
checkPaymentCompleted,
{
completed: 'process',
failed: 'error_handler',
}
);Returns:
'completed': Ifstate.payment_completedis true'failed': Otherwise
Custom Conditional Functions
function shouldRetryPayment(state: PaymentState): string {
if (state.payment_error && state.payment_error.includes('INSUFFICIENT_FUNDS')) {
return 'error'; // Don't retry insufficient funds
}
if (state.payment_error) {
return 'retry'; // Retry other errors
}
return 'continue';
}
workflow.addConditionalEdges(
'make_payment',
shouldRetryPayment,
{
continue: 'process',
retry: 'make_payment',
error: 'error_handler',
}
);Complete Example
import { StateGraph } from '@langchain/langgraph';
import { fetchWithPaymentNode, PaymentState } from '@shade402/langgraph';
import { Keypair } from '@solana/web3.js';
interface MyState extends PaymentState {
query: string;
result?: any;
}
const workflow = new StateGraph<MyState>({
channels: {
query: { reducer: (x, y) => y ?? x },
api_url: { reducer: (x, y) => y ?? x },
api_response: { reducer: (x, y) => y ?? x },
wallet_keypair: { reducer: (x, y) => y ?? x },
max_payment_amount: { reducer: (x, y) => y ?? x },
result: { reducer: (x, y) => y ?? x },
},
});
// Fetch API
workflow.addNode('fetch_api', fetchWithPaymentNode);
// Process response
workflow.addNode('process', async (state: MyState) => {
const data = JSON.parse(state.api_response || '{}');
return {
...state,
result: {
query: state.query,
data: data,
paymentId: data.paymentId,
},
};
});
// Error handler
workflow.addNode('error_handler', async (state: MyState) => {
return {
...state,
result: {
error: state.payment_error,
query: state.query,
},
};
});
workflow.setEntryPoint('fetch_api');
workflow.addConditionalEdges(
'fetch_api',
(state: MyState) => {
if (state.payment_error) return 'error';
if (state.api_response) return 'success';
return 'error';
},
{
success: 'process',
error: 'error_handler',
}
);
const app = workflow.compile();
// Run workflow
const wallet = Keypair.generate();
const result = await app.invoke({
query: 'Analyze this data',
api_url: 'https://api.example.com/analyze',
wallet_keypair: wallet,
max_payment_amount: '1.0',
rpc_url: process.env.SOLANA_RPC_URL,
});
console.log(result.result);Best Practices
Define clear state structure
Handle errors appropriately
Set maximum payment amounts
Use conditional edges for flow control
Process API responses in separate nodes
Monitor payment usage
Log payment transactions
Test with devnet before production
Use secure wallet storage
Consider rate limiting
Next Steps
Learn about LangChain Integration
Check out Examples for more workflows
Review Security best practices
Last updated
