pay-lobster 1.0.0

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.
Files changed (120) hide show
  1. package/README.md +401 -0
  2. package/README.md.bak +401 -0
  3. package/dist/agent.d.ts +132 -0
  4. package/dist/agent.d.ts.map +1 -0
  5. package/dist/agent.js +224 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/analytics.d.ts +120 -0
  8. package/dist/analytics.d.ts.map +1 -0
  9. package/dist/analytics.js +345 -0
  10. package/dist/analytics.js.map +1 -0
  11. package/dist/approvals.d.ts +168 -0
  12. package/dist/approvals.d.ts.map +1 -0
  13. package/dist/approvals.js +406 -0
  14. package/dist/approvals.js.map +1 -0
  15. package/dist/circle-client.d.ts +152 -0
  16. package/dist/circle-client.d.ts.map +1 -0
  17. package/dist/circle-client.js +266 -0
  18. package/dist/circle-client.js.map +1 -0
  19. package/dist/commission.d.ts +191 -0
  20. package/dist/commission.d.ts.map +1 -0
  21. package/dist/commission.js +475 -0
  22. package/dist/commission.js.map +1 -0
  23. package/dist/condition-builder.d.ts +98 -0
  24. package/dist/condition-builder.d.ts.map +1 -0
  25. package/dist/condition-builder.js +193 -0
  26. package/dist/condition-builder.js.map +1 -0
  27. package/dist/contacts.d.ts +179 -0
  28. package/dist/contacts.d.ts.map +1 -0
  29. package/dist/contacts.js +445 -0
  30. package/dist/contacts.js.map +1 -0
  31. package/dist/easy.d.ts +22 -0
  32. package/dist/easy.d.ts.map +1 -0
  33. package/dist/easy.js +40 -0
  34. package/dist/easy.js.map +1 -0
  35. package/dist/erc8004/constants.d.ts +152 -0
  36. package/dist/erc8004/constants.d.ts.map +1 -0
  37. package/dist/erc8004/constants.js +114 -0
  38. package/dist/erc8004/constants.js.map +1 -0
  39. package/dist/erc8004/discovery.d.ts +84 -0
  40. package/dist/erc8004/discovery.d.ts.map +1 -0
  41. package/dist/erc8004/discovery.js +217 -0
  42. package/dist/erc8004/discovery.js.map +1 -0
  43. package/dist/erc8004/identity.d.ts +91 -0
  44. package/dist/erc8004/identity.d.ts.map +1 -0
  45. package/dist/erc8004/identity.js +250 -0
  46. package/dist/erc8004/identity.js.map +1 -0
  47. package/dist/erc8004/index.d.ts +147 -0
  48. package/dist/erc8004/index.d.ts.map +1 -0
  49. package/dist/erc8004/index.js +225 -0
  50. package/dist/erc8004/index.js.map +1 -0
  51. package/dist/erc8004/reputation.d.ts +133 -0
  52. package/dist/erc8004/reputation.d.ts.map +1 -0
  53. package/dist/erc8004/reputation.js +277 -0
  54. package/dist/erc8004/reputation.js.map +1 -0
  55. package/dist/escrow-templates.d.ts +38 -0
  56. package/dist/escrow-templates.d.ts.map +1 -0
  57. package/dist/escrow-templates.js +419 -0
  58. package/dist/escrow-templates.js.map +1 -0
  59. package/dist/escrow.d.ts +320 -0
  60. package/dist/escrow.d.ts.map +1 -0
  61. package/dist/escrow.js +854 -0
  62. package/dist/escrow.js.map +1 -0
  63. package/dist/index.d.ts +11 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +33 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/invoices.d.ts +212 -0
  68. package/dist/invoices.d.ts.map +1 -0
  69. package/dist/invoices.js +393 -0
  70. package/dist/invoices.js.map +1 -0
  71. package/dist/notifications.d.ts +141 -0
  72. package/dist/notifications.d.ts.map +1 -0
  73. package/dist/notifications.js +350 -0
  74. package/dist/notifications.js.map +1 -0
  75. package/dist/tips.d.ts +171 -0
  76. package/dist/tips.d.ts.map +1 -0
  77. package/dist/tips.js +390 -0
  78. package/dist/tips.js.map +1 -0
  79. package/dist/types.d.ts +100 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/dist/types.js +6 -0
  82. package/dist/types.js.map +1 -0
  83. package/dist/x402-client.d.ts +127 -0
  84. package/dist/x402-client.d.ts.map +1 -0
  85. package/dist/x402-client.js +350 -0
  86. package/dist/x402-client.js.map +1 -0
  87. package/dist/x402-server.d.ts +133 -0
  88. package/dist/x402-server.d.ts.map +1 -0
  89. package/dist/x402-server.js +330 -0
  90. package/dist/x402-server.js.map +1 -0
  91. package/lib/agent.ts +273 -0
  92. package/lib/analytics.ts +474 -0
  93. package/lib/analytics.ts.bak +474 -0
  94. package/lib/approvals.ts +585 -0
  95. package/lib/approvals.ts.bak +585 -0
  96. package/lib/circle-client.ts +376 -0
  97. package/lib/circle-client.ts.bak +376 -0
  98. package/lib/commission.ts +680 -0
  99. package/lib/commission.ts.bak +680 -0
  100. package/lib/condition-builder.ts +223 -0
  101. package/lib/condition-builder.ts.bak +223 -0
  102. package/lib/contacts.ts +615 -0
  103. package/lib/contacts.ts.bak +615 -0
  104. package/lib/easy.ts +46 -0
  105. package/lib/easy.ts.bak +352 -0
  106. package/lib/erc8004/constants.ts +175 -0
  107. package/lib/erc8004/discovery.ts +299 -0
  108. package/lib/erc8004/identity.ts +327 -0
  109. package/lib/erc8004/index.ts +285 -0
  110. package/lib/erc8004/reputation.ts +368 -0
  111. package/lib/escrow-templates.ts +462 -0
  112. package/lib/escrow.ts +1216 -0
  113. package/lib/index.ts +13 -0
  114. package/lib/invoices.ts +588 -0
  115. package/lib/notifications.ts +484 -0
  116. package/lib/tips.ts +570 -0
  117. package/lib/types.ts +108 -0
  118. package/lib/x402-client.ts +471 -0
  119. package/lib/x402-server.ts +462 -0
  120. package/package.json +58 -0
