@dexterai/x402 1.4.1 → 1.5.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.
- package/dist/adapters/index.d.cts +4 -4
- package/dist/adapters/index.d.ts +4 -4
- package/dist/client/index.cjs +182 -4
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +23 -5
- package/dist/client/index.d.ts +23 -5
- package/dist/client/index.js +182 -4
- package/dist/client/index.js.map +1 -1
- package/dist/{evm-ZDwQi4QL.d.ts → evm-71SZ7cjW.d.ts} +2 -2
- package/dist/{evm-BaoETN1Y.d.cts → evm-BYjwU6ZW.d.cts} +2 -2
- package/dist/react/index.cjs +323 -3
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +108 -4
- package/dist/react/index.d.ts +108 -4
- package/dist/react/index.js +322 -3
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs +253 -2
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +101 -3
- package/dist/server/index.d.ts +101 -3
- package/dist/server/index.js +240 -0
- package/dist/server/index.js.map +1 -1
- package/dist/{types-CQGDK_7X.d.cts → types--r7urkVI.d.cts} +1 -1
- package/dist/{types-DNx7-QUN.d.ts → types-BtpD4ULe.d.ts} +1 -1
- package/dist/{types-B7T6dZ-y.d.cts → types-CcVAaoro.d.cts} +64 -2
- package/dist/{types-B7T6dZ-y.d.ts → types-CcVAaoro.d.ts} +64 -2
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/{x402-client-COrn-FQk.d.ts → x402-client-BxQWcK2Z.d.ts} +8 -1
- package/dist/{x402-client-DHmpVhEK.d.cts → x402-client-Dcm2FQ9f.d.cts} +8 -1
- package/package.json +1 -1
package/dist/server/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PaymentAccept, V as VerifyResponse, S as SettleResponse,
|
|
2
|
-
export { B as BASE_MAINNET_NETWORK, D as DEXTER_FACILITATOR_URL,
|
|
1
|
+
import { P as PaymentAccept, V as VerifyResponse, S as SettleResponse, c as PaymentRequired } from '../types-CcVAaoro.cjs';
|
|
2
|
+
export { d as AccessPassClaims, A as AccessPassClientConfig, b as AccessPassInfo, a as AccessPassTier, B as BASE_MAINNET_NETWORK, D as DEXTER_FACILITATOR_URL, e as SOLANA_MAINNET_NETWORK, f as USDC_BASE, U as USDC_MINT } from '../types-CcVAaoro.cjs';
|
|
3
3
|
import { Request, RequestHandler } from 'express';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -308,6 +308,104 @@ interface X402Request extends Request {
|
|
|
308
308
|
*/
|
|
309
309
|
declare function x402Middleware(config: X402MiddlewareConfig): RequestHandler;
|
|
310
310
|
|
|
311
|
+
/**
|
|
312
|
+
* x402 Access Pass Middleware
|
|
313
|
+
*
|
|
314
|
+
* Pay once, get a time-limited JWT for unlimited API requests.
|
|
315
|
+
* Supports both predefined tiers and custom durations.
|
|
316
|
+
*
|
|
317
|
+
* @example Tier-based pricing
|
|
318
|
+
* ```typescript
|
|
319
|
+
* app.use('/api', x402AccessPass({
|
|
320
|
+
* payTo: 'YourSolanaAddress...',
|
|
321
|
+
* tiers: {
|
|
322
|
+
* '1h': '0.50', // $0.50 for 1 hour
|
|
323
|
+
* '24h': '2.00', // $2.00 for 24 hours
|
|
324
|
+
* },
|
|
325
|
+
* }));
|
|
326
|
+
* ```
|
|
327
|
+
*
|
|
328
|
+
* @example Rate-based custom durations
|
|
329
|
+
* ```typescript
|
|
330
|
+
* app.use('/api', x402AccessPass({
|
|
331
|
+
* payTo: 'YourSolanaAddress...',
|
|
332
|
+
* ratePerHour: '0.50', // $0.50/hour, any duration
|
|
333
|
+
* }));
|
|
334
|
+
* ```
|
|
335
|
+
*
|
|
336
|
+
* @example Both tiers and custom durations
|
|
337
|
+
* ```typescript
|
|
338
|
+
* app.use('/api', x402AccessPass({
|
|
339
|
+
* payTo: 'YourSolanaAddress...',
|
|
340
|
+
* tiers: { '1h': '0.50', '24h': '2.00' },
|
|
341
|
+
* ratePerHour: '0.50', // fallback for custom durations
|
|
342
|
+
* }));
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Access pass middleware configuration
|
|
348
|
+
*/
|
|
349
|
+
interface X402AccessPassConfig {
|
|
350
|
+
/** Address to receive payments (Solana pubkey or EVM address) */
|
|
351
|
+
payTo: string;
|
|
352
|
+
/** CAIP-2 network identifier @default 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp' */
|
|
353
|
+
network?: string;
|
|
354
|
+
/** x402 facilitator URL @default 'https://x402-facilitator.dexter.cash' */
|
|
355
|
+
facilitatorUrl?: string;
|
|
356
|
+
/** Asset config @default USDC on specified network */
|
|
357
|
+
asset?: {
|
|
358
|
+
address: string;
|
|
359
|
+
decimals: number;
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* Predefined pricing tiers.
|
|
363
|
+
* Keys are tier IDs (e.g., '5m', '1h', '24h').
|
|
364
|
+
* Values are prices in USD (e.g., '0.50').
|
|
365
|
+
* Duration is parsed from the ID: '5m' = 5 minutes, '1h' = 1 hour, '24h' = 24 hours, '7d' = 7 days.
|
|
366
|
+
*/
|
|
367
|
+
tiers?: Record<string, string>;
|
|
368
|
+
/**
|
|
369
|
+
* Rate per hour in USD for custom durations.
|
|
370
|
+
* When set, buyers can request any duration via ?duration=<seconds> query param.
|
|
371
|
+
*/
|
|
372
|
+
ratePerHour?: string;
|
|
373
|
+
/** HMAC secret for JWT signing. Auto-generated if not provided. */
|
|
374
|
+
secret?: Buffer;
|
|
375
|
+
/** Issuer string for JWT 'iss' claim @default 'x402-access-pass' */
|
|
376
|
+
issuer?: string;
|
|
377
|
+
/** Enable verbose logging */
|
|
378
|
+
verbose?: boolean;
|
|
379
|
+
/** Description shown in 402 response */
|
|
380
|
+
description?: string;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Extended request with access pass info
|
|
384
|
+
*/
|
|
385
|
+
interface X402AccessPassRequest extends Request {
|
|
386
|
+
/** Access pass info (present when request is authenticated via valid pass) */
|
|
387
|
+
accessPass?: {
|
|
388
|
+
tier: string;
|
|
389
|
+
duration: number;
|
|
390
|
+
expiresAt: string;
|
|
391
|
+
payer: string;
|
|
392
|
+
network: string;
|
|
393
|
+
};
|
|
394
|
+
/** x402 payment info (present when a new pass was just purchased) */
|
|
395
|
+
x402?: {
|
|
396
|
+
transaction: string;
|
|
397
|
+
payer: string;
|
|
398
|
+
network: string;
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Create x402 access pass middleware for Express.
|
|
403
|
+
*
|
|
404
|
+
* Protects routes with time-limited access passes purchased via x402 payments.
|
|
405
|
+
* Supports predefined tiers, custom durations, or both.
|
|
406
|
+
*/
|
|
407
|
+
declare function x402AccessPass(config: X402AccessPassConfig): RequestHandler;
|
|
408
|
+
|
|
311
409
|
/**
|
|
312
410
|
* Dynamic Pricing for x402
|
|
313
411
|
*
|
|
@@ -751,4 +849,4 @@ declare const MODEL_PRICING_MAP: Record<string, {
|
|
|
751
849
|
tier: string;
|
|
752
850
|
}>;
|
|
753
851
|
|
|
754
|
-
export { type AssetConfig, type BuildRequirementsOptions, type DynamicPricing, type DynamicPricingConfig, FacilitatorClient, MODEL_PRICING, MODEL_PRICING_MAP, MODEL_REGISTRY, type ModelApiType, type ModelDefinition, type ModelModality, type ModelParameters, type ModelPricing$1 as ModelPricing, type ModelTier, PaymentAccept, PaymentRequired, type PriceQuote, type ModelPricing as RegistryModelPricing, SettleResponse, type SupportedKind, type SupportedResponse, type TokenPriceQuote, type TokenPricing, type TokenPricingConfig, VerifyResponse, type X402MiddlewareConfig, type X402Request, type X402Server, type X402ServerConfig, countTokens, createDynamicPricing, createTokenPricing, createX402Server, estimateCost, findModel, formatModelPricing, formatPricing, formatTokenPricing, getActiveModels, getAvailableModelIds, getAvailableModels, getCheapestModel, getModel, getModelsByFamily, getModelsByTier, getTextModels, isValidModel, isValidModelId, x402Middleware };
|
|
852
|
+
export { type AssetConfig, type BuildRequirementsOptions, type DynamicPricing, type DynamicPricingConfig, FacilitatorClient, MODEL_PRICING, MODEL_PRICING_MAP, MODEL_REGISTRY, type ModelApiType, type ModelDefinition, type ModelModality, type ModelParameters, type ModelPricing$1 as ModelPricing, type ModelTier, PaymentAccept, PaymentRequired, type PriceQuote, type ModelPricing as RegistryModelPricing, SettleResponse, type SupportedKind, type SupportedResponse, type TokenPriceQuote, type TokenPricing, type TokenPricingConfig, VerifyResponse, type X402AccessPassConfig, type X402AccessPassRequest, type X402MiddlewareConfig, type X402Request, type X402Server, type X402ServerConfig, countTokens, createDynamicPricing, createTokenPricing, createX402Server, estimateCost, findModel, formatModelPricing, formatPricing, formatTokenPricing, getActiveModels, getAvailableModelIds, getAvailableModels, getCheapestModel, getModel, getModelsByFamily, getModelsByTier, getTextModels, isValidModel, isValidModelId, x402AccessPass, x402Middleware };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PaymentAccept, V as VerifyResponse, S as SettleResponse,
|
|
2
|
-
export { B as BASE_MAINNET_NETWORK, D as DEXTER_FACILITATOR_URL,
|
|
1
|
+
import { P as PaymentAccept, V as VerifyResponse, S as SettleResponse, c as PaymentRequired } from '../types-CcVAaoro.js';
|
|
2
|
+
export { d as AccessPassClaims, A as AccessPassClientConfig, b as AccessPassInfo, a as AccessPassTier, B as BASE_MAINNET_NETWORK, D as DEXTER_FACILITATOR_URL, e as SOLANA_MAINNET_NETWORK, f as USDC_BASE, U as USDC_MINT } from '../types-CcVAaoro.js';
|
|
3
3
|
import { Request, RequestHandler } from 'express';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -308,6 +308,104 @@ interface X402Request extends Request {
|
|
|
308
308
|
*/
|
|
309
309
|
declare function x402Middleware(config: X402MiddlewareConfig): RequestHandler;
|
|
310
310
|
|
|
311
|
+
/**
|
|
312
|
+
* x402 Access Pass Middleware
|
|
313
|
+
*
|
|
314
|
+
* Pay once, get a time-limited JWT for unlimited API requests.
|
|
315
|
+
* Supports both predefined tiers and custom durations.
|
|
316
|
+
*
|
|
317
|
+
* @example Tier-based pricing
|
|
318
|
+
* ```typescript
|
|
319
|
+
* app.use('/api', x402AccessPass({
|
|
320
|
+
* payTo: 'YourSolanaAddress...',
|
|
321
|
+
* tiers: {
|
|
322
|
+
* '1h': '0.50', // $0.50 for 1 hour
|
|
323
|
+
* '24h': '2.00', // $2.00 for 24 hours
|
|
324
|
+
* },
|
|
325
|
+
* }));
|
|
326
|
+
* ```
|
|
327
|
+
*
|
|
328
|
+
* @example Rate-based custom durations
|
|
329
|
+
* ```typescript
|
|
330
|
+
* app.use('/api', x402AccessPass({
|
|
331
|
+
* payTo: 'YourSolanaAddress...',
|
|
332
|
+
* ratePerHour: '0.50', // $0.50/hour, any duration
|
|
333
|
+
* }));
|
|
334
|
+
* ```
|
|
335
|
+
*
|
|
336
|
+
* @example Both tiers and custom durations
|
|
337
|
+
* ```typescript
|
|
338
|
+
* app.use('/api', x402AccessPass({
|
|
339
|
+
* payTo: 'YourSolanaAddress...',
|
|
340
|
+
* tiers: { '1h': '0.50', '24h': '2.00' },
|
|
341
|
+
* ratePerHour: '0.50', // fallback for custom durations
|
|
342
|
+
* }));
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Access pass middleware configuration
|
|
348
|
+
*/
|
|
349
|
+
interface X402AccessPassConfig {
|
|
350
|
+
/** Address to receive payments (Solana pubkey or EVM address) */
|
|
351
|
+
payTo: string;
|
|
352
|
+
/** CAIP-2 network identifier @default 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp' */
|
|
353
|
+
network?: string;
|
|
354
|
+
/** x402 facilitator URL @default 'https://x402-facilitator.dexter.cash' */
|
|
355
|
+
facilitatorUrl?: string;
|
|
356
|
+
/** Asset config @default USDC on specified network */
|
|
357
|
+
asset?: {
|
|
358
|
+
address: string;
|
|
359
|
+
decimals: number;
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* Predefined pricing tiers.
|
|
363
|
+
* Keys are tier IDs (e.g., '5m', '1h', '24h').
|
|
364
|
+
* Values are prices in USD (e.g., '0.50').
|
|
365
|
+
* Duration is parsed from the ID: '5m' = 5 minutes, '1h' = 1 hour, '24h' = 24 hours, '7d' = 7 days.
|
|
366
|
+
*/
|
|
367
|
+
tiers?: Record<string, string>;
|
|
368
|
+
/**
|
|
369
|
+
* Rate per hour in USD for custom durations.
|
|
370
|
+
* When set, buyers can request any duration via ?duration=<seconds> query param.
|
|
371
|
+
*/
|
|
372
|
+
ratePerHour?: string;
|
|
373
|
+
/** HMAC secret for JWT signing. Auto-generated if not provided. */
|
|
374
|
+
secret?: Buffer;
|
|
375
|
+
/** Issuer string for JWT 'iss' claim @default 'x402-access-pass' */
|
|
376
|
+
issuer?: string;
|
|
377
|
+
/** Enable verbose logging */
|
|
378
|
+
verbose?: boolean;
|
|
379
|
+
/** Description shown in 402 response */
|
|
380
|
+
description?: string;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Extended request with access pass info
|
|
384
|
+
*/
|
|
385
|
+
interface X402AccessPassRequest extends Request {
|
|
386
|
+
/** Access pass info (present when request is authenticated via valid pass) */
|
|
387
|
+
accessPass?: {
|
|
388
|
+
tier: string;
|
|
389
|
+
duration: number;
|
|
390
|
+
expiresAt: string;
|
|
391
|
+
payer: string;
|
|
392
|
+
network: string;
|
|
393
|
+
};
|
|
394
|
+
/** x402 payment info (present when a new pass was just purchased) */
|
|
395
|
+
x402?: {
|
|
396
|
+
transaction: string;
|
|
397
|
+
payer: string;
|
|
398
|
+
network: string;
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Create x402 access pass middleware for Express.
|
|
403
|
+
*
|
|
404
|
+
* Protects routes with time-limited access passes purchased via x402 payments.
|
|
405
|
+
* Supports predefined tiers, custom durations, or both.
|
|
406
|
+
*/
|
|
407
|
+
declare function x402AccessPass(config: X402AccessPassConfig): RequestHandler;
|
|
408
|
+
|
|
311
409
|
/**
|
|
312
410
|
* Dynamic Pricing for x402
|
|
313
411
|
*
|
|
@@ -751,4 +849,4 @@ declare const MODEL_PRICING_MAP: Record<string, {
|
|
|
751
849
|
tier: string;
|
|
752
850
|
}>;
|
|
753
851
|
|
|
754
|
-
export { type AssetConfig, type BuildRequirementsOptions, type DynamicPricing, type DynamicPricingConfig, FacilitatorClient, MODEL_PRICING, MODEL_PRICING_MAP, MODEL_REGISTRY, type ModelApiType, type ModelDefinition, type ModelModality, type ModelParameters, type ModelPricing$1 as ModelPricing, type ModelTier, PaymentAccept, PaymentRequired, type PriceQuote, type ModelPricing as RegistryModelPricing, SettleResponse, type SupportedKind, type SupportedResponse, type TokenPriceQuote, type TokenPricing, type TokenPricingConfig, VerifyResponse, type X402MiddlewareConfig, type X402Request, type X402Server, type X402ServerConfig, countTokens, createDynamicPricing, createTokenPricing, createX402Server, estimateCost, findModel, formatModelPricing, formatPricing, formatTokenPricing, getActiveModels, getAvailableModelIds, getAvailableModels, getCheapestModel, getModel, getModelsByFamily, getModelsByTier, getTextModels, isValidModel, isValidModelId, x402Middleware };
|
|
852
|
+
export { type AssetConfig, type BuildRequirementsOptions, type DynamicPricing, type DynamicPricingConfig, FacilitatorClient, MODEL_PRICING, MODEL_PRICING_MAP, MODEL_REGISTRY, type ModelApiType, type ModelDefinition, type ModelModality, type ModelParameters, type ModelPricing$1 as ModelPricing, type ModelTier, PaymentAccept, PaymentRequired, type PriceQuote, type ModelPricing as RegistryModelPricing, SettleResponse, type SupportedKind, type SupportedResponse, type TokenPriceQuote, type TokenPricing, type TokenPricingConfig, VerifyResponse, type X402AccessPassConfig, type X402AccessPassRequest, type X402MiddlewareConfig, type X402Request, type X402Server, type X402ServerConfig, countTokens, createDynamicPricing, createTokenPricing, createX402Server, estimateCost, findModel, formatModelPricing, formatPricing, formatTokenPricing, getActiveModels, getAvailableModelIds, getAvailableModels, getCheapestModel, getModel, getModelsByFamily, getModelsByTier, getTextModels, isValidModel, isValidModelId, x402AccessPass, x402Middleware };
|
package/dist/server/index.js
CHANGED
|
@@ -357,6 +357,245 @@ function x402Middleware(config) {
|
|
|
357
357
|
};
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
+
// src/server/access-pass.ts
|
|
361
|
+
import crypto from "crypto";
|
|
362
|
+
var DURATION_REGEX = /^(\d+)(m|h|d|w)$/;
|
|
363
|
+
function parseTierDuration(tierId) {
|
|
364
|
+
const match = tierId.match(DURATION_REGEX);
|
|
365
|
+
if (!match) return null;
|
|
366
|
+
const value = parseInt(match[1], 10);
|
|
367
|
+
const unit = match[2];
|
|
368
|
+
switch (unit) {
|
|
369
|
+
case "m":
|
|
370
|
+
return value * 60;
|
|
371
|
+
case "h":
|
|
372
|
+
return value * 3600;
|
|
373
|
+
case "d":
|
|
374
|
+
return value * 86400;
|
|
375
|
+
case "w":
|
|
376
|
+
return value * 604800;
|
|
377
|
+
default:
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function formatDuration(seconds) {
|
|
382
|
+
if (seconds >= 604800 && seconds % 604800 === 0) return `${seconds / 604800} week${seconds / 604800 > 1 ? "s" : ""}`;
|
|
383
|
+
if (seconds >= 86400 && seconds % 86400 === 0) return `${seconds / 86400} day${seconds / 86400 > 1 ? "s" : ""}`;
|
|
384
|
+
if (seconds >= 3600 && seconds % 3600 === 0) return `${seconds / 3600} hour${seconds / 3600 > 1 ? "s" : ""}`;
|
|
385
|
+
if (seconds >= 60 && seconds % 60 === 0) return `${seconds / 60} minute${seconds / 60 > 1 ? "s" : ""}`;
|
|
386
|
+
return `${seconds} second${seconds > 1 ? "s" : ""}`;
|
|
387
|
+
}
|
|
388
|
+
function signJwt(payload, secret) {
|
|
389
|
+
const header = Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })).toString("base64url");
|
|
390
|
+
const body = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
391
|
+
const sig = crypto.createHmac("sha256", secret).update(`${header}.${body}`).digest("base64url");
|
|
392
|
+
return `${header}.${body}.${sig}`;
|
|
393
|
+
}
|
|
394
|
+
function verifyJwt(token, secret) {
|
|
395
|
+
try {
|
|
396
|
+
const parts = token.split(".");
|
|
397
|
+
if (parts.length !== 3) return null;
|
|
398
|
+
const [header, body, sig] = parts;
|
|
399
|
+
const expected = crypto.createHmac("sha256", secret).update(`${header}.${body}`).digest("base64url");
|
|
400
|
+
if (sig !== expected) return null;
|
|
401
|
+
const payload = JSON.parse(Buffer.from(body, "base64url").toString());
|
|
402
|
+
if (payload.exp && Date.now() / 1e3 > payload.exp) return null;
|
|
403
|
+
if (payload.sub !== "x402-access-pass") return null;
|
|
404
|
+
return payload;
|
|
405
|
+
} catch {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
var DEFAULT_NETWORK2 = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
410
|
+
var USDC_DECIMALS2 = 6;
|
|
411
|
+
function x402AccessPass(config) {
|
|
412
|
+
const {
|
|
413
|
+
payTo,
|
|
414
|
+
network = DEFAULT_NETWORK2,
|
|
415
|
+
asset,
|
|
416
|
+
facilitatorUrl,
|
|
417
|
+
tiers: tierPrices,
|
|
418
|
+
ratePerHour,
|
|
419
|
+
secret = crypto.randomBytes(32),
|
|
420
|
+
issuer = "x402-access-pass",
|
|
421
|
+
verbose = false,
|
|
422
|
+
description
|
|
423
|
+
} = config;
|
|
424
|
+
if (!tierPrices && !ratePerHour) {
|
|
425
|
+
throw new Error("x402AccessPass: at least one of `tiers` or `ratePerHour` is required");
|
|
426
|
+
}
|
|
427
|
+
const log = verbose ? console.log.bind(console, "[x402:access-pass]") : () => {
|
|
428
|
+
};
|
|
429
|
+
const decimals = asset?.decimals ?? USDC_DECIMALS2;
|
|
430
|
+
const builtTiers = [];
|
|
431
|
+
if (tierPrices) {
|
|
432
|
+
for (const [id, price] of Object.entries(tierPrices)) {
|
|
433
|
+
const seconds = parseTierDuration(id);
|
|
434
|
+
if (!seconds) {
|
|
435
|
+
console.warn(`x402AccessPass: skipping tier "${id}" \u2014 unrecognized duration format (use 5m, 1h, 24h, 7d)`);
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
builtTiers.push({
|
|
439
|
+
id,
|
|
440
|
+
label: formatDuration(seconds),
|
|
441
|
+
seconds,
|
|
442
|
+
price,
|
|
443
|
+
priceAtomic: toAtomicUnits(parseFloat(price), decimals)
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
builtTiers.sort((a, b) => a.seconds - b.seconds);
|
|
447
|
+
}
|
|
448
|
+
const server = createX402Server({
|
|
449
|
+
payTo,
|
|
450
|
+
network,
|
|
451
|
+
asset,
|
|
452
|
+
facilitatorUrl
|
|
453
|
+
});
|
|
454
|
+
const passInfo = {
|
|
455
|
+
tiers: builtTiers.length > 0 ? builtTiers : void 0,
|
|
456
|
+
ratePerHour: ratePerHour || void 0,
|
|
457
|
+
issuer
|
|
458
|
+
};
|
|
459
|
+
const passInfoEncoded = btoa(JSON.stringify(passInfo));
|
|
460
|
+
function calculateCustomPrice(durationSeconds) {
|
|
461
|
+
if (!ratePerHour) {
|
|
462
|
+
throw new Error("Custom durations not supported \u2014 no ratePerHour configured");
|
|
463
|
+
}
|
|
464
|
+
const hours = durationSeconds / 3600;
|
|
465
|
+
const price = (parseFloat(ratePerHour) * hours).toFixed(decimals > 4 ? 4 : 2);
|
|
466
|
+
return { price, priceAtomic: toAtomicUnits(parseFloat(price), decimals) };
|
|
467
|
+
}
|
|
468
|
+
function resolvePricing(req) {
|
|
469
|
+
const tierParam = req.query.tier;
|
|
470
|
+
const durationParam = req.query.duration;
|
|
471
|
+
if (tierParam) {
|
|
472
|
+
const found = builtTiers.find((t) => t.id === tierParam);
|
|
473
|
+
if (found) {
|
|
474
|
+
return { tier: found.id, seconds: found.seconds, price: found.price, priceAtomic: found.priceAtomic, label: found.label };
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (durationParam) {
|
|
478
|
+
const seconds = parseInt(durationParam, 10);
|
|
479
|
+
if (seconds > 0 && ratePerHour) {
|
|
480
|
+
const pricing2 = calculateCustomPrice(seconds);
|
|
481
|
+
return { tier: "custom", seconds, ...pricing2, label: formatDuration(seconds) };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (builtTiers.length > 0) {
|
|
485
|
+
const t = builtTiers[0];
|
|
486
|
+
return { tier: t.id, seconds: t.seconds, price: t.price, priceAtomic: t.priceAtomic, label: t.label };
|
|
487
|
+
}
|
|
488
|
+
const pricing = calculateCustomPrice(3600);
|
|
489
|
+
return { tier: "custom", seconds: 3600, ...pricing, label: "1 hour" };
|
|
490
|
+
}
|
|
491
|
+
return async (req, res, next) => {
|
|
492
|
+
try {
|
|
493
|
+
const auth = req.headers.authorization;
|
|
494
|
+
if (auth?.startsWith("Bearer ")) {
|
|
495
|
+
const claims = verifyJwt(auth.slice(7), secret);
|
|
496
|
+
if (claims) {
|
|
497
|
+
log("Valid access pass:", claims.tier, "| expires:", new Date(claims.exp * 1e3).toISOString());
|
|
498
|
+
req.accessPass = {
|
|
499
|
+
tier: claims.tier,
|
|
500
|
+
duration: claims.duration,
|
|
501
|
+
expiresAt: new Date(claims.exp * 1e3).toISOString(),
|
|
502
|
+
payer: claims.payer,
|
|
503
|
+
network: claims.network
|
|
504
|
+
};
|
|
505
|
+
return next();
|
|
506
|
+
}
|
|
507
|
+
log("Invalid or expired access pass token");
|
|
508
|
+
}
|
|
509
|
+
const paymentSignature = req.headers["payment-signature"];
|
|
510
|
+
if (paymentSignature) {
|
|
511
|
+
log("Payment signature received, verifying for pass purchase...");
|
|
512
|
+
const verifyResult = await server.verifyPayment(paymentSignature);
|
|
513
|
+
if (!verifyResult.isValid) {
|
|
514
|
+
log("Payment verification failed:", verifyResult.invalidReason);
|
|
515
|
+
res.status(402).json({ error: "Payment verification failed", reason: verifyResult.invalidReason });
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const settleResult = await server.settlePayment(paymentSignature);
|
|
519
|
+
if (!settleResult.success) {
|
|
520
|
+
log("Payment settlement failed:", settleResult.errorReason);
|
|
521
|
+
res.status(402).json({ error: "Payment settlement failed", reason: settleResult.errorReason });
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
log("Payment settled:", settleResult.transaction);
|
|
525
|
+
const pricing2 = resolvePricing(req);
|
|
526
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
527
|
+
const claims = {
|
|
528
|
+
sub: "x402-access-pass",
|
|
529
|
+
tier: pricing2.tier,
|
|
530
|
+
duration: pricing2.seconds,
|
|
531
|
+
iat: now,
|
|
532
|
+
exp: now + pricing2.seconds,
|
|
533
|
+
payer: verifyResult.payer ?? "",
|
|
534
|
+
network,
|
|
535
|
+
iss: issuer
|
|
536
|
+
};
|
|
537
|
+
const jwt = signJwt(claims, secret);
|
|
538
|
+
req.x402 = {
|
|
539
|
+
transaction: settleResult.transaction,
|
|
540
|
+
payer: verifyResult.payer ?? "",
|
|
541
|
+
network
|
|
542
|
+
};
|
|
543
|
+
const paymentResponseData = {
|
|
544
|
+
success: true,
|
|
545
|
+
transaction: settleResult.transaction,
|
|
546
|
+
network,
|
|
547
|
+
payer: verifyResult.payer ?? ""
|
|
548
|
+
};
|
|
549
|
+
res.setHeader("PAYMENT-RESPONSE", btoa(JSON.stringify(paymentResponseData)));
|
|
550
|
+
res.setHeader("ACCESS-PASS", jwt);
|
|
551
|
+
res.json({
|
|
552
|
+
accessPass: {
|
|
553
|
+
token: jwt,
|
|
554
|
+
tier: pricing2.tier,
|
|
555
|
+
duration: pricing2.label,
|
|
556
|
+
durationSeconds: pricing2.seconds,
|
|
557
|
+
expiresAt: new Date((now + pricing2.seconds) * 1e3).toISOString(),
|
|
558
|
+
usage: "Include on subsequent requests as: Authorization: Bearer <token>"
|
|
559
|
+
},
|
|
560
|
+
transaction: settleResult.transaction,
|
|
561
|
+
payer: verifyResult.payer
|
|
562
|
+
});
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
log("No access pass or payment, returning 402");
|
|
566
|
+
const pricing = resolvePricing(req);
|
|
567
|
+
const amountAtomic = pricing.priceAtomic;
|
|
568
|
+
const resourceUrl = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
|
|
569
|
+
const requirements = await server.buildRequirements({
|
|
570
|
+
amountAtomic,
|
|
571
|
+
resourceUrl,
|
|
572
|
+
description: description || `Access pass: ${pricing.label}`,
|
|
573
|
+
mimeType: "application/json"
|
|
574
|
+
});
|
|
575
|
+
const encoded = server.encodeRequirements(requirements);
|
|
576
|
+
res.setHeader("PAYMENT-REQUIRED", encoded);
|
|
577
|
+
res.setHeader("X-ACCESS-PASS-TIERS", passInfoEncoded);
|
|
578
|
+
res.status(402).json({
|
|
579
|
+
error: "Access pass required",
|
|
580
|
+
message: "Purchase an access pass to unlock unlimited API access for a time window.",
|
|
581
|
+
accepts: requirements.accepts,
|
|
582
|
+
resource: requirements.resource,
|
|
583
|
+
accessPass: {
|
|
584
|
+
tiers: builtTiers.length > 0 ? builtTiers : void 0,
|
|
585
|
+
ratePerHour: ratePerHour || void 0,
|
|
586
|
+
usage: "Add ?tier=<id> or ?duration=<seconds> to your payment request to choose a pass duration."
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
} catch (error) {
|
|
590
|
+
log("Access pass middleware error:", error);
|
|
591
|
+
res.status(500).json({
|
|
592
|
+
error: "Payment processing error",
|
|
593
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
360
599
|
// src/server/dynamic-pricing.ts
|
|
361
600
|
function simpleHash(str) {
|
|
362
601
|
let hash = 2166136261;
|
|
@@ -1156,6 +1395,7 @@ export {
|
|
|
1156
1395
|
getTextModels,
|
|
1157
1396
|
isValidModel,
|
|
1158
1397
|
isValidModelId,
|
|
1398
|
+
x402AccessPass,
|
|
1159
1399
|
x402Middleware
|
|
1160
1400
|
};
|
|
1161
1401
|
//# sourceMappingURL=index.js.map
|