15 min
Lifecycle Hooks & Observability
Learning Objectives
- Understand the three lifecycle hooks
- Log payments for debugging
- Abort payments that exceed cost limits
- Recover from payment failures
Why Hooks?
In production, you need to:
•Log payments for auditing
•Monitor costs and track spending
•Abort payments that exceed budgets
•Recover from transient failures
Lifecycle hooks let you insert custom logic at key points in the payment flow.
The Three Hooks
1.onBeforePaymentCreation — Called before creating payment (can abort)
2.onAfterPaymentCreation — Called after successful payment creation
3.onPaymentCreationFailure — Called when payment creation fails (can recover)
typescript
import { x402Client } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
const client = new x402Client()
.onBeforePaymentCreation(async (context) => {
console.log('About to create payment:', {
network: context.selectedRequirements.network,
amount: context.selectedRequirements.amount,
payTo: context.selectedRequirements.payTo,
});
// Abort if price exceeds $0.10
const amountUSDC = parseInt(context.selectedRequirements.amount) / 1e6;
if (amountUSDC > 0.10) {
return {
abort: true,
reason: 'Price exceeds maximum ($0.10)',
};
}
return undefined; // Continue with payment
})
.onAfterPaymentCreation(async (context) => {
console.log('✅ Payment created successfully');
console.log('Signature:', context.paymentPayload.signature?.slice(0, 20) + '...');
// Send to analytics
await analytics.track('payment_created', {
network: context.paymentPayload.network,
amount: context.selectedRequirements.amount,
});
return undefined;
})
.onPaymentCreationFailure(async (context) => {
console.error('❌ Payment failed:', context.error.message);
// Retry on nonce errors
if (context.error.message.includes('nonce')) {
console.log('Nonce conflict, retrying...');
return {
recovered: true,
payload: undefined, // Will regenerate
};
}
return undefined; // Don't recover
});
registerExactEvmScheme(client, { signer });onBeforePaymentCreation is perfect for implementing cost controls and budget limits!
Real Use Cases
Cost Tracking:
typescript
let totalSpent = 0;
client.onAfterPaymentCreation(async (ctx) => {
const amountUSDC = parseInt(ctx.selectedRequirements.amount) / 1e6;
totalSpent += amountUSDC;
console.log(`Total spent: $${totalSpent.toFixed(4)}`);
return undefined;
});Budget Limits:
typescript
const MAX_PER_REQUEST = 0.05; // $0.05
client.onBeforePaymentCreation(async (ctx) => {
const amountUSDC = parseInt(ctx.selectedRequirements.amount) / 1e6;
if (amountUSDC > MAX_PER_REQUEST) {
return { abort: true, reason: 'Exceeds per-request limit' };
}
return undefined;
});Logging to File:
typescript
import fs from 'fs';
client.onAfterPaymentCreation(async (ctx) => {
fs.appendFileSync('payments.log', `[${new Date().toISOString()}] Paid ${ctx.selectedRequirements.amount} to ${ctx.selectedRequirements.payTo}\n`);
return undefined;
});Add cost tracking to a client
Complete the TODOs to track total spending and abort expensive requests
Requirements:
Tracks spending
totalSpent variable is updated after payments
Aborts expensive requests
Payments that exceed budget are aborted
Your Solution