@@ -0,0 +1,462 @@
1
+ /**
2
+ * x402 Payment Server Middleware
3
+ *
4
+ * Express middleware for HTTP 402 Payment Required.
5
+ * Generates payment challenges and verifies payments via Coinbase facilitator.
6
+ */
7
+
8
+ import { Request, Response, NextFunction } from 'express';
9
+ import crypto from 'crypto';
10
+
11
+ // x402 Configuration
12
+ export interface X402ServerConfig {
13
+ facilitatorUrl?: string; // Coinbase x402 facilitator
14
+ network: string; // e.g., 'ETH-SEPOLIA'
15
+ receiverAddress: string; // Your wallet address
16
+ acceptedAssets?: string[]; // Default: ['USDC']
17
+ challengeExpiry?: number; // Seconds (default: 300 = 5 min)
18
+
19
+ // Custom verification function (optional)
20
+ verifyPayment?: (signature: string, amount: string) => Promise<boolean>;
21
+ }
22
+
23
+ // Payment Challenge Response
24
+ export interface PaymentChallenge {
25
+ 'x-payment-required': {
26
+ version: '1';
27
+ network: string;
28
+ receiver: string;
29
+ asset: string;
30
+ amount: string;
31
+ description: string;
32
+ expires: number;
33
+ nonce: string;
34
+ };
35
+ }
36
+
37
+ // Global config (can be set once for all middleware)
38
+ let globalConfig: X402ServerConfig | null = null;
39
+
40
+ /**
41
+ * Configure x402 server globally
42
+ */
43
+ export function configureX402Server(config: X402ServerConfig): void {
44
+ globalConfig = {
45
+ facilitatorUrl: config.facilitatorUrl || 'https://x402.coinbase.com',
46
+ acceptedAssets: config.acceptedAssets || ['USDC'],
47
+ challengeExpiry: config.challengeExpiry || 300,
48
+ ...config,
49
+ };
50
+ }
51
+
52
+ /**
53
+ * Get effective config (merge global + local)
54
+ */
55
+ function getConfig(localConfig?: Partial<X402ServerConfig>): X402ServerConfig {
56
+ if (!globalConfig && !localConfig) {
57
+ throw new Error('x402 server not configured. Call configureX402Server() first.');
58
+ }
59
+
60
+ return {
61
+ facilitatorUrl: localConfig?.facilitatorUrl || globalConfig?.facilitatorUrl || 'https://x402.coinbase.com',
62
+ network: localConfig?.network || globalConfig?.network || 'ETH-SEPOLIA',
63
+ receiverAddress: localConfig?.receiverAddress || globalConfig?.receiverAddress || '',
64
+ acceptedAssets: localConfig?.acceptedAssets || globalConfig?.acceptedAssets || ['USDC'],
65
+ challengeExpiry: localConfig?.challengeExpiry || globalConfig?.challengeExpiry || 300,
66
+ verifyPayment: localConfig?.verifyPayment || globalConfig?.verifyPayment,
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Create payment challenge
72
+ */
73
+ export function createPaymentChallenge(
74
+ amount: string,
75
+ description: string,
76
+ config?: Partial<X402ServerConfig>
77
+ ): PaymentChallenge {
78
+ const effectiveConfig = getConfig(config);
79
+
80
+ return {
81
+ 'x-payment-required': {
82
+ version: '1',
83
+ network: effectiveConfig.network,
84
+ receiver: effectiveConfig.receiverAddress,
85
+ asset: 'USDC',
86
+ amount,
87
+ description,
88
+ expires: Math.floor(Date.now() / 1000) + (effectiveConfig.challengeExpiry || 300),
89
+ nonce: crypto.randomUUID(),
90
+ },
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Paywall middleware - require payment to access endpoint
96
+ */
97
+ export function paywall(
98
+ price: string,
99
+ description: string,
100
+ config?: Partial<X402ServerConfig>
101
+ ) {
102
+ return async (req: Request, res: Response, next: NextFunction) => {
103
+ const paymentSignature = req.headers['x-payment-signature'];
104
+
105
+ if (!paymentSignature) {
106
+ // No payment provided - return 402 with challenge
107
+ const challenge = createPaymentChallenge(price, description, config);
108
+
109
+ res.status(402).json({
110
+ error: 'Payment Required',
111
+ message: `This endpoint requires ${price} USDC`,
112
+ ...challenge,
113
+ });
114
+ return;
115
+ }
116
+
117
+ // Verify payment
118
+ try {
119
+ const effectiveConfig = getConfig(config);
120
+ const verified = await verifyPayment(
121
+ paymentSignature as string,
122
+ price,
123
+ effectiveConfig
124
+ );
125
+
126
+ if (verified) {
127
+ // Payment verified - proceed
128
+ (req as any).paymentVerified = true;
129
+ (req as any).paymentAmount = price;
130
+ (req as any).paymentSignature = paymentSignature;
131
+ next();
132
+ } else {
133
+ res.status(402).json({
134
+ error: 'Payment Invalid',
135
+ message: 'Payment signature could not be verified',
136
+ });
137
+ }
138
+ } catch (error) {
139
+ console.error('Payment verification error:', error);
140
+ res.status(500).json({
141
+ error: 'Payment Verification Failed',
142
+ message: (error as Error).message,
143
+ });
144
+ }
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Dynamic paywall - determine price at runtime
150
+ */
151
+ export function dynamicPaywall(
152
+ priceFn: (req: Request) => string | Promise<string>,
153
+ descriptionFn: (req: Request) => string | Promise<string>,
154
+ config?: Partial<X402ServerConfig>
155
+ ) {
156
+ return async (req: Request, res: Response, next: NextFunction) => {
157
+ try {
158
+ // Determine price and description
159
+ const price = await Promise.resolve(priceFn(req));
160
+ const description = await Promise.resolve(descriptionFn(req));
161
+
162
+ // Use standard paywall
163
+ const middleware = paywall(price, description, config);
164
+ await middleware(req, res, next);
165
+ } catch (error) {
166
+ res.status(500).json({
167
+ error: 'Paywall Configuration Error',
168
+ message: (error as Error).message,
169
+ });
170
+ }
171
+ };
172
+ }
173
+
174
+ /**
175
+ * Verify payment with Coinbase x402 facilitator
176
+ */
177
+ async function verifyPayment(
178
+ signature: string,
179
+ expectedAmount: string,
180
+ config: X402ServerConfig
181
+ ): Promise<boolean> {
182
+ // Use custom verification if provided
183
+ if (config.verifyPayment) {
184
+ return config.verifyPayment(signature, expectedAmount);
185
+ }
186
+
187
+ // Default: verify with Coinbase facilitator
188
+ try {
189
+ const response = await fetch(`${config.facilitatorUrl}/verify`, {
190
+ method: 'POST',
191
+ headers: {
192
+ 'Content-Type': 'application/json',
193
+ },
194
+ body: JSON.stringify({
195
+ signature,
196
+ expectedAmount,
197
+ network: config.network,
198
+ receiver: config.receiverAddress,
199
+ }),
200
+ });
201
+
202
+ if (!response.ok) {
203
+ return false;
204
+ }
205
+
206
+ const result = await response.json();
207
+ return result.valid === true;
208
+ } catch (error) {
209
+ console.error('Facilitator verification error:', error);
210
+ return false;
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Mock verification for testing (accepts any signature)
216
+ */
217
+ export function mockVerification(): (signature: string, amount: string) => Promise<boolean> {
218
+ return async (signature: string, amount: string) => {
219
+ // In testing, accept any non-empty signature
220
+ return signature.length > 0;
221
+ };
222
+ }
223
+
224
+ /**
225
+ * Simple signature verification (hash-based)
226
+ * Use only for testing - NOT secure for production
227
+ */
228
+ export function simpleVerification(secret: string): (signature: string, amount: string) => Promise<boolean> {
229
+ return async (signature: string, amount: string) => {
230
+ // Verify signature matches hash of secret + amount
231
+ const expectedSig = crypto.createHash('sha256')
232
+ .update(secret + amount)
233
+ .digest('hex');
234
+
235
+ return signature === expectedSig;
236
+ };
237
+ }
238
+
239
+ /**
240
+ * Pricing helper - combine multiple tiers
241
+ */
242
+ export class PricingTier {
243
+ private tiers: Map<string, string> = new Map();
244
+
245
+ add(key: string, price: string): this {
246
+ this.tiers.set(key, price);
247
+ return this;
248
+ }
249
+
250
+ get(key: string): string | undefined {
251
+ return this.tiers.get(key);
252
+ }
253
+
254
+ middleware(
255
+ keyFn: (req: Request) => string,
256
+ descriptionFn: (req: Request) => string,
257
+ config?: Partial<X402ServerConfig>
258
+ ) {
259
+ return dynamicPaywall(
260
+ (req) => {
261
+ const key = keyFn(req);
262
+ const price = this.tiers.get(key);
263
+ if (!price) {
264
+ throw new Error(`No price configured for tier: ${key}`);
265
+ }
266
+ return price;
267
+ },
268
+ descriptionFn,
269
+ config
270
+ );
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Usage-based pricing
276
+ */
277
+ export interface UsageConfig {
278
+ basePrice: string; // Base price per request
279
+ perUnit: string; // Price per unit
280
+ unit: string; // Unit name (e.g., 'cpu-second', 'MB', 'query')
281
+ calculate: (req: Request) => number | Promise<number>; // Calculate units
282
+ }
283
+
284
+ export function usagePaywall(
285
+ usage: UsageConfig,
286
+ config?: Partial<X402ServerConfig>
287
+ ) {
288
+ return dynamicPaywall(
289
+ async (req) => {
290
+ const units = await Promise.resolve(usage.calculate(req));
291
+ const base = parseFloat(usage.basePrice);
292
+ const perUnit = parseFloat(usage.perUnit);
293
+ const total = base + (units * perUnit);
294
+ return total.toFixed(2);
295
+ },
296
+ (req) => `Usage-based pricing: base + ${usage.perUnit} per ${usage.unit}`,
297
+ config
298
+ );
299
+ }
300
+
301
+ /**
302
+ * Subscription paywall (check for valid subscription)
303
+ */
304
+ export interface SubscriptionConfig {
305
+ getSubscription: (req: Request) => Promise<{
306
+ active: boolean;
307
+ expiresAt: string;
308
+ } | null>;
309
+ price: string;
310
+ period: 'monthly' | 'yearly';
311
+ renewalUrl?: string;
312
+ }
313
+
314
+ export function subscriptionPaywall(
315
+ subscription: SubscriptionConfig,
316
+ config?: Partial<X402ServerConfig>
317
+ ) {
318
+ return async (req: Request, res: Response, next: NextFunction) => {
319
+ // Check for active subscription
320
+ const sub = await subscription.getSubscription(req);
321
+
322
+ if (sub && sub.active && new Date(sub.expiresAt) > new Date()) {
323
+ // Active subscription - allow access
324
+ (req as any).subscription = sub;
325
+ next();
326
+ return;
327
+ }
328
+
329
+ // No active subscription - require payment
330
+ const description = `${subscription.period} subscription - ${subscription.price} USDC`;
331
+ const challenge = createPaymentChallenge(subscription.price, description, config);
332
+
333
+ res.status(402).json({
334
+ error: 'Subscription Required',
335
+ message: `This endpoint requires an active ${subscription.period} subscription`,
336
+ subscription: {
337
+ price: subscription.price,
338
+ period: subscription.period,
339
+ renewalUrl: subscription.renewalUrl,
340
+ },
341
+ ...challenge,
342
+ });
343
+ };
344
+ }
345
+
346
+ /**
347
+ * Rate-limited paywall (free tier + paid tier)
348
+ */
349
+ export interface RateLimitConfig {
350
+ free: {
351
+ limit: number; // Free requests per window
352
+ windowMs: number; // Window in milliseconds
353
+ };
354
+ paid: {
355
+ price: string; // Price per additional request
356
+ description: string;
357
+ };
358
+ getUserId: (req: Request) => string;
359
+ }
360
+
361
+ // Simple in-memory rate limiter
362
+ const rateLimitStore = new Map<string, { count: number; resetAt: number }>();
363
+
364
+ export function rateLimitedPaywall(
365
+ rateLimitConfig: RateLimitConfig,
366
+ config?: Partial<X402ServerConfig>
367
+ ) {
368
+ return async (req: Request, res: Response, next: NextFunction) => {
369
+ const userId = rateLimitConfig.getUserId(req);
370
+ const now = Date.now();
371
+
372
+ // Get or create usage record
373
+ let usage = rateLimitStore.get(userId);
374
+
375
+ if (!usage || usage.resetAt < now) {
376
+ // New window
377
+ usage = {
378
+ count: 0,
379
+ resetAt: now + rateLimitConfig.free.windowMs,
380
+ };
381
+ rateLimitStore.set(userId, usage);
382
+ }
383
+
384
+ // Check if within free tier
385
+ if (usage.count < rateLimitConfig.free.limit) {
386
+ usage.count++;
387
+ next();
388
+ return;
389
+ }
390
+
391
+ // Exceeded free tier - require payment
392
+ const paymentSignature = req.headers['x-payment-signature'];
393
+
394
+ if (!paymentSignature) {
395
+ const challenge = createPaymentChallenge(
396
+ rateLimitConfig.paid.price,
397
+ rateLimitConfig.paid.description,
398
+ config
399
+ );
400
+
401
+ res.status(402).json({
402
+ error: 'Rate Limit Exceeded',
403
+ message: `Free tier limit reached (${rateLimitConfig.free.limit} requests per window)`,
404
+ rateTier: 'paid',
405
+ ...challenge,
406
+ });
407
+ return;
408
+ }
409
+
410
+ // Verify payment for additional request
411
+ const effectiveConfig = getConfig(config);
412
+ const verified = await verifyPayment(
413
+ paymentSignature as string,
414
+ rateLimitConfig.paid.price,
415
+ effectiveConfig
416
+ );
417
+
418
+ if (verified) {
419
+ (req as any).paymentVerified = true;
420
+ (req as any).paidRequest = true;
421
+ next();
422
+ } else {
423
+ res.status(402).json({
424
+ error: 'Payment Invalid',
425
+ message: 'Payment signature could not be verified',
426
+ });
427
+ }
428
+ };
429
+ }
430
+
431
+ /**
432
+ * Express router with common x402 endpoints
433
+ */
434
+ export function createX402Router() {
435
+ // This would return an Express router with health/pricing endpoints
436
+ // Implemented in the specific application
437
+ return null;
438
+ }
439
+
440
+ // Export pricing helpers
441
+ export const PRICING = {
442
+ micro: '0.01',
443
+ small: '0.05',
444
+ medium: '0.10',
445
+ large: '0.25',
446
+ premium: '0.50',
447
+ enterprise: '1.00',
448
+ };
449
+
450
+ export default {
451
+ configureX402Server,
452
+ createPaymentChallenge,
453
+ paywall,
454
+ dynamicPaywall,
455
+ usagePaywall,
456
+ subscriptionPaywall,
457
+ rateLimitedPaywall,
458
+ mockVerification,
459
+ simpleVerification,
460
+ PricingTier,
461
+ PRICING,
462
+ };
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "pay-lobster",
3
+ "version": "1.0.0",
4
+ "description": "Payment infrastructure for AI agents - The Stripe for autonomous agents. Send, receive, and escrow USDC on Base.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "pay-lobster": "./dist/cli.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "prepublishOnly": "npm run build",
13
+ "cli": "npx tsx lib/index.ts",
14
+ "test": "echo \"Tests coming soon\" && exit 0"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "lib",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "dependencies": {
23
+ "@circle-fin/developer-controlled-wallets": "^2.0.0",
24
+ "ethers": "^6.9.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^22.0.0",
28
+ "typescript": "^5.7.0",
29
+ "tsx": "^4.0.0"
30
+ },
31
+ "keywords": [
32
+ "usdc",
33
+ "payments",
34
+ "ai-agents",
35
+ "crypto",
36
+ "base",
37
+ "circle",
38
+ "blockchain",
39
+ "escrow",
40
+ "stablecoin",
41
+ "web3",
42
+ "openclaw",
43
+ "agent-commerce"
44
+ ],
45
+ "author": "Jakub Adamowicz <realtorjakub@gmail.com>",
46
+ "license": "MIT",
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "git+https://github.com/itsGustav/Lobster-Pay.git"
50
+ },
51
+ "bugs": {
52
+ "url": "https://github.com/itsGustav/Lobster-Pay/issues"
53
+ },
54
+ "homepage": "https://paylobster.com",
55
+ "engines": {
56
+ "node": ">=18.0.0"
57
+ }
58
+ }