( requirement: PaymentRequirement, sessionSpentUSD: number, )
| 53 | * Validate that a payment requirement is within configured limits. |
| 54 | */ |
| 55 | export function validatePaymentRequirement( |
| 56 | requirement: PaymentRequirement, |
| 57 | sessionSpentUSD: number, |
| 58 | ): { valid: boolean; reason?: string } { |
| 59 | const config = getX402Config() |
| 60 | |
| 61 | if (!config.enabled) { |
| 62 | return { valid: false, reason: 'x402 payments are not enabled' } |
| 63 | } |
| 64 | |
| 65 | const amountUSD = tokenAmountToUSD(requirement.maxAmountRequired) |
| 66 | |
| 67 | if (amountUSD > config.maxPaymentPerRequestUSD) { |
| 68 | return { |
| 69 | valid: false, |
| 70 | reason: `Payment of $${amountUSD.toFixed(4)} exceeds per-request limit of $${config.maxPaymentPerRequestUSD.toFixed(2)}`, |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | if (sessionSpentUSD + amountUSD > config.maxSessionSpendUSD) { |
| 75 | return { |
| 76 | valid: false, |
| 77 | reason: `Payment would exceed session limit of $${config.maxSessionSpendUSD.toFixed(2)} (already spent $${sessionSpentUSD.toFixed(4)})`, |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | // Validate network matches config |
| 82 | if (requirement.network !== config.network) { |
| 83 | return { |
| 84 | valid: false, |
| 85 | reason: `Payment requires network ${requirement.network} but wallet is configured for ${config.network}`, |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | // Validate asset is USDC on the configured network |
| 90 | const expectedAsset = USDC_ADDRESSES[requirement.network] |
| 91 | if ( |
| 92 | expectedAsset && |
| 93 | requirement.asset.toLowerCase() !== expectedAsset.toLowerCase() |
| 94 | ) { |
| 95 | return { |
| 96 | valid: false, |
| 97 | reason: `Unknown payment token ${requirement.asset} (expected USDC: ${expectedAsset})`, |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | return { valid: true } |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * EIP-712 domain separator for EIP-3009 transferWithAuthorization. |
no test coverage detected