moltspay 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/index.ts","../../src/chains/index.ts"],"sourcesContent":["/**\n * MoltsPay Server - Payment infrastructure for AI Agents\n * \n * Uses x402 protocol for gasless, pay-for-success payments.\n * \n * Usage:\n * const server = new MoltsPayServer('./moltspay.services.json');\n * server.skill('text-to-video', async (params) => { ... });\n * server.listen(3000);\n */\n\nimport { readFileSync } from 'fs';\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\nimport { ethers } from 'ethers';\nimport { getChain } from '../chains/index.js';\nimport type { ChainName } from '../chains/index.js';\nimport {\n ServicesManifest,\n ServiceConfig,\n SkillFunction,\n RegisteredSkill,\n MoltsPayServerOptions,\n} from './types.js';\n\nexport * from './types.js';\n\n// x402 constants\nconst X402_VERSION = 2;\nconst PAYMENT_REQUIRED_HEADER = 'x-payment-required';\nconst PAYMENT_HEADER = 'x-payment';\n\ninterface EIP3009Authorization {\n from: string;\n to: string;\n value: string;\n validAfter: string;\n validBefore: string;\n nonce: string;\n}\n\ninterface X402PaymentPayload {\n x402Version: number;\n scheme: string;\n network: string;\n payload: {\n signature: string;\n authorization: EIP3009Authorization;\n };\n}\n\nexport class MoltsPayServer {\n private manifest: ServicesManifest;\n private skills: Map<string, RegisteredSkill> = new Map();\n private options: MoltsPayServerOptions;\n private provider: ethers.JsonRpcProvider | null = null;\n private wallet: ethers.Wallet | null = null;\n\n constructor(servicesPath: string, options: MoltsPayServerOptions = {}) {\n // Load services manifest\n const content = readFileSync(servicesPath, 'utf-8');\n this.manifest = JSON.parse(content) as ServicesManifest;\n \n this.options = {\n port: options.port || 3000,\n host: options.host || '0.0.0.0',\n privateKey: options.privateKey || process.env.MOLTSPAY_PRIVATE_KEY,\n };\n\n // Initialize provider and wallet for claiming payments\n if (this.options.privateKey) {\n try {\n const chain = getChain(this.manifest.provider.chain as ChainName);\n this.provider = new ethers.JsonRpcProvider(chain.rpc);\n this.wallet = new ethers.Wallet(this.options.privateKey, this.provider);\n console.log(`[MoltsPay] Payment wallet: ${this.wallet.address}`);\n } catch (err) {\n console.warn('[MoltsPay] Warning: Could not initialize wallet for payment claims');\n }\n }\n\n console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);\n console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);\n console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);\n console.log(`[MoltsPay] Protocol: x402 (gasless, pay-for-success)`);\n }\n\n /**\n * Register a skill handler for a service\n */\n skill(serviceId: string, handler: SkillFunction): this {\n const config = this.manifest.services.find(s => s.id === serviceId);\n if (!config) {\n throw new Error(`Service '${serviceId}' not found in manifest`);\n }\n\n this.skills.set(serviceId, {\n id: serviceId,\n config,\n handler,\n });\n\n console.log(`[MoltsPay] Registered skill: ${serviceId} ($${config.price} ${config.currency})`);\n return this;\n }\n\n /**\n * Start the server\n */\n listen(port?: number): void {\n const p = port || this.options.port!;\n \n const server = createServer((req, res) => this.handleRequest(req, res));\n \n server.listen(p, this.options.host, () => {\n console.log(`[MoltsPay] Server listening on http://${this.options.host}:${p}`);\n console.log(`[MoltsPay] Endpoints:`);\n console.log(` GET /services - List available services`);\n console.log(` POST /execute - Execute service (x402 payment)`);\n });\n }\n\n private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const url = new URL(req.url || '/', `http://${req.headers.host}`);\n const path = url.pathname;\n const method = req.method || 'GET';\n\n // CORS headers\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Payment');\n res.setHeader('Access-Control-Expose-Headers', 'X-Payment-Required, X-Payment-Response');\n\n if (method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n try {\n if (method === 'GET' && path === '/services') {\n return this.handleGetServices(res);\n }\n\n if (method === 'POST' && path === '/execute') {\n const body = await this.readBody(req);\n const paymentHeader = req.headers[PAYMENT_HEADER] as string | undefined;\n return this.handleExecute(body, paymentHeader, res);\n }\n\n // Not found\n this.sendJson(res, 404, { error: 'Not found' });\n } catch (err: any) {\n console.error('[MoltsPay] Error:', err);\n this.sendJson(res, 500, { error: err.message || 'Internal error' });\n }\n }\n\n /**\n * GET /services - List available services\n */\n private handleGetServices(res: ServerResponse): void {\n const chain = getChain(this.manifest.provider.chain as ChainName);\n \n const services = this.manifest.services.map(s => ({\n id: s.id,\n name: s.name,\n description: s.description,\n price: s.price,\n currency: s.currency,\n input: s.input,\n output: s.output,\n available: this.skills.has(s.id),\n }));\n\n this.sendJson(res, 200, {\n provider: this.manifest.provider,\n services,\n x402: {\n version: X402_VERSION,\n network: `eip155:${chain.chainId}`,\n schemes: ['exact'],\n },\n });\n }\n\n /**\n * POST /execute - Execute service with x402 payment\n * Body: { service: string, params: object }\n * Header: X-Payment (optional - if missing, returns 402)\n */\n private async handleExecute(\n body: any,\n paymentHeader: string | undefined,\n res: ServerResponse\n ): Promise<void> {\n const { service, params } = body;\n\n if (!service) {\n return this.sendJson(res, 400, { error: 'Missing service' });\n }\n\n const skill = this.skills.get(service);\n if (!skill) {\n return this.sendJson(res, 404, { error: `Service '${service}' not found or not registered` });\n }\n\n // Validate required params\n for (const [key, field] of Object.entries(skill.config.input)) {\n if (field.required && (!params || params[key] === undefined)) {\n return this.sendJson(res, 400, { error: `Missing required param: ${key}` });\n }\n }\n\n // If no payment header, return 402 with payment requirements\n if (!paymentHeader) {\n return this.sendPaymentRequired(skill.config, res);\n }\n\n // Verify payment\n let payment: X402PaymentPayload;\n try {\n const decoded = Buffer.from(paymentHeader, 'base64').toString('utf-8');\n payment = JSON.parse(decoded);\n } catch {\n return this.sendJson(res, 400, { error: 'Invalid X-Payment header' });\n }\n\n // Validate payment\n const validation = this.validatePayment(payment, skill.config);\n if (!validation.valid) {\n return this.sendJson(res, 402, { error: validation.error });\n }\n\n // Execute skill FIRST (pay-for-success)\n console.log(`[MoltsPay] Executing skill: ${service}`);\n let result: any;\n try {\n result = await skill.handler(params || {});\n } catch (err: any) {\n console.error('[MoltsPay] Skill execution failed:', err.message);\n // Don't claim payment if skill fails\n return this.sendJson(res, 500, {\n error: 'Service execution failed',\n message: err.message,\n });\n }\n\n // Skill succeeded - now claim payment\n console.log(`[MoltsPay] Skill succeeded, claiming payment...`);\n let txHash: string | null = null;\n try {\n txHash = await this.claimPayment(payment);\n console.log(`[MoltsPay] Payment claimed: ${txHash}`);\n } catch (err: any) {\n console.error('[MoltsPay] Payment claim failed:', err.message);\n // Still return result even if payment claim fails\n // (we can retry claiming later)\n }\n\n this.sendJson(res, 200, {\n success: true,\n result,\n payment: txHash ? { txHash, status: 'claimed' } : { status: 'pending' },\n });\n }\n\n /**\n * Return 402 with x402 payment requirements\n */\n private sendPaymentRequired(config: ServiceConfig, res: ServerResponse): void {\n const chain = getChain(this.manifest.provider.chain as ChainName);\n const amountInUnits = Math.floor(config.price * 1e6).toString();\n\n const requirements = [{\n scheme: 'exact',\n network: `eip155:${chain.chainId}`,\n maxAmountRequired: amountInUnits,\n resource: this.manifest.provider.wallet,\n description: `${config.name} - $${config.price} ${config.currency}`,\n }];\n\n const encoded = Buffer.from(JSON.stringify(requirements)).toString('base64');\n\n res.writeHead(402, {\n 'Content-Type': 'application/json',\n [PAYMENT_REQUIRED_HEADER]: encoded,\n });\n res.end(JSON.stringify({\n error: 'Payment required',\n message: `Service requires $${config.price} ${config.currency}`,\n x402: requirements[0],\n }, null, 2));\n }\n\n /**\n * Validate x402 payment payload\n */\n private validatePayment(\n payment: X402PaymentPayload,\n config: ServiceConfig\n ): { valid: boolean; error?: string } {\n if (payment.x402Version !== X402_VERSION) {\n return { valid: false, error: `Unsupported x402 version: ${payment.x402Version}` };\n }\n\n if (payment.scheme !== 'exact') {\n return { valid: false, error: `Unsupported scheme: ${payment.scheme}` };\n }\n\n const chain = getChain(this.manifest.provider.chain as ChainName);\n const expectedNetwork = `eip155:${chain.chainId}`;\n if (payment.network !== expectedNetwork) {\n return { valid: false, error: `Network mismatch: expected ${expectedNetwork}` };\n }\n\n const auth = payment.payload.authorization;\n \n // Check recipient\n if (auth.to.toLowerCase() !== this.manifest.provider.wallet.toLowerCase()) {\n return { valid: false, error: 'Payment recipient mismatch' };\n }\n\n // Check amount\n const amount = Number(auth.value) / 1e6;\n if (amount < config.price) {\n return { valid: false, error: `Insufficient amount: $${amount} < $${config.price}` };\n }\n\n // Check expiry\n const now = Math.floor(Date.now() / 1000);\n if (Number(auth.validBefore) < now) {\n return { valid: false, error: 'Payment authorization expired' };\n }\n\n if (Number(auth.validAfter) > now) {\n return { valid: false, error: 'Payment authorization not yet valid' };\n }\n\n return { valid: true };\n }\n\n /**\n * Claim payment using transferWithAuthorization\n */\n private async claimPayment(payment: X402PaymentPayload): Promise<string> {\n if (!this.wallet || !this.provider) {\n throw new Error('Wallet not configured for payment claims');\n }\n\n const chain = getChain(this.manifest.provider.chain as ChainName);\n const auth = payment.payload.authorization;\n const sig = payment.payload.signature;\n\n // Parse signature\n const { r, s, v } = ethers.Signature.from(sig);\n\n // USDC transferWithAuthorization ABI\n const usdcAbi = [\n 'function transferWithAuthorization(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s)'\n ];\n\n const usdc = new ethers.Contract(chain.usdc, usdcAbi, this.wallet);\n\n const tx = await usdc.transferWithAuthorization(\n auth.from,\n auth.to,\n auth.value,\n auth.validAfter,\n auth.validBefore,\n auth.nonce,\n v,\n r,\n s\n );\n\n const receipt = await tx.wait();\n return receipt.hash;\n }\n\n private async readBody(req: IncomingMessage): Promise<any> {\n return new Promise((resolve, reject) => {\n let body = '';\n req.on('data', chunk => body += chunk);\n req.on('end', () => {\n try {\n resolve(body ? JSON.parse(body) : {});\n } catch {\n reject(new Error('Invalid JSON'));\n }\n });\n req.on('error', reject);\n });\n }\n\n private sendJson(res: ServerResponse, status: number, data: any): void {\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(data, null, 2));\n }\n}\n","/**\n * Blockchain Configuration\n */\n\nimport type { ChainConfig, ChainName } from '../types/index.js';\n\nexport const CHAINS: Record<ChainName, ChainConfig> = {\n // ============ Mainnet ============\n base: {\n name: 'Base',\n chainId: 8453,\n rpc: 'https://mainnet.base.org',\n usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n explorer: 'https://basescan.org/address/',\n explorerTx: 'https://basescan.org/tx/',\n avgBlockTime: 2,\n },\n polygon: {\n name: 'Polygon',\n chainId: 137,\n rpc: 'https://polygon-rpc.com',\n usdc: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n explorer: 'https://polygonscan.com/address/',\n explorerTx: 'https://polygonscan.com/tx/',\n avgBlockTime: 2,\n },\n ethereum: {\n name: 'Ethereum',\n chainId: 1,\n rpc: 'https://eth.llamarpc.com',\n usdc: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',\n explorer: 'https://etherscan.io/address/',\n explorerTx: 'https://etherscan.io/tx/',\n avgBlockTime: 12,\n },\n\n // ============ Testnet ============\n base_sepolia: {\n name: 'Base Sepolia',\n chainId: 84532,\n rpc: 'https://sepolia.base.org',\n usdc: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n explorer: 'https://sepolia.basescan.org/address/',\n explorerTx: 'https://sepolia.basescan.org/tx/',\n avgBlockTime: 2,\n },\n sepolia: {\n name: 'Sepolia',\n chainId: 11155111,\n rpc: 'https://rpc.sepolia.org',\n usdc: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',\n explorer: 'https://sepolia.etherscan.io/address/',\n explorerTx: 'https://sepolia.etherscan.io/tx/',\n avgBlockTime: 12,\n },\n};\n\n/**\n * Get chain configuration\n */\nexport function getChain(name: ChainName): ChainConfig {\n const config = CHAINS[name];\n if (!config) {\n throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(', ')}`);\n }\n return config;\n}\n\n/**\n * List all supported chains\n */\nexport function listChains(): ChainName[] {\n return Object.keys(CHAINS) as ChainName[];\n}\n\n/**\n * Get chain config by chainId\n */\nexport function getChainById(chainId: number): ChainConfig | undefined {\n return Object.values(CHAINS).find(c => c.chainId === chainId);\n}\n\n/**\n * ERC20 ABI (minimal, only required methods)\n */\nexport const ERC20_ABI = [\n 'function balanceOf(address owner) view returns (uint256)',\n 'function transfer(address to, uint256 amount) returns (bool)',\n 'function approve(address spender, uint256 amount) returns (bool)',\n 'function allowance(address owner, address spender) view returns (uint256)',\n 'function decimals() view returns (uint8)',\n 'function symbol() view returns (string)',\n 'function name() view returns (string)',\n 'function nonces(address owner) view returns (uint256)',\n 'function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)',\n 'event Transfer(address indexed from, address indexed to, uint256 value)',\n 'event Approval(address indexed owner, address indexed spender, uint256 value)',\n];\n\nexport type { ChainConfig, ChainName };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/server/index.ts"],"sourcesContent":["/**\n * MoltsPay Server - Payment infrastructure for AI Agents\n * \n * Supports both testnet (x402.org) and mainnet (CDP) facilitators.\n * Server does NOT need private key - facilitator handles on-chain settlement.\n * \n * Environment variables (from ~/.moltspay/.env or process.env):\n * USE_MAINNET=true - Use Base mainnet (requires CDP keys)\n * CDP_API_KEY_ID=xxx - Coinbase Developer Platform API key ID\n * CDP_API_KEY_SECRET=xxx - CDP API key secret\n * \n * Usage:\n * const server = new MoltsPayServer('./moltspay.services.json');\n * server.skill('text-to-video', async (params) => { ... });\n * server.listen(3000);\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\nimport * as path from 'path';\nimport { getChain } from '../chains/index.js';\nimport type { ChainName } from '../chains/index.js';\nimport {\n ServicesManifest,\n ServiceConfig,\n SkillFunction,\n RegisteredSkill,\n MoltsPayServerOptions,\n} from './types.js';\n\nexport * from './types.js';\n\n// x402 constants\nconst X402_VERSION = 2;\nconst PAYMENT_REQUIRED_HEADER = 'x-payment-required';\nconst PAYMENT_HEADER = 'x-payment';\nconst PAYMENT_RESPONSE_HEADER = 'x-payment-response';\n\n// Facilitator URLs\nconst FACILITATOR_TESTNET = 'https://www.x402.org/facilitator';\nconst FACILITATOR_MAINNET = 'https://api.cdp.coinbase.com/platform/v2/x402';\n\ninterface X402PaymentPayload {\n x402Version: number;\n scheme: string;\n network: string;\n payload: any;\n}\n\ninterface CDPConfig {\n useMainnet: boolean;\n apiKeyId?: string;\n apiKeySecret?: string;\n}\n\n/**\n * Load environment from .env files\n */\nfunction loadEnvFiles(): void {\n // Try to load dotenv\n try {\n const dotenv = require('dotenv');\n \n // Priority: current dir > ~/.moltspay/\n const envPaths = [\n path.join(process.cwd(), '.env'),\n path.join(process.env.HOME || '', '.moltspay', '.env'),\n ];\n \n for (const envPath of envPaths) {\n if (existsSync(envPath)) {\n dotenv.config({ path: envPath });\n console.log(`[MoltsPay] Loaded config from ${envPath}`);\n break;\n }\n }\n } catch {\n // dotenv not installed, use process.env only\n }\n}\n\n/**\n * Get CDP configuration from environment\n */\nfunction getCDPConfig(): CDPConfig {\n loadEnvFiles();\n \n return {\n useMainnet: process.env.USE_MAINNET?.toLowerCase() === 'true',\n apiKeyId: process.env.CDP_API_KEY_ID,\n apiKeySecret: process.env.CDP_API_KEY_SECRET,\n };\n}\n\n/**\n * Generate CDP auth headers for API requests\n */\nasync function getCDPAuthHeaders(\n method: string,\n urlPath: string,\n body?: any\n): Promise<Record<string, string>> {\n const config = getCDPConfig();\n \n if (!config.apiKeyId || !config.apiKeySecret) {\n throw new Error('CDP_API_KEY_ID and CDP_API_KEY_SECRET required for mainnet');\n }\n \n try {\n // Import CDP SDK auth\n const { getAuthHeaders } = await import('@coinbase/cdp-sdk/auth');\n \n const headers = await getAuthHeaders({\n apiKeyId: config.apiKeyId,\n apiKeySecret: config.apiKeySecret,\n requestMethod: method,\n requestHost: 'api.cdp.coinbase.com',\n requestPath: urlPath,\n requestBody: body,\n });\n \n return headers;\n } catch (err: any) {\n console.error('[MoltsPay] Failed to generate CDP auth headers:', err.message);\n throw err;\n }\n}\n\nexport class MoltsPayServer {\n private manifest: ServicesManifest;\n private skills: Map<string, RegisteredSkill> = new Map();\n private options: MoltsPayServerOptions;\n private cdpConfig: CDPConfig;\n private facilitatorUrl: string;\n private networkId: string;\n\n constructor(servicesPath: string, options: MoltsPayServerOptions = {}) {\n // Load CDP config first\n this.cdpConfig = getCDPConfig();\n \n // Load services manifest\n const content = readFileSync(servicesPath, 'utf-8');\n this.manifest = JSON.parse(content) as ServicesManifest;\n \n this.options = {\n port: options.port || 3000,\n host: options.host || '0.0.0.0',\n };\n\n // Determine facilitator and network based on config\n if (this.cdpConfig.useMainnet) {\n if (!this.cdpConfig.apiKeyId || !this.cdpConfig.apiKeySecret) {\n console.warn('[MoltsPay] WARNING: USE_MAINNET=true but CDP keys not set!');\n console.warn('[MoltsPay] Set CDP_API_KEY_ID and CDP_API_KEY_SECRET in ~/.moltspay/.env');\n }\n this.facilitatorUrl = FACILITATOR_MAINNET;\n this.networkId = 'eip155:8453'; // Base mainnet\n } else {\n this.facilitatorUrl = options.facilitatorUrl || FACILITATOR_TESTNET;\n this.networkId = 'eip155:84532'; // Base Sepolia testnet\n }\n\n const networkName = this.cdpConfig.useMainnet ? 'Base mainnet' : 'Base Sepolia (testnet)';\n const facilitatorName = this.cdpConfig.useMainnet ? 'CDP' : 'x402.org';\n \n console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);\n console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);\n console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);\n console.log(`[MoltsPay] Network: ${this.networkId} (${networkName})`);\n console.log(`[MoltsPay] Facilitator: ${facilitatorName} (${this.facilitatorUrl})`);\n if (this.cdpConfig.useMainnet && this.cdpConfig.apiKeyId) {\n console.log(`[MoltsPay] CDP API Key: ${this.cdpConfig.apiKeyId.slice(0, 8)}...`);\n }\n console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);\n }\n\n /**\n * Register a skill handler for a service\n */\n skill(serviceId: string, handler: SkillFunction): this {\n const config = this.manifest.services.find(s => s.id === serviceId);\n if (!config) {\n throw new Error(`Service '${serviceId}' not found in manifest`);\n }\n this.skills.set(serviceId, { id: serviceId, config, handler });\n return this;\n }\n\n /**\n * Start HTTP server\n */\n listen(port?: number): void {\n const p = port || this.options.port || 3000;\n const host = this.options.host || '0.0.0.0';\n\n const server = createServer((req, res) => this.handleRequest(req, res));\n server.listen(p, host, () => {\n console.log(`[MoltsPay] Server listening on http://${host}:${p}`);\n console.log(`[MoltsPay] Endpoints:`);\n console.log(` GET /services - List available services`);\n console.log(` POST /execute - Execute service (x402 payment)`);\n });\n }\n\n /**\n * Handle incoming request\n */\n private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n // CORS\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Payment');\n res.setHeader('Access-Control-Expose-Headers', 'X-Payment-Required, X-Payment-Response');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n try {\n const url = new URL(req.url || '/', `http://${req.headers.host}`);\n \n if (url.pathname === '/services' && req.method === 'GET') {\n return this.handleGetServices(res);\n }\n\n if (url.pathname === '/execute' && req.method === 'POST') {\n const body = await this.readBody(req);\n const paymentHeader = req.headers[PAYMENT_HEADER] as string | undefined;\n return await this.handleExecute(body, paymentHeader, res);\n }\n\n // Not found\n this.sendJson(res, 404, { error: 'Not found' });\n } catch (err: any) {\n console.error('[MoltsPay] Error:', err);\n this.sendJson(res, 500, { error: err.message || 'Internal error' });\n }\n }\n\n /**\n * GET /services - List available services\n */\n private handleGetServices(res: ServerResponse): void {\n const services = this.manifest.services.map(s => ({\n id: s.id,\n name: s.name,\n description: s.description,\n price: s.price,\n currency: s.currency,\n input: s.input,\n output: s.output,\n available: this.skills.has(s.id),\n }));\n\n this.sendJson(res, 200, {\n provider: this.manifest.provider,\n services,\n x402: {\n version: X402_VERSION,\n network: this.networkId,\n schemes: ['exact'],\n facilitator: this.cdpConfig.useMainnet ? 'cdp' : 'x402.org',\n mainnet: this.cdpConfig.useMainnet,\n },\n });\n }\n\n /**\n * POST /execute - Execute service with x402 payment\n */\n private async handleExecute(\n body: any,\n paymentHeader: string | undefined,\n res: ServerResponse\n ): Promise<void> {\n const { service, params } = body;\n\n if (!service) {\n return this.sendJson(res, 400, { error: 'Missing service' });\n }\n\n const skill = this.skills.get(service);\n if (!skill) {\n return this.sendJson(res, 404, { error: `Service '${service}' not found or not registered` });\n }\n\n // Validate required params\n for (const [key, field] of Object.entries(skill.config.input)) {\n if (field.required && (!params || params[key] === undefined)) {\n return this.sendJson(res, 400, { error: `Missing required param: ${key}` });\n }\n }\n\n // If no payment header, return 402 with payment requirements\n if (!paymentHeader) {\n return this.sendPaymentRequired(skill.config, res);\n }\n\n // Parse payment payload\n let payment: X402PaymentPayload;\n try {\n const decoded = Buffer.from(paymentHeader, 'base64').toString('utf-8');\n payment = JSON.parse(decoded);\n } catch {\n return this.sendJson(res, 400, { error: 'Invalid X-Payment header' });\n }\n\n // Validate basic payment fields\n const validation = this.validatePayment(payment, skill.config);\n if (!validation.valid) {\n return this.sendJson(res, 402, { error: validation.error });\n }\n\n // Verify payment with facilitator\n console.log(`[MoltsPay] Verifying payment with facilitator...`);\n const verifyResult = await this.verifyWithFacilitator(payment, skill.config);\n if (!verifyResult.valid) {\n return this.sendJson(res, 402, { error: `Payment verification failed: ${verifyResult.error}` });\n }\n\n // Execute skill FIRST (pay-for-success)\n console.log(`[MoltsPay] Executing skill: ${service}`);\n let result: any;\n try {\n result = await skill.handler(params || {});\n } catch (err: any) {\n console.error('[MoltsPay] Skill execution failed:', err.message);\n return this.sendJson(res, 500, {\n error: 'Service execution failed',\n message: err.message,\n });\n }\n\n // Skill succeeded - now settle payment with facilitator\n console.log(`[MoltsPay] Skill succeeded, settling payment...`);\n let settlement: any = null;\n try {\n settlement = await this.settleWithFacilitator(payment, skill.config);\n console.log(`[MoltsPay] Payment settled: ${settlement.transaction || 'pending'}`);\n } catch (err: any) {\n console.error('[MoltsPay] Settlement failed:', err.message);\n }\n\n // Build response\n const responseHeaders: Record<string, string> = {};\n if (settlement) {\n const responsePayload = {\n success: true,\n transaction: settlement.transaction,\n network: payment.network,\n };\n responseHeaders[PAYMENT_RESPONSE_HEADER] = Buffer.from(\n JSON.stringify(responsePayload)\n ).toString('base64');\n }\n\n this.sendJson(res, 200, {\n success: true,\n result,\n payment: settlement \n ? { transaction: settlement.transaction, status: 'settled' }\n : { status: 'pending' },\n }, responseHeaders);\n }\n\n /**\n * Return 402 with x402 payment requirements\n */\n private sendPaymentRequired(config: ServiceConfig, res: ServerResponse): void {\n const amountInUnits = Math.floor(config.price * 1e6).toString();\n\n const requirements = [{\n scheme: 'exact',\n network: this.networkId,\n maxAmountRequired: amountInUnits,\n resource: this.manifest.provider.wallet,\n description: `${config.name} - $${config.price} ${config.currency}`,\n extra: JSON.stringify({ \n facilitator: this.cdpConfig.useMainnet ? 'cdp' : 'x402.org',\n mainnet: this.cdpConfig.useMainnet,\n }),\n }];\n\n const encoded = Buffer.from(JSON.stringify(requirements)).toString('base64');\n\n res.writeHead(402, {\n 'Content-Type': 'application/json',\n [PAYMENT_REQUIRED_HEADER]: encoded,\n });\n res.end(JSON.stringify({\n error: 'Payment required',\n message: `Service requires $${config.price} ${config.currency}`,\n x402: requirements[0],\n }, null, 2));\n }\n\n /**\n * Basic payment validation\n */\n private validatePayment(\n payment: X402PaymentPayload,\n config: ServiceConfig\n ): { valid: boolean; error?: string } {\n if (payment.x402Version !== X402_VERSION) {\n return { valid: false, error: `Unsupported x402 version: ${payment.x402Version}` };\n }\n\n if (payment.scheme !== 'exact') {\n return { valid: false, error: `Unsupported scheme: ${payment.scheme}` };\n }\n\n if (payment.network !== this.networkId) {\n return { valid: false, error: `Network mismatch: expected ${this.networkId}, got ${payment.network}` };\n }\n\n return { valid: true };\n }\n\n /**\n * Verify payment with facilitator (testnet or CDP)\n */\n private async verifyWithFacilitator(\n payment: X402PaymentPayload,\n config: ServiceConfig\n ): Promise<{ valid: boolean; error?: string }> {\n try {\n const amountInUnits = Math.floor(config.price * 1e6).toString();\n\n const requirements = {\n scheme: 'exact',\n network: this.networkId,\n maxAmountRequired: amountInUnits,\n resource: this.manifest.provider.wallet,\n payTo: this.manifest.provider.wallet,\n };\n\n const requestBody = {\n paymentPayload: payment,\n paymentRequirements: requirements,\n };\n\n // Build headers\n let headers: Record<string, string> = { 'Content-Type': 'application/json' };\n \n if (this.cdpConfig.useMainnet) {\n // Add CDP auth headers for mainnet\n const authHeaders = await getCDPAuthHeaders(\n 'POST',\n '/platform/v2/x402/verify',\n requestBody\n );\n headers = { ...headers, ...authHeaders };\n }\n\n const response = await fetch(`${this.facilitatorUrl}/verify`, {\n method: 'POST',\n headers,\n body: JSON.stringify(requestBody),\n });\n\n const result = await response.json() as any;\n\n if (!response.ok || !result.isValid) {\n return { valid: false, error: result.invalidReason || result.error || 'Verification failed' };\n }\n\n return { valid: true };\n } catch (err: any) {\n return { valid: false, error: `Facilitator error: ${err.message}` };\n }\n }\n\n /**\n * Settle payment with facilitator (execute on-chain transfer)\n */\n private async settleWithFacilitator(\n payment: X402PaymentPayload,\n config: ServiceConfig\n ): Promise<{ transaction?: string; status: string }> {\n const amountInUnits = Math.floor(config.price * 1e6).toString();\n\n const requirements = {\n scheme: 'exact',\n network: this.networkId,\n maxAmountRequired: amountInUnits,\n resource: this.manifest.provider.wallet,\n payTo: this.manifest.provider.wallet,\n };\n\n const requestBody = {\n paymentPayload: payment,\n paymentRequirements: requirements,\n };\n\n // Build headers\n let headers: Record<string, string> = { 'Content-Type': 'application/json' };\n \n if (this.cdpConfig.useMainnet) {\n // Add CDP auth headers for mainnet\n const authHeaders = await getCDPAuthHeaders(\n 'POST',\n '/platform/v2/x402/settle',\n requestBody\n );\n headers = { ...headers, ...authHeaders };\n }\n\n const response = await fetch(`${this.facilitatorUrl}/settle`, {\n method: 'POST',\n headers,\n body: JSON.stringify(requestBody),\n });\n\n const result = await response.json() as any;\n\n if (!response.ok || !result.success) {\n throw new Error(result.error || result.errorReason || 'Settlement failed');\n }\n\n return {\n transaction: result.transaction,\n status: result.status || 'settled',\n };\n }\n\n private async readBody(req: IncomingMessage): Promise<any> {\n return new Promise((resolve, reject) => {\n let body = '';\n req.on('data', chunk => body += chunk);\n req.on('end', () => {\n try {\n resolve(body ? JSON.parse(body) : {});\n } catch {\n reject(new Error('Invalid JSON'));\n }\n });\n req.on('error', reject);\n });\n }\n\n private sendJson(\n res: ServerResponse, \n status: number, \n data: any,\n extraHeaders?: Record<string, string>\n ): void {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (extraHeaders) {\n Object.assign(headers, extraHeaders);\n }\n res.writeHead(status, headers);\n res.end(JSON.stringify(data, null, 2));\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,98 +1,103 @@
1
- // src/server/index.ts
2
- import { readFileSync } from "fs";
3
- import { createServer } from "http";
4
- import { ethers } from "ethers";
5
-
6
- // src/chains/index.ts
7
- var CHAINS = {
8
- // ============ Mainnet ============
9
- base: {
10
- name: "Base",
11
- chainId: 8453,
12
- rpc: "https://mainnet.base.org",
13
- usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
14
- explorer: "https://basescan.org/address/",
15
- explorerTx: "https://basescan.org/tx/",
16
- avgBlockTime: 2
17
- },
18
- polygon: {
19
- name: "Polygon",
20
- chainId: 137,
21
- rpc: "https://polygon-rpc.com",
22
- usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
23
- explorer: "https://polygonscan.com/address/",
24
- explorerTx: "https://polygonscan.com/tx/",
25
- avgBlockTime: 2
26
- },
27
- ethereum: {
28
- name: "Ethereum",
29
- chainId: 1,
30
- rpc: "https://eth.llamarpc.com",
31
- usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
32
- explorer: "https://etherscan.io/address/",
33
- explorerTx: "https://etherscan.io/tx/",
34
- avgBlockTime: 12
35
- },
36
- // ============ Testnet ============
37
- base_sepolia: {
38
- name: "Base Sepolia",
39
- chainId: 84532,
40
- rpc: "https://sepolia.base.org",
41
- usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
42
- explorer: "https://sepolia.basescan.org/address/",
43
- explorerTx: "https://sepolia.basescan.org/tx/",
44
- avgBlockTime: 2
45
- },
46
- sepolia: {
47
- name: "Sepolia",
48
- chainId: 11155111,
49
- rpc: "https://rpc.sepolia.org",
50
- usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
51
- explorer: "https://sepolia.etherscan.io/address/",
52
- explorerTx: "https://sepolia.etherscan.io/tx/",
53
- avgBlockTime: 12
54
- }
55
- };
56
- function getChain(name) {
57
- const config = CHAINS[name];
58
- if (!config) {
59
- throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(", ")}`);
60
- }
61
- return config;
62
- }
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
63
7
 
64
8
  // src/server/index.ts
9
+ import { readFileSync, existsSync } from "fs";
10
+ import { createServer } from "http";
11
+ import * as path from "path";
65
12
  var X402_VERSION = 2;
66
13
  var PAYMENT_REQUIRED_HEADER = "x-payment-required";
67
14
  var PAYMENT_HEADER = "x-payment";
15
+ var PAYMENT_RESPONSE_HEADER = "x-payment-response";
16
+ var FACILITATOR_TESTNET = "https://www.x402.org/facilitator";
17
+ var FACILITATOR_MAINNET = "https://api.cdp.coinbase.com/platform/v2/x402";
18
+ function loadEnvFiles() {
19
+ try {
20
+ const dotenv = __require("dotenv");
21
+ const envPaths = [
22
+ path.join(process.cwd(), ".env"),
23
+ path.join(process.env.HOME || "", ".moltspay", ".env")
24
+ ];
25
+ for (const envPath of envPaths) {
26
+ if (existsSync(envPath)) {
27
+ dotenv.config({ path: envPath });
28
+ console.log(`[MoltsPay] Loaded config from ${envPath}`);
29
+ break;
30
+ }
31
+ }
32
+ } catch {
33
+ }
34
+ }
35
+ function getCDPConfig() {
36
+ loadEnvFiles();
37
+ return {
38
+ useMainnet: process.env.USE_MAINNET?.toLowerCase() === "true",
39
+ apiKeyId: process.env.CDP_API_KEY_ID,
40
+ apiKeySecret: process.env.CDP_API_KEY_SECRET
41
+ };
42
+ }
43
+ async function getCDPAuthHeaders(method, urlPath, body) {
44
+ const config = getCDPConfig();
45
+ if (!config.apiKeyId || !config.apiKeySecret) {
46
+ throw new Error("CDP_API_KEY_ID and CDP_API_KEY_SECRET required for mainnet");
47
+ }
48
+ try {
49
+ const { getAuthHeaders } = await import("@coinbase/cdp-sdk/auth");
50
+ const headers = await getAuthHeaders({
51
+ apiKeyId: config.apiKeyId,
52
+ apiKeySecret: config.apiKeySecret,
53
+ requestMethod: method,
54
+ requestHost: "api.cdp.coinbase.com",
55
+ requestPath: urlPath,
56
+ requestBody: body
57
+ });
58
+ return headers;
59
+ } catch (err) {
60
+ console.error("[MoltsPay] Failed to generate CDP auth headers:", err.message);
61
+ throw err;
62
+ }
63
+ }
68
64
  var MoltsPayServer = class {
69
65
  manifest;
70
66
  skills = /* @__PURE__ */ new Map();
71
67
  options;
72
- provider = null;
73
- wallet = null;
68
+ cdpConfig;
69
+ facilitatorUrl;
70
+ networkId;
74
71
  constructor(servicesPath, options = {}) {
72
+ this.cdpConfig = getCDPConfig();
75
73
  const content = readFileSync(servicesPath, "utf-8");
76
74
  this.manifest = JSON.parse(content);
77
75
  this.options = {
78
76
  port: options.port || 3e3,
79
- host: options.host || "0.0.0.0",
80
- privateKey: options.privateKey || process.env.MOLTSPAY_PRIVATE_KEY
77
+ host: options.host || "0.0.0.0"
81
78
  };
82
- if (this.options.privateKey) {
83
- try {
84
- const chain = getChain(this.manifest.provider.chain);
85
- this.provider = new ethers.JsonRpcProvider(chain.rpc);
86
- this.wallet = new ethers.Wallet(this.options.privateKey, this.provider);
87
- console.log(`[MoltsPay] Payment wallet: ${this.wallet.address}`);
88
- } catch (err) {
89
- console.warn("[MoltsPay] Warning: Could not initialize wallet for payment claims");
79
+ if (this.cdpConfig.useMainnet) {
80
+ if (!this.cdpConfig.apiKeyId || !this.cdpConfig.apiKeySecret) {
81
+ console.warn("[MoltsPay] WARNING: USE_MAINNET=true but CDP keys not set!");
82
+ console.warn("[MoltsPay] Set CDP_API_KEY_ID and CDP_API_KEY_SECRET in ~/.moltspay/.env");
90
83
  }
84
+ this.facilitatorUrl = FACILITATOR_MAINNET;
85
+ this.networkId = "eip155:8453";
86
+ } else {
87
+ this.facilitatorUrl = options.facilitatorUrl || FACILITATOR_TESTNET;
88
+ this.networkId = "eip155:84532";
91
89
  }
90
+ const networkName = this.cdpConfig.useMainnet ? "Base mainnet" : "Base Sepolia (testnet)";
91
+ const facilitatorName = this.cdpConfig.useMainnet ? "CDP" : "x402.org";
92
92
  console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
93
93
  console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
94
94
  console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);
95
- console.log(`[MoltsPay] Protocol: x402 (gasless, pay-for-success)`);
95
+ console.log(`[MoltsPay] Network: ${this.networkId} (${networkName})`);
96
+ console.log(`[MoltsPay] Facilitator: ${facilitatorName} (${this.facilitatorUrl})`);
97
+ if (this.cdpConfig.useMainnet && this.cdpConfig.apiKeyId) {
98
+ console.log(`[MoltsPay] CDP API Key: ${this.cdpConfig.apiKeyId.slice(0, 8)}...`);
99
+ }
100
+ console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);
96
101
  }
97
102
  /**
98
103
  * Register a skill handler for a service
@@ -102,48 +107,45 @@ var MoltsPayServer = class {
102
107
  if (!config) {
103
108
  throw new Error(`Service '${serviceId}' not found in manifest`);
104
109
  }
105
- this.skills.set(serviceId, {
106
- id: serviceId,
107
- config,
108
- handler
109
- });
110
- console.log(`[MoltsPay] Registered skill: ${serviceId} ($${config.price} ${config.currency})`);
110
+ this.skills.set(serviceId, { id: serviceId, config, handler });
111
111
  return this;
112
112
  }
113
113
  /**
114
- * Start the server
114
+ * Start HTTP server
115
115
  */
116
116
  listen(port) {
117
- const p = port || this.options.port;
117
+ const p = port || this.options.port || 3e3;
118
+ const host = this.options.host || "0.0.0.0";
118
119
  const server = createServer((req, res) => this.handleRequest(req, res));
119
- server.listen(p, this.options.host, () => {
120
- console.log(`[MoltsPay] Server listening on http://${this.options.host}:${p}`);
120
+ server.listen(p, host, () => {
121
+ console.log(`[MoltsPay] Server listening on http://${host}:${p}`);
121
122
  console.log(`[MoltsPay] Endpoints:`);
122
123
  console.log(` GET /services - List available services`);
123
124
  console.log(` POST /execute - Execute service (x402 payment)`);
124
125
  });
125
126
  }
