UNPKG

pricing4ts

Version:

![NPM Version](https://img.shields.io/npm/v/pricing4ts) Pricing4TS is a TypeScript-based toolkit designed to enhance the server-side functionality of a pricing-driven SaaS by enabling the seamless integration of pricing plans into the application logic. T

140 lines (125 loc) 4.57 kB
import { Feature, Pricing, UsageLimit } from '../../types'; import { PricingPlanEvaluationError } from '../exceptions/PricingPlanEvaluationError'; import { retrievePricingFromPath } from '../server'; export type Configuration = Record< 'features' | 'usageLimits', Record<string, Feature | UsageLimit> >; /** * An abstract class from which to create a component that adapts the pricing * configuration to the application domain */ export abstract class PricingContext { /** * Returns path of the pricing configuration YAML file. * This file should be located in the resources folder, and the path should be * relative to it. * * @returns Configuration file path */ abstract getConfigFilePath(): string; /** * Returns the secret used to encode the pricing JWT. * * @returns JWT secret string */ abstract getJwtSecret(): string; /** * Returns the expiration time of the JWT in milliseconds * * @returns JWT expiration time in milliseconds */ getJwtExpiration(): number { return 86400000; } /** * This method can be used to determine which users are affected * by the pricing, so a pricing-driven JWT will be only generated * for them. * * @returns A boolean indicating the condition to include or exclude the pricing evaluation context in the JWT. */ userAffectedByPricing(): boolean { return true; } /** * This method should return the user context that will be used to evaluate the pricing plan. * It should consider which users have accessed the service and what * information is available. * * @returns Map with the user context */ abstract getSubscriptionContext(): Record<string, boolean | string | number>; /** * This method should return the plan name of the current user. * With this information, the library will be able to build the Plan * object of the user from the configuration. * * @returns String with the current user's plan name */ abstract getUserPlan(): string; /** * This method should return the add-ons that the user has * subscribed to. The add-ons are used to extend the features and usage limits * of the user's plan. * * @returns Array of strings with the add-ons names */ abstract getUserAddOns(): string[]; /** * This method returns the plan context of the current user, represented by a * *Record<string, boolean | string | number | PaymentType[]>*. It's used to evaluate the pricing plan. * * @return current user's plan context */ getPlanContext(): Configuration { const userPlan = this.getPricing().plans![this.getUserPlan()]; const pricingAddOns = this.getPricing().addOns ?? {}; const pricingContextToReturn = { features: { ...userPlan.features }, usageLimits: userPlan.usageLimits ? { ...userPlan.usageLimits } : {}, }; for (const addOn of Object.keys(pricingAddOns)) { if (this.getUserAddOns().includes(addOn)) { if (pricingAddOns[addOn].features) { for (const feature of Object.keys(pricingAddOns[addOn].features)) { pricingContextToReturn.features![feature].value = pricingAddOns[addOn].features![feature].value; } } if (pricingAddOns[addOn].usageLimits) { for (const usageLimit of Object.keys(pricingAddOns[addOn].usageLimits)) { pricingContextToReturn.usageLimits![usageLimit].value = pricingAddOns[addOn].usageLimits![usageLimit].value; } } if (pricingAddOns[addOn].usageLimitsExtensions) { for (const usageLimit of Object.keys(pricingAddOns[addOn].usageLimitsExtensions)) { if (pricingContextToReturn.usageLimits![usageLimit].value) { (pricingContextToReturn.usageLimits![usageLimit].value as number) += pricingAddOns[ addOn ].usageLimitsExtensions![usageLimit].value as number; } else { pricingContextToReturn.usageLimits![usageLimit].value = pricingAddOns[addOn].usageLimitsExtensions![usageLimit].value; } } } } } return pricingContextToReturn; } /** * This method returns the PricingManager object that is being used to * evaluate the pricing plan. * * @returns PricingManager object */ getPricing(): Pricing { try { return retrievePricingFromPath(this.getConfigFilePath()); } catch (error) { throw new PricingPlanEvaluationError((error as Error).message); } } }