@perkos/service-pricing 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.
@@ -0,0 +1,317 @@
1
+ import { Address } from '@perkos/types-x402';
2
+
3
+ /**
4
+ * @perkos/service-pricing
5
+ * Dynamic pricing service with strategy patterns for x402 payment protocol
6
+ *
7
+ * Provides vendor-centric pricing with multiple strategy types:
8
+ * - Fixed: Static price per request
9
+ * - Tiered: Volume-based pricing tiers
10
+ * - Usage-based: Pay per unit (token, byte, time)
11
+ * - Time-bucket: Peak/off-peak pricing
12
+ * - Subscription: Recurring plans with credits
13
+ */
14
+
15
+ type PricingStrategyType = "fixed" | "tiered" | "usage-based" | "time-bucket" | "auction" | "subscription" | "custom";
16
+ type PricingUnit = "per-request" | "per-token" | "per-byte" | "per-second" | "per-minute" | "per-hour" | "per-day" | "per-month";
17
+ type UserTier = "free" | "basic" | "premium" | "enterprise";
18
+ interface PricingContext {
19
+ request: RequestContext;
20
+ user?: UserContext;
21
+ vendor: VendorContext;
22
+ endpoint: EndpointContext;
23
+ environment?: EnvironmentContext;
24
+ timestamp: number;
25
+ }
26
+ interface RequestContext {
27
+ method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
28
+ path: string;
29
+ headers: Record<string, string>;
30
+ query: Record<string, string>;
31
+ body?: unknown;
32
+ contentLength?: number;
33
+ clientIp?: string;
34
+ }
35
+ interface UserContext {
36
+ address: Address;
37
+ tier?: UserTier;
38
+ usage?: UsageHistory;
39
+ reputation?: number;
40
+ subscription?: SubscriptionStatus;
41
+ }
42
+ interface UsageHistory {
43
+ requestCount: number;
44
+ totalVolume: string;
45
+ periodStart: number;
46
+ periodEnd: number;
47
+ }
48
+ interface SubscriptionStatus {
49
+ active: boolean;
50
+ planId: string;
51
+ tierName: string;
52
+ remainingCredits?: number;
53
+ expiresAt?: number;
54
+ }
55
+ interface VendorContext {
56
+ id: string;
57
+ name: string;
58
+ walletAddress: Address;
59
+ network: string;
60
+ chainId: number | null;
61
+ asset: string;
62
+ basePriceUsd: string | null;
63
+ pricingConfig: VendorPricingConfig | null;
64
+ }
65
+ interface EndpointContext {
66
+ id: string;
67
+ path: string;
68
+ method: string;
69
+ priceUsd: string;
70
+ pricingConfig?: EndpointPricingConfig | null;
71
+ }
72
+ interface EnvironmentContext {
73
+ networkLoad?: number;
74
+ timeBucket?: "peak" | "off-peak" | "normal";
75
+ gasPrice?: string;
76
+ marketRate?: number;
77
+ }
78
+ interface PriceResult {
79
+ amount: string;
80
+ asset: Address;
81
+ network: string;
82
+ breakdown?: PriceBreakdown[];
83
+ validUntil?: number;
84
+ minAmount?: string;
85
+ display?: PriceDisplay;
86
+ cacheKey?: string;
87
+ }
88
+ interface PriceBreakdown {
89
+ component: string;
90
+ amount: string;
91
+ description?: string;
92
+ percentage?: number;
93
+ }
94
+ interface PriceDisplay {
95
+ formatted: string;
96
+ symbol: string;
97
+ decimals: number;
98
+ }
99
+ interface VendorPricingConfig {
100
+ id: string;
101
+ vendorId: string;
102
+ strategyType: PricingStrategyType;
103
+ name: string;
104
+ isDefault: boolean;
105
+ parameters: StrategyParameters;
106
+ cache?: CacheConfig;
107
+ routePatterns?: string[];
108
+ priority: number;
109
+ enabled: boolean;
110
+ createdAt: string;
111
+ updatedAt: string;
112
+ }
113
+ interface EndpointPricingConfig {
114
+ strategyType?: PricingStrategyType;
115
+ parameters?: Partial<StrategyParameters>;
116
+ cache?: Partial<CacheConfig>;
117
+ }
118
+ interface CacheConfig {
119
+ enabled: boolean;
120
+ ttlSeconds: number;
121
+ customKeyTemplate?: string;
122
+ }
123
+ type StrategyParameters = FixedPricingParams | TieredPricingParams | UsageBasedPricingParams | TimeBucketPricingParams | AuctionPricingParams | SubscriptionPricingParams | CustomPricingParams;
124
+ interface FixedPricingParams {
125
+ type: "fixed";
126
+ price: string;
127
+ asset: Address;
128
+ network: string;
129
+ }
130
+ interface TieredPricingParams {
131
+ type: "tiered";
132
+ tiers: PricingTier[];
133
+ asset: Address;
134
+ network: string;
135
+ resetPeriod: number;
136
+ }
137
+ interface PricingTier {
138
+ name: string;
139
+ upTo: number;
140
+ pricePerRequest: string;
141
+ discount?: number;
142
+ }
143
+ interface UsageBasedPricingParams {
144
+ type: "usage-based";
145
+ pricePerUnit: string;
146
+ unit: PricingUnit;
147
+ minimumCharge?: string;
148
+ maximumCharge?: string;
149
+ asset: Address;
150
+ network: string;
151
+ unitExtractor?: string;
152
+ }
153
+ interface TimeBucketPricingParams {
154
+ type: "time-bucket";
155
+ basePrice: string;
156
+ peakMultiplier: number;
157
+ offPeakMultiplier: number;
158
+ peakHours: Array<{
159
+ start: number;
160
+ end: number;
161
+ }>;
162
+ timezone: string;
163
+ asset: Address;
164
+ network: string;
165
+ }
166
+ interface AuctionPricingParams {
167
+ type: "auction";
168
+ floorPrice: string;
169
+ ceilingPrice: string;
170
+ adjustmentRate: number;
171
+ demandFactor?: number;
172
+ asset: Address;
173
+ network: string;
174
+ }
175
+ interface SubscriptionPricingParams {
176
+ type: "subscription";
177
+ plans: SubscriptionPlan[];
178
+ overagePrice?: string;
179
+ asset: Address;
180
+ network: string;
181
+ }
182
+ interface SubscriptionPlan {
183
+ id: string;
184
+ name: string;
185
+ monthlyPrice: string;
186
+ includedRequests: number;
187
+ features: string[];
188
+ }
189
+ interface CustomPricingParams {
190
+ type: "custom";
191
+ calculatorEndpoint?: string;
192
+ customParams: Record<string, unknown>;
193
+ asset: Address;
194
+ network: string;
195
+ }
196
+ interface PricingStrategy {
197
+ readonly name: string;
198
+ readonly type: PricingStrategyType;
199
+ calculatePrice(context: PricingContext): Promise<PriceResult>;
200
+ generateCacheKey(context: PricingContext): string;
201
+ validate(): ValidationResult;
202
+ }
203
+ interface ValidationResult {
204
+ valid: boolean;
205
+ errors?: string[];
206
+ }
207
+ interface PricingServiceConfig {
208
+ cache: CacheConfig;
209
+ enableLogging: boolean;
210
+ fallbackPrice?: string;
211
+ fallbackAsset?: Address;
212
+ fallbackNetwork?: string;
213
+ usdcDecimals: number;
214
+ }
215
+ declare const ASSET_SYMBOLS: Record<string, string>;
216
+ /**
217
+ * Convert USD string to atomic units (USDC decimals = 6)
218
+ */
219
+ declare function usdToAtomicUnits(usdAmount: string, decimals?: number): string;
220
+ /**
221
+ * Convert atomic units to USD string
222
+ */
223
+ declare function atomicUnitsToUsd(atomicUnits: string, decimals?: number): string;
224
+ /**
225
+ * Format price for display
226
+ */
227
+ declare function formatPrice(amount: bigint, asset: Address, decimals?: number): PriceDisplay;
228
+ /**
229
+ * Apply percentage discount to amount
230
+ */
231
+ declare function applyDiscount(amount: bigint, discountPercent: number): bigint;
232
+ /**
233
+ * Apply multiplier to amount
234
+ */
235
+ declare function applyMultiplier(amount: bigint, multiplier: number): bigint;
236
+ /**
237
+ * Generate deterministic cache key
238
+ */
239
+ declare function generateCacheKey(strategyType: string, configId: string, context: PricingContext): string;
240
+ /**
241
+ * Sort object keys deterministically
242
+ */
243
+ declare function sortObject(obj: Record<string, unknown>): Record<string, unknown>;
244
+ /**
245
+ * Get current time bucket based on timezone
246
+ */
247
+ declare function getTimeBucket(timezone: string, peakHours: Array<{
248
+ start: number;
249
+ end: number;
250
+ }>): "peak" | "off-peak" | "normal";
251
+ /**
252
+ * Determine tier for request count
253
+ */
254
+ declare function determineTier(requestCount: number, tiers: PricingTier[]): PricingTier | undefined;
255
+ /**
256
+ * Calculate usage-based units from request
257
+ */
258
+ declare function calculateUsageUnits(context: PricingContext, unit: PricingUnit): number;
259
+ /**
260
+ * Build price result structure
261
+ */
262
+ declare function buildPriceResult(amount: bigint, asset: Address, network: string, ttlSeconds?: number, breakdown?: PriceBreakdown[]): PriceResult;
263
+ /**
264
+ * Abstract base class for pricing strategies
265
+ */
266
+ declare abstract class BasePricingStrategy implements PricingStrategy {
267
+ abstract readonly name: string;
268
+ abstract readonly type: PricingStrategyType;
269
+ protected config: VendorPricingConfig;
270
+ protected decimals: number;
271
+ constructor(config: VendorPricingConfig, decimals?: number);
272
+ abstract calculatePrice(context: PricingContext): Promise<PriceResult>;
273
+ generateCacheKey(context: PricingContext): string;
274
+ validate(): ValidationResult;
275
+ protected getParams<T extends StrategyParameters>(): T;
276
+ protected getCacheTtl(): number;
277
+ }
278
+ /**
279
+ * Fixed pricing strategy - static price per request
280
+ */
281
+ declare class FixedPricingStrategy extends BasePricingStrategy {
282
+ readonly name = "Fixed Pricing";
283
+ readonly type: PricingStrategyType;
284
+ calculatePrice(context: PricingContext): Promise<PriceResult>;
285
+ validate(): ValidationResult;
286
+ }
287
+ /**
288
+ * Tiered pricing strategy - volume-based discounts
289
+ */
290
+ declare class TieredPricingStrategy extends BasePricingStrategy {
291
+ readonly name = "Tiered Pricing";
292
+ readonly type: PricingStrategyType;
293
+ calculatePrice(context: PricingContext): Promise<PriceResult>;
294
+ validate(): ValidationResult;
295
+ }
296
+ /**
297
+ * Usage-based pricing strategy - pay per unit
298
+ */
299
+ declare class UsageBasedPricingStrategy extends BasePricingStrategy {
300
+ readonly name = "Usage-Based Pricing";
301
+ readonly type: PricingStrategyType;
302
+ calculatePrice(context: PricingContext): Promise<PriceResult>;
303
+ }
304
+ /**
305
+ * Time-bucket pricing strategy - peak/off-peak pricing
306
+ */
307
+ declare class TimeBucketPricingStrategy extends BasePricingStrategy {
308
+ readonly name = "Time-Bucket Pricing";
309
+ readonly type: PricingStrategyType;
310
+ calculatePrice(context: PricingContext): Promise<PriceResult>;
311
+ }
312
+ /**
313
+ * Create a pricing strategy instance from configuration
314
+ */
315
+ declare function createPricingStrategy(config: VendorPricingConfig, decimals?: number): PricingStrategy;
316
+
317
+ export { ASSET_SYMBOLS, type AuctionPricingParams, BasePricingStrategy, type CacheConfig, type CustomPricingParams, type EndpointContext, type EndpointPricingConfig, type EnvironmentContext, type FixedPricingParams, FixedPricingStrategy, type PriceBreakdown, type PriceDisplay, type PriceResult, type PricingContext, type PricingServiceConfig, type PricingStrategy, type PricingStrategyType, type PricingTier, type PricingUnit, type RequestContext, type StrategyParameters, type SubscriptionPlan, type SubscriptionPricingParams, type SubscriptionStatus, type TieredPricingParams, TieredPricingStrategy, type TimeBucketPricingParams, TimeBucketPricingStrategy, type UsageBasedPricingParams, UsageBasedPricingStrategy, type UsageHistory, type UserContext, type UserTier, type ValidationResult, type VendorContext, type VendorPricingConfig, applyDiscount, applyMultiplier, atomicUnitsToUsd, buildPriceResult, calculateUsageUnits, createPricingStrategy, determineTier, formatPrice, generateCacheKey, getTimeBucket, sortObject, usdToAtomicUnits };
@@ -0,0 +1,317 @@
1
+ import { Address } from '@perkos/types-x402';
2
+
3
+ /**
4
+ * @perkos/service-pricing
5
+ * Dynamic pricing service with strategy patterns for x402 payment protocol
6
+ *
7
+ * Provides vendor-centric pricing with multiple strategy types:
8
+ * - Fixed: Static price per request
9
+ * - Tiered: Volume-based pricing tiers
10
+ * - Usage-based: Pay per unit (token, byte, time)
11
+ * - Time-bucket: Peak/off-peak pricing
12
+ * - Subscription: Recurring plans with credits
13
+ */
14
+
15
+ type PricingStrategyType = "fixed" | "tiered" | "usage-based" | "time-bucket" | "auction" | "subscription" | "custom";
16
+ type PricingUnit = "per-request" | "per-token" | "per-byte" | "per-second" | "per-minute" | "per-hour" | "per-day" | "per-month";
17
+ type UserTier = "free" | "basic" | "premium" | "enterprise";
18
+ interface PricingContext {
19
+ request: RequestContext;
20
+ user?: UserContext;
21
+ vendor: VendorContext;
22
+ endpoint: EndpointContext;
23
+ environment?: EnvironmentContext;
24
+ timestamp: number;
25
+ }
26
+ interface RequestContext {
27
+ method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
28
+ path: string;
29
+ headers: Record<string, string>;
30
+ query: Record<string, string>;
31
+ body?: unknown;
32
+ contentLength?: number;
33
+ clientIp?: string;
34
+ }
35
+ interface UserContext {
36
+ address: Address;
37
+ tier?: UserTier;
38
+ usage?: UsageHistory;
39
+ reputation?: number;
40
+ subscription?: SubscriptionStatus;
41
+ }
42
+ interface UsageHistory {
43
+ requestCount: number;
44
+ totalVolume: string;
45
+ periodStart: number;
46
+ periodEnd: number;
47
+ }
48
+ interface SubscriptionStatus {
49
+ active: boolean;
50
+ planId: string;
51
+ tierName: string;
52
+ remainingCredits?: number;
53
+ expiresAt?: number;
54
+ }
55
+ interface VendorContext {
56
+ id: string;
57
+ name: string;
58
+ walletAddress: Address;
59
+ network: string;
60
+ chainId: number | null;
61
+ asset: string;
62
+ basePriceUsd: string | null;
63
+ pricingConfig: VendorPricingConfig | null;
64
+ }
65
+ interface EndpointContext {
66
+ id: string;
67
+ path: string;
68
+ method: string;
69
+ priceUsd: string;
70
+ pricingConfig?: EndpointPricingConfig | null;
71
+ }
72
+ interface EnvironmentContext {
73
+ networkLoad?: number;
74
+ timeBucket?: "peak" | "off-peak" | "normal";
75
+ gasPrice?: string;
76
+ marketRate?: number;
77
+ }
78
+ interface PriceResult {
79
+ amount: string;
80
+ asset: Address;
81
+ network: string;
82
+ breakdown?: PriceBreakdown[];
83
+ validUntil?: number;
84
+ minAmount?: string;
85
+ display?: PriceDisplay;
86
+ cacheKey?: string;
87
+ }
88
+ interface PriceBreakdown {
89
+ component: string;
90
+ amount: string;
91
+ description?: string;
92
+ percentage?: number;
93
+ }
94
+ interface PriceDisplay {
95
+ formatted: string;
96
+ symbol: string;
97
+ decimals: number;
98
+ }
99
+ interface VendorPricingConfig {
100
+ id: string;
101
+ vendorId: string;
102
+ strategyType: PricingStrategyType;
103
+ name: string;
104
+ isDefault: boolean;
105
+ parameters: StrategyParameters;
106
+ cache?: CacheConfig;
107
+ routePatterns?: string[];
108
+ priority: number;
109
+ enabled: boolean;
110
+ createdAt: string;
111
+ updatedAt: string;
112
+ }
113
+ interface EndpointPricingConfig {
114
+ strategyType?: PricingStrategyType;
115
+ parameters?: Partial<StrategyParameters>;
116
+ cache?: Partial<CacheConfig>;
117
+ }
118
+ interface CacheConfig {
119
+ enabled: boolean;
120
+ ttlSeconds: number;
121
+ customKeyTemplate?: string;
122
+ }
123
+ type StrategyParameters = FixedPricingParams | TieredPricingParams | UsageBasedPricingParams | TimeBucketPricingParams | AuctionPricingParams | SubscriptionPricingParams | CustomPricingParams;
124
+ interface FixedPricingParams {
125
+ type: "fixed";
126
+ price: string;
127
+ asset: Address;
128
+ network: string;
129
+ }
130
+ interface TieredPricingParams {
131
+ type: "tiered";
132
+ tiers: PricingTier[];
133
+ asset: Address;
134
+ network: string;
135
+ resetPeriod: number;
136
+ }
137
+ interface PricingTier {
138
+ name: string;
139
+ upTo: number;
140
+ pricePerRequest: string;
141
+ discount?: number;
142
+ }
143
+ interface UsageBasedPricingParams {
144
+ type: "usage-based";
145
+ pricePerUnit: string;
146
+ unit: PricingUnit;
147
+ minimumCharge?: string;
148
+ maximumCharge?: string;
149
+ asset: Address;
150
+ network: string;
151
+ unitExtractor?: string;
152
+ }
153
+ interface TimeBucketPricingParams {
154
+ type: "time-bucket";
155
+ basePrice: string;
156
+ peakMultiplier: number;
157
+ offPeakMultiplier: number;
158
+ peakHours: Array<{
159
+ start: number;
160
+ end: number;
161
+ }>;
162
+ timezone: string;
163
+ asset: Address;
164
+ network: string;
165
+ }
166
+ interface AuctionPricingParams {
167
+ type: "auction";
168
+ floorPrice: string;
169
+ ceilingPrice: string;
170
+ adjustmentRate: number;
171
+ demandFactor?: number;
172
+ asset: Address;
173
+ network: string;
174
+ }
175
+ interface SubscriptionPricingParams {
176
+ type: "subscription";
177
+ plans: SubscriptionPlan[];
178
+ overagePrice?: string;
179
+ asset: Address;
180
+ network: string;
181
+ }
182
+ interface SubscriptionPlan {
183
+ id: string;
184
+ name: string;
185
+ monthlyPrice: string;
186
+ includedRequests: number;
187
+ features: string[];
188
+ }
189
+ interface CustomPricingParams {
190
+ type: "custom";
191
+ calculatorEndpoint?: string;
192
+ customParams: Record<string, unknown>;
193
+ asset: Address;
194
+ network: string;
195
+ }
196
+ interface PricingStrategy {
197
+ readonly name: string;
198
+ readonly type: PricingStrategyType;
199
+ calculatePrice(context: PricingContext): Promise<PriceResult>;
200
+ generateCacheKey(context: PricingContext): string;
201
+ validate(): ValidationResult;
202
+ }
203
+ interface ValidationResult {
204
+ valid: boolean;
205
+ errors?: string[];
206
+ }
207
+ interface PricingServiceConfig {
208
+ cache: CacheConfig;
209
+ enableLogging: boolean;
210
+ fallbackPrice?: string;
211
+ fallbackAsset?: Address;
212
+ fallbackNetwork?: string;
213
+ usdcDecimals: number;
214
+ }
215
+ declare const ASSET_SYMBOLS: Record<string, string>;
216
+ /**
217
+ * Convert USD string to atomic units (USDC decimals = 6)
218
+ */
219
+ declare function usdToAtomicUnits(usdAmount: string, decimals?: number): string;
220
+ /**
221
+ * Convert atomic units to USD string
222
+ */
223
+ declare function atomicUnitsToUsd(atomicUnits: string, decimals?: number): string;
224
+ /**
225
+ * Format price for display
226
+ */
227
+ declare function formatPrice(amount: bigint, asset: Address, decimals?: number): PriceDisplay;
228
+ /**
229
+ * Apply percentage discount to amount
230
+ */
231
+ declare function applyDiscount(amount: bigint, discountPercent: number): bigint;
232
+ /**
233
+ * Apply multiplier to amount
234
+ */
235
+ declare function applyMultiplier(amount: bigint, multiplier: number): bigint;
236
+ /**
237
+ * Generate deterministic cache key
238
+ */
239
+ declare function generateCacheKey(strategyType: string, configId: string, context: PricingContext): string;
240
+ /**
241
+ * Sort object keys deterministically
242
+ */
243
+ declare function sortObject(obj: Record<string, unknown>): Record<string, unknown>;
244
+ /**
245
+ * Get current time bucket based on timezone
246
+ */
247
+ declare function getTimeBucket(timezone: string, peakHours: Array<{
248
+ start: number;
249
+ end: number;
250
+ }>): "peak" | "off-peak" | "normal";
251
+ /**
252
+ * Determine tier for request count
253
+ */
254
+ declare function determineTier(requestCount: number, tiers: PricingTier[]): PricingTier | undefined;
255
+ /**
256
+ * Calculate usage-based units from request
257
+ */
258
+ declare function calculateUsageUnits(context: PricingContext, unit: PricingUnit): number;
259
+ /**
260
+ * Build price result structure
261
+ */
262
+ declare function buildPriceResult(amount: bigint, asset: Address, network: string, ttlSeconds?: number, breakdown?: PriceBreakdown[]): PriceResult;
263
+ /**
264
+ * Abstract base class for pricing strategies
265
+ */
266
+ declare abstract class BasePricingStrategy implements PricingStrategy {
267
+ abstract readonly name: string;
268
+ abstract readonly type: PricingStrategyType;
269
+ protected config: VendorPricingConfig;
270
+ protected decimals: number;
271
+ constructor(config: VendorPricingConfig, decimals?: number);
272
+ abstract calculatePrice(context: PricingContext): Promise<PriceResult>;
273
+ generateCacheKey(context: PricingContext): string;
274
+ validate(): ValidationResult;
275
+ protected getParams<T extends StrategyParameters>(): T;
276
+ protected getCacheTtl(): number;
277
+ }
278
+ /**
279
+ * Fixed pricing strategy - static price per request
280
+ */
281
+ declare class FixedPricingStrategy extends BasePricingStrategy {
282
+ readonly name = "Fixed Pricing";
283
+ readonly type: PricingStrategyType;
284
+ calculatePrice(context: PricingContext): Promise<PriceResult>;
285
+ validate(): ValidationResult;
286
+ }
287
+ /**
288
+ * Tiered pricing strategy - volume-based discounts
289
+ */
290
+ declare class TieredPricingStrategy extends BasePricingStrategy {
291
+ readonly name = "Tiered Pricing";
292
+ readonly type: PricingStrategyType;
293
+ calculatePrice(context: PricingContext): Promise<PriceResult>;
294
+ validate(): ValidationResult;
295
+ }
296
+ /**
297
+ * Usage-based pricing strategy - pay per unit
298
+ */
299
+ declare class UsageBasedPricingStrategy extends BasePricingStrategy {
300
+ readonly name = "Usage-Based Pricing";
301
+ readonly type: PricingStrategyType;
302
+ calculatePrice(context: PricingContext): Promise<PriceResult>;
303
+ }
304
+ /**
305
+ * Time-bucket pricing strategy - peak/off-peak pricing
306
+ */
307
+ declare class TimeBucketPricingStrategy extends BasePricingStrategy {
308
+ readonly name = "Time-Bucket Pricing";
309
+ readonly type: PricingStrategyType;
310
+ calculatePrice(context: PricingContext): Promise<PriceResult>;
311
+ }
312
+ /**
313
+ * Create a pricing strategy instance from configuration
314
+ */
315
+ declare function createPricingStrategy(config: VendorPricingConfig, decimals?: number): PricingStrategy;
316
+
317
+ export { ASSET_SYMBOLS, type AuctionPricingParams, BasePricingStrategy, type CacheConfig, type CustomPricingParams, type EndpointContext, type EndpointPricingConfig, type EnvironmentContext, type FixedPricingParams, FixedPricingStrategy, type PriceBreakdown, type PriceDisplay, type PriceResult, type PricingContext, type PricingServiceConfig, type PricingStrategy, type PricingStrategyType, type PricingTier, type PricingUnit, type RequestContext, type StrategyParameters, type SubscriptionPlan, type SubscriptionPricingParams, type SubscriptionStatus, type TieredPricingParams, TieredPricingStrategy, type TimeBucketPricingParams, TimeBucketPricingStrategy, type UsageBasedPricingParams, UsageBasedPricingStrategy, type UsageHistory, type UserContext, type UserTier, type ValidationResult, type VendorContext, type VendorPricingConfig, applyDiscount, applyMultiplier, atomicUnitsToUsd, buildPriceResult, calculateUsageUnits, createPricingStrategy, determineTier, formatPrice, generateCacheKey, getTimeBucket, sortObject, usdToAtomicUnits };
package/dist/index.js ADDED
@@ -0,0 +1,387 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ASSET_SYMBOLS: () => ASSET_SYMBOLS,
24
+ BasePricingStrategy: () => BasePricingStrategy,
25
+ FixedPricingStrategy: () => FixedPricingStrategy,
26
+ TieredPricingStrategy: () => TieredPricingStrategy,
27
+ TimeBucketPricingStrategy: () => TimeBucketPricingStrategy,
28
+ UsageBasedPricingStrategy: () => UsageBasedPricingStrategy,
29
+ applyDiscount: () => applyDiscount,
30
+ applyMultiplier: () => applyMultiplier,
31
+ atomicUnitsToUsd: () => atomicUnitsToUsd,
32
+ buildPriceResult: () => buildPriceResult,
33
+ calculateUsageUnits: () => calculateUsageUnits,
34
+ createPricingStrategy: () => createPricingStrategy,
35
+ determineTier: () => determineTier,
36
+ formatPrice: () => formatPrice,
37
+ generateCacheKey: () => generateCacheKey,
38
+ getTimeBucket: () => getTimeBucket,
39
+ sortObject: () => sortObject,
40
+ usdToAtomicUnits: () => usdToAtomicUnits
41
+ });
42
+ module.exports = __toCommonJS(index_exports);
43
+ var ASSET_SYMBOLS = {
44
+ // USDC addresses across networks
45
+ "0x036CbD53842c5426634e7929541eC2318f3dCF7e": "USDC",
46
+ // Base Sepolia
47
+ "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913": "USDC",
48
+ // Base Mainnet
49
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": "USDC",
50
+ // Ethereum Mainnet
51
+ "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359": "USDC",
52
+ // Polygon
53
+ "0xaf88d065e77c8cC2239327C5EDb3A432268e5831": "USDC",
54
+ // Arbitrum
55
+ "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85": "USDC",
56
+ // Optimism
57
+ "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E": "USDC"
58
+ // Avalanche
59
+ };
60
+ function usdToAtomicUnits(usdAmount, decimals = 6) {
61
+ const parts = usdAmount.split(".");
62
+ const integerPart = parts[0] || "0";
63
+ const fractionalPart = (parts[1] || "").padEnd(decimals, "0").slice(0, decimals);
64
+ const atomicUnits = BigInt(integerPart + fractionalPart);
65
+ return atomicUnits.toString();
66
+ }
67
+ function atomicUnitsToUsd(atomicUnits, decimals = 6) {
68
+ const amount = BigInt(atomicUnits);
69
+ const divisor = BigInt(10 ** decimals);
70
+ const integerPart = amount / divisor;
71
+ const fractionalPart = amount % divisor;
72
+ const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
73
+ return `${integerPart}.${fractionalStr}`;
74
+ }
75
+ function formatPrice(amount, asset, decimals = 6) {
76
+ const divisor = BigInt(10 ** decimals);
77
+ const integerPart = amount / divisor;
78
+ const fractionalPart = amount % divisor;
79
+ const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
80
+ const formatted = `${integerPart}.${fractionalStr}`;
81
+ const symbol = ASSET_SYMBOLS[asset] || ASSET_SYMBOLS[asset.toLowerCase()] || "USDC";
82
+ return {
83
+ formatted,
84
+ symbol,
85
+ decimals
86
+ };
87
+ }
88
+ function applyDiscount(amount, discountPercent) {
89
+ if (discountPercent <= 0 || discountPercent > 100) return amount;
90
+ const multiplier = BigInt(Math.floor((100 - discountPercent) * 100));
91
+ return amount * multiplier / 10000n;
92
+ }
93
+ function applyMultiplier(amount, multiplier) {
94
+ const multiplierInt = BigInt(Math.floor(multiplier * 1e4));
95
+ return amount * multiplierInt / 10000n;
96
+ }
97
+ function generateCacheKey(strategyType, configId, context) {
98
+ const keyComponents = {
99
+ strategy: strategyType,
100
+ configId,
101
+ method: context.request.method,
102
+ path: context.request.path,
103
+ query: sortObject(context.request.query),
104
+ bodyHash: context.request.body ? hashObject(context.request.body) : null,
105
+ vendorId: context.vendor.id,
106
+ endpointId: context.endpoint.id,
107
+ user: context.user?.address || "anonymous"
108
+ };
109
+ const keyString = JSON.stringify(keyComponents);
110
+ return simpleHash(keyString);
111
+ }
112
+ function sortObject(obj) {
113
+ if (!obj || typeof obj !== "object") return {};
114
+ return Object.keys(obj).sort().reduce((acc, key) => {
115
+ acc[key] = obj[key];
116
+ return acc;
117
+ }, {});
118
+ }
119
+ function simpleHash(str) {
120
+ let hash = 0;
121
+ for (let i = 0; i < str.length; i++) {
122
+ const char = str.charCodeAt(i);
123
+ hash = (hash << 5) - hash + char;
124
+ hash = hash & hash;
125
+ }
126
+ return Math.abs(hash).toString(16).padStart(8, "0");
127
+ }
128
+ function hashObject(obj) {
129
+ return simpleHash(JSON.stringify(obj));
130
+ }
131
+ function getTimeBucket(timezone, peakHours) {
132
+ try {
133
+ const now = /* @__PURE__ */ new Date();
134
+ const formatter = new Intl.DateTimeFormat("en-US", {
135
+ hour: "numeric",
136
+ hour12: false,
137
+ timeZone: timezone
138
+ });
139
+ const hour = parseInt(formatter.format(now), 10);
140
+ for (const peak of peakHours) {
141
+ if (hour >= peak.start && hour < peak.end) {
142
+ return "peak";
143
+ }
144
+ }
145
+ if (hour >= 22 || hour < 6) {
146
+ return "off-peak";
147
+ }
148
+ return "normal";
149
+ } catch {
150
+ return "normal";
151
+ }
152
+ }
153
+ function determineTier(requestCount, tiers) {
154
+ const sortedTiers = [...tiers].sort((a, b) => a.upTo - b.upTo);
155
+ for (const tier of sortedTiers) {
156
+ if (tier.upTo === -1 || requestCount <= tier.upTo) {
157
+ return tier;
158
+ }
159
+ }
160
+ return sortedTiers[sortedTiers.length - 1];
161
+ }
162
+ function calculateUsageUnits(context, unit) {
163
+ switch (unit) {
164
+ case "per-request":
165
+ return 1;
166
+ case "per-byte":
167
+ return context.request.contentLength || 0;
168
+ case "per-token":
169
+ return Math.ceil((context.request.contentLength || 0) / 4);
170
+ default:
171
+ return 1;
172
+ }
173
+ }
174
+ function buildPriceResult(amount, asset, network, ttlSeconds = 300, breakdown) {
175
+ return {
176
+ amount: amount.toString(),
177
+ asset,
178
+ network,
179
+ breakdown,
180
+ validUntil: Math.floor(Date.now() / 1e3) + ttlSeconds,
181
+ display: formatPrice(amount, asset)
182
+ };
183
+ }
184
+ var BasePricingStrategy = class {
185
+ constructor(config, decimals = 6) {
186
+ this.config = config;
187
+ this.decimals = decimals;
188
+ }
189
+ generateCacheKey(context) {
190
+ return generateCacheKey(this.type, this.config.id, context);
191
+ }
192
+ validate() {
193
+ const errors = [];
194
+ if (!this.config.parameters) {
195
+ errors.push("Strategy parameters are required");
196
+ }
197
+ if (!this.config.vendorId) {
198
+ errors.push("Vendor ID is required");
199
+ }
200
+ return {
201
+ valid: errors.length === 0,
202
+ errors: errors.length > 0 ? errors : void 0
203
+ };
204
+ }
205
+ getParams() {
206
+ return this.config.parameters;
207
+ }
208
+ getCacheTtl() {
209
+ return this.config.cache?.ttlSeconds ?? 300;
210
+ }
211
+ };
212
+ var FixedPricingStrategy = class extends BasePricingStrategy {
213
+ constructor() {
214
+ super(...arguments);
215
+ this.name = "Fixed Pricing";
216
+ this.type = "fixed";
217
+ }
218
+ async calculatePrice(context) {
219
+ const params = this.getParams();
220
+ const amount = BigInt(params.price);
221
+ return buildPriceResult(
222
+ amount,
223
+ params.asset,
224
+ params.network,
225
+ this.getCacheTtl(),
226
+ [{ component: "Base Price", amount: params.price }]
227
+ );
228
+ }
229
+ validate() {
230
+ const base = super.validate();
231
+ if (!base.valid) return base;
232
+ const errors = [];
233
+ const params = this.getParams();
234
+ if (!params.price || BigInt(params.price) <= 0n) {
235
+ errors.push("Fixed price must be positive");
236
+ }
237
+ if (!params.asset) {
238
+ errors.push("Asset address is required");
239
+ }
240
+ return {
241
+ valid: errors.length === 0,
242
+ errors: errors.length > 0 ? errors : void 0
243
+ };
244
+ }
245
+ };
246
+ var TieredPricingStrategy = class extends BasePricingStrategy {
247
+ constructor() {
248
+ super(...arguments);
249
+ this.name = "Tiered Pricing";
250
+ this.type = "tiered";
251
+ }
252
+ async calculatePrice(context) {
253
+ const params = this.getParams();
254
+ const requestCount = context.user?.usage?.requestCount || 0;
255
+ const tier = determineTier(requestCount, params.tiers);
256
+ if (!tier) {
257
+ throw new Error("No applicable pricing tier found");
258
+ }
259
+ let amount = BigInt(tier.pricePerRequest);
260
+ if (tier.discount && tier.discount > 0) {
261
+ amount = applyDiscount(amount, tier.discount);
262
+ }
263
+ return buildPriceResult(
264
+ amount,
265
+ params.asset,
266
+ params.network,
267
+ this.getCacheTtl(),
268
+ [
269
+ { component: "Base Price", amount: tier.pricePerRequest },
270
+ ...tier.discount ? [{ component: "Tier Discount", amount: `-${tier.discount}%`, percentage: tier.discount }] : []
271
+ ]
272
+ );
273
+ }
274
+ validate() {
275
+ const base = super.validate();
276
+ if (!base.valid) return base;
277
+ const errors = [];
278
+ const params = this.getParams();
279
+ if (!params.tiers || params.tiers.length === 0) {
280
+ errors.push("At least one pricing tier is required");
281
+ }
282
+ return {
283
+ valid: errors.length === 0,
284
+ errors: errors.length > 0 ? errors : void 0
285
+ };
286
+ }
287
+ };
288
+ var UsageBasedPricingStrategy = class extends BasePricingStrategy {
289
+ constructor() {
290
+ super(...arguments);
291
+ this.name = "Usage-Based Pricing";
292
+ this.type = "usage-based";
293
+ }
294
+ async calculatePrice(context) {
295
+ const params = this.getParams();
296
+ const units = calculateUsageUnits(context, params.unit);
297
+ let amount = BigInt(params.pricePerUnit) * BigInt(units);
298
+ if (params.minimumCharge) {
299
+ const min = BigInt(params.minimumCharge);
300
+ if (amount < min) {
301
+ amount = min;
302
+ }
303
+ }
304
+ if (params.maximumCharge) {
305
+ const max = BigInt(params.maximumCharge);
306
+ if (amount > max) {
307
+ amount = max;
308
+ }
309
+ }
310
+ return buildPriceResult(
311
+ amount,
312
+ params.asset,
313
+ params.network,
314
+ this.getCacheTtl(),
315
+ [
316
+ { component: `${units} ${params.unit}`, amount: amount.toString() }
317
+ ]
318
+ );
319
+ }
320
+ };
321
+ var TimeBucketPricingStrategy = class extends BasePricingStrategy {
322
+ constructor() {
323
+ super(...arguments);
324
+ this.name = "Time-Bucket Pricing";
325
+ this.type = "time-bucket";
326
+ }
327
+ async calculatePrice(context) {
328
+ const params = this.getParams();
329
+ const bucket = getTimeBucket(params.timezone, params.peakHours);
330
+ let amount = BigInt(params.basePrice);
331
+ let multiplier = 1;
332
+ switch (bucket) {
333
+ case "peak":
334
+ multiplier = params.peakMultiplier;
335
+ break;
336
+ case "off-peak":
337
+ multiplier = params.offPeakMultiplier;
338
+ break;
339
+ }
340
+ amount = applyMultiplier(amount, multiplier);
341
+ return buildPriceResult(
342
+ amount,
343
+ params.asset,
344
+ params.network,
345
+ this.getCacheTtl(),
346
+ [
347
+ { component: "Base Price", amount: params.basePrice },
348
+ { component: `${bucket} multiplier`, amount: `x${multiplier}` }
349
+ ]
350
+ );
351
+ }
352
+ };
353
+ function createPricingStrategy(config, decimals = 6) {
354
+ switch (config.strategyType) {
355
+ case "fixed":
356
+ return new FixedPricingStrategy(config, decimals);
357
+ case "tiered":
358
+ return new TieredPricingStrategy(config, decimals);
359
+ case "usage-based":
360
+ return new UsageBasedPricingStrategy(config, decimals);
361
+ case "time-bucket":
362
+ return new TimeBucketPricingStrategy(config, decimals);
363
+ default:
364
+ throw new Error(`Unsupported strategy type: ${config.strategyType}`);
365
+ }
366
+ }
367
+ // Annotate the CommonJS export names for ESM import in node:
368
+ 0 && (module.exports = {
369
+ ASSET_SYMBOLS,
370
+ BasePricingStrategy,
371
+ FixedPricingStrategy,
372
+ TieredPricingStrategy,
373
+ TimeBucketPricingStrategy,
374
+ UsageBasedPricingStrategy,
375
+ applyDiscount,
376
+ applyMultiplier,
377
+ atomicUnitsToUsd,
378
+ buildPriceResult,
379
+ calculateUsageUnits,
380
+ createPricingStrategy,
381
+ determineTier,
382
+ formatPrice,
383
+ generateCacheKey,
384
+ getTimeBucket,
385
+ sortObject,
386
+ usdToAtomicUnits
387
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,345 @@
1
+ // src/index.ts
2
+ var ASSET_SYMBOLS = {
3
+ // USDC addresses across networks
4
+ "0x036CbD53842c5426634e7929541eC2318f3dCF7e": "USDC",
5
+ // Base Sepolia
6
+ "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913": "USDC",
7
+ // Base Mainnet
8
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": "USDC",
9
+ // Ethereum Mainnet
10
+ "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359": "USDC",
11
+ // Polygon
12
+ "0xaf88d065e77c8cC2239327C5EDb3A432268e5831": "USDC",
13
+ // Arbitrum
14
+ "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85": "USDC",
15
+ // Optimism
16
+ "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E": "USDC"
17
+ // Avalanche
18
+ };
19
+ function usdToAtomicUnits(usdAmount, decimals = 6) {
20
+ const parts = usdAmount.split(".");
21
+ const integerPart = parts[0] || "0";
22
+ const fractionalPart = (parts[1] || "").padEnd(decimals, "0").slice(0, decimals);
23
+ const atomicUnits = BigInt(integerPart + fractionalPart);
24
+ return atomicUnits.toString();
25
+ }
26
+ function atomicUnitsToUsd(atomicUnits, decimals = 6) {
27
+ const amount = BigInt(atomicUnits);
28
+ const divisor = BigInt(10 ** decimals);
29
+ const integerPart = amount / divisor;
30
+ const fractionalPart = amount % divisor;
31
+ const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
32
+ return `${integerPart}.${fractionalStr}`;
33
+ }
34
+ function formatPrice(amount, asset, decimals = 6) {
35
+ const divisor = BigInt(10 ** decimals);
36
+ const integerPart = amount / divisor;
37
+ const fractionalPart = amount % divisor;
38
+ const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
39
+ const formatted = `${integerPart}.${fractionalStr}`;
40
+ const symbol = ASSET_SYMBOLS[asset] || ASSET_SYMBOLS[asset.toLowerCase()] || "USDC";
41
+ return {
42
+ formatted,
43
+ symbol,
44
+ decimals
45
+ };
46
+ }
47
+ function applyDiscount(amount, discountPercent) {
48
+ if (discountPercent <= 0 || discountPercent > 100) return amount;
49
+ const multiplier = BigInt(Math.floor((100 - discountPercent) * 100));
50
+ return amount * multiplier / 10000n;
51
+ }
52
+ function applyMultiplier(amount, multiplier) {
53
+ const multiplierInt = BigInt(Math.floor(multiplier * 1e4));
54
+ return amount * multiplierInt / 10000n;
55
+ }
56
+ function generateCacheKey(strategyType, configId, context) {
57
+ const keyComponents = {
58
+ strategy: strategyType,
59
+ configId,
60
+ method: context.request.method,
61
+ path: context.request.path,
62
+ query: sortObject(context.request.query),
63
+ bodyHash: context.request.body ? hashObject(context.request.body) : null,
64
+ vendorId: context.vendor.id,
65
+ endpointId: context.endpoint.id,
66
+ user: context.user?.address || "anonymous"
67
+ };
68
+ const keyString = JSON.stringify(keyComponents);
69
+ return simpleHash(keyString);
70
+ }
71
+ function sortObject(obj) {
72
+ if (!obj || typeof obj !== "object") return {};
73
+ return Object.keys(obj).sort().reduce((acc, key) => {
74
+ acc[key] = obj[key];
75
+ return acc;
76
+ }, {});
77
+ }
78
+ function simpleHash(str) {
79
+ let hash = 0;
80
+ for (let i = 0; i < str.length; i++) {
81
+ const char = str.charCodeAt(i);
82
+ hash = (hash << 5) - hash + char;
83
+ hash = hash & hash;
84
+ }
85
+ return Math.abs(hash).toString(16).padStart(8, "0");
86
+ }
87
+ function hashObject(obj) {
88
+ return simpleHash(JSON.stringify(obj));
89
+ }
90
+ function getTimeBucket(timezone, peakHours) {
91
+ try {
92
+ const now = /* @__PURE__ */ new Date();
93
+ const formatter = new Intl.DateTimeFormat("en-US", {
94
+ hour: "numeric",
95
+ hour12: false,
96
+ timeZone: timezone
97
+ });
98
+ const hour = parseInt(formatter.format(now), 10);
99
+ for (const peak of peakHours) {
100
+ if (hour >= peak.start && hour < peak.end) {
101
+ return "peak";
102
+ }
103
+ }
104
+ if (hour >= 22 || hour < 6) {
105
+ return "off-peak";
106
+ }
107
+ return "normal";
108
+ } catch {
109
+ return "normal";
110
+ }
111
+ }
112
+ function determineTier(requestCount, tiers) {
113
+ const sortedTiers = [...tiers].sort((a, b) => a.upTo - b.upTo);
114
+ for (const tier of sortedTiers) {
115
+ if (tier.upTo === -1 || requestCount <= tier.upTo) {
116
+ return tier;
117
+ }
118
+ }
119
+ return sortedTiers[sortedTiers.length - 1];
120
+ }
121
+ function calculateUsageUnits(context, unit) {
122
+ switch (unit) {
123
+ case "per-request":
124
+ return 1;
125
+ case "per-byte":
126
+ return context.request.contentLength || 0;
127
+ case "per-token":
128
+ return Math.ceil((context.request.contentLength || 0) / 4);
129
+ default:
130
+ return 1;
131
+ }
132
+ }
133
+ function buildPriceResult(amount, asset, network, ttlSeconds = 300, breakdown) {
134
+ return {
135
+ amount: amount.toString(),
136
+ asset,
137
+ network,
138
+ breakdown,
139
+ validUntil: Math.floor(Date.now() / 1e3) + ttlSeconds,
140
+ display: formatPrice(amount, asset)
141
+ };
142
+ }
143
+ var BasePricingStrategy = class {
144
+ constructor(config, decimals = 6) {
145
+ this.config = config;
146
+ this.decimals = decimals;
147
+ }
148
+ generateCacheKey(context) {
149
+ return generateCacheKey(this.type, this.config.id, context);
150
+ }
151
+ validate() {
152
+ const errors = [];
153
+ if (!this.config.parameters) {
154
+ errors.push("Strategy parameters are required");
155
+ }
156
+ if (!this.config.vendorId) {
157
+ errors.push("Vendor ID is required");
158
+ }
159
+ return {
160
+ valid: errors.length === 0,
161
+ errors: errors.length > 0 ? errors : void 0
162
+ };
163
+ }
164
+ getParams() {
165
+ return this.config.parameters;
166
+ }
167
+ getCacheTtl() {
168
+ return this.config.cache?.ttlSeconds ?? 300;
169
+ }
170
+ };
171
+ var FixedPricingStrategy = class extends BasePricingStrategy {
172
+ constructor() {
173
+ super(...arguments);
174
+ this.name = "Fixed Pricing";
175
+ this.type = "fixed";
176
+ }
177
+ async calculatePrice(context) {
178
+ const params = this.getParams();
179
+ const amount = BigInt(params.price);
180
+ return buildPriceResult(
181
+ amount,
182
+ params.asset,
183
+ params.network,
184
+ this.getCacheTtl(),
185
+ [{ component: "Base Price", amount: params.price }]
186
+ );
187
+ }
188
+ validate() {
189
+ const base = super.validate();
190
+ if (!base.valid) return base;
191
+ const errors = [];
192
+ const params = this.getParams();
193
+ if (!params.price || BigInt(params.price) <= 0n) {
194
+ errors.push("Fixed price must be positive");
195
+ }
196
+ if (!params.asset) {
197
+ errors.push("Asset address is required");
198
+ }
199
+ return {
200
+ valid: errors.length === 0,
201
+ errors: errors.length > 0 ? errors : void 0
202
+ };
203
+ }
204
+ };
205
+ var TieredPricingStrategy = class extends BasePricingStrategy {
206
+ constructor() {
207
+ super(...arguments);
208
+ this.name = "Tiered Pricing";
209
+ this.type = "tiered";
210
+ }
211
+ async calculatePrice(context) {
212
+ const params = this.getParams();
213
+ const requestCount = context.user?.usage?.requestCount || 0;
214
+ const tier = determineTier(requestCount, params.tiers);
215
+ if (!tier) {
216
+ throw new Error("No applicable pricing tier found");
217
+ }
218
+ let amount = BigInt(tier.pricePerRequest);
219
+ if (tier.discount && tier.discount > 0) {
220
+ amount = applyDiscount(amount, tier.discount);
221
+ }
222
+ return buildPriceResult(
223
+ amount,
224
+ params.asset,
225
+ params.network,
226
+ this.getCacheTtl(),
227
+ [
228
+ { component: "Base Price", amount: tier.pricePerRequest },
229
+ ...tier.discount ? [{ component: "Tier Discount", amount: `-${tier.discount}%`, percentage: tier.discount }] : []
230
+ ]
231
+ );
232
+ }
233
+ validate() {
234
+ const base = super.validate();
235
+ if (!base.valid) return base;
236
+ const errors = [];
237
+ const params = this.getParams();
238
+ if (!params.tiers || params.tiers.length === 0) {
239
+ errors.push("At least one pricing tier is required");
240
+ }
241
+ return {
242
+ valid: errors.length === 0,
243
+ errors: errors.length > 0 ? errors : void 0
244
+ };
245
+ }
246
+ };
247
+ var UsageBasedPricingStrategy = class extends BasePricingStrategy {
248
+ constructor() {
249
+ super(...arguments);
250
+ this.name = "Usage-Based Pricing";
251
+ this.type = "usage-based";
252
+ }
253
+ async calculatePrice(context) {
254
+ const params = this.getParams();
255
+ const units = calculateUsageUnits(context, params.unit);
256
+ let amount = BigInt(params.pricePerUnit) * BigInt(units);
257
+ if (params.minimumCharge) {
258
+ const min = BigInt(params.minimumCharge);
259
+ if (amount < min) {
260
+ amount = min;
261
+ }
262
+ }
263
+ if (params.maximumCharge) {
264
+ const max = BigInt(params.maximumCharge);
265
+ if (amount > max) {
266
+ amount = max;
267
+ }
268
+ }
269
+ return buildPriceResult(
270
+ amount,
271
+ params.asset,
272
+ params.network,
273
+ this.getCacheTtl(),
274
+ [
275
+ { component: `${units} ${params.unit}`, amount: amount.toString() }
276
+ ]
277
+ );
278
+ }
279
+ };
280
+ var TimeBucketPricingStrategy = class extends BasePricingStrategy {
281
+ constructor() {
282
+ super(...arguments);
283
+ this.name = "Time-Bucket Pricing";
284
+ this.type = "time-bucket";
285
+ }
286
+ async calculatePrice(context) {
287
+ const params = this.getParams();
288
+ const bucket = getTimeBucket(params.timezone, params.peakHours);
289
+ let amount = BigInt(params.basePrice);
290
+ let multiplier = 1;
291
+ switch (bucket) {
292
+ case "peak":
293
+ multiplier = params.peakMultiplier;
294
+ break;
295
+ case "off-peak":
296
+ multiplier = params.offPeakMultiplier;
297
+ break;
298
+ }
299
+ amount = applyMultiplier(amount, multiplier);
300
+ return buildPriceResult(
301
+ amount,
302
+ params.asset,
303
+ params.network,
304
+ this.getCacheTtl(),
305
+ [
306
+ { component: "Base Price", amount: params.basePrice },
307
+ { component: `${bucket} multiplier`, amount: `x${multiplier}` }
308
+ ]
309
+ );
310
+ }
311
+ };
312
+ function createPricingStrategy(config, decimals = 6) {
313
+ switch (config.strategyType) {
314
+ case "fixed":
315
+ return new FixedPricingStrategy(config, decimals);
316
+ case "tiered":
317
+ return new TieredPricingStrategy(config, decimals);
318
+ case "usage-based":
319
+ return new UsageBasedPricingStrategy(config, decimals);
320
+ case "time-bucket":
321
+ return new TimeBucketPricingStrategy(config, decimals);
322
+ default:
323
+ throw new Error(`Unsupported strategy type: ${config.strategyType}`);
324
+ }
325
+ }
326
+ export {
327
+ ASSET_SYMBOLS,
328
+ BasePricingStrategy,
329
+ FixedPricingStrategy,
330
+ TieredPricingStrategy,
331
+ TimeBucketPricingStrategy,
332
+ UsageBasedPricingStrategy,
333
+ applyDiscount,
334
+ applyMultiplier,
335
+ atomicUnitsToUsd,
336
+ buildPriceResult,
337
+ calculateUsageUnits,
338
+ createPricingStrategy,
339
+ determineTier,
340
+ formatPrice,
341
+ generateCacheKey,
342
+ getTimeBucket,
343
+ sortObject,
344
+ usdToAtomicUnits
345
+ };
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@perkos/service-pricing",
3
+ "version": "1.0.0",
4
+ "description": "Dynamic pricing service with strategy patterns for x402 payment protocol",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup src/index.ts --format cjs,esm --dts",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "pricing",
24
+ "dynamic-pricing",
25
+ "x402",
26
+ "payment",
27
+ "strategy-pattern",
28
+ "tiered-pricing",
29
+ "usage-based-pricing"
30
+ ],
31
+ "author": "PerkOS",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/PerkOS-xyz/pkg-service-pricing"
36
+ },
37
+ "dependencies": {
38
+ "@perkos/types-x402": "^1.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "tsup": "^8.5.0",
42
+ "typescript": "^5.8.3"
43
+ }
44
+ }