18 min
Lifecycle Hooks & Payment Processing
Learning Objectives
- Understand the 6 payment lifecycle hooks
- Add custom validation before verification
- Log payments to a database
- Implement recovery logic for failed payments
- Abort payments based on custom rules
What Are Lifecycle Hooks?
Lifecycle hooks let you run custom code at specific points in the payment flow:
Verification Hooks:
•
onBeforeVerify - Before verifying payment signature (can abort)•
onAfterVerify - After successful verification•
onVerifyFailure - When verification fails (can recover)Settlement Hooks:
•
onBeforeSettle - Before submitting transaction (can abort)•
onAfterSettle - After successful settlement•
onSettleFailure - When settlement fails (can recover)Setting Up Hooks
Add hooks to your resource server using the builder pattern:
typescript
import { x402ResourceServer, HTTPFacilitatorClient } from '@x402/core/server';
import { registerExactEvmScheme } from '@x402/evm/exact/server';
const facilitatorClient = new HTTPFacilitatorClient({
url: 'https://api.cdp.coinbase.com/platform/v2/x402'
});
const server = new x402ResourceServer(facilitatorClient)
.register('eip155:84532', new ExactEvmScheme())
.onBeforeVerify(async (context) => {
console.log('Before verify hook', context);
// Return { abort: true, reason: string } to abort
return undefined;
})
.onAfterSettle(async (context) => {
console.log('After settle hook', context.result.transaction);
return undefined;
});Use Case 1: Custom Validation (Fraud Detection)
Block payments that exceed a maximum amount:
typescript
const server = new x402ResourceServer(facilitatorClient)
.register('eip155:84532', new ExactEvmScheme())
.onBeforeVerify(async (context) => {
// Convert amount from atomic units (e.g., 10000 = 0.01 USDC)
const amountUSDC = parseInt(context.requirements.amount) / 1e6;
if (amountUSDC > 1.00) {
return {
abort: true,
reason: 'Payment amount exceeds maximum ($1.00)',
};
}
return undefined; // Continue with verification
});When you return
{ abort: true } from onBeforeVerify, the payment is rejected and the client receives a 402 error.Use Case 2: Database Logging
Log all successful settlements to your database:
typescript
const server = new x402ResourceServer(facilitatorClient)
.register('eip155:84532', new ExactEvmScheme())
.onAfterSettle(async (context) => {
// Log to database
await db.payments.create({
transactionHash: context.result.transaction,
payer: context.result.payer,
network: context.result.network,
amount: context.result.requirements.amount,
timestamp: new Date(),
});
console.log(`✅ Payment logged: ${context.result.transaction}`);
return undefined;
});Use Case 3: Recovery Logic
Recover from settlement failures (e.g., nonce errors):
typescript
const server = new x402ResourceServer(facilitatorClient)
.register('eip155:84532', new ExactEvmScheme())
.onSettleFailure(async (context) => {
console.error('Settlement failed:', context.error.message);
// If it's a nonce error, we can retry
if (context.error.message.includes('nonce')) {
console.log('Nonce error detected, attempting recovery...');
// Return recovered=true to indicate recovery
return {
recovered: true,
result: {
success: true,
transaction: context.signature.payload.txHash || '0x...',
network: context.requirements.network,
payer: context.signature.from,
requirements: context.requirements,
},
};
}
return undefined; // No recovery
});Recovery hooks are powerful - you can retry failed transactions, use alternative settlement methods, or accept payments from backup facilitators.
All Hooks at Once
Here's an example with all hooks:
typescript
const server = new x402ResourceServer(facilitatorClient)
.register('eip155:84532', new ExactEvmScheme())
.onBeforeVerify(async (ctx) => {
console.log('1. Before verification');
return undefined;
})
.onAfterVerify(async (ctx) => {
console.log('2. After verification ✓');
return undefined;
})
.onVerifyFailure(async (ctx) => {
console.error('2. Verification failed:', ctx.error);
return undefined;
})
.onBeforeSettle(async (ctx) => {
console.log('3. Before settlement');
return undefined;
})
.onAfterSettle(async (ctx) => {
console.log('4. After settlement ✓', ctx.result.transaction);
return undefined;
})
.onSettleFailure(async (ctx) => {
console.error('4. Settlement failed:', ctx.error);
return undefined;
});Implement Payment Logging with Hooks
Add lifecycle hooks to log payments and implement fraud detection
Requirements:
Rejects high payments
Payments over $0.10 are rejected
Logs settlements
Successful settlements are logged
Handles failures
Settlement failures are caught
Your Solution