127
+ /**
128
+ * Handle incoming request
129
+ */
126
130
  async handleRequest(req, res) {
127
- const url = new URL(req.url || "/", `http://${req.headers.host}`);
128
- const path = url.pathname;
129
- const method = req.method || "GET";
130
131
  res.setHeader("Access-Control-Allow-Origin", "*");
131
132
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
132
133
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment");
133
134
  res.setHeader("Access-Control-Expose-Headers", "X-Payment-Required, X-Payment-Response");
134
- if (method === "OPTIONS") {
135
+ if (req.method === "OPTIONS") {
135
136
  res.writeHead(204);
136
137
  res.end();
137
138
  return;
138
139
  }
139
140
  try {
140
- if (method === "GET" && path === "/services") {
141
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
142
+ if (url.pathname === "/services" && req.method === "GET") {
141
143
  return this.handleGetServices(res);
142
144
  }
143
- if (method === "POST" && path === "/execute") {
145
+ if (url.pathname === "/execute" && req.method === "POST") {
144
146
  const body = await this.readBody(req);
145
147
  const paymentHeader = req.headers[PAYMENT_HEADER];
146
- return this.handleExecute(body, paymentHeader, res);
148
+ return await this.handleExecute(body, paymentHeader, res);
147
149
  }
148
150
  this.sendJson(res, 404, { error: "Not found" });
149
151
  } catch (err) {
@@ -155,7 +157,6 @@ var MoltsPayServer = class {
155
157
  * GET /services - List available services
156
158
  */
157
159
  handleGetServices(res) {
158
- const chain = getChain(this.manifest.provider.chain);
159
160
  const services = this.manifest.services.map((s) => ({
160
161
  id: s.id,
161
162
  name: s.name,
@@ -171,15 +172,15 @@ var MoltsPayServer = class {
171
172
  services,
172
173
  x402: {
173
174
  version: X402_VERSION,
174
- network: `eip155:${chain.chainId}`,
175
- schemes: ["exact"]
175
+ network: this.networkId,
176
+ schemes: ["exact"],
177
+ facilitator: this.cdpConfig.useMainnet ? "cdp" : "x402.org",
178
+ mainnet: this.cdpConfig.useMainnet
176
179
  }
177
180
  });
178
181
  }
179
182
  /**
180
183
  * POST /execute - Execute service with x402 payment
181
- * Body: { service: string, params: object }
182
- * Header: X-Payment (optional - if missing, returns 402)
183
184
  */
184
185
  async handleExecute(body, paymentHeader, res) {
185
186
  const { service, params } = body;
@@ -209,6 +210,11 @@ var MoltsPayServer = class {
209
210
  if (!validation.valid) {
210
211
  return this.sendJson(res, 402, { error: validation.error });
211
212
  }
213
+ console.log(`[MoltsPay] Verifying payment with facilitator...`);
214
+ const verifyResult = await this.verifyWithFacilitator(payment, skill.config);
215
+ if (!verifyResult.valid) {
216
+ return this.sendJson(res, 402, { error: `Payment verification failed: ${verifyResult.error}` });
217
+ }
212
218
  console.log(`[MoltsPay] Executing skill: ${service}`);
213
219
  let result;
214
220
  try {
@@ -220,32 +226,46 @@ var MoltsPayServer = class {
220
226
  message: err.message
221
227
  });
222
228
  }
223
- console.log(`[MoltsPay] Skill succeeded, claiming payment...`);
224
- let txHash = null;
229
+ console.log(`[MoltsPay] Skill succeeded, settling payment...`);
230
+ let settlement = null;
225
231
  try {
226
- txHash = await this.claimPayment(payment);
227
- console.log(`[MoltsPay] Payment claimed: ${txHash}`);
232
+ settlement = await this.settleWithFacilitator(payment, skill.config);
233
+ console.log(`[MoltsPay] Payment settled: ${settlement.transaction || "pending"}`);
228
234
  } catch (err) {
229
- console.error("[MoltsPay] Payment claim failed:", err.message);
235
+ console.error("[MoltsPay] Settlement failed:", err.message);
236
+ }
237
+ const responseHeaders = {};
238
+ if (settlement) {
239
+ const responsePayload = {
240
+ success: true,
241
+ transaction: settlement.transaction,
242
+ network: payment.network
243
+ };
244
+ responseHeaders[PAYMENT_RESPONSE_HEADER] = Buffer.from(
245
+ JSON.stringify(responsePayload)
246
+ ).toString("base64");
230
247
  }
231
248
  this.sendJson(res, 200, {
232
249
  success: true,
233
250
  result,
234
- payment: txHash ? { txHash, status: "claimed" } : { status: "pending" }
235
- });
251
+ payment: settlement ? { transaction: settlement.transaction, status: "settled" } : { status: "pending" }
252
+ }, responseHeaders);
236
253
  }
237
254
  /**
238
255
  * Return 402 with x402 payment requirements
239
256
  */
240
257
  sendPaymentRequired(config, res) {
241
- const chain = getChain(this.manifest.provider.chain);
242
258
  const amountInUnits = Math.floor(config.price * 1e6).toString();
243
259
  const requirements = [{
244
260
  scheme: "exact",
245
- network: `eip155:${chain.chainId}`,
261
+ network: this.networkId,
246
262
  maxAmountRequired: amountInUnits,
247
263
  resource: this.manifest.provider.wallet,
248
- description: `${config.name} - $${config.price} ${config.currency}`
264
+ description: `${config.name} - $${config.price} ${config.currency}`,
265
+ extra: JSON.stringify({
266
+ facilitator: this.cdpConfig.useMainnet ? "cdp" : "x402.org",
267
+ mainnet: this.cdpConfig.useMainnet
268
+ })
249
269
  }];
250
270
  const encoded = Buffer.from(JSON.stringify(requirements)).toString("base64");
251
271
  res.writeHead(402, {
@@ -259,7 +279,7 @@ var MoltsPayServer = class {
259
279
  }, null, 2));
260
280
  }
261
281
  /**
262
- * Validate x402 payment payload
282
+ * Basic payment validation
263
283
  */
264
284
  validatePayment(payment, config) {
265
285
  if (payment.x402Version !== X402_VERSION) {
@@ -268,56 +288,89 @@ var MoltsPayServer = class {
268
288
  if (payment.scheme !== "exact") {
269
289
  return { valid: false, error: `Unsupported scheme: ${payment.scheme}` };
270
290
  }
271
- const chain = getChain(this.manifest.provider.chain);
272
- const expectedNetwork = `eip155:${chain.chainId}`;
273
- if (payment.network !== expectedNetwork) {
274
- return { valid: false, error: `Network mismatch: expected ${expectedNetwork}` };
275
- }
276
- const auth = payment.payload.authorization;
277
- if (auth.to.toLowerCase() !== this.manifest.provider.wallet.toLowerCase()) {
278
- return { valid: false, error: "Payment recipient mismatch" };
279
- }
280
- const amount = Number(auth.value) / 1e6;
281
- if (amount < config.price) {
282
- return { valid: false, error: `Insufficient amount: $${amount} < $${config.price}` };
283
- }
284
- const now = Math.floor(Date.now() / 1e3);
285
- if (Number(auth.validBefore) < now) {
286
- return { valid: false, error: "Payment authorization expired" };
287
- }
288
- if (Number(auth.validAfter) > now) {
289
- return { valid: false, error: "Payment authorization not yet valid" };
291
+ if (payment.network !== this.networkId) {
292
+ return { valid: false, error: `Network mismatch: expected ${this.networkId}, got ${payment.network}` };
290
293
  }
291
294
  return { valid: true };
292
295
  }
293
296
  /**
294
- * Claim payment using transferWithAuthorization
297
+ * Verify payment with facilitator (testnet or CDP)
295
298
  */
296
- async claimPayment(payment) {
297
- if (!this.wallet || !this.provider) {
298
- throw new Error("Wallet not configured for payment claims");
299
+ async verifyWithFacilitator(payment, config) {
300
+ try {
301
+ const amountInUnits = Math.floor(config.price * 1e6).toString();
302
+ const requirements = {
303
+ scheme: "exact",
304
+ network: this.networkId,
305
+ maxAmountRequired: amountInUnits,
306
+ resource: this.manifest.provider.wallet,
307
+ payTo: this.manifest.provider.wallet
308
+ };
309
+ const requestBody = {
310
+ paymentPayload: payment,
311
+ paymentRequirements: requirements
312
+ };
313
+ let headers = { "Content-Type": "application/json" };
314
+ if (this.cdpConfig.useMainnet) {
315
+ const authHeaders = await getCDPAuthHeaders(
316
+ "POST",
317
+ "/platform/v2/x402/verify",
318
+ requestBody
319
+ );
320
+ headers = { ...headers, ...authHeaders };
321
+ }
322
+ const response = await fetch(`${this.facilitatorUrl}/verify`, {
323
+ method: "POST",
324
+ headers,
325
+ body: JSON.stringify(requestBody)
326
+ });
327
+ const result = await response.json();
328
+ if (!response.ok || !result.isValid) {
329
+ return { valid: false, error: result.invalidReason || result.error || "Verification failed" };
330
+ }
331
+ return { valid: true };
332
+ } catch (err) {
333
+ return { valid: false, error: `Facilitator error: ${err.message}` };
299
334
  }
300
- const chain = getChain(this.manifest.provider.chain);
301
- const auth = payment.payload.authorization;
302
- const sig = payment.payload.signature;
303
- const { r, s, v } = ethers.Signature.from(sig);
304
- const usdcAbi = [
305
- "function transferWithAuthorization(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s)"
306
- ];
307
- const usdc = new ethers.Contract(chain.usdc, usdcAbi, this.wallet);
308
- const tx = await usdc.transferWithAuthorization(
309
- auth.from,
310
- auth.to,
311
- auth.value,
312
- auth.validAfter,
313
- auth.validBefore,
314
- auth.nonce,
315
- v,
316
- r,
317
- s
318
- );
319
- const receipt = await tx.wait();
320
- return receipt.hash;
335
+ }
336
+ /**
337
+ * Settle payment with facilitator (execute on-chain transfer)
338
+ */
339
+ async settleWithFacilitator(payment, config) {
340
+ const amountInUnits = Math.floor(config.price * 1e6).toString();
341
+ const requirements = {
342
+ scheme: "exact",
343
+ network: this.networkId,
344
+ maxAmountRequired: amountInUnits,
345
+ resource: this.manifest.provider.wallet,
346
+ payTo: this.manifest.provider.wallet
347
+ };
348
+ const requestBody = {
349
+ paymentPayload: payment,
350
+ paymentRequirements: requirements
351
+ };
352
+ let headers = { "Content-Type": "application/json" };
353
+ if (this.cdpConfig.useMainnet) {
354
+ const authHeaders = await getCDPAuthHeaders(
355
+ "POST",
356
+ "/platform/v2/x402/settle",
357
+ requestBody
358
+ );
359
+ headers = { ...headers, ...authHeaders };
360
+ }
361
+ const response = await fetch(`${this.facilitatorUrl}/settle`, {
362
+ method: "POST",
363
+ headers,
364
+ body: JSON.stringify(requestBody)
365
+ });
366
+ const result = await response.json();
367
+ if (!response.ok || !result.success) {
368
+ throw new Error(result.error || result.errorReason || "Settlement failed");
369
+ }
370
+ return {
371
+ transaction: result.transaction,
372
+ status: result.status || "settled"
373
+ };
321
374
  }
322
375
  async readBody(req) {
323
376
  return new Promise((resolve, reject) => {
@@ -333,8 +386,12 @@ var MoltsPayServer = class {
333
386
  req.on("error", reject);
334
387
  });
335
388
  }
336
- sendJson(res, status, data) {
337
- res.writeHead(status, { "Content-Type": "application/json" });
389
+ sendJson(res, status, data, extraHeaders) {
390
+ const headers = { "Content-Type": "application/json" };
391
+ if (extraHeaders) {
392
+ Object.assign(headers, extraHeaders);
393
+ }
394
+ res.writeHead(status, headers);
338
395
  res.end(JSON.stringify(data, null, 2));
339
396
  }
340
397
  };