@freemius/sdk 0.0.6 → 0.2.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/index.d.mts +262 -231
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +262 -231
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +228 -119
- package/dist/index.mjs +226 -120
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["defaultPagingOptions: PagingOptions","client: FsApiClient","discountsMap: Map<string, SubscriptionDiscountEntity[]>","secretKey: string","publicKey: string","crypto","productId: string","publicKey: string","secretKey: string","errorResponse: Record<string, unknown>","pricing: PricingService","purchase: PurchaseService","callback?: PurchaseCallback","requestBody: unknown","secretKey: string","proxyUrl?: string","callback?: RedirectCallback","afterProcessUrl?: string","purchase: PurchaseService","pricing: PricingService","secretKey: string","actionHandlers: CheckoutAction[]","productId: FSId","publicKey: string","secretKey: string","purchase: PurchaseService","pricing: PricingService","api: ApiService","action: CustomerPortalActionService","checkout: CheckoutService","checkoutOptions: CheckoutOptions","portalData: PortalData","plans: Map<string, PlanEntity>","pricings: Map<string, PricingEntity>","portalSubscriptions: PortalSubscriptions","subscriptionData: PortalSubscription","repository: PortalDataRepository","getUser: UserRetriever","endpoint: string","isSandbox?: boolean","purchase: PurchaseService","user: UserRetriever","callback?: RestoreCallback","subscriptionOnly: boolean","purchases: PurchaseInfo[] | null","repository: PortalDataRepository","action: CustomerPortalActionService","purchase: PurchaseService","actionHandlers: PortalAction[]","schema","api: ApiService","auth: AuthService","requestBody: unknown","payload: BillingUpdatePayload","api: ApiService","auth: AuthService","schema","schema","api: ApiService","auth: AuthService","requestBody: unknown","api: ApiService","auth: AuthService","requestBody: unknown","api: ApiService","authService: AuthService","api: ApiService","checkout: CheckoutService","authService: AuthService","purchase: PurchaseService","secretKey: string","onError: (error: unknown) => void","evt: EventEntity","secretKey: string","chunks: Buffer[]","productId: FSId","secretKey: string","api: ApiService","plans: PricingData['plans']","api: ApiService"],"sources":["../src/contracts/types.ts","../src/api/parser.ts","../package.json","../src/api/client.ts","../src/api/ApiBase.ts","../src/api/License.ts","../src/api/Product.ts","../src/api/Subscription.ts","../src/api/User.ts","../src/api/Payment.ts","../src/utils/ops.ts","../src/services/ApiService.ts","../src/checkout/Checkout.ts","../src/errors/ActionError.ts","../src/checkout/PricingRetriever.ts","../src/checkout/PurchaseProcessor.ts","../src/models/CheckoutRedirectInfo.ts","../src/checkout/RedirectProcessor.ts","../src/checkout/CheckoutRequestProcessor.ts","../src/services/CheckoutService.ts","../src/customer-portal/PortalDataRepository.ts","../src/customer-portal/PortalDataRetriever.ts","../src/customer-portal/PurchaseRestorer.ts","../src/customer-portal/PortalRequestProcessor.ts","../src/customer-portal/BillingAction.ts","../src/customer-portal/InvoiceAction.ts","../src/customer-portal/SubscriptionCancellationAction.ts","../src/customer-portal/SubscriptionRenewalCouponAction.ts","../src/customer-portal/CustomerPortalActionService.ts","../src/services/CustomerPortalService.ts","../src/webhook/WebhookListener.ts","../src/services/WebhookService.ts","../src/services/AuthService.ts","../src/services/PricingService.ts","../src/models/PurchaseInfo.ts","../src/services/PurchaseService.ts","../src/services/EntitlementService.ts","../src/Freemius.ts"],"sourcesContent":["import { FSId } from '../api/types';\n\n/**\n * This file holds all generic types used across the SDK, not specific to any contract.\n */\nexport enum BILLING_CYCLE {\n MONTHLY = 'monthly',\n YEARLY = 'yearly',\n ONEOFF = 'oneoff',\n}\n\nexport interface PagingOptions {\n count?: number;\n offset?: number;\n}\n\nexport type ApiEntitiesFilter<T> = Omit<NonNullable<T>, 'count' | 'offset'>;\n\nexport enum CURRENCY {\n USD = 'USD',\n EUR = 'EUR',\n GBP = 'GBP',\n}\n\nexport type PaymentMethod = 'card' | 'paypal' | 'ideal';\n\nexport type UserRetriever = () => Promise<\n { id: FSId; primaryLicenseId?: FSId; email?: string } | { email: string } | null\n>;\n\n/**\n * @todo - Add a more unified way to get handlers so that we can simplify the Checkout & Customer Portal request processors.\n */\nexport interface RequestProcessor<Config> {\n createProcessor(config: Config): (request: Request) => Promise<Response>;\n\n process(config: Config, request: Request): Promise<Response>;\n}\n","import { BillingCycleApiEnum, CurrencyApiEnum, FSId } from './types';\nimport { BILLING_CYCLE, CURRENCY, PaymentMethod } from '../contracts/types';\n\nexport function idToNumber(id: FSId): number {\n if (typeof id === 'number') {\n return id;\n } else if (typeof id === 'bigint') {\n return Number(id);\n } else if (typeof id === 'string') {\n const parsed = Number.parseInt(id, 10);\n\n if (Number.isNaN(parsed)) {\n throw new Error(`Invalid FSId: ${id}`);\n }\n\n return parsed;\n } else {\n throw new Error(`Unsupported FSId type: ${typeof id}`);\n }\n}\n\nexport function idToString(id: FSId): string {\n if (typeof id === 'string') {\n return id;\n } else if (typeof id === 'number' || typeof id === 'bigint') {\n return String(id);\n } else {\n throw new Error(`Unsupported FSId type: ${typeof id}`);\n }\n}\n\nexport function isIdsEqual(id1: FSId, id2: FSId): boolean {\n return idToString(id1) === idToString(id2);\n}\n\nexport function parseBillingCycle(cycle?: BillingCycleApiEnum): BILLING_CYCLE {\n const billingCycle = Number.parseInt(cycle?.toString() ?? '', 10);\n\n if (billingCycle === 1) {\n return BILLING_CYCLE.MONTHLY;\n }\n\n if (billingCycle === 12) {\n return BILLING_CYCLE.YEARLY;\n }\n\n return BILLING_CYCLE.ONEOFF;\n}\n\nexport function parseNumber(value: unknown): number | null {\n if (typeof value === 'number') {\n return value;\n } else if (typeof value === 'string') {\n const parsed = Number.parseFloat(value);\n return Number.isNaN(parsed) ? null : parsed;\n } else {\n return null;\n }\n}\n\nexport function parseDateTime(dateString?: string | null): Date | null {\n if (!dateString) {\n return null;\n }\n\n // Freemius date format is \"YYYY-MM-DD HH:mm:ss\" in UTC\n const dateParts = dateString.split(' ');\n if (dateParts.length !== 2) {\n return null;\n }\n\n const date = dateParts[0]!.split('-');\n const time = dateParts[1]!.split(':');\n if (date.length !== 3 || time.length !== 3) {\n return null;\n }\n\n const year = Number.parseInt(date[0]!);\n const month = Number.parseInt(date[1]!) - 1; // Months are zero-based\n const day = Number.parseInt(date[2]!);\n const hours = Number.parseInt(time[0]!);\n const minutes = Number.parseInt(time[1]!);\n const seconds = Number.parseInt(time[2]!);\n\n if (\n Number.isNaN(year) ||\n Number.isNaN(month) ||\n Number.isNaN(day) ||\n Number.isNaN(hours) ||\n Number.isNaN(minutes) ||\n Number.isNaN(seconds)\n ) {\n return null;\n }\n\n const utcDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds, 0));\n\n return utcDate;\n}\n\nexport function parseDate(dateString?: string | null): Date | null {\n if (!dateString) {\n return null;\n }\n\n // Freemius date format is \"YYYY-MM-DD\"\n return parseDateTime(dateString + ' 00:00:00');\n}\n\nexport function parseCurrency(currency?: CurrencyApiEnum): CURRENCY | null {\n switch (currency?.toLowerCase?.()) {\n case 'usd':\n return CURRENCY.USD;\n case 'eur':\n return CURRENCY.EUR;\n case 'gbp':\n return CURRENCY.GBP;\n default:\n return null;\n }\n}\n\nexport function parsePaymentMethod(gateway?: string | null): PaymentMethod | null {\n return gateway?.startsWith('stripe') ? 'card' : gateway?.startsWith('paypal') ? 'paypal' : null;\n}\n","{\n \"name\": \"@freemius/sdk\",\n \"version\": \"0.0.6\",\n \"description\": \"JS SDK for integrating your SaaS with Freemius\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"import\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n }\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"dev\": \"tsdown --watch\",\n \"clean\": \"rm -rf dist\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"echo \\\"No tests yet\\\"\",\n \"openapi:generate\": \"openapi-typescript https://freemius.com/help/documentation/api/openapi.yaml -o ./src/api/schema.d.ts\",\n \"openapi:generate:local\": \"openapi-typescript http://api-doc.freemius-local.com:8080/openapi.yaml -o ./src/api/schema.d.ts\",\n \"prepublish\": \"npm run build\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+ssh://git@github.com/Freemius/freemius-js.git\"\n },\n \"keywords\": [\n \"node.js\",\n \"freemius\",\n \"sdk\",\n \"saas\"\n ],\n \"author\": \"Freemius Inc\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/Freemius/freemius-js/issues\"\n },\n \"homepage\": \"http://freemius.com/help/documentation/saas-sdk/js-sdk/\",\n \"dependencies\": {\n \"openapi-fetch\": \"^0.14.0\"\n },\n \"peerDependencies\": {\n \"zod\": \"^4.0.0\",\n \"@freemius/checkout\": \"^1.3.1\"\n },\n \"devDependencies\": {\n \"zod\": \"^4.0.0\",\n \"@types/node\": \"^24.2.0\",\n \"tsdown\": \"^0.14.1\",\n \"typescript\": \"^5.9.2\"\n }\n}\n","import createClient from 'openapi-fetch';\nimport type { paths } from './schema';\nimport { version } from '../../package.json';\n\nfunction detectPlatform(): string {\n // Check for Bun runtime\n if (typeof globalThis !== 'undefined' && 'Bun' in globalThis) return 'Bun';\n\n // Check for Deno runtime\n if (typeof globalThis !== 'undefined' && 'Deno' in globalThis) return 'Deno';\n\n // Check for Node.js\n if (\n typeof globalThis !== 'undefined' &&\n 'process' in globalThis &&\n globalThis.process &&\n typeof globalThis.process === 'object' &&\n 'versions' in globalThis.process &&\n globalThis.process.versions &&\n 'node' in globalThis.process.versions\n ) {\n return 'Node';\n }\n\n // Check for browser environment\n if (typeof globalThis !== 'undefined' && 'window' in globalThis) return 'Browser';\n\n return 'Unknown';\n}\n\nexport function createApiClient(baseUrl: string, bearerToken?: string) {\n const platform = detectPlatform();\n\n const client = createClient<paths>({\n baseUrl,\n headers: {\n Authorization: `Bearer ${bearerToken}`,\n 'User-Agent': `Freemius-JS-SDK/${version} (${platform})`,\n },\n });\n\n return client;\n}\n\nexport type FsApiClient = ReturnType<typeof createApiClient>;\n","import { PagingOptions } from '../contracts/types';\nimport { idToNumber } from './parser';\nimport { FsApiClient } from './client';\nimport { FSId } from './types';\nimport { QuerySerializerOptions } from 'openapi-fetch';\n\nexport const PAGING_MAX_LIMIT = 50;\nexport const PAGING_DEFAULT_LIMIT = PAGING_MAX_LIMIT;\n\nexport const defaultPagingOptions: PagingOptions = {\n count: PAGING_DEFAULT_LIMIT,\n offset: 0,\n};\n\nexport abstract class ApiBase<EntityType, FilterType extends Record<string, unknown> = Record<string, unknown>> {\n public readonly productId: number;\n\n constructor(\n productId: FSId,\n protected readonly client: FsApiClient\n ) {\n this.productId = idToNumber(productId);\n }\n\n abstract retrieve(id: FSId): Promise<EntityType | null>;\n\n abstract retrieveMany(filter?: FilterType, pagination?: PagingOptions): Promise<EntityType[]>;\n\n /**\n * Async generator that yields all entities by paginating through retrieveMany.\n * @param filter Optional filter for entities\n * @param pageSize Optional page size (default: PAGING_DEFAULT_LIMIT)\n *\n * @example\n * // Usage example:\n * for await (const entity of apiInstance.iterateAll({ status: 'active' })) {\n * console.log(entity);\n * }\n */\n async *iterateAll(\n filter?: FilterType,\n pageSize: number = PAGING_DEFAULT_LIMIT\n ): AsyncGenerator<EntityType, void, unknown> {\n let offset = 0;\n\n while (true) {\n const page = await this.retrieveMany(filter, { count: pageSize, offset });\n\n if (!page.length) {\n break;\n }\n\n for (const entity of page) {\n yield entity;\n }\n\n if (page.length < pageSize) {\n break;\n }\n\n offset += page.length;\n }\n }\n\n // Helper methods\n\n getPagingParams(paging: PagingOptions = defaultPagingOptions): { count: number; offset: number } {\n return {\n count: paging.count ?? PAGING_DEFAULT_LIMIT,\n offset: paging.offset ?? 0,\n };\n }\n\n getIdForPath(id: FSId): number {\n return idToNumber(id);\n }\n\n isGoodResponse(response: Response): boolean {\n return response.status >= 200 && response.status < 300;\n }\n\n /**\n * @note - We must use this serializer when sending arrays as query parameter to our API.\n */\n getQuerySerializerForArray(): QuerySerializerOptions {\n return {\n array: {\n explode: false,\n style: 'form',\n },\n };\n }\n}\n","import { PagingOptions } from '../contracts/types';\nimport { ApiBase } from './ApiBase';\nimport { LicenseEntity, LicenseFilterOptions, SubscriptionEntity, FSId } from './types';\n\nexport class License extends ApiBase<LicenseEntity, LicenseFilterOptions> {\n async retrieve(licenseId: FSId) {\n const licenseResponse = await this.client.GET(`/products/{product_id}/licenses/{license_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n license_id: this.getIdForPath(licenseId),\n },\n },\n });\n\n if (!this.isGoodResponse(licenseResponse.response) || !licenseResponse.data || !licenseResponse.data.id) {\n return null;\n }\n\n return licenseResponse.data;\n }\n\n async retrieveMany(filter?: LicenseFilterOptions, pagination?: PagingOptions) {\n const response = await this.client.GET(`/products/{product_id}/licenses.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n ...this.getPagingParams(pagination),\n ...(filter ?? {}),\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data || !Array.isArray(response.data.licenses)) {\n return [];\n }\n\n return response.data.licenses;\n }\n\n async retrieveSubscription(licenseId: FSId): Promise<SubscriptionEntity | null> {\n const subscriptionResponse = await this.client.GET(\n `/products/{product_id}/licenses/{license_id}/subscription.json`,\n {\n params: {\n path: {\n product_id: this.productId,\n license_id: this.getIdForPath(licenseId),\n },\n },\n }\n );\n\n if (\n !this.isGoodResponse(subscriptionResponse.response) ||\n !subscriptionResponse.data ||\n !subscriptionResponse.data.id\n ) {\n return null;\n }\n\n return subscriptionResponse.data;\n }\n\n async retrieveCheckoutUpgradeAuthorization(licenseId: FSId): Promise<string | null> {\n const response = await this.client.POST(`/products/{product_id}/licenses/{license_id}/checkout/link.json`, {\n params: {\n path: {\n product_id: this.productId,\n license_id: this.getIdForPath(licenseId),\n },\n },\n body: {\n is_payment_method_update: true,\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data || !response.data.settings) {\n return null;\n }\n\n return response.data.settings.authorization as string;\n }\n}\n","import { ApiBase } from './ApiBase';\nimport { ProductEntity, PricingTableData, CouponEntityEnriched } from './types';\n\nexport class Product extends ApiBase<ProductEntity, never> {\n async retrieve(): Promise<ProductEntity | null> {\n const response = await this.client.GET(`/products/{product_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data) {\n return null;\n }\n\n return response.data;\n }\n\n async retrieveMany(): Promise<ProductEntity[]> {\n throw new Error('retrieveMany is not supported for Product API');\n }\n\n async retrievePricingData(): Promise<PricingTableData | null> {\n const response = await this.client.GET(`/products/{product_id}/pricing.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data) {\n return null;\n }\n\n return response.data;\n }\n\n async retrieveSubscriptionCancellationCoupon(): Promise<CouponEntityEnriched[] | null> {\n const response = await this.client.GET(`/products/{product_id}/coupons/special.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n type: 'subscription_cancellation',\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data || !response.data.coupon_entities) {\n return null;\n }\n\n return response.data.coupon_entities;\n }\n}\n","import { PagingOptions } from '../contracts/types';\nimport { ApiBase } from './ApiBase';\nimport {\n SubscriptionEntity,\n SubscriptionFilterOptions,\n FSId,\n SubscriptionCancellationReasonType,\n SubscriptionCancellationResult,\n SubscriptionRenewalCouponResult,\n} from './types';\n\nexport class Subscription extends ApiBase<SubscriptionEntity, SubscriptionFilterOptions> {\n async retrieve(subscriptionId: FSId) {\n const result = await this.client.GET(`/products/{product_id}/subscriptions/{subscription_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n subscription_id: this.getIdForPath(subscriptionId),\n },\n },\n });\n\n if (!this.isGoodResponse(result.response) || !result.data || !result.data.id) {\n return null;\n }\n\n return result.data;\n }\n\n async retrieveMany(filter?: SubscriptionFilterOptions, pagination?: PagingOptions) {\n const result = await this.client.GET(`/products/{product_id}/subscriptions.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n ...this.getPagingParams(pagination),\n ...(filter ?? {}),\n },\n },\n });\n\n if (!this.isGoodResponse(result.response) || !result.data || !Array.isArray(result.data.subscriptions)) {\n return [];\n }\n\n return result.data.subscriptions;\n }\n\n async applyRenewalCoupon(\n subscriptionId: FSId,\n couponId: string,\n logAutoRenew: boolean\n ): Promise<SubscriptionRenewalCouponResult | null> {\n const result = await this.client.PUT(`/products/{product_id}/subscriptions/{subscription_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n subscription_id: this.getIdForPath(subscriptionId),\n },\n },\n body: {\n auto_renew: logAutoRenew,\n coupon_id: Number.parseInt(couponId, 10),\n },\n });\n\n if (!this.isGoodResponse(result.response) || !result.data || !result.data.id) {\n return null;\n }\n\n return result.data;\n }\n\n async cancel(\n subscriptionId: FSId,\n feedback?: string,\n reasonIds?: SubscriptionCancellationReasonType[]\n ): Promise<SubscriptionCancellationResult | null> {\n const result = await this.client.DELETE(`/products/{product_id}/subscriptions/{subscription_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n subscription_id: this.getIdForPath(subscriptionId),\n },\n query: {\n reason: feedback,\n reason_ids: reasonIds ?? [],\n },\n },\n querySerializer: this.getQuerySerializerForArray(),\n });\n\n if (!this.isGoodResponse(result.response) || !result.data || !result.data.id) {\n return null;\n }\n\n return result.data;\n }\n}\n","import { PagingOptions } from '../contracts/types';\nimport { ApiBase } from './ApiBase';\nimport { idToString } from './parser';\nimport {\n LicenseEntity,\n PaymentEntity,\n UserBillingEntity,\n UserEntity,\n UserFilterOptions,\n FSId,\n UserSubscriptionFilterOptions,\n UserLicenseFilterOptions,\n UserPaymentFilterOptions,\n BillingUpdatePayload,\n UserSubscriptionWithDiscounts,\n SubscriptionDiscountEntity,\n} from './types';\n\nconst USER_FIELDS = 'email,first,last,picture,is_verified,id,created,updated,is_marketing_allowed';\n\nexport class User extends ApiBase<UserEntity, UserFilterOptions> {\n async retrieve(userId: FSId) {\n const userResponse = await this.client.GET(`/products/{product_id}/users/{user_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n user_id: this.getIdForPath(userId),\n },\n query: {\n fields: USER_FIELDS,\n },\n },\n });\n\n if (userResponse.response.status !== 200 || !userResponse.data || !userResponse.data.id) {\n return null;\n }\n\n return userResponse.data;\n }\n\n async retrieveMany(filter?: UserFilterOptions, pagination?: PagingOptions) {\n const response = await this.client.GET(`/products/{product_id}/users.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n ...this.getPagingParams(pagination),\n ...(filter ?? {}),\n fields: USER_FIELDS,\n },\n },\n });\n\n if (response.response.status !== 200 || !response.data || !Array.isArray(response.data.users)) {\n return [];\n }\n\n return response.data.users;\n }\n\n async retrieveByEmail(email: string): Promise<UserEntity | null> {\n const response = await this.client.GET(`/products/{product_id}/users.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n email,\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !Array.isArray(response.data?.users)) {\n return null;\n }\n\n return response.data.users?.[0] ?? null;\n }\n\n async retrieveBilling(userId: FSId): Promise<UserBillingEntity | null> {\n const billingResponse = await this.client.GET(`/products/{product_id}/users/{user_id}/billing.json`, {\n params: {\n path: {\n product_id: this.productId,\n user_id: this.getIdForPath(userId),\n },\n },\n });\n\n if (billingResponse.response.status !== 200 || !billingResponse.data || !billingResponse.data) {\n return null;\n }\n\n return billingResponse.data;\n }\n\n async retrieveSubscriptions(\n userId: FSId,\n filters?: UserSubscriptionFilterOptions,\n pagination?: PagingOptions\n ): Promise<UserSubscriptionWithDiscounts[]> {\n const result = await this.client.GET(`/products/{product_id}/users/{user_id}/subscriptions.json`, {\n params: {\n path: {\n product_id: this.productId,\n user_id: this.getIdForPath(userId),\n },\n query: {\n ...(filters ?? {}),\n ...this.getPagingParams(pagination),\n },\n },\n });\n\n if (!this.isGoodResponse(result.response) || !result.data || !Array.isArray(result.data.subscriptions)) {\n return [];\n }\n\n const discountsMap: Map<string, SubscriptionDiscountEntity[]> = new Map();\n\n if (result.data.discounts) {\n Object.entries(result.data.discounts).forEach(([subscriptionId, discounts]) => {\n discountsMap.set(idToString(subscriptionId), discounts);\n });\n }\n\n return result.data.subscriptions.map((subscription) => ({\n ...subscription,\n discounts: discountsMap.get(idToString(subscription.id!)) || [],\n }));\n }\n\n async retrieveLicenses(\n userId: FSId,\n filters?: UserLicenseFilterOptions,\n pagination?: PagingOptions\n ): Promise<LicenseEntity[]> {\n const response = await this.client.GET(`/products/{product_id}/users/{user_id}/licenses.json`, {\n params: {\n path: {\n product_id: this.productId,\n user_id: this.getIdForPath(userId),\n },\n query: {\n ...(filters ?? {}),\n ...this.getPagingParams(pagination),\n },\n },\n });\n\n if (response.response.status !== 200 || !response.data || !Array.isArray(response.data.licenses)) {\n return [];\n }\n\n return response.data.licenses;\n }\n\n async retrievePayments(\n userId: FSId,\n filters?: UserPaymentFilterOptions,\n pagination?: PagingOptions\n ): Promise<PaymentEntity[]> {\n const response = await this.client.GET(`/products/{product_id}/users/{user_id}/payments.json`, {\n params: {\n path: {\n product_id: this.productId,\n user_id: this.getIdForPath(userId),\n },\n query: {\n ...(filters ?? {}),\n ...this.getPagingParams(pagination),\n },\n },\n });\n\n if (response.response.status !== 200 || !response.data || !Array.isArray(response.data.payments)) {\n return [];\n }\n\n return response.data.payments;\n }\n\n async updateBilling(userId: FSId, payload: BillingUpdatePayload): Promise<UserBillingEntity | null> {\n const response = await this.client.PUT(`/products/{product_id}/users/{user_id}/billing.json`, {\n params: {\n path: {\n product_id: this.productId,\n user_id: this.getIdForPath(userId),\n },\n },\n body: payload,\n });\n\n if (!this.isGoodResponse(response.response) || !response.data || !response.data) {\n return null;\n }\n\n return response.data;\n }\n}\n","import { PagingOptions } from '../contracts/types';\nimport { ApiBase } from './ApiBase';\nimport { FSId, PaymentEntity, PaymentFilterOptions } from './types';\n\nexport class Payment extends ApiBase<PaymentEntity, PaymentFilterOptions> {\n async retrieve(paymentId: FSId): Promise<PaymentEntity | null> {\n const response = await this.client.GET(`/products/{product_id}/payments/{payment_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n payment_id: this.getIdForPath(paymentId),\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data || !response.data.id) {\n return null;\n }\n\n return response.data;\n }\n\n async retrieveMany(filter?: PaymentFilterOptions, pagination?: PagingOptions) {\n const response = await this.client.GET(`/products/{product_id}/payments.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n ...this.getPagingParams(pagination),\n ...(filter ?? {}),\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data || !Array.isArray(response.data.payments)) {\n return [];\n }\n\n return response.data.payments;\n }\n\n async retrieveInvoice(paymentId: FSId) {\n const response = await this.client.GET(`/products/{product_id}/payments/{payment_id}/invoice.pdf`, {\n params: {\n path: {\n payment_id: this.getIdForPath(paymentId),\n product_id: this.productId,\n },\n },\n parseAs: 'blob', // Expecting a PDF blob response\n });\n\n if (!this.isGoodResponse(response.response) || !response.data) {\n return null;\n }\n\n return response.data as Blob; // Assuming the response is a PDF blob\n }\n}\n","export function splitName(name: string): { firstName: string; lastName: string } {\n const parts = name.split(' ');\n\n return {\n firstName: parts[0] ?? '',\n lastName: parts.slice(1).join(' ') ?? '',\n };\n}\n\nexport function isTestServer(): boolean {\n return process.env.FREEMIUS_INTERNAL__IS_DEVELOPMENT_MODE === 'true';\n}\n","import * as crypto from 'crypto';\nimport { ApiAuthParams, FSId } from '../api/types';\nimport { FsApiClient, createApiClient } from '../api/client';\nimport { License } from '../api/License';\nimport { Product } from '../api/Product';\nimport { Subscription } from '../api/Subscription';\nimport { User } from '../api/User';\nimport { idToString } from '../api/parser';\nimport { Payment } from '../api/Payment';\nimport { isTestServer } from '../utils/ops';\n\nconst API_ENDPOINT_PRODUCTION = 'https://api.freemius.com/v1/';\nconst API_ENDPOINT_TEST = 'http://api.freemius-local.com:8080/v1/';\n\n/**\n * @todo - Add a proper user-agent string with SDK version.\n */\nexport class ApiService {\n private readonly client: FsApiClient;\n\n public readonly productId: string;\n\n public readonly user: User;\n\n public readonly license: License;\n\n public readonly product: Product;\n\n public readonly subscription: Subscription;\n\n public readonly payment: Payment;\n\n public readonly baseUrl: string;\n\n constructor(\n productId: FSId,\n apiKey: string,\n private readonly secretKey: string,\n private readonly publicKey: string\n ) {\n this.baseUrl = isTestServer() ? API_ENDPOINT_TEST : API_ENDPOINT_PRODUCTION;\n\n this.client = createApiClient(this.baseUrl, apiKey);\n this.productId = idToString(productId);\n\n this.user = new User(this.productId, this.client);\n this.license = new License(this.productId, this.client);\n this.product = new Product(this.productId, this.client);\n this.subscription = new Subscription(this.productId, this.client);\n this.payment = new Payment(this.productId, this.client);\n }\n\n /**\n * Low level API client for direct access to the Freemius API.\n * Use this for advanced use cases where you need to make custom API calls.\n *\n * For regular operations, prefer using the provided services like `User`, `Subscription`, `License` etc.\n */\n get __unstable_ApiClient(): FsApiClient {\n return this.client;\n }\n\n public createSignedUrl(path: string): string {\n return this.getSignedUrl(this.createUrl(path));\n }\n\n public createUrl(path: string): string {\n // Trim leading slashes to avoid double slashes in the URL\n path = path.replace(/^\\/+/, '');\n\n return `${this.baseUrl}products/${this.productId}/${path}`;\n }\n\n /**\n * Generate signed URL for the given full URL. The authentication is valid for 15 minutes only.\n */\n public getSignedUrl(fullUrl: string): string {\n const url = new URL(fullUrl);\n const resourcePath = url.pathname;\n\n const auth = this.generateAuthorizationParams(resourcePath);\n\n // Build query parameters\n url.searchParams.set('auth_date', auth.date);\n url.searchParams.set('authorization', auth.authorization);\n\n return url.toString();\n }\n\n /**\n * Generate authorization parameters for signing\n */\n public generateAuthorizationParams(\n resourcePath: string,\n method: 'POST' | 'PUT' | 'GET' | 'DELETE' = 'GET',\n jsonEncodedParams: string = '',\n contentType: string = ''\n ): ApiAuthParams {\n const eol = '\\n';\n let contentMd5 = '';\n const date = this.toDateTimeString(new Date());\n\n if (['POST', 'PUT'].includes(method) && jsonEncodedParams) {\n contentMd5 = crypto.createHash('md5').update(jsonEncodedParams).digest('hex');\n }\n\n const stringToSign = [method, contentMd5, contentType, date, resourcePath].join(eol);\n\n // If secret and public keys are identical, it means that\n // the signature uses public key hash encoding.\n const authType = this.secretKey !== this.publicKey ? 'FS' : 'FSP';\n\n const signature = crypto.createHmac('sha256', this.secretKey).update(stringToSign).digest('hex');\n const base64 = this.base64UrlEncode(signature);\n\n return {\n date,\n authorization: `${authType} ${this.productId}:${this.publicKey}:${base64}`,\n };\n }\n\n /**\n * Base64 encoding that doesn't need to be urlencode()ed.\n * Exactly the same as base64_encode except it uses\n * - instead of +\n * _ instead of /\n */\n private base64UrlEncode(input: string): string {\n return Buffer.from(input, 'utf8').toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, ''); // Remove padding\n }\n\n private toDateTimeString(date: Date): string {\n // Format date as \"YYYY-MM-DD HH:mm:ss\"\n const year = date.getUTCFullYear();\n const month = String(date.getUTCMonth() + 1).padStart(2, '0'); // Months are zero-based\n const day = String(date.getUTCDate()).padStart(2, '0');\n const hours = String(date.getUTCHours()).padStart(2, '0');\n const minutes = String(date.getUTCMinutes()).padStart(2, '0');\n const seconds = String(date.getUTCSeconds()).padStart(2, '0');\n\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;\n }\n}\n","import {\n CheckoutOptions,\n CheckoutPopupParams,\n convertCheckoutOptionsToQueryParams,\n buildFreemiusQueryFromOptions,\n} from '@freemius/checkout';\nimport { createHash } from 'crypto';\nimport { isTestServer, splitName } from '../utils/ops';\nimport { CheckoutBuilderUserOptions } from '../contracts/checkout';\n\nexport type CheckoutSerialized = {\n options: CheckoutOptions;\n link: string;\n baseUrl: string;\n};\n\n/**\n * A builder class for constructing checkout parameters. This class provides a fluent\n * API to create Checkout parameters for a product with various configurations.\n *\n * Every method returns the existing instance of the builder for chainability,\n * The final `getOptions()` method returns the constructed `CheckoutOptions` object.\n */\nexport class Checkout {\n static createSandboxToken(\n productId: string,\n secretKey: string,\n publicKey: string\n ): NonNullable<CheckoutPopupParams['sandbox']> {\n const timestamp = Math.floor(Date.now() / 1000).toString();\n const token = `${timestamp}${productId}${secretKey}${publicKey}checkout`;\n\n return {\n ctx: timestamp,\n token: createHash('md5').update(token).digest('hex'),\n };\n }\n\n private options: CheckoutOptions;\n\n constructor(\n private readonly productId: string,\n private readonly publicKey: string,\n private readonly secretKey: string\n ) {\n this.options = { product_id: productId };\n }\n\n /**\n * Enables sandbox mode for testing purposes.\n *\n * @returns A new builder instance with sandbox configuration\n */\n setSandbox(): Checkout {\n this.options = {\n ...this.options,\n sandbox: Checkout.createSandboxToken(this.productId, this.secretKey!, this.publicKey!),\n };\n\n return this;\n }\n\n /**\n * Sets user information for the checkout session.\n *\n * @param user User object with email and optional name fields. The shape matches the session from `better-auth` or next-auth packages. Also handles `null` or `undefined` gracefully.\n * @param readonly If true, the user information will be read-only in the checkout session.\n *\n * @returns A new builder instance with user configuration\n */\n setUser(user: CheckoutBuilderUserOptions, readonly: boolean = true): Checkout {\n if (!user) {\n return this;\n }\n\n let firstName = user.firstName ?? '';\n let lastName = user.lastName ?? '';\n\n if (user.name) {\n const { firstName: fn, lastName: ln } = splitName(user.name);\n firstName = fn;\n lastName = ln;\n }\n\n this.options = {\n ...this.options,\n user_email: user.email,\n user_firstname: firstName,\n user_lastname: lastName,\n readonly_user: readonly,\n };\n\n return this;\n }\n\n /**\n * Applies recommended UI settings for better user experience.\n * This includes fullscreen mode, upsells, refund badge, and reviews display.\n *\n * @returns A new builder instance with recommended UI settings\n */\n setRecommendations(): Checkout {\n this.options = {\n ...this.options,\n fullscreen: true,\n show_refund_badge: true,\n show_reviews: true,\n locale: 'auto',\n currency: 'auto',\n };\n\n return this;\n }\n\n /**\n * Sets the plan ID for the checkout.\n *\n * @param planId The plan ID to purchase\n * @returns A new builder instance with plan ID set\n */\n setPlan(planId: string | number): Checkout {\n this.options = {\n ...this.options,\n plan_id: planId.toString(),\n };\n\n return this;\n }\n\n /**\n * Sets the number of licenses to purchase.\n *\n * @param count Number of licenses\n * @returns A new builder instance with license count set\n */\n setQuota(count: number): Checkout {\n this.options = {\n ...this.options,\n licenses: count,\n };\n\n return this;\n }\n\n setPricing(pricingId: string | number): Checkout {\n this.options = {\n ...this.options,\n pricing_id: pricingId.toString(),\n };\n\n return this;\n }\n\n setTitle(title: string): Checkout {\n this.options = {\n ...this.options,\n title,\n };\n\n return this;\n }\n\n /**\n * Sets a coupon code for the checkout.\n *\n * @param coupon The coupon code to apply\n * @param hideUI Whether to hide the coupon input field from users\n * @returns A new builder instance with coupon configuration\n */\n setCoupon(options: { code: string; hideUI?: boolean }): Checkout {\n const { code: coupon, hideUI = false } = options;\n\n this.options = {\n ...this.options,\n coupon,\n hide_coupon: hideUI,\n };\n\n return this;\n }\n\n /**\n * Enables trial mode for the checkout.\n *\n * @param mode Trial type - true/false for plan default, or specific 'free'/'paid' mode\n * @returns A new builder instance with trial configuration\n */\n setTrial(mode: 'free' | 'paid' | boolean = true): Checkout {\n this.options = {\n ...this.options,\n trial: mode,\n };\n\n return this;\n }\n\n /**\n * Configures the visual layout and appearance of the checkout.\n *\n * @param options Appearance configuration options\n * @returns A new builder instance with appearance configuration\n */\n setAppearance(options: {\n layout?: 'vertical' | 'horizontal' | null;\n formPosition?: 'left' | 'right';\n fullscreen?: boolean;\n modalTitle?: string;\n id?: string; // Custom body ID for CSS targeting\n }): Checkout {\n this.options = { ...this.options };\n\n if (options.layout !== undefined) {\n this.options.layout = options.layout;\n }\n if (options.formPosition !== undefined) {\n this.options.form_position = options.formPosition;\n }\n if (options.fullscreen !== undefined) {\n this.options.fullscreen = options.fullscreen;\n }\n if (options.modalTitle !== undefined) {\n this.options.modal_title = options.modalTitle;\n }\n\n if (options.id !== undefined) {\n this.options.id = options.id;\n }\n\n return this;\n }\n\n /**\n * Configures discount display settings.\n *\n * @param options Discount configuration options\n * @returns A new builder instance with discount configuration\n */\n setDiscounts(options: {\n annual?: boolean;\n multisite?: boolean | 'auto';\n bundle?: boolean | 'maximize';\n showMonthlySwitch?: boolean;\n }): Checkout {\n this.options = { ...this.options };\n\n if (options.annual !== undefined) {\n this.options.annual_discount = options.annual;\n }\n if (options.multisite !== undefined) {\n this.options.multisite_discount = options.multisite;\n }\n if (options.bundle !== undefined) {\n this.options.bundle_discount = options.bundle;\n }\n if (options.showMonthlySwitch !== undefined) {\n this.options.show_monthly_switch = options.showMonthlySwitch;\n }\n\n return this;\n }\n\n /**\n * Configures billing cycle selector interface.\n *\n * @param selector Type of billing cycle selector to show\n * @param defaultCycle Default billing cycle to select\n * @returns A new builder instance with billing cycle configuration\n */\n setBillingCycle(\n defaultCycle: 'monthly' | 'annual' | 'lifetime',\n selector?: 'list' | 'responsive_list' | 'dropdown'\n ): Checkout {\n this.options = { ...this.options };\n\n if (selector !== undefined) {\n this.options.billing_cycle_selector = selector;\n }\n if (defaultCycle !== undefined) {\n this.options.billing_cycle = defaultCycle;\n }\n\n return this;\n }\n\n /**\n * Sets the language/locale for the checkout.\n *\n * @param locale Language setting - 'auto', 'auto-beta', or specific locale like 'en_US'\n * @returns A new builder instance with locale configuration\n */\n setLanguage(locale: 'auto' | 'auto-beta' | string = 'auto'): Checkout {\n this.options = {\n ...this.options,\n language: locale,\n };\n\n return this;\n }\n\n /**\n * Configures review and badge display settings.\n *\n * @param options Review and badge configuration\n * @returns A new builder instance with reviews and badges configuration\n */\n setSocialProofing(options: {\n showReviews?: boolean;\n reviewId?: number;\n showRefundBadge?: boolean;\n refundPolicyPosition?: 'below_form' | 'below_breakdown' | 'dynamic';\n }): Checkout {\n this.options = { ...this.options };\n\n if (options.showReviews !== undefined) {\n this.options.show_reviews = options.showReviews;\n }\n if (options.reviewId !== undefined) {\n this.options.review_id = options.reviewId;\n }\n if (options.showRefundBadge !== undefined) {\n this.options.show_refund_badge = options.showRefundBadge;\n }\n if (options.refundPolicyPosition !== undefined) {\n this.options.refund_policy_position = options.refundPolicyPosition;\n }\n\n return this;\n }\n\n /**\n * Enhanced currency configuration.\n *\n * @param currency Primary currency or 'auto' for automatic detection\n * @param defaultCurrency Default currency when using 'auto'\n * @param showInlineSelector Whether to show inline currency selector\n * @returns A new builder instance with currency configuration\n */\n setCurrency(\n currency: 'usd' | 'eur' | 'gbp' | 'auto',\n defaultCurrency: 'usd' | 'eur' | 'gbp' = 'usd',\n showInlineSelector: boolean = true\n ): Checkout {\n this.options = {\n ...this.options,\n show_inline_currency_selector: showInlineSelector,\n default_currency: defaultCurrency,\n currency: currency,\n };\n\n return this;\n }\n\n /**\n * Configures navigation and cancel behavior.\n *\n * @param cancelUrl URL for back button when in page mode\n * @param cancelIcon Custom cancel icon URL\n * @returns A new builder instance with navigation configuration\n */\n setCancelButton(cancelUrl?: string, cancelIcon?: string): Checkout {\n this.options = { ...this.options };\n\n if (cancelUrl !== undefined) {\n this.options.cancel_url = cancelUrl;\n }\n if (cancelIcon !== undefined) {\n this.options.cancel_icon = cancelIcon;\n }\n\n return this;\n }\n\n /**\n * Associates purchases with an affiliate account.\n *\n * @param userId Affiliate user ID\n * @returns A new builder instance with affiliate configuration\n */\n setAffiliate(userId: number): Checkout {\n this.options = {\n ...this.options,\n affiliate_user_id: userId,\n };\n\n return this;\n }\n\n /**\n * Sets a custom image/icon for the checkout.\n *\n * @param imageUrl Secure HTTPS URL to the image\n * @returns A new builder instance with custom image\n */\n setImage(imageUrl: string): Checkout {\n this.options = {\n ...this.options,\n image: imageUrl,\n };\n\n return this;\n }\n\n /**\n * Configures the checkout for license renewal.\n *\n * @param licenseKey The license key to renew\n * @returns A new builder instance configured for renewal\n */\n setLicenseRenewal(licenseKey: string): Checkout {\n this.options = {\n ...this.options,\n license_key: licenseKey,\n };\n\n return this;\n }\n\n /**\n * Builds and returns the final checkout options to be used with the `@freemius/checkout` package.\n *\n * @note - This is async by purpose so that we can allow for future enhancements that might require async operations.\n *\n * @returns The constructed CheckoutOptions object\n */\n getOptions(): CheckoutOptions {\n return { ...this.options };\n }\n\n /**\n * Generates a checkout link based on the current builder state.\n *\n * @note - This is async by purpose so that we can allow for future enhancements that might require async operations.\n */\n getLink(): string {\n const checkoutOptions = convertCheckoutOptionsToQueryParams(this.options);\n\n const queryParams = buildFreemiusQueryFromOptions(checkoutOptions);\n\n const url = new URL(`${this.getBaseUrl()}/product/${this.productId}/`);\n url.search = queryParams;\n\n return url.toString();\n }\n\n serialize(): CheckoutSerialized {\n return {\n options: this.getOptions(),\n link: this.getLink(),\n baseUrl: this.getBaseUrl(),\n };\n }\n\n private getBaseUrl(): string {\n return isTestServer() ? 'http://checkout.freemius-local.com:8080' : 'https://checkout.freemius.com';\n }\n}\n","export class ActionError extends Error {\n public readonly statusCode: number;\n public readonly validationIssues?: unknown;\n\n constructor(message: string, statusCode: number = 400, validationIssues?: unknown) {\n super(message);\n this.name = 'ActionError';\n this.statusCode = statusCode;\n this.validationIssues = validationIssues;\n }\n\n toResponse(): Response {\n const errorResponse: Record<string, unknown> = {\n message: this.message,\n };\n\n if (this.validationIssues) {\n errorResponse.issues = this.validationIssues;\n }\n\n return Response.json(errorResponse, { status: this.statusCode });\n }\n\n // Essential factory methods\n static badRequest(message: string): ActionError {\n return new ActionError(message, 400);\n }\n\n static unauthorized(message: string = 'Unauthorized'): ActionError {\n return new ActionError(message, 401);\n }\n\n static notFound(message: string = 'Not found'): ActionError {\n return new ActionError(message, 404);\n }\n\n static validationFailed(message: string, validationIssues: unknown): ActionError {\n return new ActionError(message, 400, validationIssues);\n }\n\n static internalError(message: string = 'Internal server error'): ActionError {\n return new ActionError(message, 500);\n }\n}\n","import { CheckoutAction } from '../contracts/checkout';\nimport { PricingService } from '../services/PricingService';\n\nexport class PricingRetriever implements CheckoutAction {\n constructor(private readonly pricing: PricingService) {}\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return action === 'pricing_data';\n }\n\n async processAction(request: Request): Promise<Response> {\n // Get the topupPlanId from the query parameters\n const url = new URL(request.url);\n const topupPlanId = url.searchParams.get('topupPlanId') || undefined;\n\n const pricingData = await this.pricing.retrieve(topupPlanId);\n\n return Response.json(pricingData);\n }\n}\n","import { ActionError } from '../errors/ActionError';\nimport * as zod from 'zod';\nimport { PurchaseService } from '../services/PurchaseService';\nimport { CheckoutAction } from '../contracts/checkout';\nimport { PurchaseInfo } from '../models/PurchaseInfo';\n\nexport type PurchaseCallback = (purchase: PurchaseInfo) => Promise<Response | void>;\n\nexport class PurchaseProcessor implements CheckoutAction {\n constructor(\n private readonly purchase: PurchaseService,\n private readonly callback?: PurchaseCallback\n ) {}\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return request.method === 'POST' && action === 'process_purchase';\n }\n\n async processAction(request: Request): Promise<Response> {\n const purchaseSchema = zod.object({\n purchase: zod.object({\n license_id: zod.string(),\n }),\n });\n\n const contentType = request.headers.get('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n throw ActionError.badRequest('Invalid content type. Expected application/json');\n }\n\n // Parse request body\n let requestBody: unknown;\n try {\n requestBody = await request.json();\n } catch {\n throw ActionError.badRequest('Request body must be valid JSON');\n }\n\n // Validate request body with Zod schema\n const parseResult = purchaseSchema.safeParse(requestBody);\n if (!parseResult.success) {\n throw ActionError.validationFailed('Invalid request body format', parseResult.error.issues);\n }\n\n const {\n purchase: { license_id: licenseId },\n } = parseResult.data;\n const purchase = await this.purchase.retrievePurchase(licenseId);\n\n if (!purchase) {\n throw ActionError.notFound('No purchase data found for the provided license ID');\n }\n\n if (this.callback) {\n const callbackResponse = await this.callback(purchase);\n\n if (callbackResponse) {\n return callbackResponse;\n }\n }\n\n return Response.json(purchase.toData());\n }\n}\n","import { idToString, parseBillingCycle, parseCurrency, parseDateTime, parseNumber } from '../api/parser';\nimport { BillingCycleApiEnum, CurrencyApiEnum, FSId } from '../api/types';\nimport { CheckoutRedirectData } from '../contracts/checkout';\nimport { BILLING_CYCLE, CURRENCY } from '../contracts/types';\n\nexport class CheckoutRedirectInfo implements CheckoutRedirectData {\n user_id: string;\n plan_id: string;\n email: string;\n pricing_id: string;\n currency: CURRENCY;\n license_id: string;\n expiration: Date | null;\n quota: number | null;\n action: 'payment_method_update' | 'license_update' | null;\n amount: number;\n tax: number;\n type: 'subscription' | 'one-off';\n subscription_id: string | null;\n billing_cycle: BILLING_CYCLE | null;\n payment_id: string | null;\n\n // Add these to the constructor to initialize the new members\n constructor(data: Record<string, unknown>) {\n this.user_id = idToString(data.user_id as FSId);\n this.plan_id = idToString(data.plan_id as FSId);\n this.email = data.email as string;\n this.pricing_id = idToString(data.pricing_id as FSId);\n this.currency = data.currency ? parseCurrency(data.currency as CurrencyApiEnum)! : CURRENCY.USD;\n this.license_id = idToString(data.license_id as FSId);\n this.expiration = data.expiration ? parseDateTime(data.expiration as string) : null;\n this.quota = data.quota ? parseNumber(data.quota) : null;\n this.action = data.action ? (data.action as 'payment_method_update' | 'license_update') : null;\n this.amount = parseNumber(data.amount as string)!;\n this.tax = parseNumber(data.tax as string)!;\n this.subscription_id = data.subscription_id ? idToString(data.subscription_id as string) : null;\n this.billing_cycle = data.billing_cycle ? parseBillingCycle(data.billing_cycle as BillingCycleApiEnum) : null;\n this.payment_id = data.payment_id ? idToString(data.payment_id as string) : null;\n this.type = this.subscription_id ? 'subscription' : 'one-off';\n }\n\n isSubscription(): boolean {\n return this.type === 'subscription';\n }\n\n toData(): CheckoutRedirectData {\n return {\n user_id: this.user_id,\n plan_id: this.plan_id,\n email: this.email,\n pricing_id: this.pricing_id,\n currency: this.currency,\n license_id: this.license_id,\n expiration: this.expiration,\n quota: this.quota,\n action: this.action,\n amount: this.amount,\n tax: this.tax,\n type: this.type,\n subscription_id: this.subscription_id,\n billing_cycle: this.billing_cycle,\n payment_id: this.payment_id,\n };\n }\n}\n","import { createHmac, timingSafeEqual } from 'crypto';\nimport { CheckoutRedirectInfo } from '../models/CheckoutRedirectInfo';\nimport { CheckoutAction } from '../contracts/checkout';\nimport { ActionError } from '../errors/ActionError';\n\nexport type RedirectCallback = (info: CheckoutRedirectInfo) => Promise<Response | void>;\n\nexport class RedirectProcessor implements CheckoutAction {\n constructor(\n private readonly secretKey: string,\n private readonly proxyUrl?: string,\n private readonly callback?: RedirectCallback,\n private readonly afterProcessUrl?: string\n ) {}\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n\n return request.method === 'GET' && url.searchParams.has('signature');\n }\n\n async processAction(request: Request): Promise<Response> {\n const info = await this.getRedirectInfo(request.url);\n\n if (!info) {\n throw ActionError.badRequest('Invalid or missing redirect signature');\n }\n\n if (this.callback) {\n const callbackResponse = await this.callback(info);\n\n if (callbackResponse) {\n return callbackResponse;\n }\n }\n\n const url = new URL(this.afterProcessUrl ?? this.proxyUrl ?? request.url);\n\n // Delete all existing search params\n url.search = '';\n\n // Add new search params\n url.searchParams.set('plan', info.plan_id);\n url.searchParams.set('is_subscription', info.isSubscription() ? '1' : '0');\n url.searchParams.set('quote', info.quota?.toString() ?? '0');\n\n // Default response if no callback is provided or callback does not return a response\n return Response.redirect(url.href, 302);\n }\n\n async getRedirectInfo(currentUrl: string): Promise<CheckoutRedirectInfo | null> {\n const url = new URL(\n currentUrl\n // Handle spaces in the URL, which may be encoded as %20 or +\n .replace(/%20/g, '+')\n );\n\n const signature = url.searchParams.get('signature');\n\n if (!signature) {\n return null;\n }\n\n // Replace URL parts if proxyUrl is set. This is useful when your app is behind a proxy (which is common).\n if (this.proxyUrl) {\n const proxy = new URL(this.proxyUrl);\n url.protocol = proxy.protocol;\n url.host = proxy.host;\n url.port = proxy.port;\n }\n\n const cleanUrl = this.getCleanUrl(url.href);\n\n // Calculate HMAC SHA256\n const calculatedSignature = createHmac('sha256', this.secretKey).update(cleanUrl).digest('hex');\n\n // Compare signatures securely\n const result = timingSafeEqual(Buffer.from(calculatedSignature), Buffer.from(signature));\n\n if (!result) {\n return null;\n }\n\n const params = Object.fromEntries(url.searchParams.entries());\n\n if (!params.user_id || !params.plan_id || !params.pricing_id || !params.email) {\n return null;\n }\n\n return new CheckoutRedirectInfo(params);\n }\n\n // Helper to get the current absolute URL (removing \"&signature=...\" or \"?signature=...\" by string slicing)\n getCleanUrl(url: string): string {\n const signatureParam = '&signature=';\n const signatureParamFirst = '?signature=';\n\n let signaturePos = url.indexOf(signatureParam);\n\n if (signaturePos === -1) {\n signaturePos = url.indexOf(signatureParamFirst);\n }\n\n if (signaturePos === -1) {\n // No signature param found, return as is\n return url;\n }\n\n return url.substring(0, signaturePos);\n }\n}\n","import { CheckoutAction } from '../contracts/checkout';\nimport { ActionError } from '../errors/ActionError';\nimport { PricingService } from '../services/PricingService';\nimport { PricingRetriever } from './PricingRetriever';\nimport { PurchaseService } from '../services/PurchaseService';\nimport { PurchaseCallback, PurchaseProcessor } from './PurchaseProcessor';\nimport { RedirectCallback, RedirectProcessor } from './RedirectProcessor';\nimport { RequestProcessor } from '../contracts/types';\n\nexport type CheckoutRequestConfig = {\n /**\n * The real public/proxy URL where the application is accessible from the internet.\n * This is useful for authenticating the Checkout Redirection requests.\n */\n proxyUrl?: string;\n /**\n * The function to be called when a purchase is made.\n */\n onPurchase?: PurchaseCallback;\n /**\n * The function to be called when a redirect is made after checkout.\n */\n onRedirect?: RedirectCallback;\n /**\n * Specifies a URL to redirect to after processing the redirect action. If not provided, it will redirect to the `proxyUrl` or the original request URL.\n */\n afterProcessUrl?: string;\n};\n\nexport class CheckoutRequestProcessor implements RequestProcessor<CheckoutRequestConfig> {\n constructor(\n private readonly purchase: PurchaseService,\n private readonly pricing: PricingService,\n private readonly secretKey: string\n ) {}\n\n createProcessor(config: CheckoutRequestConfig): (request: Request) => Promise<Response> {\n return (request: Request) => this.process(config, request);\n }\n\n async process(config: CheckoutRequestConfig, request: Request): Promise<Response> {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n if (!action) {\n return Response.json({ error: 'Action parameter is required' }, { status: 400 });\n }\n\n const actionHandlers: CheckoutAction[] = [\n this.getPricingRetriever(),\n this.getRedirectProcessor({\n proxyUrl: config.proxyUrl,\n callback: config.onRedirect,\n afterProcessUrl: config.afterProcessUrl,\n }),\n this.getPurchaseProcessor({ callback: config.onPurchase }),\n ];\n\n try {\n for (const actionHandler of actionHandlers) {\n if (actionHandler.canHandle(request)) {\n return await actionHandler.processAction(request);\n }\n }\n } catch (error) {\n if (error instanceof ActionError) {\n return error.toResponse();\n }\n\n console.error('Error processing action:', error);\n return ActionError.internalError('Internal Server Error').toResponse();\n }\n\n return ActionError.badRequest('Unsupported action').toResponse();\n }\n\n /**\n * Processes the redirect from Freemius Checkout.\n *\n * This method verifies the signature in the URL and returns a CheckoutRedirectInfo object if successful.\n *\n * For nextjs like applications, make sure to replace the URL from the `Request` object with the right hostname to take care of the proxy.\n *\n * For example, if you have put the nextjs application behind nginx proxy (or ngrok during local development), then nextjs will still see the `request.url` as `https://localhost:3000/...`.\n * In this case, you should replace it with the actual URL of your application, like `https://xyz.ngrok-free.app/...`.\n *\n * @example\n * ```ts\n * export async function GET(request: Request) {\n * // Replace the URL with the actual hostname of your application\n * // This is important for the signature verification to work correctly.\n * const data = await freemius.checkout.action.getRedirectProcessor({\n * proxyUrl: 'https://xyz.ngrok-free.app',\n * async callback(info) {\n * // Handle the redirect info here, like creating a license, etc.\n * // Return a Response object to override the default redirect behavior.\n * return Response.redirect('/custom-success-page', 302);\n * },\n * });\n *\n * return data.processAction(request);\n * }\n * ```\n */\n getRedirectProcessor(config: {\n proxyUrl?: string | undefined;\n callback?: RedirectCallback | undefined;\n afterProcessUrl?: string | undefined;\n }): RedirectProcessor {\n return new RedirectProcessor(this.secretKey, config.proxyUrl, config.callback, config.afterProcessUrl);\n }\n\n getPurchaseProcessor(config: { callback?: PurchaseCallback | undefined }): PurchaseProcessor {\n return new PurchaseProcessor(this.purchase, config.callback);\n }\n\n getPricingRetriever() {\n return new PricingRetriever(this.pricing);\n }\n}\n","import { CheckoutPopupParams } from '@freemius/checkout';\nimport { FSId } from '../api/types';\nimport { idToString } from '../api/parser';\nimport { Checkout } from '../checkout/Checkout';\nimport { CheckoutBuilderOptions } from '../contracts/checkout';\nimport { PurchaseService } from './PurchaseService';\nimport { PricingService } from './PricingService';\nimport { CheckoutRequestProcessor } from '../checkout/CheckoutRequestProcessor';\nimport { RedirectProcessor } from '../checkout/RedirectProcessor';\n\nexport class CheckoutService {\n public readonly request: CheckoutRequestProcessor;\n\n constructor(\n private readonly productId: FSId,\n private readonly publicKey: string,\n private readonly secretKey: string,\n private readonly purchase: PurchaseService,\n private readonly pricing: PricingService\n ) {\n this.request = new CheckoutRequestProcessor(this.purchase, this.pricing, this.secretKey);\n }\n\n /**\n * Use this to build a Checkout for your product.\n * You can build a Checkout link or options for the popup.\n *\n * @param withRecommendation If true, the checkout will include a recommendation for the user.\n *\n * @return A new instance of CheckoutBuilder with the product ID and public key.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const checkout = await freemius.checkout.create({user: session?.user})\n * .getOptions(); // Or .getLink() for a hosted checkout link\n * ```\n *\n * @example\n * Advanced configuration: You can also skip the convenience options and rather use the builder directly to configure the checkout.\n *\n * ```typescript\n * const checkout = freemius.checkout.create()\n * .setUser(user, true)\n * .setPlan('1234')\n * .setCoupon({\n * code: 'DISCOUNT2023',\n * hideUI: false\n * })\n * .setSandbox()\n * .getOptions();\n * ```\n *\n * @example\n */\n async create(options: CheckoutBuilderOptions = {}): Promise<Checkout> {\n const { user, isSandbox = false, withRecommendation = true, title, image, planId, quota, trial } = options;\n\n const builder = new Checkout(idToString(this.productId), this.publicKey, this.secretKey);\n\n if (user) {\n builder.setUser(user, true);\n }\n\n if (withRecommendation) {\n builder.setRecommendations();\n }\n\n if (isSandbox) {\n builder.setSandbox();\n }\n\n if (title) {\n builder.setTitle(title);\n }\n\n if (image) {\n builder.setImage(image);\n }\n\n if (planId) {\n builder.setPlan(planId);\n }\n\n if (quota) {\n builder.setQuota(quota);\n }\n\n if (trial) {\n builder.setTrial(trial);\n }\n\n return builder;\n }\n\n /**\n * Retrieves the sandbox parameters for the checkout.\n *\n * This shouldn't be used in production, but is useful for testing purposes.\n *\n * @note This is intentionally set as `async` because we would use the API in the future to generate more fine grained sandbox params (for example for a specific email address only).\n *\n * @todo - This has a duplication with the `inSandbox` method in the builder. Consider refactoring to avoid this duplication.\n * Also think about whether we should make the builder's `inSandbox` method async as well.\n */\n async getSandboxParams(): Promise<NonNullable<CheckoutPopupParams['sandbox']>> {\n return Checkout.createSandboxToken(idToString(this.productId), this.secretKey, this.publicKey);\n }\n\n /**\n * Processes a redirect URL and returns the checkout redirect information if valid.\n *\n * This is useful for handling redirects from the checkout portal back to your application.\n *\n * @param url The current URL to process.\n * @param proxyUrl Optional proxy URL to replace parts of the URL for signature verification.\n *\n * @returns A promise that resolves to the checkout redirect information or null if invalid.\n *\n * @example\n * ```typescript\n * const redirectInfo = await freemius.checkout.processRedirect(window.location.href);\n *\n * if (redirectInfo) {\n * // Handle valid redirect info\n * } else {\n * // Handle invalid or missing redirect info\n * }\n * ```\n */\n processRedirect(url: string, proxyUrl?: string) {\n const processor = new RedirectProcessor(this.secretKey, proxyUrl);\n return processor.getRedirectInfo(url);\n }\n}\n","import { CheckoutOptions } from '@freemius/checkout';\nimport {\n idToString,\n isIdsEqual,\n parseBillingCycle,\n parseCurrency,\n parseDateTime,\n parseNumber,\n parsePaymentMethod,\n} from '../api/parser';\nimport {\n BillingEntity,\n CouponEntityEnriched,\n FSId,\n PaymentEntity,\n PlanEntity,\n PricingEntity,\n PricingTableData,\n SellingUnit,\n UserEntity,\n UserSubscriptionEntity,\n} from '../api/types';\nimport { PortalBilling, PortalData, PortalPayment, PortalSubscription, PortalSubscriptions } from '../contracts/portal';\nimport { CURRENCY } from '../contracts/types';\nimport { ApiService } from '../services/ApiService';\nimport { CheckoutService } from '../services/CheckoutService';\nimport { CustomerPortalActionService } from './CustomerPortalActionService';\n\ntype CustomerPortalDataOption = {\n /**\n * The API endpoint for which secure authenticated signed URLs will be created where the customer portal will send requests to fetch additional data (e.g., invoices, licenses, subscriptions). This should be an endpoint in your SaaS application that you will implement to handle these requests.\n */\n endpoint: string;\n /**\n * ID of the primary licenses, in case your software has pricing model supporting multiple active subscriptions. If not provided the first active subscription will be used instead.\n */\n primaryLicenseId?: FSId | null;\n /**\n * Whether to create actions in sandbox mode (For example Checkout, Upgrade etc).\n */\n sandbox?: boolean;\n};\n\nexport type CustomerPortalDataWithUserOption = {\n /**\n * ID of the user from Freemius.\n */\n userId: FSId;\n} & CustomerPortalDataOption;\n\nexport type CustomerPortalDataWithEmailOption = {\n /**\n * The email address of the user from Freemius.\n */\n email: string;\n} & CustomerPortalDataOption;\n\nexport class PortalDataRepository {\n constructor(\n private readonly api: ApiService,\n private readonly action: CustomerPortalActionService,\n private readonly checkout: CheckoutService\n ) {}\n\n async retrievePortalDataByEmail(config: CustomerPortalDataWithEmailOption): Promise<PortalData | null> {\n const user = await this.api.user.retrieveByEmail(config.email);\n\n if (!user) {\n return null;\n }\n\n return this.retrievePortalData({\n user,\n endpoint: config.endpoint,\n primaryLicenseId: config.primaryLicenseId ?? null,\n sandbox: config.sandbox ?? false,\n });\n }\n\n async retrievePortalDataByUserId(config: CustomerPortalDataWithUserOption): Promise<PortalData | null> {\n const user = await this.api.user.retrieve(config.userId);\n\n if (!user) {\n return null;\n }\n\n return this.retrievePortalData({\n user,\n endpoint: config.endpoint,\n primaryLicenseId: config.primaryLicenseId ?? null,\n sandbox: config.sandbox ?? false,\n });\n }\n\n async retrievePortalData(config: {\n user: UserEntity;\n endpoint: string;\n primaryLicenseId?: FSId | null;\n sandbox?: boolean;\n }): Promise<PortalData | null> {\n const { user, endpoint, primaryLicenseId = null, sandbox = false } = config;\n const userId = user.id!;\n\n const data = await this.retrieveApiData(userId);\n\n if (!data) {\n return null;\n }\n\n const { pricingData, subscriptions, payments, billing, coupons } = data;\n\n const plans = this.getPlansById(pricingData);\n const pricings = this.getPricingById(pricingData);\n\n const checkoutOptions: CheckoutOptions = {\n product_id: this.api.productId,\n };\n\n if (sandbox) {\n checkoutOptions.sandbox = await this.checkout.getSandboxParams();\n }\n\n const portalData: PortalData = {\n endpoint,\n user,\n checkoutOptions,\n billing: this.getBilling(billing, userId, endpoint),\n subscriptions: await this.getSubscriptions(subscriptions, plans, pricings, primaryLicenseId, endpoint),\n payments: this.getPayments(payments, plans, pricings, userId, endpoint),\n plans: pricingData.plans ?? [],\n sellingUnit: (pricingData.plugin?.selling_unit_label as SellingUnit) ?? {\n singular: 'Unit',\n plural: 'Units',\n },\n productId: this.api.productId,\n cancellationCoupons: coupons,\n };\n\n return portalData;\n }\n\n async retrieveApiData(userId: FSId): Promise<{\n pricingData: PricingTableData;\n subscriptions: UserSubscriptionEntity[];\n payments: PaymentEntity[];\n billing: BillingEntity | null;\n coupons: CouponEntityEnriched[] | null;\n } | null> {\n const [pricingData, subscriptions, payments, billing, coupons] = await Promise.all([\n this.api.product.retrievePricingData(),\n this.api.user.retrieveSubscriptions(userId, { extended: true, enrich_with_cancellation_discounts: true }),\n this.api.user.retrievePayments(userId),\n this.api.user.retrieveBilling(userId),\n this.api.product.retrieveSubscriptionCancellationCoupon(),\n ]);\n\n if (!pricingData || !subscriptions) {\n return null;\n }\n\n return { pricingData, subscriptions, payments, billing, coupons };\n }\n\n getPayments(\n payments: PaymentEntity[],\n plans: Map<string, PlanEntity>,\n pricings: Map<string, PricingEntity>,\n userId: FSId,\n endpoint: string\n ): PortalPayment[] {\n return payments.map((payment) => ({\n ...payment,\n invoiceUrl: this.action.invoice.createAuthenticatedUrl(payment.id!, idToString(userId), endpoint),\n paymentMethod: parsePaymentMethod(payment.gateway)!,\n createdAt: parseDateTime(payment.created) ?? new Date(),\n planTitle: plans.get(payment.plan_id!)?.title ?? `Plan ${payment.plan_id!}`,\n quota: pricings.get(payment.pricing_id!)?.licenses ?? null,\n }));\n }\n\n getPlansById(pricingData: PricingTableData): Map<string, PlanEntity> {\n const plans: Map<string, PlanEntity> = new Map();\n\n pricingData.plans?.forEach((plan) => {\n plan.title = plan.title ?? plan.name ?? `Plan ${plan.id!}`;\n plans.set(idToString(plan.id!), plan);\n });\n\n return plans;\n }\n\n getPricingById(pricingData: PricingTableData): Map<string, PricingEntity> {\n const pricings: Map<string, PricingEntity> = new Map();\n\n pricingData.plans?.forEach((plan) => {\n plan.pricing?.forEach((p) => {\n pricings.set(idToString(p.id!), p);\n });\n });\n\n return pricings;\n }\n\n getBilling(billing: BillingEntity | null, userId: FSId, endpoint: string): PortalBilling {\n return {\n ...(billing ?? {}),\n updateUrl: this.action.billing.createAuthenticatedUrl(billing?.id ?? 'new', idToString(userId), endpoint),\n };\n }\n\n async getSubscriptions(\n subscriptions: UserSubscriptionEntity[],\n plans: Map<string, PlanEntity>,\n pricings: Map<string, PricingEntity>,\n primaryLicenseId: FSId | null = null,\n endpoint: string\n ): Promise<PortalSubscriptions> {\n const portalSubscriptions: PortalSubscriptions = {\n primary: null,\n active: [],\n past: [],\n };\n\n subscriptions.forEach((subscription) => {\n const isActive = null === subscription.canceled_at;\n const trialEndsData = subscription.trial_ends ? parseDateTime(subscription.trial_ends) : null;\n const isTrial = trialEndsData ? trialEndsData > new Date() : false;\n const isFreeTrial = isTrial && !subscription.gateway;\n\n const subscriptionData: PortalSubscription = {\n subscriptionId: idToString(subscription.id!),\n planId: idToString(subscription.plan_id!),\n pricingId: idToString(subscription.pricing_id!),\n planTitle: plans.get(subscription.plan_id!)?.title ?? `Plan ${subscription.plan_id!}`,\n renewalAmount: parseNumber(subscription.renewal_amount)!,\n initialAmount: parseNumber(subscription.initial_amount)!,\n billingCycle: parseBillingCycle(subscription.billing_cycle),\n isActive: isActive,\n renewalDate: parseDateTime(subscription.next_payment),\n licenseId: idToString(subscription.license_id!),\n currency: parseCurrency(subscription.currency) ?? CURRENCY.USD,\n createdAt: parseDateTime(subscription.created) ?? new Date(),\n cancelledAt: subscription.canceled_at ? parseDateTime(subscription.canceled_at) : null,\n quota: pricings.get(subscription.pricing_id!)?.licenses ?? null,\n paymentMethod: parsePaymentMethod(subscription.gateway),\n isTrial: isTrial,\n trialEnds: isTrial ? trialEndsData : null,\n isFreeTrial: isFreeTrial,\n applyRenewalCancellationCouponUrl: this.isRenewalCancellationCouponApplicable(subscription)\n ? this.action.renewalCoupon.createAuthenticatedUrl(\n idToString(subscription.id!),\n idToString(subscription.user_id!),\n endpoint\n )\n : null,\n cancelRenewalUrl: this.action.cancelRenewal.createAuthenticatedUrl(\n idToString(subscription.id!),\n idToString(subscription.user_id!),\n endpoint\n ),\n };\n\n if (isActive) {\n portalSubscriptions.active.push(subscriptionData);\n } else {\n portalSubscriptions.past.push(subscriptionData);\n }\n\n if (isActive && primaryLicenseId && isIdsEqual(subscription.license_id!, primaryLicenseId)) {\n portalSubscriptions.primary = subscriptionData;\n }\n });\n\n // Sort subscriptions by created date, most recent first\n portalSubscriptions.active.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n portalSubscriptions.past.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n\n if (!portalSubscriptions.primary) {\n // If no primary subscription is set, use the first active or inactive subscription as primary\n portalSubscriptions.primary = portalSubscriptions.active[0] ?? portalSubscriptions.past[0] ?? null;\n }\n\n // @todo - Right now add the checkout upgrade authorization to the primary subscription if it exists. In the future our API itself can return this data.\n if (portalSubscriptions.primary) {\n portalSubscriptions.primary.checkoutUpgradeAuthorization =\n await this.api.license.retrieveCheckoutUpgradeAuthorization(portalSubscriptions.primary.licenseId);\n }\n\n return portalSubscriptions;\n }\n\n /**\n * Check if coupon application is impossible due to certain conditions.\n * This function can be used to determine if a coupon can be applied to a subscription.\n * Introduced initially for PayPal subscriptions with a renewal date less than 48 hours in the future.\n *\n * @author @DanieleAlessandra\n * @author @swashata (Ported to SDK)\n *\n * @returns boolean\n */\n isRenewalCancellationCouponApplicable(subscription: UserSubscriptionEntity): boolean {\n // If it was already applied, do not allow applying it again.\n if (subscription.has_subscription_cancellation_discount || (subscription.total_gross ?? 0) <= 0) {\n return false;\n }\n\n // The block is only for PayPal subscriptions\n if (subscription.gateway !== 'paypal') {\n return true;\n }\n\n // Convert next_payment to time.\n const nextPaymentTime = parseDateTime(subscription.next_payment)?.getTime() ?? 0;\n\n // Get the current time.\n const currentTime = new Date().getTime();\n\n // Check if the next payment is at least 48 hours in the future.\n const fortyEightHoursInMs = 48 * 60 * 60 * 1000;\n\n return nextPaymentTime <= currentTime + fortyEightHoursInMs;\n }\n}\n","import { PortalAction } from '../contracts/portal';\nimport { UserRetriever } from '../contracts/types';\nimport { PortalDataRepository } from './PortalDataRepository';\n\nexport class PortalDataRetriever implements PortalAction {\n constructor(\n private readonly repository: PortalDataRepository,\n private readonly getUser: UserRetriever,\n private readonly endpoint: string,\n private readonly isSandbox?: boolean\n ) {}\n\n createAuthenticatedUrl(): string {\n throw new Error('Method not implemented.');\n }\n\n verifyAuthentication(): boolean {\n return true;\n }\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return request.method === 'GET' && action === 'portal_data';\n }\n\n async processAction(): Promise<Response> {\n const user = await this.getUser();\n\n if (!user || !('id' in user)) {\n return Response.json(null);\n }\n\n return Response.json(\n await this.repository.retrievePortalDataByUserId({\n userId: user.id,\n endpoint: this.endpoint,\n primaryLicenseId: null,\n sandbox: this.isSandbox ?? false,\n })\n );\n }\n}\n","import { PortalAction } from '../contracts/portal';\nimport { UserRetriever } from '../contracts/types';\nimport { ActionError } from '../errors/ActionError';\nimport { PurchaseInfo } from '../models/PurchaseInfo';\nimport { PurchaseService } from '../services/PurchaseService';\n\nexport type RestoreCallback = (purchases: PurchaseInfo[]) => Promise<Response | void>;\n\nexport class PurchaseRestorer implements PortalAction {\n constructor(\n private readonly purchase: PurchaseService,\n private readonly user: UserRetriever,\n private readonly callback?: RestoreCallback,\n private readonly subscriptionOnly: boolean = false\n ) {}\n\n createAuthenticatedUrl(): string {\n throw new Error('Method not implemented.');\n }\n\n verifyAuthentication(): boolean {\n // This is handled later in the processAction method\n return true;\n }\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return request.method === 'POST' && action === 'restore_purchase';\n }\n\n async processAction(): Promise<Response> {\n let purchases: PurchaseInfo[] | null = null;\n const user = await this.user();\n\n if (!user) {\n throw ActionError.unauthorized('User not authenticated');\n }\n\n if (this.subscriptionOnly) {\n purchases =\n 'id' in user\n ? await this.purchase.retrieveSubscriptions(user.id)\n : await this.purchase.retrieveSubscriptionsByEmail(user.email);\n } else {\n purchases =\n 'id' in user\n ? await this.purchase.retrievePurchases(user.id)\n : await this.purchase.retrievePurchasesByEmail(user.email);\n }\n\n if (!purchases) {\n throw ActionError.notFound('No purchases found for the provided user');\n }\n\n if (this.callback) {\n const callbackResponse = await this.callback(purchases);\n\n if (callbackResponse) {\n return callbackResponse;\n }\n }\n\n return Response.json(purchases.map((p) => p.toData()));\n }\n}\n","import { PortalAction } from '../contracts/portal';\nimport { RequestProcessor, UserRetriever } from '../contracts/types';\nimport { ActionError } from '../errors/ActionError';\nimport { PurchaseService } from '../services/PurchaseService';\nimport { CustomerPortalActionService } from './CustomerPortalActionService';\nimport { PortalDataRepository } from './PortalDataRepository';\nimport { PortalDataRetriever } from './PortalDataRetriever';\nimport { PurchaseRestorer, RestoreCallback } from './PurchaseRestorer';\n\nexport type PortalRequestConfig = {\n getUser: UserRetriever;\n onRestore?: RestoreCallback;\n restoreSubscriptionsOnly?: boolean;\n isSandbox?: boolean;\n portalEndpoint: string;\n};\n\nexport class PortalRequestProcessor implements RequestProcessor<PortalRequestConfig> {\n constructor(\n private readonly repository: PortalDataRepository,\n private readonly action: CustomerPortalActionService,\n private readonly purchase: PurchaseService\n ) {}\n\n createProcessor(config: PortalRequestConfig): (request: Request) => Promise<Response> {\n return (request: Request) => this.process(config, request);\n }\n\n /**\n * Process actions done by the user in the customer portal.\n */\n async process(config: PortalRequestConfig, request: Request): Promise<Response> {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n if (!action) {\n return ActionError.badRequest('Action parameter is required').toResponse();\n }\n\n const actionHandlers: PortalAction[] = [\n new PortalDataRetriever(this.repository, config.getUser, config.portalEndpoint, config.isSandbox),\n ...this.action.getAllHandlers(),\n ];\n\n if (config.onRestore) {\n actionHandlers.push(\n new PurchaseRestorer(\n this.purchase,\n config.getUser,\n config.onRestore,\n config.restoreSubscriptionsOnly ?? false\n )\n );\n }\n\n try {\n for (const actionHandler of actionHandlers) {\n if (actionHandler.canHandle(request)) {\n if (actionHandler.verifyAuthentication(request)) {\n return await actionHandler.processAction(request);\n } else {\n throw ActionError.unauthorized('Invalid authentication token');\n }\n }\n }\n } catch (error) {\n if (error instanceof ActionError) {\n return error.toResponse();\n }\n\n console.error('Error processing action:', error);\n return ActionError.internalError('Internal Server Error').toResponse();\n }\n\n return ActionError.badRequest('Unsupported action').toResponse();\n }\n}\n","import { BillingUpdatePayload } from '../api/types';\nimport { PortalAction } from '../contracts/portal';\nimport { ApiService } from '../services/ApiService';\nimport { AuthService } from '../services/AuthService';\nimport { ActionError } from '../errors/ActionError';\nimport * as zod from 'zod';\n\n// Define Zod schema for billing request body\nconst schema = zod.object({\n business_name: zod.string().optional(),\n tax_id: zod.string().optional(),\n phone: zod.string().optional(),\n address_apt: zod.string().optional(),\n address_street: zod.string().optional(),\n address_city: zod.string().optional(),\n address_state: zod.string().optional(),\n address_country_code: zod.string().optional(),\n address_zip: zod.string().optional(),\n});\n\nexport type BillingRequest = zod.infer<typeof schema>;\n\nexport class BillingAction implements PortalAction {\n private readonly actionName = 'billing';\n\n constructor(\n private readonly api: ApiService,\n private readonly auth: AuthService\n ) {}\n\n private createAction(id: string): string {\n return `billing_${id}`;\n }\n\n createAuthenticatedUrl(id: string, userId: string, endpoint: string): string {\n const token = this.auth.createActionToken(this.createAction(id), userId);\n\n const url = new URL(endpoint);\n url.searchParams.set('action', this.actionName);\n url.searchParams.set('token', token);\n url.searchParams.set('billingId', id);\n url.searchParams.set('userId', userId);\n\n return url.href;\n }\n\n verifyAuthentication(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n const token = url.searchParams.get('token');\n const billingId = url.searchParams.get('billingId');\n const userId = url.searchParams.get('userId');\n\n if (!action || !token || !billingId || !userId) {\n return false;\n }\n\n return this.auth.verifyActionToken(token, this.createAction(billingId), userId);\n }\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return action === this.actionName;\n }\n\n async processAction(request: Request): Promise<Response> {\n // Check content type\n const contentType = request.headers.get('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n throw ActionError.badRequest('Invalid content type. Expected application/json');\n }\n\n // Parse request body\n let requestBody: unknown;\n try {\n requestBody = await request.json();\n } catch {\n throw ActionError.badRequest('Request body must be valid JSON');\n }\n\n // Validate request body with Zod schema\n const parseResult = schema.safeParse(requestBody);\n if (!parseResult.success) {\n throw ActionError.validationFailed('Invalid request body format', parseResult.error.issues);\n }\n\n const billingData = parseResult.data;\n\n // Extract URL parameters for billing ID and user ID\n const url = new URL(request.url);\n const billingId = url.searchParams.get('billingId');\n const userId = url.searchParams.get('userId');\n\n if (!billingId) {\n throw ActionError.badRequest('Missing required parameter: billingId');\n }\n\n if (!userId) {\n throw ActionError.badRequest('Missing required parameter: userId');\n }\n\n const payload: BillingUpdatePayload = {};\n\n if (billingData.business_name) {\n payload.business_name = billingData.business_name;\n }\n if (billingData.tax_id) {\n payload.tax_id = billingData.tax_id;\n }\n if (billingData.phone) {\n payload.phone = billingData.phone;\n }\n\n if (billingData.address_apt) {\n payload.address_apt = billingData.address_apt;\n }\n if (billingData.address_street) {\n payload.address_street = billingData.address_street;\n }\n if (billingData.address_city) {\n payload.address_city = billingData.address_city;\n }\n if (billingData.address_state) {\n payload.address_state = billingData.address_state;\n }\n if (billingData.address_country_code) {\n payload.address_country_code = billingData.address_country_code;\n }\n if (billingData.address_zip) {\n payload.address_zip = billingData.address_zip;\n }\n\n const response = await this.api.user.updateBilling(userId, payload);\n\n if (!response) {\n throw ActionError.internalError('Failed to update billing information');\n }\n\n return Response.json(response, { status: 200 });\n }\n}\n","import { PortalAction } from '../contracts/portal';\nimport { ApiService } from '../services/ApiService';\nimport * as zod from 'zod';\nimport { AuthService } from '../services/AuthService';\nimport { ActionError } from '../errors/ActionError';\n\nexport class InvoiceAction implements PortalAction {\n private readonly actionName = 'invoice';\n\n constructor(\n private readonly api: ApiService,\n private readonly auth: AuthService\n ) {}\n\n private createAction(id: string): string {\n return `invoice_${id}`;\n }\n\n createAuthenticatedUrl(id: string, userId: string, endpoint: string): string {\n const token = this.auth.createActionToken(this.createAction(id), userId);\n\n const url = new URL(endpoint);\n url.searchParams.set('action', this.actionName);\n url.searchParams.set('token', token);\n url.searchParams.set('invoiceId', id);\n url.searchParams.set('userId', userId);\n\n return url.href;\n }\n\n verifyAuthentication(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n const token = url.searchParams.get('token');\n const invoiceId = url.searchParams.get('invoiceId');\n const userId = url.searchParams.get('userId');\n\n if (!action || !token || !invoiceId || !userId) {\n return false;\n }\n\n return this.auth.verifyActionToken(token, this.createAction(invoiceId), userId);\n }\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return action === this.actionName;\n }\n\n async processAction(request: Request): Promise<Response> {\n // Verify the request that is has valid invoiceId and userId with zod schema validation\n const schema = zod.object({\n invoiceId: zod.string().min(1, 'Invoice ID is required'),\n userId: zod.string().min(1, 'User ID is required'),\n });\n\n const url = new URL(request.url);\n const invoiceId = url.searchParams.get('invoiceId');\n const userIdParam = url.searchParams.get('userId');\n\n const parseResult = schema.safeParse({ invoiceId, userId: userIdParam });\n\n if (!parseResult.success) {\n throw ActionError.validationFailed('Invalid request parameters', parseResult.error.issues);\n }\n\n const { invoiceId: validInvoiceId } = parseResult.data;\n\n try {\n const pdf = await this.api.payment.retrieveInvoice(validInvoiceId);\n\n if (pdf) {\n return new Response(pdf, {\n headers: {\n 'Content-Type': 'application/pdf',\n 'Content-Disposition': `inline; filename=\"invoice_${validInvoiceId}.pdf\"`,\n },\n });\n } else {\n throw ActionError.notFound('Invoice not found');\n }\n } catch (error) {\n if (error instanceof ActionError) {\n throw error;\n }\n\n throw ActionError.internalError('Failed to retrieve invoice');\n }\n }\n}\n","import * as zod from 'zod';\nimport { idToString } from '../api/parser';\nimport { FSId, SubscriptionCancellationReasonType } from '../api/types';\nimport { PortalAction } from '../contracts/portal';\nimport { ActionError } from '../errors/ActionError';\nimport { ApiService } from '../services/ApiService';\nimport { AuthService } from '../services/AuthService';\n\nconst schema = zod.object({\n feedback: zod.string().optional(),\n reason_ids: zod\n .array(zod.enum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15']))\n .optional(),\n});\n\nexport type SubscriptionCancellationRequest = zod.infer<typeof schema>;\n\nexport class SubscriptionCancellationAction implements PortalAction {\n private readonly actionName = 'subscription_cancellation';\n\n constructor(\n private readonly api: ApiService,\n private readonly auth: AuthService\n ) {}\n\n private createAction(id: string): string {\n return `cancellation_${id}`;\n }\n\n createAuthenticatedUrl(id: string, userId: FSId, endpoint: string): string {\n const token = this.auth.createActionToken(this.createAction(id), userId);\n\n const url = new URL(endpoint);\n url.searchParams.set('action', this.actionName);\n url.searchParams.set('token', token);\n url.searchParams.set('subscriptionId', id);\n url.searchParams.set('userId', idToString(userId));\n\n return url.href;\n }\n\n verifyAuthentication(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n const token = url.searchParams.get('token');\n const subscriptionId = url.searchParams.get('subscriptionId');\n const userId = url.searchParams.get('userId');\n\n if (!action || !token || !subscriptionId || !userId) {\n return false;\n }\n\n return this.auth.verifyActionToken(token, this.createAction(subscriptionId), userId);\n }\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return action === this.actionName && request.method === 'POST';\n }\n\n async processAction(request: Request): Promise<Response> {\n // Get feedback and reason_ids from the body\n\n // Check content type\n const contentType = request.headers.get('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n throw ActionError.badRequest('Invalid content type. Expected application/json');\n }\n\n let requestBody: unknown;\n try {\n requestBody = await request.json();\n } catch {\n throw ActionError.badRequest('Request body must be valid JSON');\n }\n\n // Validate request body with Zod schema\n const parseResult = schema.safeParse(requestBody);\n if (!parseResult.success) {\n throw ActionError.validationFailed('Invalid request body format', parseResult.error.issues);\n }\n\n // Get subscriptionId and userId from the URL\n const url = new URL(request.url);\n const subscriptionId = url.searchParams.get('subscriptionId');\n const userId = url.searchParams.get('userId');\n\n if (!subscriptionId || !userId) {\n throw ActionError.badRequest('Missing subscriptionId or userId');\n }\n\n const reasonIds = parseResult.data.reason_ids\n ? (parseResult.data.reason_ids.map((id) => parseInt(id, 10)) as SubscriptionCancellationReasonType[])\n : undefined;\n\n const result = await this.api.subscription.cancel(subscriptionId, parseResult.data.feedback, reasonIds);\n\n if (!result) {\n throw ActionError.internalError('Failed to cancel the subscription');\n }\n\n return Response.json(result, { status: 200 });\n }\n}\n","import { idToString } from '../api/parser';\nimport { FSId } from '../api/types';\nimport { PortalAction } from '../contracts/portal';\nimport { ActionError } from '../errors/ActionError';\nimport { ApiService } from '../services/ApiService';\nimport { AuthService } from '../services/AuthService';\nimport * as zod from 'zod';\n\nconst schema = zod.object({\n couponId: zod.string().min(1),\n});\n\nexport type ApplyRenewalCouponRequest = zod.infer<typeof schema>;\n\nexport class SubscriptionRenewalCouponAction implements PortalAction {\n private readonly actionName = 'subscription_cancellation_coupon';\n\n constructor(\n private readonly api: ApiService,\n private readonly auth: AuthService\n ) {}\n\n private createAction(id: string): string {\n return `renewal_coupon_${id}`;\n }\n\n createAuthenticatedUrl(id: string, userId: FSId, endpoint: string): string {\n const token = this.auth.createActionToken(this.createAction(id), userId);\n\n const url = new URL(endpoint);\n url.searchParams.set('action', this.actionName);\n url.searchParams.set('token', token);\n url.searchParams.set('subscriptionId', id);\n url.searchParams.set('userId', idToString(userId));\n\n return url.href;\n }\n\n verifyAuthentication(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n const token = url.searchParams.get('token');\n const subscriptionId = url.searchParams.get('subscriptionId');\n const userId = url.searchParams.get('userId');\n\n if (!action || !token || !subscriptionId || !userId) {\n return false;\n }\n\n return this.auth.verifyActionToken(token, this.createAction(subscriptionId), userId);\n }\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return action === this.actionName && request.method === 'POST';\n }\n\n async processAction(request: Request): Promise<Response> {\n // Check content type\n const contentType = request.headers.get('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n throw ActionError.badRequest('Invalid content type. Expected application/json');\n }\n\n let requestBody: unknown;\n try {\n requestBody = await request.json();\n } catch {\n throw ActionError.badRequest('Request body must be valid JSON');\n }\n\n // Validate request body with Zod schema\n const parseResult = schema.safeParse(requestBody);\n if (!parseResult.success) {\n throw ActionError.validationFailed('Invalid request body format', parseResult.error.issues);\n }\n\n // Get the subscriptionId from the URL\n const url = new URL(request.url);\n const subscriptionId = url.searchParams.get('subscriptionId');\n if (!subscriptionId) {\n throw ActionError.badRequest('Missing subscriptionId in the request URL');\n }\n\n const result = await this.api.subscription.applyRenewalCoupon(subscriptionId, parseResult.data.couponId, true);\n\n if (!result) {\n throw ActionError.internalError('Failed to apply renewal coupon to the subscription');\n }\n\n return Response.json(result, { status: 200 });\n }\n}\n","import { PortalAction } from '../contracts/portal';\nimport { ApiService } from '../services/ApiService';\nimport { AuthService } from '../services/AuthService';\nimport { BillingAction } from './BillingAction';\nimport { InvoiceAction } from './InvoiceAction';\nimport { SubscriptionCancellationAction } from './SubscriptionCancellationAction';\nimport { SubscriptionRenewalCouponAction } from './SubscriptionRenewalCouponAction';\n\nexport class CustomerPortalActionService {\n public readonly invoice: InvoiceAction;\n public readonly billing: BillingAction;\n public readonly renewalCoupon: SubscriptionRenewalCouponAction;\n public readonly cancelRenewal: SubscriptionCancellationAction;\n\n constructor(\n private readonly api: ApiService,\n private readonly authService: AuthService\n ) {\n this.invoice = new InvoiceAction(this.api, this.authService);\n this.billing = new BillingAction(this.api, this.authService);\n this.renewalCoupon = new SubscriptionRenewalCouponAction(this.api, this.authService);\n this.cancelRenewal = new SubscriptionCancellationAction(this.api, this.authService);\n }\n\n getAllHandlers(): PortalAction[] {\n return [this.invoice, this.billing, this.renewalCoupon, this.cancelRenewal];\n }\n}\n","import { ApiService } from './ApiService';\nimport { PortalData } from '../contracts/portal';\nimport { CheckoutService } from './CheckoutService';\nimport { AuthService } from './AuthService';\nimport {\n CustomerPortalDataWithEmailOption,\n CustomerPortalDataWithUserOption,\n PortalDataRepository,\n} from '../customer-portal/PortalDataRepository';\nimport { PurchaseService } from './PurchaseService';\nimport { PortalRequestProcessor } from '../customer-portal/PortalRequestProcessor';\nimport { CustomerPortalActionService } from '../customer-portal/CustomerPortalActionService';\nimport { PurchaseInfo } from '../models/PurchaseInfo';\n\nexport class CustomerPortalService {\n private readonly repository: PortalDataRepository;\n\n public readonly action: CustomerPortalActionService;\n public readonly request: PortalRequestProcessor;\n\n constructor(\n private readonly api: ApiService,\n private readonly checkout: CheckoutService,\n private readonly authService: AuthService,\n private readonly purchase: PurchaseService\n ) {\n this.action = new CustomerPortalActionService(this.api, this.authService);\n\n this.repository = new PortalDataRepository(this.api, this.action, this.checkout);\n\n this.request = new PortalRequestProcessor(this.repository, this.action, this.purchase);\n }\n\n /**\n * Retrieves the customer portal data for a user, including subscriptions, billing, and payments.\n */\n async retrieveData(option: CustomerPortalDataWithUserOption): Promise<PortalData | null> {\n return this.repository.retrievePortalDataByUserId(option);\n }\n\n async retrieveDataByEmail(option: CustomerPortalDataWithEmailOption): Promise<PortalData | null> {\n return this.repository.retrievePortalDataByEmail(option);\n }\n\n /**\n * Creates a restorer function that processes an array of purchases by invoking the provided callback for each purchase.\n */\n createRestorer(callback: (purchase: PurchaseInfo) => Promise<void>): (purchases: PurchaseInfo[]) => Promise<void> {\n return async (purchases: PurchaseInfo[]) => {\n await Promise.all(purchases.map((purchase) => callback(purchase)));\n };\n }\n}\n","import crypto from 'crypto';\nimport { WebhookEventHandler, WebhookEventType, WebhookEvent } from './events';\nimport { EventEntity } from '../api/types';\n\nexport interface NormalizedRequest {\n headers: Record<string, string | string[] | undefined> | Headers;\n rawBody: string | Buffer; // MUST be the exact raw body Freemius sent\n}\n\nexport type WebhookListenerResponse = {\n status: number;\n} & ({ success: true } | { success: false; error: string });\n\nconst SIGNATURE_HEADER = 'x-signature';\n\n// @todo - Add a method `onAny` to listen to all events with a single handler\nexport class WebhookListener {\n private eventHandlers: Map<WebhookEventType, Set<WebhookEventHandler<WebhookEventType>>> = new Map();\n\n constructor(\n private readonly secretKey: string,\n private readonly onError: (error: unknown) => void = console.error\n ) {}\n\n // Overload for single event type\n on<T extends WebhookEventType>(type: T, handler: WebhookEventHandler<T>): this;\n // Overload for array of event types\n on<T extends WebhookEventType>(types: T[], handler: WebhookEventHandler<T>): this;\n // Implementation\n on<T extends WebhookEventType>(typeOrTypes: T | T[], handler: WebhookEventHandler<T>): this {\n const types = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes];\n\n for (const type of types) {\n if (!this.eventHandlers.has(type)) {\n this.eventHandlers.set(type, new Set());\n }\n\n const existingHandlers = this.eventHandlers.get(type)!;\n existingHandlers?.add(handler as WebhookEventHandler<WebhookEventType>);\n }\n\n return this;\n }\n\n // Overload for single event type\n off<T extends WebhookEventType>(type: T, handler: WebhookEventHandler<T>): this;\n // Overload for array of event types\n off<T extends WebhookEventType>(types: T[], handler: WebhookEventHandler<T>): this;\n // Implementation\n off<T extends WebhookEventType>(typeOrTypes: T | T[], handler: WebhookEventHandler<T>): this {\n const types = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes];\n\n for (const type of types) {\n const currentHandlers = this.eventHandlers.get(type);\n if (!currentHandlers) {\n continue;\n }\n\n // Set.delete() returns true if the element was in the set\n currentHandlers.delete(handler as WebhookEventHandler<WebhookEventType>);\n\n // Remove the entire entry if no handlers remain\n if (currentHandlers.size === 0) {\n this.eventHandlers.delete(type);\n }\n }\n\n return this;\n }\n\n // Remove all handlers for specific event type(s)\n removeAll<T extends WebhookEventType>(typeOrTypes: T | T[]): this {\n const types = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes];\n\n for (const type of types) {\n this.eventHandlers.delete(type);\n }\n\n return this;\n }\n\n // Get handler count for debugging\n getHandlerCount<T extends WebhookEventType>(type: T): number {\n return this.eventHandlers.get(type)?.size ?? 0;\n }\n\n // Get total number of event types with handlers\n getEventTypeCount(): number {\n return this.eventHandlers.size;\n }\n\n // Get all registered event types\n getRegisteredEventTypes(): WebhookEventType[] {\n return Array.from(this.eventHandlers.keys());\n }\n\n // Check if a specific event type has any handlers\n hasHandlers<T extends WebhookEventType>(type: T): boolean {\n const handlers = this.eventHandlers.get(type);\n return handlers !== undefined && handlers.size > 0;\n }\n\n // Check if a specific handler is registered for an event type\n hasHandler<T extends WebhookEventType>(type: T, handler: WebhookEventHandler<T>): boolean {\n const handlers = this.eventHandlers.get(type);\n return handlers ? handlers.has(handler as WebhookEventHandler<WebhookEventType>) : false;\n }\n\n // Get all handlers for a specific event type (useful for debugging)\n getHandlers<T extends WebhookEventType>(type: T): Set<WebhookEventHandler<WebhookEventType>> {\n return this.eventHandlers.get(type) || new Set();\n }\n\n // Get total count of all handlers across all event types\n getTotalHandlerCount(): number {\n let total = 0;\n for (const handlers of this.eventHandlers.values()) {\n total += handlers.size;\n }\n return total;\n }\n\n /**\n * Verify hex HMAC signature against the raw body.\n */\n verifySignature(rawBody: string | Buffer, signature: string | null): boolean {\n if (!signature) {\n return false;\n }\n\n const mac = crypto.createHmac('sha256', this.secretKey).update(rawBody).digest('hex');\n\n try {\n return crypto.timingSafeEqual(Buffer.from(mac, 'hex'), Buffer.from(signature, 'hex'));\n } catch {\n return false;\n }\n }\n\n /**\n * Process a normalized request.\n * Returns an object you can map to your framework's response easily.\n */\n async process(input: NormalizedRequest): Promise<WebhookListenerResponse> {\n const sig = this.getHeader(SIGNATURE_HEADER, input.headers);\n\n if (!this.verifySignature(input.rawBody, sig)) {\n return { status: 401, success: false, error: 'Invalid signature' };\n }\n\n let evt: EventEntity;\n try {\n const parsed = JSON.parse(\n typeof input.rawBody === 'string' ? input.rawBody : input.rawBody.toString('utf8')\n );\n\n if (!parsed || typeof parsed.type !== 'string') {\n return { status: 400, success: false, error: 'Invalid payload' };\n }\n\n evt = parsed as EventEntity;\n } catch {\n return { status: 400, success: false, error: 'Malformed JSON' };\n }\n\n const eventType = evt.type as WebhookEventType;\n const eventHandlers = this.eventHandlers.get(eventType);\n\n if (!eventHandlers || eventHandlers.size === 0) {\n // Optionally log unhandled events\n console.warn(`No handlers registered for event type: ${eventType}`);\n }\n\n try {\n // Execute handlers with proper type casting\n const promises = Array.from(eventHandlers || []).map((handler) => {\n const typedHandler = handler as WebhookEventHandler<typeof eventType>;\n const typedEvent = evt as WebhookEvent<typeof eventType>;\n return typedHandler(typedEvent);\n });\n\n // Execute handlers in parallel for better performance\n await Promise.all(promises);\n } catch (error) {\n this.onError?.(error as Error);\n return { status: 500, success: false, error: 'Internal Server Error' };\n }\n\n return { status: 200, success: true };\n }\n\n private getHeader(name: string, headers: NormalizedRequest['headers']): string | null {\n const lname = name.toLowerCase();\n\n if (headers instanceof Headers) {\n return headers.get(lname);\n }\n\n const v = headers[lname] ?? headers[name];\n\n if (Array.isArray(v)) {\n return v[0] ?? null;\n }\n\n return (v as string | undefined) ?? null;\n }\n}\n","import { WebhookListener } from '../webhook/WebhookListener';\n\nexport class WebhookService {\n constructor(private readonly secretKey: string) {}\n\n createListener(onError?: (error: unknown) => void): WebhookListener {\n return new WebhookListener(this.secretKey, onError);\n }\n\n createRequestProcessor(listener: WebhookListener): (request: Request) => Promise<Response> {\n return (request: Request) => this.processFetch(listener, request);\n }\n\n /**\n * WHATWG Fetch API adapter for modern JavaScript environments.\n *\n * Compatible with:\n * - Next.js App Router (route.ts files)\n * - Cloudflare Workers\n * - Deno\n * - Bun\n * - Vercel Edge Functions\n * - Any environment supporting the WHATWG Fetch API\n *\n * This method reads the request body as text and processes the webhook,\n * returning a standard Response object that can be directly returned\n * from your endpoint handler.\n *\n * @param listener - The webhook listener instance\n * @param request - The incoming Request object (WHATWG Fetch API)\n * @returns A Response object with the webhook processing result\n *\n * @example\n * ```typescript\n * // Next.js App Router (app/webhook/route.ts)\n * export async function POST(request: Request) {\n * const listener = webhookService.createListener();\n * return await webhookService.processFetch(listener, request);\n * }\n *\n * // Cloudflare Workers\n * export default {\n * async fetch(request: Request): Promise<Response> {\n * if (new URL(request.url).pathname === '/webhook') {\n * const listener = webhookService.createListener();\n * return await webhookService.processFetch(listener, request);\n * }\n * return new Response('Not Found', { status: 404 });\n * }\n * };\n * ```\n */\n async processFetch(listener: WebhookListener, request: Request): Promise<Response> {\n const rawBody = await request.text();\n const result = await listener.process({ headers: request.headers, rawBody });\n\n return new Response(JSON.stringify(result), {\n status: result.status,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n /**\n * Native Node.js HTTP server adapter.\n *\n * Reads the raw body from the request stream and writes the HTTP response directly.\n *\n * @example\n * ```typescript\n * import { createServer } from 'http';\n *\n * const server = createServer(async (req, res) => {\n * if (req.url === '/webhook') {\n * await freemius.webhook.processNodeHttp(listener, req, res);\n * }\n * });\n * ```\n */\n async processNodeHttp(\n listener: WebhookListener,\n req: import('http').IncomingMessage,\n res: import('http').ServerResponse\n ): Promise<void> {\n // Read the raw body from the request stream\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(chunk);\n }\n\n const rawBody = Buffer.concat(chunks);\n\n const result = await listener.process({ headers: req.headers, rawBody });\n\n res.statusCode = result.status;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify(result));\n }\n\n // @todo - Implement other adapters.\n\n // /**\n // * Express adapter.\n // * Route MUST use: express.raw({ type: '* / *' })\n // * Writes the HTTP response directly.\n // */\n // async fromExpress(\n // listener: WebhookListener,\n // req: import('express').Request,\n // res: import('express').Response\n // ): Promise<void> {\n // const { status, ...body } = await listener.process({ headers: req.headers as any, rawBody: req.body });\n // res.status(status).json(body);\n // }\n\n // /**\n // * Fastify adapter.\n // * Must register raw buffer parser:\n // * fastify.addContentTypeParser('* /*', { parseAs: 'buffer' }, (_req, body, done) => done(null, body))\n // * Writes the HTTP response directly.\n // */\n // async fromFastify(\n // listener: WebhookListener,\n // request: { headers: Record<string, string | string[] | undefined>; body: Buffer },\n // reply: { code: (n: number) => any; send: (payload: any) => any }\n // ): Promise<void> {\n // const { status, ...body } = await listener.process({ headers: request.headers, rawBody: request.body });\n // reply.code(status).send(body);\n // }\n}\n","import { createHmac, randomBytes, timingSafeEqual } from 'crypto';\nimport { FSId } from '../api/types';\nimport { idToString } from '../api/parser';\n\nexport interface TokenPayload {\n expiresAt: number;\n nonce: string;\n}\n\nexport class AuthService {\n private static readonly TOKEN_SEPARATOR = '::';\n private static readonly DEFAULT_EXPIRY_MINUTES = 60; // 1 hour default\n\n constructor(\n private readonly productId: FSId,\n private readonly secretKey: string\n ) {\n if (!secretKey || secretKey.length < 32) {\n throw new Error('Secret key must be at least 32 characters long');\n }\n }\n\n /**\n * Creates a secure token for a specific action that can be performed by a user.\n *\n * @param action The action identifier (e.g., 'download_invoice', 'update_billing')\n * @param userId The ID of the user who can perform this action\n * @param expiryMinutes Optional expiry time in minutes (default: 60 minutes)\n * @returns A secure token string\n */\n createActionToken(\n action: string,\n userId: FSId,\n expiryMinutes: number = AuthService.DEFAULT_EXPIRY_MINUTES\n ): string {\n if (!action || action.trim().length === 0) {\n throw new Error('Action cannot be empty');\n }\n\n const now = Date.now();\n const expiresAt = now + expiryMinutes * 60 * 1000;\n const nonce = randomBytes(16).toString('hex');\n\n // Create the minimal payload\n const payload = this.encodeTokenPayload({ expiresAt, nonce });\n\n // Create signature based on all the data\n const signature = this.signData(action.trim(), userId, expiresAt, nonce);\n\n return `${payload}${AuthService.TOKEN_SEPARATOR}${signature}`;\n }\n\n /**\n * Verifies and validates an action token.\n *\n * @param token The token to verify\n * @param action The expected action\n * @param userId The expected user ID\n * @returns true if valid, false otherwise\n */\n verifyActionToken(token: string, action: string, userId: FSId): boolean {\n try {\n if (!token || typeof token !== 'string' || !action || !userId) {\n return false;\n }\n\n const parts = token.split(AuthService.TOKEN_SEPARATOR);\n if (parts.length !== 2) {\n return false;\n }\n\n const [payloadPart, signature] = parts;\n\n if (!payloadPart || !signature) {\n return false;\n }\n\n // Decode the payload\n const payload = this.decodeTokenPayload(payloadPart);\n if (!payload) {\n return false;\n }\n\n // Check expiry\n const now = Date.now();\n if (payload.expiresAt <= now) {\n return false;\n }\n\n // Verify signature by recreating it with the provided action and userId\n const expectedSignature = this.signData(action.trim(), userId, payload.expiresAt, payload.nonce);\n\n return this.constantTimeEqual(signature, expectedSignature);\n } catch {\n return false;\n }\n }\n\n private encodeTokenPayload(payload: TokenPayload): string {\n const jsonString = JSON.stringify(payload);\n return Buffer.from(jsonString).toString('base64url');\n }\n\n private decodeTokenPayload(payloadPart: string): TokenPayload | null {\n try {\n const jsonString = Buffer.from(payloadPart, 'base64url').toString('utf8');\n const data = JSON.parse(jsonString);\n\n // Validate required fields\n if (typeof data.expiresAt !== 'number' || !data.nonce) {\n return null;\n }\n\n return data as TokenPayload;\n } catch {\n return null;\n }\n }\n\n private signData(action: string, userId: FSId, expiresAt: number, nonce: string): string {\n const data = `${action}:${idToString(userId)}:${idToString(this.productId)}:${expiresAt}:${nonce}`;\n\n return createHmac('sha256', this.secretKey).update(data).digest('hex');\n }\n\n private constantTimeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) {\n return false;\n }\n\n try {\n return timingSafeEqual(Buffer.from(a), Buffer.from(b));\n } catch {\n return false;\n }\n }\n}\n","import { isIdsEqual } from '../api/parser';\nimport { FSId, PricingEntity, SellingUnit } from '../api/types';\nimport { PricingData } from '../contracts/pricing';\nimport { PortalPlans } from '../contracts/portal';\nimport { ApiService } from '../services/ApiService';\n\nexport class PricingService {\n constructor(private readonly api: ApiService) {}\n\n async retrieve(topupPlanId?: FSId): Promise<PricingData> {\n const pricingData = await this.api.product.retrievePricingData();\n\n const plans: PricingData['plans'] =\n pricingData?.plans\n ?.filter((plan) => {\n return plan.pricing?.some((p) => this.isValidPricing(p));\n })\n .map((plan) => {\n return {\n ...plan,\n pricing: plan.pricing?.filter((p) => this.isValidPricing(p)) ?? [],\n };\n }) ?? [];\n\n return {\n plans: plans,\n topupPlan: this.findTopupPlan(plans, topupPlanId),\n sellingUnit: (pricingData?.plugin?.selling_unit_label as SellingUnit) ?? {\n singular: 'Unit',\n plural: 'Units',\n },\n };\n }\n\n findTopupPlan(plans?: PortalPlans, planId?: FSId): PortalPlans[number] | null {\n if (!plans) {\n return null;\n }\n\n const topupPlan = plans.find((plan) => {\n return (\n // Either search by the explicitly provided plan ID\n (isIdsEqual(plan.id!, planId ?? '') && !plan.is_hidden) ||\n // Or try to guess: A topup plan is where all pricing have one-off purchase set\n plan.pricing?.filter((p) => this.isValidPricing(p)).every((p) => p.lifetime_price)\n );\n });\n\n return topupPlan ?? null;\n }\n\n private isValidPricing(pricing: PricingEntity): boolean {\n return !!(pricing.monthly_price || pricing.annual_price || pricing.lifetime_price);\n }\n}\n","import { BILLING_CYCLE, CURRENCY } from '../contracts/types';\nimport {\n parseBillingCycle,\n parseNumber,\n idToString,\n isIdsEqual,\n parseDateTime,\n parseCurrency,\n parsePaymentMethod,\n} from '../api/parser';\nimport { PurchaseData, PurchaseEntitlementData } from '../contracts/purchase';\nimport { PricingTableData, FSId, UserEntity, LicenseEntity, SubscriptionEntity } from '../api/types';\n\nexport class PurchaseInfo implements PurchaseData {\n readonly email: string;\n readonly firstName: string;\n readonly lastName: string;\n readonly userId: string;\n readonly planId: string;\n readonly pricingId: string;\n readonly licenseId: string;\n readonly expiration: Date | null;\n readonly canceled: boolean;\n readonly subscriptionId: string | null;\n readonly billingCycle: BILLING_CYCLE | null;\n readonly quota: number | null;\n readonly initialAmount: number | null;\n readonly renewalAmount: number | null;\n readonly currency: CURRENCY | null;\n readonly renewalDate: Date | null;\n readonly paymentMethod: 'card' | 'paypal' | 'ideal' | null;\n readonly created: Date;\n\n constructor(user: UserEntity, license: LicenseEntity, subscription: SubscriptionEntity | null) {\n this.email = user.email!;\n this.firstName = user.first ?? '';\n this.lastName = user.last ?? '';\n this.userId = idToString(license.user_id!);\n this.canceled = license.is_cancelled ?? false;\n this.expiration = license.expiration ? parseDateTime(license.expiration) : null;\n this.licenseId = idToString(license.id!);\n this.planId = idToString(license.plan_id!);\n this.subscriptionId = null;\n this.billingCycle = null;\n this.quota = license.quota ?? null;\n this.pricingId = idToString(license.pricing_id!);\n this.initialAmount = null;\n this.renewalAmount = null;\n this.currency = null;\n this.renewalDate = null;\n this.paymentMethod = null;\n this.created = parseDateTime(license.created) ?? new Date();\n\n if (subscription) {\n this.subscriptionId = idToString(subscription.id!);\n this.billingCycle = parseBillingCycle(subscription.billing_cycle);\n this.initialAmount = parseNumber(subscription.initial_amount);\n this.renewalAmount = parseNumber(subscription.renewal_amount);\n this.currency = parseCurrency(subscription.currency);\n this.renewalDate = subscription.next_payment ? parseDateTime(subscription.next_payment) : null;\n this.paymentMethod = parsePaymentMethod(subscription.gateway);\n }\n }\n\n isPlan(planId: FSId): boolean {\n return this.planId === idToString(planId);\n }\n\n isFromPlans(planIds: FSId[]): boolean {\n return planIds.some((planId) => this.isPlan(planId));\n }\n\n toData(): PurchaseData {\n return {\n email: this.email,\n firstName: this.firstName,\n lastName: this.lastName,\n userId: this.userId,\n planId: this.planId,\n pricingId: this.pricingId,\n licenseId: this.licenseId,\n expiration: this.expiration,\n canceled: this.canceled,\n subscriptionId: this.subscriptionId,\n billingCycle: this.billingCycle,\n quota: this.quota,\n isActive: this.isActive,\n initialAmount: this.initialAmount,\n renewalAmount: this.renewalAmount,\n currency: this.currency,\n renewalDate: this.renewalDate,\n paymentMethod: this.paymentMethod,\n created: this.created,\n };\n }\n\n /**\n * A convenience method to convert the purchase info to a format suitable for database storage.\n */\n toEntitlementRecord<T extends Record<string, unknown>>(additionalData: T = {} as T): PurchaseEntitlementData & T {\n return {\n ...additionalData,\n fsLicenseId: this.licenseId,\n fsPlanId: this.planId,\n fsPricingId: this.pricingId,\n fsUserId: this.userId,\n type: this.isSubscription() ? 'subscription' : 'oneoff',\n expiration: this.expiration,\n createdAt: this.created,\n isCanceled: this.canceled,\n };\n }\n\n get isActive(): boolean {\n if (this.canceled) {\n return false;\n }\n\n if (this.expiration && this.expiration < new Date()) {\n return false;\n }\n\n return true;\n }\n\n isSubscription(): boolean {\n return this.subscriptionId !== null;\n }\n\n isAnnual(): boolean {\n return this.billingCycle === BILLING_CYCLE.YEARLY;\n }\n\n isMonthly(): boolean {\n return this.billingCycle === BILLING_CYCLE.MONTHLY;\n }\n\n isOneOff(): boolean {\n return this.billingCycle === BILLING_CYCLE.ONEOFF || this.billingCycle === null;\n }\n\n getPlanTitle(pricingData: PricingTableData | null): string {\n const plan = pricingData?.plans?.find((p) => isIdsEqual(p.id!, this.planId));\n\n return plan?.title ?? plan?.name ?? 'Deleted Plan';\n }\n}\n","import { isIdsEqual } from '../api/parser';\nimport { FSId, SubscriptionEntity, UserEntity } from '../api/types';\nimport { PurchaseData } from '../contracts/purchase';\nimport { PagingOptions } from '../contracts/types';\nimport { PurchaseInfo } from '../models/PurchaseInfo';\nimport { ApiService } from './ApiService';\n\nexport class PurchaseService {\n constructor(private readonly api: ApiService) {}\n\n /**\n * Retrieve purchase information from the Freemius API based on the license ID.\n *\n * The license is the primary entitlement for a purchase, and it may or may not be associated with a subscription.\n * With this method, you can retrieve detailed information about the purchase, including user details, plan, expiration, and more.\n */\n async retrievePurchase(licenseId: FSId): Promise<PurchaseInfo | null> {\n const [license, subscription] = await Promise.all([\n await this.api.license.retrieve(licenseId),\n await this.api.license.retrieveSubscription(licenseId),\n ]);\n\n if (!license) {\n return null;\n }\n\n const user = await this.api.user.retrieve(license.user_id!);\n\n if (!user) {\n return null;\n }\n\n return new PurchaseInfo(user, license, subscription);\n }\n\n /**\n * A helper method to retrieve raw purchase data instead of a full PurchaseInfo object.\n *\n * This is useful when passing data from server to client in frameworks like Next.js, where only serializable data should be sent.\n */\n async retrievePurchaseData(licenseId: FSId): Promise<PurchaseData | null> {\n const purchaseInfo = await this.retrievePurchase(licenseId);\n\n if (!purchaseInfo) {\n return null;\n }\n\n return purchaseInfo.toData();\n }\n\n async retrievePurchases(userOrEntity: FSId | UserEntity, pagination?: PagingOptions): Promise<PurchaseInfo[]> {\n const user = typeof userOrEntity === 'object' ? userOrEntity : await this.api.user.retrieve(userOrEntity);\n\n if (!user) {\n return [];\n }\n\n const licenses = await this.api.user.retrieveLicenses(user.id!, { type: 'active' }, pagination);\n\n if (!licenses || !licenses.length) {\n return [];\n }\n\n // @todo - Improve rate limiting by batching requests or using a more efficient method/ new API endpoint.\n const licenseSubscriptionPromises = licenses.map(async (license) => {\n const subscription =\n license.expiration !== null ? await this.api.license.retrieveSubscription(license.id!) : null;\n\n return new PurchaseInfo(user, license, subscription);\n });\n\n return await Promise.all(licenseSubscriptionPromises).then((results) =>\n results\n // Remove null values and sort by creation date, where the recent purchase is first.\n .filter((result): result is PurchaseInfo => result !== null)\n .sort((a, b) => b.created.getTime() - a.created.getTime())\n );\n }\n\n async retrievePurchasesData(userOrEntity: FSId | UserEntity, pagination?: PagingOptions): Promise<PurchaseData[]> {\n const purchaseInfos = await this.retrievePurchases(userOrEntity, pagination);\n\n return purchaseInfos.map((info) => info.toData());\n }\n\n /**\n * Retrieve a list of active subscriptions for a user. You can use this method to find or sync subscriptions from freemius to your system.\n */\n async retrieveSubscriptions(userOrEntity: FSId | UserEntity, pagination?: PagingOptions): Promise<PurchaseInfo[]> {\n const user = typeof userOrEntity === 'object' ? userOrEntity : await this.api.user.retrieve(userOrEntity);\n\n if (!user) {\n return [];\n }\n\n const subscriptions = await this.api.user.retrieveSubscriptions(\n user.id!,\n {\n filter: 'active',\n },\n pagination\n );\n\n if (!subscriptions || !subscriptions.length) {\n return [];\n }\n\n // @todo - Improve rate limiting by batching requests or using a more efficient method.\n const licenseSubscriptionPromises = subscriptions.map(async (subscription) => {\n const license = await this.api.license.retrieve(subscription.license_id!);\n\n if (!license) {\n return null;\n }\n\n return new PurchaseInfo(user, license, subscription);\n });\n\n return await Promise.all(licenseSubscriptionPromises).then((results) =>\n results\n // Remove null values and sort by creation date, where the recent purchase is first.\n .filter((result): result is PurchaseInfo => result !== null)\n .sort((a, b) => b.created.getTime() - a.created.getTime())\n );\n }\n\n /**\n * Retrieve a list of purchase data for a user.\n *\n * This is a convenience method that returns the purchase data in a format suitable for client-side rendering or serialization.\n */\n async retrieveSubscriptionsData(userId: FSId, pagination?: PagingOptions): Promise<PurchaseData[]> {\n const purchaseInfos = await this.retrieveSubscriptions(userId, pagination);\n\n return purchaseInfos.map((info) => info.toData());\n }\n\n async retrieveBySubscription(\n subscription: SubscriptionEntity,\n subscriptionUser?: UserEntity\n ): Promise<PurchaseInfo | null> {\n if (!subscription.license_id) {\n return null;\n }\n\n const license = await this.api.license.retrieve(subscription.license_id);\n\n if (!license) {\n return null;\n }\n\n const user =\n subscriptionUser && isIdsEqual(subscriptionUser.id!, license.user_id!)\n ? subscriptionUser\n : await this.api.user.retrieve(license.user_id!);\n\n if (!user) {\n return null;\n }\n\n return new PurchaseInfo(user, license, subscription);\n }\n\n async retrieveSubscriptionsByEmail(email: string, pagination?: PagingOptions): Promise<PurchaseInfo[]> {\n const user = await this.api.user.retrieveByEmail(email);\n\n if (!user) {\n return [];\n }\n\n return await this.retrieveSubscriptions(user.id!, pagination);\n }\n\n async retrievePurchasesByEmail(email: string, pagination?: PagingOptions): Promise<PurchaseInfo[]> {\n const user = await this.api.user.retrieveByEmail(email);\n\n if (!user) {\n return [];\n }\n\n return await this.retrievePurchases(user.id!, pagination);\n }\n}\n","import { parseDateTime } from '../api/parser';\nimport { PurchaseEntitlementData } from '../contracts/purchase';\nimport { UserRetriever } from '../contracts/types';\n\nexport class EntitlementService {\n /**\n * Get the active subscription entitlement from a list of entitlements stored in your own database.\n *\n * @param entitlements - Array of entitlements to filter\n * @returns The single active entitlement, or null if none found\n * @throws Error if multiple active entitlements are found\n */\n getActive<T extends PurchaseEntitlementData>(entitlements: T[] | null): T | null {\n const activeEntitlements = this.getActives(entitlements);\n\n if (!activeEntitlements || activeEntitlements.length === 0) {\n return null;\n }\n\n if (activeEntitlements.length > 1) {\n throw new Error(`Multiple active entitlements found: ${activeEntitlements.length} entitlements are active`);\n }\n\n return activeEntitlements[0]!;\n }\n\n /**\n * Get all active subscription entitlements from a list of entitlements stored in your own database.\n *\n * @param entitlements - Array of entitlements to filter\n * @returns Array of active entitlements, or null if none found\n */\n getActives<T extends PurchaseEntitlementData>(entitlements: T[] | null): T[] | null {\n if (!entitlements || entitlements.length === 0) {\n return null;\n }\n\n const activeEntitlements = entitlements.filter((entitlement) => {\n if (entitlement.type !== 'subscription') {\n return false;\n }\n\n if (entitlement.isCanceled) {\n return false;\n }\n\n if (entitlement.expiration === null) {\n return true;\n }\n\n const expiration =\n entitlement.expiration instanceof Date ? entitlement.expiration : parseDateTime(entitlement.expiration);\n\n if (expiration && expiration < new Date()) {\n return false;\n }\n\n return true;\n });\n\n return activeEntitlements.length > 0 ? activeEntitlements : null;\n }\n\n getFsUser(entitlement: PurchaseEntitlementData | null, email?: string): Awaited<ReturnType<UserRetriever>> {\n if (entitlement) {\n return {\n email,\n id: entitlement.fsUserId,\n primaryLicenseId: entitlement.fsLicenseId,\n } as Awaited<ReturnType<UserRetriever>>;\n }\n\n return email ? { email } : null;\n }\n\n /**\n * Calculates the number of complete months elapsed since the entitlement subscription was created.\n *\n * @param entitlement - The entitlement to check\n * @returns Number of complete months elapsed, or -1 if entitlement is null\n */\n getElapsedMonth(entitlement: PurchaseEntitlementData | null): number {\n if (!entitlement) {\n return -1;\n }\n\n const created =\n entitlement.createdAt instanceof Date ? entitlement.createdAt : parseDateTime(entitlement.createdAt);\n\n if (!created) {\n return -1;\n }\n\n const now = new Date();\n\n let months = (now.getFullYear() - created.getFullYear()) * 12 + (now.getMonth() - created.getMonth());\n\n // Adjust if current day is before the created day\n if (now.getDate() < created.getDate()) {\n months -= 1;\n }\n\n return months;\n }\n\n /**\n * Calculates the number of complete years elapsed since the entitlement subscription was created.\n *\n * @param entitlement - The entitlement to check\n * @returns Number of complete years elapsed, or -1 if entitlement is null\n */\n getElapsedYear(entitlement: PurchaseEntitlementData | null): number {\n if (!entitlement) {\n return -1;\n }\n\n const created =\n entitlement.createdAt instanceof Date ? entitlement.createdAt : parseDateTime(entitlement.createdAt);\n\n if (!created) {\n return -1;\n }\n\n const now = new Date();\n\n let years = now.getFullYear() - created.getFullYear();\n\n // Adjust if current month/day is before the created month/day\n if (\n now.getMonth() < created.getMonth() ||\n (now.getMonth() === created.getMonth() && now.getDate() < created.getDate())\n ) {\n years -= 1;\n }\n\n return years;\n }\n}\n","import { ApiService } from './services/ApiService';\nimport { CheckoutService } from './services/CheckoutService';\nimport { CustomerPortalService } from './services/CustomerPortalService';\nimport { FSId } from './api/types';\nimport { WebhookService } from './services/WebhookService';\nimport { AuthService } from './services/AuthService';\nimport { PricingService } from './services/PricingService';\nimport { PurchaseService } from './services/PurchaseService';\nimport { EntitlementService } from './services/EntitlementService';\n\nexport type FreemiusConfig = {\n productId: FSId;\n apiKey: string;\n secretKey: string;\n publicKey: string;\n};\n\nexport class Freemius {\n public readonly api: ApiService;\n\n public readonly checkout: CheckoutService;\n\n public readonly purchase: PurchaseService;\n\n public readonly customerPortal: CustomerPortalService;\n\n public readonly webhook: WebhookService;\n\n public readonly pricing: PricingService;\n\n public readonly entitlement = new EntitlementService();\n\n private readonly auth: AuthService;\n\n constructor(config: FreemiusConfig) {\n const { productId, apiKey, secretKey, publicKey } = config;\n\n this.api = new ApiService(productId, apiKey, secretKey, publicKey);\n this.auth = new AuthService(productId, secretKey);\n this.pricing = new PricingService(this.api);\n this.purchase = new PurchaseService(this.api);\n this.checkout = new CheckoutService(productId, publicKey, secretKey, this.purchase, this.pricing);\n this.customerPortal = new CustomerPortalService(this.api, this.checkout, this.auth, this.purchase);\n this.webhook = new WebhookService(secretKey);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["errorResponse: Record<string, unknown>","defaultPagingOptions: PagingOptions","client: FsApiClient","discountsMap: Map<string, SubscriptionDiscountEntity[]>","secretKey: string","publicKey: string","crypto","productId: string","publicKey: string","secretKey: string","pricing: PricingService","purchase: PurchaseService","callback?: PurchaseCallback","requestBody: unknown","secretKey: string","proxyUrl?: string","callback?: RedirectCallback","afterProcessUrl?: string","purchase: PurchaseService","pricing: PricingService","secretKey: string","actionHandlers: CheckoutAction[]","api: ApiService","productId: FSId","publicKey: string","secretKey: string","purchase: PurchaseService","pricing: PricingService","api: ApiService","action: CustomerPortalActionService","checkout: CheckoutService","checkoutOptions: CheckoutOptions","portalData: PortalData","plans: Map<string, PlanEntity>","pricings: Map<string, PricingEntity>","portalSubscriptions: PortalSubscriptions","subscriptionData: PortalSubscription","repository: PortalDataRepository","getUser: UserRetriever","endpoint: string","isSandbox?: boolean","purchase: PurchaseService","user: UserRetriever","callback?: RestoreCallback","subscriptionOnly: boolean","purchases: PurchaseInfo[] | null","repository: PortalDataRepository","action: CustomerPortalActionService","purchase: PurchaseService","actionHandlers: PortalAction[]","schema","api: ApiService","auth: AuthService","requestBody: unknown","payload: BillingUpdatePayload","api: ApiService","auth: AuthService","schema","schema","api: ApiService","auth: AuthService","requestBody: unknown","api: ApiService","auth: AuthService","requestBody: unknown","api: ApiService","authService: AuthService","api: ApiService","checkout: CheckoutService","authService: AuthService","purchase: PurchaseService","api: ApiService","secretKey: string","onError: (error: unknown) => Promise<void>","authenticationMethod: WebhookAuthenticationMethod","api: ApiService","secretKey: string","chunks: Buffer[]","productId: FSId","secretKey: string","api: ApiService","plans: PricingData['plans']","api: ApiService"],"sources":["../src/contracts/types.ts","../src/contracts/webhook.ts","../src/api/parser.ts","../src/errors/ActionError.ts","../src/errors/WebhookError.ts","../package.json","../src/api/client.ts","../src/api/ApiBase.ts","../src/api/License.ts","../src/api/Product.ts","../src/api/Subscription.ts","../src/api/User.ts","../src/api/Payment.ts","../src/api/WebhookEvent.ts","../src/utils/ops.ts","../src/services/ApiService.ts","../src/checkout/Checkout.ts","../src/checkout/PricingRetriever.ts","../src/checkout/PurchaseProcessor.ts","../src/models/CheckoutRedirectInfo.ts","../src/checkout/RedirectProcessor.ts","../src/checkout/CheckoutRequestProcessor.ts","../src/services/CheckoutService.ts","../src/customer-portal/PortalDataRepository.ts","../src/customer-portal/PortalDataRetriever.ts","../src/customer-portal/PurchaseRestorer.ts","../src/customer-portal/PortalRequestProcessor.ts","../src/customer-portal/BillingAction.ts","../src/customer-portal/InvoiceAction.ts","../src/customer-portal/SubscriptionCancellationAction.ts","../src/customer-portal/SubscriptionRenewalCouponAction.ts","../src/customer-portal/CustomerPortalActionService.ts","../src/services/CustomerPortalService.ts","../src/webhook/WebhookListener.ts","../src/services/WebhookService.ts","../src/services/AuthService.ts","../src/services/PricingService.ts","../src/models/PurchaseInfo.ts","../src/services/PurchaseService.ts","../src/services/EntitlementService.ts","../src/Freemius.ts"],"sourcesContent":["import { FSId } from '../api/types';\n\n/**\n * This file holds all generic types used across the SDK, not specific to any contract.\n */\nexport enum BILLING_CYCLE {\n MONTHLY = 'monthly',\n YEARLY = 'yearly',\n ONEOFF = 'oneoff',\n}\n\nexport interface PagingOptions {\n count?: number;\n offset?: number;\n}\n\nexport type ApiEntitiesFilter<T> = Omit<NonNullable<T>, 'count' | 'offset'>;\n\nexport enum CURRENCY {\n USD = 'USD',\n EUR = 'EUR',\n GBP = 'GBP',\n}\n\nexport type PaymentMethod = 'card' | 'paypal' | 'ideal';\n\nexport type UserRetriever = () => Promise<\n { id: FSId; primaryLicenseId?: FSId; email?: string } | { email: string } | null\n>;\n\n/**\n * @todo - Add a more unified way to get handlers so that we can simplify the Checkout & Customer Portal request processors.\n */\nexport interface RequestProcessor<Config> {\n createProcessor(config: Config): (request: Request) => Promise<Response>;\n\n process(config: Config, request: Request): Promise<Response>;\n}\n","export type WebhookListenerResponse = {\n status: number;\n} & ({ success: true } | { success: false; error: string });\n\nexport enum WebhookAuthenticationMethod {\n SignatureHeader = 'SignatureHeader',\n Api = 'Api',\n}\n","import { BillingCycleApiEnum, CurrencyApiEnum, FSId } from './types';\nimport { BILLING_CYCLE, CURRENCY, PaymentMethod } from '../contracts/types';\n\nexport function idToNumber(id: FSId): number {\n if (typeof id === 'number') {\n return id;\n } else if (typeof id === 'bigint') {\n return Number(id);\n } else if (typeof id === 'string') {\n const parsed = Number.parseInt(id, 10);\n\n if (Number.isNaN(parsed)) {\n throw new Error(`Invalid FSId: ${id}`);\n }\n\n return parsed;\n } else {\n throw new Error(`Unsupported FSId type: ${typeof id}`);\n }\n}\n\nexport function idToString(id: FSId): string {\n if (typeof id === 'string') {\n return id;\n } else if (typeof id === 'number' || typeof id === 'bigint') {\n return String(id);\n } else {\n throw new Error(`Unsupported FSId type: ${typeof id}`);\n }\n}\n\nexport function isIdsEqual(id1: FSId, id2: FSId): boolean {\n return idToString(id1) === idToString(id2);\n}\n\nexport function parseBillingCycle(cycle?: BillingCycleApiEnum): BILLING_CYCLE {\n const billingCycle = Number.parseInt(cycle?.toString() ?? '', 10);\n\n if (billingCycle === 1) {\n return BILLING_CYCLE.MONTHLY;\n }\n\n if (billingCycle === 12) {\n return BILLING_CYCLE.YEARLY;\n }\n\n return BILLING_CYCLE.ONEOFF;\n}\n\nexport function parseNumber(value: unknown): number | null {\n if (typeof value === 'number') {\n return value;\n } else if (typeof value === 'string') {\n const parsed = Number.parseFloat(value);\n return Number.isNaN(parsed) ? null : parsed;\n } else {\n return null;\n }\n}\n\nexport function parseDateTime(dateString?: string | null): Date | null {\n if (!dateString) {\n return null;\n }\n\n // Freemius date format is \"YYYY-MM-DD HH:mm:ss\" in UTC\n const dateParts = dateString.split(' ');\n if (dateParts.length !== 2) {\n return null;\n }\n\n const date = dateParts[0]!.split('-');\n const time = dateParts[1]!.split(':');\n if (date.length !== 3 || time.length !== 3) {\n return null;\n }\n\n const year = Number.parseInt(date[0]!);\n const month = Number.parseInt(date[1]!) - 1; // Months are zero-based\n const day = Number.parseInt(date[2]!);\n const hours = Number.parseInt(time[0]!);\n const minutes = Number.parseInt(time[1]!);\n const seconds = Number.parseInt(time[2]!);\n\n if (\n Number.isNaN(year) ||\n Number.isNaN(month) ||\n Number.isNaN(day) ||\n Number.isNaN(hours) ||\n Number.isNaN(minutes) ||\n Number.isNaN(seconds)\n ) {\n return null;\n }\n\n const utcDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds, 0));\n\n return utcDate;\n}\n\nexport function parseDate(dateString?: string | null): Date | null {\n if (!dateString) {\n return null;\n }\n\n // Freemius date format is \"YYYY-MM-DD\"\n return parseDateTime(dateString + ' 00:00:00');\n}\n\nexport function parseCurrency(currency?: CurrencyApiEnum): CURRENCY | null {\n switch (currency?.toLowerCase?.()) {\n case 'usd':\n return CURRENCY.USD;\n case 'eur':\n return CURRENCY.EUR;\n case 'gbp':\n return CURRENCY.GBP;\n default:\n return null;\n }\n}\n\nexport function parsePaymentMethod(gateway?: string | null): PaymentMethod | null {\n return gateway?.startsWith('stripe') ? 'card' : gateway?.startsWith('paypal') ? 'paypal' : null;\n}\n","export class ActionError extends Error {\n public readonly statusCode: number;\n public readonly validationIssues?: unknown;\n\n constructor(message: string, statusCode: number = 400, validationIssues?: unknown) {\n super(message);\n this.name = 'ActionError';\n this.statusCode = statusCode;\n this.validationIssues = validationIssues;\n }\n\n toResponse(): Response {\n const errorResponse: Record<string, unknown> = {\n message: this.message,\n };\n\n if (this.validationIssues) {\n errorResponse.issues = this.validationIssues;\n }\n\n return Response.json(errorResponse, { status: this.statusCode });\n }\n\n // Essential factory methods\n static badRequest(message: string): ActionError {\n return new ActionError(message, 400);\n }\n\n static unauthorized(message: string = 'Unauthorized'): ActionError {\n return new ActionError(message, 401);\n }\n\n static notFound(message: string = 'Not found'): ActionError {\n return new ActionError(message, 404);\n }\n\n static validationFailed(message: string, validationIssues: unknown): ActionError {\n return new ActionError(message, 400, validationIssues);\n }\n\n static internalError(message: string = 'Internal server error'): ActionError {\n return new ActionError(message, 500);\n }\n}\n","import { WebhookListenerResponse } from '../contracts/webhook';\n\nexport class WebhookError extends Error {\n public readonly statusCode: number;\n\n constructor(message: string, statusCode: number = 400) {\n super(message);\n this.name = 'WebhookError';\n this.statusCode = statusCode;\n }\n\n toResponse(): WebhookListenerResponse {\n return {\n status: this.statusCode,\n success: false,\n error: this.message,\n };\n }\n}\n","{\n \"name\": \"@freemius/sdk\",\n \"version\": \"0.2.0\",\n \"description\": \"JS SDK for integrating your SaaS with Freemius\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"import\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n }\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"dev\": \"tsdown --watch\",\n \"clean\": \"rm -rf dist\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"echo \\\"No tests yet\\\"\",\n \"openapi:generate\": \"openapi-typescript https://freemius.com/help/documentation/api/openapi.yaml -o ./src/api/schema.d.ts\",\n \"openapi:generate:local\": \"openapi-typescript http://api-doc.freemius-local.com:8080/openapi.yaml -o ./src/api/schema.d.ts\",\n \"prepublish\": \"npm run build\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+ssh://git@github.com/Freemius/freemius-js.git\"\n },\n \"keywords\": [\n \"node.js\",\n \"freemius\",\n \"sdk\",\n \"saas\"\n ],\n \"author\": \"Freemius Inc\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/Freemius/freemius-js/issues\"\n },\n \"homepage\": \"http://freemius.com/help/documentation/saas-sdk/js-sdk/\",\n \"dependencies\": {\n \"openapi-fetch\": \"^0.14.0\"\n },\n \"peerDependencies\": {\n \"zod\": \"^4.0.0\",\n \"@freemius/checkout\": \"^1.4.0\"\n },\n \"devDependencies\": {\n \"zod\": \"^4.0.0\",\n \"@types/node\": \"^24.2.0\",\n \"tsdown\": \"^0.14.1\",\n \"typescript\": \"^5.9.2\"\n }\n}\n","import createClient from 'openapi-fetch';\nimport type { paths } from './schema';\nimport { version } from '../../package.json';\n\nfunction detectPlatform(): string {\n // Check for Bun runtime\n if (typeof globalThis !== 'undefined' && 'Bun' in globalThis) return 'Bun';\n\n // Check for Deno runtime\n if (typeof globalThis !== 'undefined' && 'Deno' in globalThis) return 'Deno';\n\n // Check for Node.js\n if (\n typeof globalThis !== 'undefined' &&\n 'process' in globalThis &&\n globalThis.process &&\n typeof globalThis.process === 'object' &&\n 'versions' in globalThis.process &&\n globalThis.process.versions &&\n 'node' in globalThis.process.versions\n ) {\n return 'Node';\n }\n\n // Check for browser environment\n if (typeof globalThis !== 'undefined' && 'window' in globalThis) return 'Browser';\n\n return 'Unknown';\n}\n\nexport function createApiClient(baseUrl: string, bearerToken?: string) {\n const platform = detectPlatform();\n\n const client = createClient<paths>({\n baseUrl,\n headers: {\n Authorization: `Bearer ${bearerToken}`,\n 'User-Agent': `Freemius-JS-SDK/${version} (${platform})`,\n },\n });\n\n return client;\n}\n\nexport type FsApiClient = ReturnType<typeof createApiClient>;\n","import { PagingOptions } from '../contracts/types';\nimport { idToNumber } from './parser';\nimport { FsApiClient } from './client';\nimport { FSId } from './types';\nimport { QuerySerializerOptions } from 'openapi-fetch';\n\nexport const PAGING_MAX_LIMIT = 50;\nexport const PAGING_DEFAULT_LIMIT = PAGING_MAX_LIMIT;\n\nexport const defaultPagingOptions: PagingOptions = {\n count: PAGING_DEFAULT_LIMIT,\n offset: 0,\n};\n\nexport abstract class ApiBase<EntityType, FilterType extends Record<string, unknown> = Record<string, unknown>> {\n public readonly productId: number;\n\n constructor(\n productId: FSId,\n protected readonly client: FsApiClient\n ) {\n this.productId = idToNumber(productId);\n }\n\n abstract retrieve(id: FSId): Promise<EntityType | null>;\n\n abstract retrieveMany(filter?: FilterType, pagination?: PagingOptions): Promise<EntityType[]>;\n\n /**\n * Async generator that yields all entities by paginating through retrieveMany.\n * @param filter Optional filter for entities\n * @param pageSize Optional page size (default: PAGING_DEFAULT_LIMIT)\n *\n * @example\n * // Usage example:\n * for await (const entity of apiInstance.iterateAll({ status: 'active' })) {\n * console.log(entity);\n * }\n */\n async *iterateAll(\n filter?: FilterType,\n pageSize: number = PAGING_DEFAULT_LIMIT\n ): AsyncGenerator<EntityType, void, unknown> {\n let offset = 0;\n\n while (true) {\n const page = await this.retrieveMany(filter, { count: pageSize, offset });\n\n if (!page.length) {\n break;\n }\n\n for (const entity of page) {\n yield entity;\n }\n\n if (page.length < pageSize) {\n break;\n }\n\n offset += page.length;\n }\n }\n\n // Helper methods\n\n getPagingParams(paging: PagingOptions = defaultPagingOptions): { count: number; offset: number } {\n return {\n count: paging.count ?? PAGING_DEFAULT_LIMIT,\n offset: paging.offset ?? 0,\n };\n }\n\n getIdForPath(id: FSId): number {\n return idToNumber(id);\n }\n\n isGoodResponse(response: Response): boolean {\n return response.status >= 200 && response.status < 300;\n }\n\n /**\n * @note - We must use this serializer when sending arrays as query parameter to our API.\n */\n getQuerySerializerForArray(): QuerySerializerOptions {\n return {\n array: {\n explode: false,\n style: 'form',\n },\n };\n }\n}\n","import { PagingOptions } from '../contracts/types';\nimport { ApiBase } from './ApiBase';\nimport { LicenseEntity, LicenseFilterOptions, SubscriptionEntity, FSId } from './types';\n\nexport class License extends ApiBase<LicenseEntity, LicenseFilterOptions> {\n async retrieve(licenseId: FSId) {\n const licenseResponse = await this.client.GET(`/products/{product_id}/licenses/{license_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n license_id: this.getIdForPath(licenseId),\n },\n },\n });\n\n if (!this.isGoodResponse(licenseResponse.response) || !licenseResponse.data || !licenseResponse.data.id) {\n return null;\n }\n\n return licenseResponse.data;\n }\n\n async retrieveMany(filter?: LicenseFilterOptions, pagination?: PagingOptions) {\n const response = await this.client.GET(`/products/{product_id}/licenses.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n ...this.getPagingParams(pagination),\n ...(filter ?? {}),\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data || !Array.isArray(response.data.licenses)) {\n return [];\n }\n\n return response.data.licenses;\n }\n\n async retrieveSubscription(licenseId: FSId): Promise<SubscriptionEntity | null> {\n const subscriptionResponse = await this.client.GET(\n `/products/{product_id}/licenses/{license_id}/subscription.json`,\n {\n params: {\n path: {\n product_id: this.productId,\n license_id: this.getIdForPath(licenseId),\n },\n },\n }\n );\n\n if (\n !this.isGoodResponse(subscriptionResponse.response) ||\n !subscriptionResponse.data ||\n !subscriptionResponse.data.id\n ) {\n return null;\n }\n\n return subscriptionResponse.data;\n }\n\n async retrieveCheckoutUpgradeAuthorization(licenseId: FSId): Promise<string | null> {\n const response = await this.client.POST(`/products/{product_id}/licenses/{license_id}/checkout/link.json`, {\n params: {\n path: {\n product_id: this.productId,\n license_id: this.getIdForPath(licenseId),\n },\n },\n body: {\n is_payment_method_update: true,\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data || !response.data.settings) {\n return null;\n }\n\n return response.data.settings.authorization as string;\n }\n}\n","import { ApiBase } from './ApiBase';\nimport { ProductEntity, PricingTableData, CouponEntityEnriched } from './types';\n\nexport class Product extends ApiBase<ProductEntity, never> {\n async retrieve(): Promise<ProductEntity | null> {\n const response = await this.client.GET(`/products/{product_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data) {\n return null;\n }\n\n return response.data;\n }\n\n async retrieveMany(): Promise<ProductEntity[]> {\n throw new Error('retrieveMany is not supported for Product API');\n }\n\n async retrievePricingData(): Promise<PricingTableData | null> {\n const response = await this.client.GET(`/products/{product_id}/pricing.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data) {\n return null;\n }\n\n return response.data;\n }\n\n async retrieveSubscriptionCancellationCoupon(): Promise<CouponEntityEnriched[] | null> {\n const response = await this.client.GET(`/products/{product_id}/coupons/special.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n type: 'subscription_cancellation',\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data || !response.data.coupon_entities) {\n return null;\n }\n\n return response.data.coupon_entities;\n }\n}\n","import { PagingOptions } from '../contracts/types';\nimport { ApiBase } from './ApiBase';\nimport {\n SubscriptionEntity,\n SubscriptionFilterOptions,\n FSId,\n SubscriptionCancellationReasonType,\n SubscriptionCancellationResult,\n SubscriptionRenewalCouponResult,\n} from './types';\n\nexport class Subscription extends ApiBase<SubscriptionEntity, SubscriptionFilterOptions> {\n async retrieve(subscriptionId: FSId) {\n const result = await this.client.GET(`/products/{product_id}/subscriptions/{subscription_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n subscription_id: this.getIdForPath(subscriptionId),\n },\n },\n });\n\n if (!this.isGoodResponse(result.response) || !result.data || !result.data.id) {\n return null;\n }\n\n return result.data;\n }\n\n async retrieveMany(filter?: SubscriptionFilterOptions, pagination?: PagingOptions) {\n const result = await this.client.GET(`/products/{product_id}/subscriptions.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n ...this.getPagingParams(pagination),\n ...(filter ?? {}),\n },\n },\n });\n\n if (!this.isGoodResponse(result.response) || !result.data || !Array.isArray(result.data.subscriptions)) {\n return [];\n }\n\n return result.data.subscriptions;\n }\n\n async applyRenewalCoupon(\n subscriptionId: FSId,\n couponId: string,\n logAutoRenew: boolean\n ): Promise<SubscriptionRenewalCouponResult | null> {\n const result = await this.client.PUT(`/products/{product_id}/subscriptions/{subscription_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n subscription_id: this.getIdForPath(subscriptionId),\n },\n },\n body: {\n auto_renew: logAutoRenew,\n coupon_id: Number.parseInt(couponId, 10),\n },\n });\n\n if (!this.isGoodResponse(result.response) || !result.data || !result.data.id) {\n return null;\n }\n\n return result.data;\n }\n\n async cancel(\n subscriptionId: FSId,\n feedback?: string,\n reasonIds?: SubscriptionCancellationReasonType[]\n ): Promise<SubscriptionCancellationResult | null> {\n const result = await this.client.DELETE(`/products/{product_id}/subscriptions/{subscription_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n subscription_id: this.getIdForPath(subscriptionId),\n },\n query: {\n reason: feedback,\n reason_ids: reasonIds ?? [],\n },\n },\n querySerializer: this.getQuerySerializerForArray(),\n });\n\n if (!this.isGoodResponse(result.response) || !result.data || !result.data.id) {\n return null;\n }\n\n return result.data;\n }\n}\n","import { PagingOptions } from '../contracts/types';\nimport { ApiBase } from './ApiBase';\nimport { idToString } from './parser';\nimport {\n LicenseEntity,\n PaymentEntity,\n UserBillingEntity,\n UserEntity,\n UserFilterOptions,\n FSId,\n UserSubscriptionFilterOptions,\n UserLicenseFilterOptions,\n UserPaymentFilterOptions,\n BillingUpdatePayload,\n UserSubscriptionWithDiscounts,\n SubscriptionDiscountEntity,\n UserCustomerPortalResult,\n} from './types';\n\nconst USER_FIELDS = 'email,first,last,picture,is_verified,id,created,updated,is_marketing_allowed';\n\nexport class User extends ApiBase<UserEntity, UserFilterOptions> {\n async retrieve(userId: FSId) {\n const userResponse = await this.client.GET(`/products/{product_id}/users/{user_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n user_id: this.getIdForPath(userId),\n },\n query: {\n fields: USER_FIELDS,\n },\n },\n });\n\n if (userResponse.response.status !== 200 || !userResponse.data || !userResponse.data.id) {\n return null;\n }\n\n return userResponse.data;\n }\n\n async retrieveMany(filter?: UserFilterOptions, pagination?: PagingOptions) {\n const response = await this.client.GET(`/products/{product_id}/users.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n ...this.getPagingParams(pagination),\n ...(filter ?? {}),\n fields: USER_FIELDS,\n },\n },\n });\n\n if (response.response.status !== 200 || !response.data || !Array.isArray(response.data.users)) {\n return [];\n }\n\n return response.data.users;\n }\n\n async retrieveByEmail(email: string): Promise<UserEntity | null> {\n const response = await this.client.GET(`/products/{product_id}/users.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n email,\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !Array.isArray(response.data?.users)) {\n return null;\n }\n\n return response.data.users?.[0] ?? null;\n }\n\n async retrieveBilling(userId: FSId): Promise<UserBillingEntity | null> {\n const billingResponse = await this.client.GET(`/products/{product_id}/users/{user_id}/billing.json`, {\n params: {\n path: {\n product_id: this.productId,\n user_id: this.getIdForPath(userId),\n },\n },\n });\n\n if (billingResponse.response.status !== 200 || !billingResponse.data || !billingResponse.data) {\n return null;\n }\n\n return billingResponse.data;\n }\n\n async retrieveSubscriptions(\n userId: FSId,\n filters?: UserSubscriptionFilterOptions,\n pagination?: PagingOptions\n ): Promise<UserSubscriptionWithDiscounts[]> {\n const result = await this.client.GET(`/products/{product_id}/users/{user_id}/subscriptions.json`, {\n params: {\n path: {\n product_id: this.productId,\n user_id: this.getIdForPath(userId),\n },\n query: {\n ...(filters ?? {}),\n ...this.getPagingParams(pagination),\n },\n },\n });\n\n if (!this.isGoodResponse(result.response) || !result.data || !Array.isArray(result.data.subscriptions)) {\n return [];\n }\n\n const discountsMap: Map<string, SubscriptionDiscountEntity[]> = new Map();\n\n if (result.data.discounts) {\n Object.entries(result.data.discounts).forEach(([subscriptionId, discounts]) => {\n discountsMap.set(idToString(subscriptionId), discounts);\n });\n }\n\n return result.data.subscriptions.map((subscription) => ({\n ...subscription,\n discounts: discountsMap.get(idToString(subscription.id!)) || [],\n }));\n }\n\n async retrieveLicenses(\n userId: FSId,\n filters?: UserLicenseFilterOptions,\n pagination?: PagingOptions\n ): Promise<LicenseEntity[]> {\n const response = await this.client.GET(`/products/{product_id}/users/{user_id}/licenses.json`, {\n params: {\n path: {\n product_id: this.productId,\n user_id: this.getIdForPath(userId),\n },\n query: {\n ...(filters ?? {}),\n ...this.getPagingParams(pagination),\n },\n },\n });\n\n if (response.response.status !== 200 || !response.data || !Array.isArray(response.data.licenses)) {\n return [];\n }\n\n return response.data.licenses;\n }\n\n async retrievePayments(\n userId: FSId,\n filters?: UserPaymentFilterOptions,\n pagination?: PagingOptions\n ): Promise<PaymentEntity[]> {\n const response = await this.client.GET(`/products/{product_id}/users/{user_id}/payments.json`, {\n params: {\n path: {\n product_id: this.productId,\n user_id: this.getIdForPath(userId),\n },\n query: {\n ...(filters ?? {}),\n ...this.getPagingParams(pagination),\n },\n },\n });\n\n if (response.response.status !== 200 || !response.data || !Array.isArray(response.data.payments)) {\n return [];\n }\n\n return response.data.payments;\n }\n\n async updateBilling(userId: FSId, payload: BillingUpdatePayload): Promise<UserBillingEntity | null> {\n const response = await this.client.PUT(`/products/{product_id}/users/{user_id}/billing.json`, {\n params: {\n path: {\n product_id: this.productId,\n user_id: this.getIdForPath(userId),\n },\n },\n body: payload,\n });\n\n if (!this.isGoodResponse(response.response) || !response.data || !response.data) {\n return null;\n }\n\n return response.data;\n }\n\n async retrieveHostedCustomerPortal(userId: FSId): Promise<UserCustomerPortalResult | null> {\n const response = await this.client.POST(`/products/{product_id}/portal/login.json`, {\n params: {\n path: { product_id: this.productId },\n },\n body: {\n id: idToString(userId),\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data) {\n return null;\n }\n\n return response.data;\n }\n\n async retrieveHostedCustomerPortalByEmail(email: string): Promise<UserCustomerPortalResult | null> {\n const response = await this.client.POST(`/products/{product_id}/portal/login.json`, {\n params: {\n path: { product_id: this.productId },\n },\n body: {\n email,\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data) {\n return null;\n }\n\n return response.data;\n }\n}\n","import { PagingOptions } from '../contracts/types';\nimport { ApiBase } from './ApiBase';\nimport { FSId, PaymentEntity, PaymentFilterOptions } from './types';\n\nexport class Payment extends ApiBase<PaymentEntity, PaymentFilterOptions> {\n async retrieve(paymentId: FSId): Promise<PaymentEntity | null> {\n const response = await this.client.GET(`/products/{product_id}/payments/{payment_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n payment_id: this.getIdForPath(paymentId),\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data || !response.data.id) {\n return null;\n }\n\n return response.data;\n }\n\n async retrieveMany(filter?: PaymentFilterOptions, pagination?: PagingOptions) {\n const response = await this.client.GET(`/products/{product_id}/payments.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n ...this.getPagingParams(pagination),\n ...(filter ?? {}),\n },\n },\n });\n\n if (!this.isGoodResponse(response.response) || !response.data || !Array.isArray(response.data.payments)) {\n return [];\n }\n\n return response.data.payments;\n }\n\n async retrieveInvoice(paymentId: FSId) {\n const response = await this.client.GET(`/products/{product_id}/payments/{payment_id}/invoice.pdf`, {\n params: {\n path: {\n payment_id: this.getIdForPath(paymentId),\n product_id: this.productId,\n },\n },\n parseAs: 'blob', // Expecting a PDF blob response\n });\n\n if (!this.isGoodResponse(response.response) || !response.data) {\n return null;\n }\n\n return response.data as Blob; // Assuming the response is a PDF blob\n }\n}\n","import { PagingOptions } from '../contracts/types';\nimport { ApiBase } from './ApiBase';\nimport { EventEntity, EventFilterOptions, FSId } from './types';\n\nexport class WebhookEvent extends ApiBase<EventEntity, EventFilterOptions> {\n async retrieve(eventId: FSId) {\n const result = await this.client.GET(`/products/{product_id}/events/{event_id}.json`, {\n params: {\n path: {\n product_id: this.productId,\n event_id: this.getIdForPath(eventId),\n },\n },\n });\n\n if (!this.isGoodResponse(result.response) || !result.data || !result.data.id) {\n return null;\n }\n\n return result.data;\n }\n\n async retrieveMany(filter?: EventFilterOptions, pagination?: PagingOptions) {\n const result = await this.client.GET(`/products/{product_id}/events.json`, {\n params: {\n path: {\n product_id: this.productId,\n },\n query: {\n ...this.getPagingParams(pagination),\n ...(filter ?? {}),\n },\n },\n });\n\n if (!this.isGoodResponse(result.response) || !result.data || !Array.isArray(result.data.events)) {\n return [];\n }\n\n return result.data.events;\n }\n}\n","export function splitName(name: string): { firstName: string; lastName: string } {\n const parts = name.split(' ');\n\n return {\n firstName: parts[0] ?? '',\n lastName: parts.slice(1).join(' ') ?? '',\n };\n}\n\nexport function isTestServer(): boolean {\n return process.env.FREEMIUS_INTERNAL__IS_DEVELOPMENT_MODE === 'true';\n}\n","import * as crypto from 'crypto';\nimport { ApiAuthParams, FSId } from '../api/types';\nimport { FsApiClient, createApiClient } from '../api/client';\nimport { License } from '../api/License';\nimport { Product } from '../api/Product';\nimport { Subscription } from '../api/Subscription';\nimport { User } from '../api/User';\nimport { idToString } from '../api/parser';\nimport { Payment } from '../api/Payment';\nimport { WebhookEvent } from '../api/WebhookEvent';\nimport { isTestServer } from '../utils/ops';\n\nconst API_ENDPOINT_PRODUCTION = 'https://api.freemius.com/v1/';\nconst API_ENDPOINT_TEST = 'http://api.freemius-local.com:8080/v1/';\n\n/**\n * @todo - Add a proper user-agent string with SDK version.\n */\nexport class ApiService {\n private readonly client: FsApiClient;\n\n public readonly productId: string;\n\n public readonly user: User;\n\n public readonly license: License;\n\n public readonly product: Product;\n\n public readonly subscription: Subscription;\n\n public readonly payment: Payment;\n\n public readonly event: WebhookEvent;\n\n public readonly baseUrl: string;\n\n constructor(\n productId: FSId,\n apiKey: string,\n private readonly secretKey: string,\n private readonly publicKey: string\n ) {\n this.baseUrl = isTestServer() ? API_ENDPOINT_TEST : API_ENDPOINT_PRODUCTION;\n\n this.client = createApiClient(this.baseUrl, apiKey);\n this.productId = idToString(productId);\n\n this.user = new User(this.productId, this.client);\n this.license = new License(this.productId, this.client);\n this.product = new Product(this.productId, this.client);\n this.subscription = new Subscription(this.productId, this.client);\n this.payment = new Payment(this.productId, this.client);\n this.event = new WebhookEvent(this.productId, this.client);\n }\n\n /**\n * Low level API client for direct access to the Freemius API.\n * Use this for advanced use cases where you need to make custom API calls.\n *\n * For regular operations, prefer using the provided services like `User`, `Subscription`, `License` etc.\n */\n get __unstable_ApiClient(): FsApiClient {\n return this.client;\n }\n\n public createSignedUrl(path: string): string {\n return this.getSignedUrl(this.createUrl(path));\n }\n\n public createUrl(path: string): string {\n // Trim leading slashes to avoid double slashes in the URL\n path = path.replace(/^\\/+/, '');\n\n return `${this.baseUrl}products/${this.productId}/${path}`;\n }\n\n /**\n * Generate signed URL for the given full URL. The authentication is valid for 15 minutes only.\n */\n public getSignedUrl(fullUrl: string): string {\n const url = new URL(fullUrl);\n const resourcePath = url.pathname;\n\n const auth = this.generateAuthorizationParams(resourcePath);\n\n // Build query parameters\n url.searchParams.set('auth_date', auth.date);\n url.searchParams.set('authorization', auth.authorization);\n\n return url.toString();\n }\n\n /**\n * Generate authorization parameters for signing\n */\n public generateAuthorizationParams(\n resourcePath: string,\n method: 'POST' | 'PUT' | 'GET' | 'DELETE' = 'GET',\n jsonEncodedParams: string = '',\n contentType: string = ''\n ): ApiAuthParams {\n const eol = '\\n';\n let contentMd5 = '';\n const date = this.toDateTimeString(new Date());\n\n if (['POST', 'PUT'].includes(method) && jsonEncodedParams) {\n contentMd5 = crypto.createHash('md5').update(jsonEncodedParams).digest('hex');\n }\n\n const stringToSign = [method, contentMd5, contentType, date, resourcePath].join(eol);\n\n // If secret and public keys are identical, it means that\n // the signature uses public key hash encoding.\n const authType = this.secretKey !== this.publicKey ? 'FS' : 'FSP';\n\n const signature = crypto.createHmac('sha256', this.secretKey).update(stringToSign).digest('hex');\n const base64 = this.base64UrlEncode(signature);\n\n return {\n date,\n authorization: `${authType} ${this.productId}:${this.publicKey}:${base64}`,\n };\n }\n\n /**\n * Base64 encoding that doesn't need to be urlencode()ed.\n * Exactly the same as base64_encode except it uses\n * - instead of +\n * _ instead of /\n */\n private base64UrlEncode(input: string): string {\n return Buffer.from(input, 'utf8').toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, ''); // Remove padding\n }\n\n private toDateTimeString(date: Date): string {\n // Format date as \"YYYY-MM-DD HH:mm:ss\"\n const year = date.getUTCFullYear();\n const month = String(date.getUTCMonth() + 1).padStart(2, '0'); // Months are zero-based\n const day = String(date.getUTCDate()).padStart(2, '0');\n const hours = String(date.getUTCHours()).padStart(2, '0');\n const minutes = String(date.getUTCMinutes()).padStart(2, '0');\n const seconds = String(date.getUTCSeconds()).padStart(2, '0');\n\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;\n }\n}\n","import {\n CheckoutOptions,\n CheckoutPopupParams,\n convertCheckoutOptionsToQueryParams,\n buildFreemiusQueryFromOptions,\n} from '@freemius/checkout';\nimport { isTestServer, splitName } from '../utils/ops';\nimport { CheckoutBuilderUserOptions } from '../contracts/checkout';\nimport { FSId } from '../api/types';\n\nexport type CheckoutSerialized = {\n options: CheckoutOptions;\n link: string;\n baseUrl: string;\n};\n\nexport type CheckoutLicenseAuthorization = { licenseId: FSId; authorization: string };\n\n/**\n * A builder class for constructing checkout parameters. This class provides a fluent\n * API to create Checkout parameters for a product with various configurations.\n *\n * Every method returns the existing instance of the builder for chainability,\n * The final `getOptions()` method returns the constructed `CheckoutOptions` object.\n */\nexport class Checkout {\n private options: CheckoutOptions;\n\n constructor(\n private readonly productId: string,\n private readonly publicKey: string,\n private readonly secretKey: string\n ) {\n this.options = { product_id: productId };\n }\n\n /**\n * Enables sandbox mode for testing purposes.\n *\n */\n setSandbox(sandbox: NonNullable<CheckoutPopupParams['sandbox']>): Checkout {\n this.options = {\n ...this.options,\n sandbox,\n };\n\n return this;\n }\n\n /**\n * Sets user information for the checkout session.\n *\n * @param user User object with email and optional name fields. The shape matches the session from `better-auth` or next-auth packages. Also handles `null` or `undefined` gracefully.\n * @param readonly If true, the user information will be read-only in the checkout session.\n *\n */\n setUser(user: CheckoutBuilderUserOptions, readonly: boolean = true): Checkout {\n if (!user) {\n return this;\n }\n\n let firstName = user.firstName ?? '';\n let lastName = user.lastName ?? '';\n\n if (user.name) {\n const { firstName: fn, lastName: ln } = splitName(user.name);\n firstName = fn;\n lastName = ln;\n }\n\n this.options = {\n ...this.options,\n user_email: user.email,\n user_firstname: firstName,\n user_lastname: lastName,\n readonly_user: readonly,\n };\n\n return this;\n }\n\n /**\n * Applies recommended UI settings for better user experience.\n * This includes fullscreen mode, upsells, refund badge, and reviews display.\n *\n */\n setRecommendations(): Checkout {\n this.options = {\n ...this.options,\n fullscreen: true,\n show_refund_badge: true,\n show_reviews: true,\n locale: 'auto',\n currency: 'auto',\n };\n\n return this;\n }\n\n /**\n * Sets the plan ID for the checkout.\n *\n * @param planId The plan ID to purchase\n */\n setPlan(planId: string | number): Checkout {\n this.options = {\n ...this.options,\n plan_id: planId.toString(),\n };\n\n return this;\n }\n\n /**\n * Sets the number of licenses to purchase.\n *\n * @param count Number of licenses\n */\n setQuota(count: number): Checkout {\n this.options = {\n ...this.options,\n licenses: count,\n };\n\n return this;\n }\n\n setPricing(pricingId: string | number): Checkout {\n this.options = {\n ...this.options,\n pricing_id: pricingId.toString(),\n };\n\n return this;\n }\n\n setTitle(title: string): Checkout {\n this.options = {\n ...this.options,\n title,\n };\n\n return this;\n }\n\n /**\n * Sets a coupon code for the checkout.\n *\n * @param coupon The coupon code to apply\n * @param hideUI Whether to hide the coupon input field from users\n */\n setCoupon(options: { code: string; hideUI?: boolean }): Checkout {\n const { code: coupon, hideUI = false } = options;\n\n this.options = {\n ...this.options,\n coupon,\n hide_coupon: hideUI,\n };\n\n return this;\n }\n\n /**\n * Enables trial mode for the checkout.\n *\n * @param mode Trial type - true/false for plan default, or specific 'free'/'paid' mode\n */\n setTrial(mode: 'free' | 'paid' | boolean = true): Checkout {\n this.options = {\n ...this.options,\n trial: mode,\n };\n\n return this;\n }\n\n /**\n * Configures the visual layout and appearance of the checkout.\n *\n * @param options Appearance configuration options\n */\n setAppearance(options: {\n layout?: 'vertical' | 'horizontal' | null;\n formPosition?: 'left' | 'right';\n fullscreen?: boolean;\n modalTitle?: string;\n id?: string; // Custom body ID for CSS targeting\n }): Checkout {\n this.options = { ...this.options };\n\n if (options.layout !== undefined) {\n this.options.layout = options.layout;\n }\n if (options.formPosition !== undefined) {\n this.options.form_position = options.formPosition;\n }\n if (options.fullscreen !== undefined) {\n this.options.fullscreen = options.fullscreen;\n }\n if (options.modalTitle !== undefined) {\n this.options.modal_title = options.modalTitle;\n }\n\n if (options.id !== undefined) {\n this.options.id = options.id;\n }\n\n return this;\n }\n\n /**\n * Configures discount display settings.\n *\n * @param options Discount configuration options\n */\n setDiscounts(options: {\n annual?: boolean;\n multisite?: boolean | 'auto';\n bundle?: boolean | 'maximize';\n showMonthlySwitch?: boolean;\n }): Checkout {\n this.options = { ...this.options };\n\n if (options.annual !== undefined) {\n this.options.annual_discount = options.annual;\n }\n if (options.multisite !== undefined) {\n this.options.multisite_discount = options.multisite;\n }\n if (options.bundle !== undefined) {\n this.options.bundle_discount = options.bundle;\n }\n if (options.showMonthlySwitch !== undefined) {\n this.options.show_monthly_switch = options.showMonthlySwitch;\n }\n\n return this;\n }\n\n /**\n * Configures billing cycle selector interface.\n *\n * @param selector Type of billing cycle selector to show\n * @param defaultCycle Default billing cycle to select\n */\n setBillingCycle(\n defaultCycle: 'monthly' | 'annual' | 'lifetime',\n selector?: 'list' | 'responsive_list' | 'dropdown'\n ): Checkout {\n this.options = { ...this.options };\n\n if (selector !== undefined) {\n this.options.billing_cycle_selector = selector;\n }\n if (defaultCycle !== undefined) {\n this.options.billing_cycle = defaultCycle;\n }\n\n return this;\n }\n\n /**\n * Sets the language/locale for the checkout.\n *\n * @param locale Language setting - 'auto', 'auto-beta', or specific locale like 'en_US'\n */\n setLanguage(locale: 'auto' | 'auto-beta' | string = 'auto'): Checkout {\n this.options = {\n ...this.options,\n language: locale,\n };\n\n return this;\n }\n\n /**\n * Configures review and badge display settings.\n *\n * @param options Review and badge configuration\n */\n setSocialProofing(options: {\n showReviews?: boolean;\n reviewId?: number;\n showRefundBadge?: boolean;\n refundPolicyPosition?: 'below_form' | 'below_breakdown' | 'dynamic';\n }): Checkout {\n this.options = { ...this.options };\n\n if (options.showReviews !== undefined) {\n this.options.show_reviews = options.showReviews;\n }\n if (options.reviewId !== undefined) {\n this.options.review_id = options.reviewId;\n }\n if (options.showRefundBadge !== undefined) {\n this.options.show_refund_badge = options.showRefundBadge;\n }\n if (options.refundPolicyPosition !== undefined) {\n this.options.refund_policy_position = options.refundPolicyPosition;\n }\n\n return this;\n }\n\n /**\n * Enhanced currency configuration.\n *\n * @param currency Primary currency or 'auto' for automatic detection\n * @param defaultCurrency Default currency when using 'auto'\n * @param showInlineSelector Whether to show inline currency selector\n */\n setCurrency(\n currency: 'usd' | 'eur' | 'gbp' | 'auto',\n defaultCurrency: 'usd' | 'eur' | 'gbp' = 'usd',\n showInlineSelector: boolean = true\n ): Checkout {\n this.options = {\n ...this.options,\n show_inline_currency_selector: showInlineSelector,\n default_currency: defaultCurrency,\n currency: currency,\n };\n\n return this;\n }\n\n /**\n * Configures navigation and cancel behavior.\n *\n * @param cancelUrl URL for back button when in page mode\n * @param cancelIcon Custom cancel icon URL\n */\n setCancelButton(cancelUrl?: string, cancelIcon?: string): Checkout {\n this.options = { ...this.options };\n\n if (cancelUrl !== undefined) {\n this.options.cancel_url = cancelUrl;\n }\n if (cancelIcon !== undefined) {\n this.options.cancel_icon = cancelIcon;\n }\n\n return this;\n }\n\n /**\n * Associates purchases with an affiliate account.\n *\n * @param userId Affiliate user ID\n */\n setAffiliate(userId: number): Checkout {\n this.options = {\n ...this.options,\n affiliate_user_id: userId,\n };\n\n return this;\n }\n\n /**\n * Sets a custom image/icon for the checkout.\n *\n * @param imageUrl Secure HTTPS URL to the image\n */\n setImage(imageUrl: string): Checkout {\n this.options = {\n ...this.options,\n image: imageUrl,\n };\n\n return this;\n }\n\n /**\n * Configures the checkout for license renewal or upgrade by the license key.\n *\n * @note - This is less secure since it exposes the license key to the client. Use only in authenticated contexts.\n *\n * @param licenseKey The license key to renew\n */\n setLicenseUpgradeByKey(licenseKey: string): Checkout {\n this.options = {\n ...this.options,\n license_key: licenseKey,\n };\n\n return this;\n }\n\n /**\n * Configures the checkout for license upgrade using an authorization token.\n *\n * @param params The license upgrade authorization parameters\n */\n setLicenseUpgradeByAuth(params: CheckoutLicenseAuthorization): Checkout {\n this.options = {\n ...this.options,\n license_id: params.licenseId,\n authorization: params.authorization,\n };\n\n return this;\n }\n\n /**\n * Builds and returns the final checkout options to be used with the `@freemius/checkout` package.\n *\n * @returns The constructed CheckoutOptions object\n */\n getOptions(): CheckoutOptions {\n return { ...this.options };\n }\n\n /**\n * Generates a checkout link based on the current builder state.\n */\n getLink(): string {\n const checkoutOptions = convertCheckoutOptionsToQueryParams(this.options);\n\n const queryParams = buildFreemiusQueryFromOptions(checkoutOptions);\n\n const url = new URL(`${this.getBaseUrl()}/product/${this.productId}/`);\n url.search = queryParams;\n\n return url.toString();\n }\n\n serialize(): CheckoutSerialized {\n return {\n options: this.getOptions(),\n link: this.getLink(),\n baseUrl: this.getBaseUrl(),\n };\n }\n\n private getBaseUrl(): string {\n return isTestServer() ? 'http://checkout.freemius-local.com:8080' : 'https://checkout.freemius.com';\n }\n}\n","import { CheckoutAction } from '../contracts/checkout';\nimport { PricingService } from '../services/PricingService';\n\nexport class PricingRetriever implements CheckoutAction {\n constructor(private readonly pricing: PricingService) {}\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return action === 'pricing_data';\n }\n\n async processAction(request: Request): Promise<Response> {\n // Get the topupPlanId from the query parameters\n const url = new URL(request.url);\n const topupPlanId = url.searchParams.get('topupPlanId') || undefined;\n\n const pricingData = await this.pricing.retrieve(topupPlanId);\n\n return Response.json(pricingData);\n }\n}\n","import { ActionError } from '../errors/ActionError';\nimport * as zod from 'zod';\nimport { PurchaseService } from '../services/PurchaseService';\nimport { CheckoutAction } from '../contracts/checkout';\nimport { PurchaseInfo } from '../models/PurchaseInfo';\n\nexport type PurchaseCallback = (purchase: PurchaseInfo) => Promise<Response | void>;\n\nexport class PurchaseProcessor implements CheckoutAction {\n constructor(\n private readonly purchase: PurchaseService,\n private readonly callback?: PurchaseCallback\n ) {}\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return request.method === 'POST' && action === 'process_purchase';\n }\n\n async processAction(request: Request): Promise<Response> {\n const purchaseSchema = zod.object({\n purchase: zod\n .object({\n license_id: zod.string(),\n })\n .optional(),\n trial: zod\n .object({\n license_id: zod.string(),\n })\n .optional(),\n });\n\n const contentType = request.headers.get('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n throw ActionError.badRequest('Invalid content type. Expected application/json');\n }\n\n // Parse request body\n let requestBody: unknown;\n try {\n requestBody = await request.json();\n } catch {\n throw ActionError.badRequest('Request body must be valid JSON');\n }\n\n // Validate request body with Zod schema\n const parseResult = purchaseSchema.safeParse(requestBody);\n if (!parseResult.success) {\n throw ActionError.validationFailed('Invalid request body format', parseResult.error.issues);\n }\n\n const licenseId = parseResult.data.purchase?.license_id ?? parseResult.data.trial?.license_id;\n\n if (!licenseId) {\n throw ActionError.badRequest(\n 'License ID is required in the request body, either from purchase or from trial.'\n );\n }\n\n const purchase = await this.purchase.retrievePurchase(licenseId);\n\n if (!purchase) {\n throw ActionError.notFound('No purchase data found for the provided license ID');\n }\n\n if (this.callback) {\n const callbackResponse = await this.callback(purchase);\n\n if (callbackResponse) {\n return callbackResponse;\n }\n }\n\n return Response.json(purchase.toData());\n }\n}\n","import { idToString, parseBillingCycle, parseCurrency, parseDateTime, parseNumber } from '../api/parser';\nimport { BillingCycleApiEnum, CurrencyApiEnum, FSId } from '../api/types';\nimport { CheckoutRedirectData } from '../contracts/checkout';\nimport { BILLING_CYCLE, CURRENCY } from '../contracts/types';\n\nexport class CheckoutRedirectInfo implements CheckoutRedirectData {\n user_id: string;\n plan_id: string;\n email: string;\n pricing_id: string;\n action: 'payment_method_update' | 'license_update' | 'trial' | 'purchase';\n license_id: string;\n expiration: Date | null;\n quota: number | null;\n\n trial: 'free' | 'paid' | null;\n trial_ends_at: Date | null;\n\n currency: CURRENCY;\n amount: number;\n tax: number;\n type: 'subscription' | 'one-off' | null;\n subscription_id: string | null;\n billing_cycle: BILLING_CYCLE | null;\n payment_id: string | null;\n\n // Add these to the constructor to initialize the new members\n constructor(data: Record<string, unknown>) {\n this.user_id = idToString(data.user_id as FSId);\n this.plan_id = idToString(data.plan_id as FSId);\n this.email = data.email as string;\n this.pricing_id = idToString(data.pricing_id as FSId);\n this.action = data.action\n ? (data.action as 'payment_method_update' | 'license_update' | 'trial' | 'purchase')\n : 'purchase';\n this.license_id = idToString(data.license_id as FSId);\n this.expiration = data.expiration ? parseDateTime(data.expiration as string) : null;\n this.quota = data.quota ? parseNumber(data.quota) : null;\n\n this.trial = data.trial ? (data.trial as 'free' | 'paid') : null;\n this.trial_ends_at = data.trial_ends_at ? parseDateTime(data.trial_ends_at as string) : null;\n\n this.currency = data.currency ? parseCurrency(data.currency as CurrencyApiEnum)! : CURRENCY.USD;\n this.amount = parseNumber(data.amount as string)!;\n this.tax = parseNumber(data.tax as string)!;\n this.subscription_id = data.subscription_id ? idToString(data.subscription_id as string) : null;\n this.billing_cycle = data.billing_cycle ? parseBillingCycle(data.billing_cycle as BillingCycleApiEnum) : null;\n this.payment_id = data.payment_id ? idToString(data.payment_id as string) : null;\n\n this.type = this.subscription_id ? 'subscription' : this.payment_id ? 'one-off' : null;\n }\n\n isSubscription(): boolean {\n return this.type === 'subscription';\n }\n\n toData(): CheckoutRedirectData {\n return {\n user_id: this.user_id,\n plan_id: this.plan_id,\n email: this.email,\n pricing_id: this.pricing_id,\n license_id: this.license_id,\n expiration: this.expiration,\n quota: this.quota,\n\n trial: this.trial,\n trial_ends_at: this.trial_ends_at,\n\n currency: this.currency,\n action: this.action,\n amount: this.amount,\n tax: this.tax,\n subscription_id: this.subscription_id,\n billing_cycle: this.billing_cycle,\n payment_id: this.payment_id,\n\n type: this.type,\n };\n }\n}\n","import { createHmac, timingSafeEqual } from 'crypto';\nimport { CheckoutRedirectInfo } from '../models/CheckoutRedirectInfo';\nimport { CheckoutAction } from '../contracts/checkout';\nimport { ActionError } from '../errors/ActionError';\n\nexport type RedirectCallback = (info: CheckoutRedirectInfo) => Promise<Response | void>;\n\nexport class RedirectProcessor implements CheckoutAction {\n constructor(\n private readonly secretKey: string,\n private readonly proxyUrl?: string,\n private readonly callback?: RedirectCallback,\n private readonly afterProcessUrl?: string\n ) {}\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n\n return request.method === 'GET' && url.searchParams.has('signature');\n }\n\n async processAction(request: Request): Promise<Response> {\n const info = await this.getRedirectInfo(request.url);\n\n if (!info) {\n throw ActionError.badRequest('Invalid or missing redirect signature');\n }\n\n if (this.callback) {\n const callbackResponse = await this.callback(info);\n\n if (callbackResponse) {\n return callbackResponse;\n }\n }\n\n const url = new URL(this.afterProcessUrl ?? this.proxyUrl ?? request.url);\n\n // Delete all existing search params\n url.search = '';\n\n // Add new search params\n url.searchParams.set('plan', info.plan_id);\n url.searchParams.set('is_subscription', info.isSubscription() ? '1' : '0');\n url.searchParams.set('quote', info.quota?.toString() ?? '0');\n\n // Default response if no callback is provided or callback does not return a response\n return Response.redirect(url.href, 302);\n }\n\n async getRedirectInfo(currentUrl: string): Promise<CheckoutRedirectInfo | null> {\n const url = new URL(\n currentUrl\n // Handle spaces in the URL, which may be encoded as %20 or +\n .replace(/%20/g, '+')\n );\n\n const signature = url.searchParams.get('signature');\n\n if (!signature) {\n return null;\n }\n\n // Replace URL parts if proxyUrl is set. This is useful when your app is behind a proxy (which is common).\n if (this.proxyUrl) {\n const proxy = new URL(this.proxyUrl);\n url.protocol = proxy.protocol;\n url.host = proxy.host;\n url.port = proxy.port;\n }\n\n const cleanUrl = this.getCleanUrl(url.href);\n\n // Calculate HMAC SHA256\n const calculatedSignature = createHmac('sha256', this.secretKey).update(cleanUrl).digest('hex');\n\n // Compare signatures securely\n try {\n const result = timingSafeEqual(Buffer.from(calculatedSignature), Buffer.from(signature));\n\n if (!result) {\n return null;\n }\n\n const params = Object.fromEntries(url.searchParams.entries());\n\n if (!params.user_id || !params.plan_id || !params.pricing_id || !params.email) {\n return null;\n }\n\n return new CheckoutRedirectInfo(params);\n } catch (e) {\n console.error('Error getting redirection information:', e);\n return null;\n }\n }\n\n // Helper to get the current absolute URL (removing \"&signature=...\" or \"?signature=...\" by string slicing)\n getCleanUrl(url: string): string {\n const signatureParam = '&signature=';\n const signatureParamFirst = '?signature=';\n\n let signaturePos = url.indexOf(signatureParam);\n\n if (signaturePos === -1) {\n signaturePos = url.indexOf(signatureParamFirst);\n }\n\n if (signaturePos === -1) {\n // No signature param found, return as is\n return url;\n }\n\n return url.substring(0, signaturePos);\n }\n}\n","import { CheckoutAction } from '../contracts/checkout';\nimport { ActionError } from '../errors/ActionError';\nimport { PricingService } from '../services/PricingService';\nimport { PricingRetriever } from './PricingRetriever';\nimport { PurchaseService } from '../services/PurchaseService';\nimport { PurchaseCallback, PurchaseProcessor } from './PurchaseProcessor';\nimport { RedirectCallback, RedirectProcessor } from './RedirectProcessor';\nimport { RequestProcessor } from '../contracts/types';\n\nexport type CheckoutRequestConfig = {\n /**\n * The real public/proxy URL where the application is accessible from the internet.\n * This is useful for authenticating the Checkout Redirection requests.\n */\n proxyUrl?: string;\n /**\n * The function to be called when a purchase is made.\n */\n onPurchase?: PurchaseCallback;\n /**\n * The function to be called when a redirect is made after checkout.\n */\n onRedirect?: RedirectCallback;\n /**\n * Specifies a URL to redirect to after processing the redirect action. If not provided, it will redirect to the `proxyUrl` or the original request URL.\n */\n afterProcessUrl?: string;\n};\n\nexport class CheckoutRequestProcessor implements RequestProcessor<CheckoutRequestConfig> {\n constructor(\n private readonly purchase: PurchaseService,\n private readonly pricing: PricingService,\n private readonly secretKey: string\n ) {}\n\n createProcessor(config: CheckoutRequestConfig): (request: Request) => Promise<Response> {\n return (request: Request) => this.process(config, request);\n }\n\n async process(config: CheckoutRequestConfig, request: Request): Promise<Response> {\n const actionHandlers: CheckoutAction[] = [\n this.getPricingRetriever(),\n this.getRedirectProcessor({\n proxyUrl: config.proxyUrl,\n callback: config.onRedirect,\n afterProcessUrl: config.afterProcessUrl,\n }),\n this.getPurchaseProcessor({ callback: config.onPurchase }),\n ];\n\n try {\n for (const actionHandler of actionHandlers) {\n if (actionHandler.canHandle(request)) {\n return await actionHandler.processAction(request);\n }\n }\n } catch (error) {\n if (error instanceof ActionError) {\n return error.toResponse();\n }\n\n console.error('Error processing action:', error);\n return ActionError.internalError('Internal Server Error').toResponse();\n }\n\n return ActionError.badRequest('Unsupported action').toResponse();\n }\n\n /**\n * Processes the redirect from Freemius Checkout.\n *\n * This method verifies the signature in the URL and returns a CheckoutRedirectInfo object if successful.\n *\n * For nextjs like applications, make sure to replace the URL from the `Request` object with the right hostname to take care of the proxy.\n *\n * For example, if you have put the nextjs application behind nginx proxy (or ngrok during local development), then nextjs will still see the `request.url` as `https://localhost:3000/...`.\n * In this case, you should replace it with the actual URL of your application, like `https://xyz.ngrok-free.app/...`.\n *\n * @example\n * ```ts\n * export async function GET(request: Request) {\n * // Replace the URL with the actual hostname of your application\n * // This is important for the signature verification to work correctly.\n * const data = await freemius.checkout.action.getRedirectProcessor({\n * proxyUrl: 'https://xyz.ngrok-free.app',\n * async callback(info) {\n * // Handle the redirect info here, like creating a license, etc.\n * // Return a Response object to override the default redirect behavior.\n * return Response.redirect('/custom-success-page', 302);\n * },\n * });\n *\n * return data.processAction(request);\n * }\n * ```\n */\n getRedirectProcessor(config: {\n proxyUrl?: string | undefined;\n callback?: RedirectCallback | undefined;\n afterProcessUrl?: string | undefined;\n }): RedirectProcessor {\n return new RedirectProcessor(this.secretKey, config.proxyUrl, config.callback, config.afterProcessUrl);\n }\n\n getPurchaseProcessor(config: { callback?: PurchaseCallback | undefined }): PurchaseProcessor {\n return new PurchaseProcessor(this.purchase, config.callback);\n }\n\n getPricingRetriever() {\n return new PricingRetriever(this.pricing);\n }\n}\n","import { CheckoutPopupParams } from '@freemius/checkout';\nimport { FSId } from '../api/types';\nimport { idToString } from '../api/parser';\nimport { Checkout, CheckoutLicenseAuthorization } from '../checkout/Checkout';\nimport { CheckoutBuilderOptions } from '../contracts/checkout';\nimport { PurchaseService } from './PurchaseService';\nimport { PricingService } from './PricingService';\nimport { CheckoutRequestProcessor } from '../checkout/CheckoutRequestProcessor';\nimport { RedirectProcessor } from '../checkout/RedirectProcessor';\nimport { createHash } from 'crypto';\nimport { ApiService } from './ApiService';\n\nexport class CheckoutService {\n public readonly request: CheckoutRequestProcessor;\n\n constructor(\n private readonly api: ApiService,\n private readonly productId: FSId,\n private readonly publicKey: string,\n private readonly secretKey: string,\n private readonly purchase: PurchaseService,\n private readonly pricing: PricingService\n ) {\n this.request = new CheckoutRequestProcessor(this.purchase, this.pricing, this.secretKey);\n }\n\n /**\n * Use this to build a Checkout for your product.\n * You can build a Checkout link or options for the popup.\n *\n * @param withRecommendation If true, the checkout will include a recommendation for the user.\n *\n * @return A new instance of CheckoutBuilder with the product ID and public key.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const checkout = await freemius.checkout.create({user: session?.user})\n * .getOptions(); // Or .getLink() for a hosted checkout link\n * ```\n *\n * @example\n * Advanced configuration: You can also skip the convenience options and rather use the builder directly to configure the checkout.\n *\n * ```typescript\n * const checkout = freemius.checkout.create()\n * .setUser(user, true)\n * .setPlan('1234')\n * .setCoupon({\n * code: 'DISCOUNT2023',\n * hideUI: false\n * })\n * .setSandbox()\n * .getOptions();\n * ```\n *\n * @example\n */\n async create(options: CheckoutBuilderOptions = {}): Promise<Checkout> {\n const {\n user,\n isSandbox = false,\n withRecommendation = true,\n title,\n image,\n planId,\n quota,\n trial,\n licenseId,\n } = options;\n\n const builder = new Checkout(idToString(this.productId), this.publicKey, this.secretKey);\n\n if (user) {\n builder.setUser(user, true);\n }\n\n if (withRecommendation) {\n builder.setRecommendations();\n }\n\n if (isSandbox) {\n builder.setSandbox(await this.getSandboxParams());\n }\n\n if (title) {\n builder.setTitle(title);\n }\n\n if (image) {\n builder.setImage(image);\n }\n\n if (planId) {\n builder.setPlan(planId);\n }\n\n if (quota) {\n builder.setQuota(quota);\n }\n\n if (trial) {\n builder.setTrial(trial);\n }\n\n if (licenseId) {\n const authorization = await this.getLicenseUpgradeAuth(licenseId);\n builder.setLicenseUpgradeByAuth(authorization);\n }\n\n return builder;\n }\n\n /**\n * Retrieves the sandbox parameters for the checkout.\n *\n * This shouldn't be used in production, but is useful for testing purposes.\n *\n * @note This is intentionally set as `async` because we would use the API in the future to generate more fine grained sandbox params (for example for a specific email address only).\n */\n async getSandboxParams(): Promise<NonNullable<CheckoutPopupParams['sandbox']>> {\n const productId = idToString(this.productId);\n const timestamp = Math.floor(Date.now() / 1000).toString();\n const token = `${timestamp}${productId}${this.secretKey}${this.publicKey}checkout`;\n\n return {\n ctx: timestamp,\n token: createHash('md5').update(token).digest('hex'),\n };\n }\n\n /**\n * Retrieves the license upgrade authorization for a given license ID.\n *\n * This is used to authorize a license upgrade during the checkout process. Useful when creating upgrade links for existing users.\n */\n async getLicenseUpgradeAuth(licenseId: FSId): Promise<CheckoutLicenseAuthorization> {\n const auth = await this.api.license.retrieveCheckoutUpgradeAuthorization(licenseId);\n\n if (!auth) {\n throw new Error('Failed to retrieve license upgrade authorization');\n }\n\n return {\n licenseId,\n authorization: auth,\n };\n }\n\n /**\n * Processes a redirect URL and returns the checkout redirect information if valid.\n *\n * This is useful for handling redirects from the checkout portal back to your application.\n *\n * @param url The current URL to process.\n * @param proxyUrl Optional proxy URL to replace parts of the URL for signature verification.\n *\n * @returns A promise that resolves to the checkout redirect information or null if invalid.\n *\n * @example\n * ```typescript\n * const redirectInfo = await freemius.checkout.processRedirect(window.location.href);\n *\n * if (redirectInfo) {\n * // Handle valid redirect info\n * } else {\n * // Handle invalid or missing redirect info\n * }\n * ```\n */\n processRedirect(url: string, proxyUrl?: string) {\n const processor = new RedirectProcessor(this.secretKey, proxyUrl);\n return processor.getRedirectInfo(url);\n }\n}\n","import { CheckoutOptions } from '@freemius/checkout';\nimport {\n idToString,\n isIdsEqual,\n parseBillingCycle,\n parseCurrency,\n parseDateTime,\n parseNumber,\n parsePaymentMethod,\n} from '../api/parser';\nimport {\n BillingEntity,\n CouponEntityEnriched,\n FSId,\n PaymentEntity,\n PlanEntity,\n PricingEntity,\n PricingTableData,\n SellingUnit,\n UserEntity,\n UserSubscriptionEntity,\n} from '../api/types';\nimport { PortalBilling, PortalData, PortalPayment, PortalSubscription, PortalSubscriptions } from '../contracts/portal';\nimport { CURRENCY } from '../contracts/types';\nimport { ApiService } from '../services/ApiService';\nimport { CheckoutService } from '../services/CheckoutService';\nimport { CustomerPortalActionService } from './CustomerPortalActionService';\n\ntype CustomerPortalDataOption = {\n /**\n * The API endpoint for which secure authenticated signed URLs will be created where the customer portal will send requests to fetch additional data (e.g., invoices, licenses, subscriptions). This should be an endpoint in your SaaS application that you will implement to handle these requests.\n */\n endpoint: string;\n /**\n * ID of the primary licenses, in case your software has pricing model supporting multiple active subscriptions. If not provided the first active subscription will be used instead.\n */\n primaryLicenseId?: FSId | null;\n /**\n * Whether to create actions in sandbox mode (For example Checkout, Upgrade etc).\n */\n sandbox?: boolean;\n};\n\nexport type CustomerPortalDataWithUserOption = {\n /**\n * ID of the user from Freemius.\n */\n userId: FSId;\n} & CustomerPortalDataOption;\n\nexport type CustomerPortalDataWithEmailOption = {\n /**\n * The email address of the user from Freemius.\n */\n email: string;\n} & CustomerPortalDataOption;\n\nexport class PortalDataRepository {\n constructor(\n private readonly api: ApiService,\n private readonly action: CustomerPortalActionService,\n private readonly checkout: CheckoutService\n ) {}\n\n async retrievePortalDataByEmail(config: CustomerPortalDataWithEmailOption): Promise<PortalData | null> {\n const user = await this.api.user.retrieveByEmail(config.email);\n\n if (!user) {\n return null;\n }\n\n return this.retrievePortalData({\n user,\n endpoint: config.endpoint,\n primaryLicenseId: config.primaryLicenseId ?? null,\n sandbox: config.sandbox ?? false,\n });\n }\n\n async retrievePortalDataByUserId(config: CustomerPortalDataWithUserOption): Promise<PortalData | null> {\n const user = await this.api.user.retrieve(config.userId);\n\n if (!user) {\n return null;\n }\n\n return this.retrievePortalData({\n user,\n endpoint: config.endpoint,\n primaryLicenseId: config.primaryLicenseId ?? null,\n sandbox: config.sandbox ?? false,\n });\n }\n\n async retrievePortalData(config: {\n user: UserEntity;\n endpoint: string;\n primaryLicenseId?: FSId | null;\n sandbox?: boolean;\n }): Promise<PortalData | null> {\n const { user, endpoint, primaryLicenseId = null, sandbox = false } = config;\n const userId = user.id!;\n\n const data = await this.retrieveApiData(userId);\n\n if (!data) {\n return null;\n }\n\n const { pricingData, subscriptions, payments, billing, coupons } = data;\n\n const plans = this.getPlansById(pricingData);\n const pricings = this.getPricingById(pricingData);\n\n const checkoutOptions: CheckoutOptions = {\n product_id: this.api.productId,\n };\n\n if (sandbox) {\n checkoutOptions.sandbox = await this.checkout.getSandboxParams();\n }\n\n const portalData: PortalData = {\n endpoint,\n user,\n checkoutOptions,\n billing: this.getBilling(billing, userId, endpoint),\n subscriptions: await this.getSubscriptions(subscriptions, plans, pricings, primaryLicenseId, endpoint),\n payments: this.getPayments(payments, plans, pricings, userId, endpoint),\n plans: pricingData.plans ?? [],\n sellingUnit: (pricingData.plugin?.selling_unit_label as SellingUnit) ?? {\n singular: 'Unit',\n plural: 'Units',\n },\n productId: this.api.productId,\n cancellationCoupons: coupons,\n };\n\n return portalData;\n }\n\n async retrieveApiData(userId: FSId): Promise<{\n pricingData: PricingTableData;\n subscriptions: UserSubscriptionEntity[];\n payments: PaymentEntity[];\n billing: BillingEntity | null;\n coupons: CouponEntityEnriched[] | null;\n } | null> {\n const [pricingData, subscriptions, payments, billing, coupons] = await Promise.all([\n this.api.product.retrievePricingData(),\n this.api.user.retrieveSubscriptions(userId, { extended: true, enrich_with_cancellation_discounts: true }),\n this.api.user.retrievePayments(userId),\n this.api.user.retrieveBilling(userId),\n this.api.product.retrieveSubscriptionCancellationCoupon(),\n ]);\n\n if (!pricingData || !subscriptions) {\n return null;\n }\n\n return { pricingData, subscriptions, payments, billing, coupons };\n }\n\n getPayments(\n payments: PaymentEntity[],\n plans: Map<string, PlanEntity>,\n pricings: Map<string, PricingEntity>,\n userId: FSId,\n endpoint: string\n ): PortalPayment[] {\n return payments.map((payment) => ({\n ...payment,\n invoiceUrl: this.action.invoice.createAuthenticatedUrl(payment.id!, idToString(userId), endpoint),\n paymentMethod: parsePaymentMethod(payment.gateway)!,\n createdAt: parseDateTime(payment.created) ?? new Date(),\n planTitle: plans.get(payment.plan_id!)?.title ?? `Plan ${payment.plan_id!}`,\n quota: pricings.get(payment.pricing_id!)?.licenses ?? null,\n }));\n }\n\n getPlansById(pricingData: PricingTableData): Map<string, PlanEntity> {\n const plans: Map<string, PlanEntity> = new Map();\n\n pricingData.plans?.forEach((plan) => {\n plan.title = plan.title ?? plan.name ?? `Plan ${plan.id!}`;\n plans.set(idToString(plan.id!), plan);\n });\n\n return plans;\n }\n\n getPricingById(pricingData: PricingTableData): Map<string, PricingEntity> {\n const pricings: Map<string, PricingEntity> = new Map();\n\n pricingData.plans?.forEach((plan) => {\n plan.pricing?.forEach((p) => {\n pricings.set(idToString(p.id!), p);\n });\n });\n\n return pricings;\n }\n\n getBilling(billing: BillingEntity | null, userId: FSId, endpoint: string): PortalBilling {\n return {\n ...(billing ?? {}),\n updateUrl: this.action.billing.createAuthenticatedUrl(billing?.id ?? 'new', idToString(userId), endpoint),\n };\n }\n\n async getSubscriptions(\n subscriptions: UserSubscriptionEntity[],\n plans: Map<string, PlanEntity>,\n pricings: Map<string, PricingEntity>,\n primaryLicenseId: FSId | null = null,\n endpoint: string\n ): Promise<PortalSubscriptions> {\n const portalSubscriptions: PortalSubscriptions = {\n primary: null,\n active: [],\n past: [],\n };\n\n subscriptions.forEach((subscription) => {\n const isActive = null === subscription.canceled_at;\n const trialEndsData = subscription.trial_ends ? parseDateTime(subscription.trial_ends) : null;\n const isTrial = trialEndsData ? trialEndsData > new Date() : false;\n const isFreeTrial = isTrial && !subscription.gateway;\n\n const subscriptionData: PortalSubscription = {\n subscriptionId: idToString(subscription.id!),\n planId: idToString(subscription.plan_id!),\n pricingId: idToString(subscription.pricing_id!),\n planTitle: plans.get(subscription.plan_id!)?.title ?? `Plan ${subscription.plan_id!}`,\n renewalAmount: parseNumber(subscription.renewal_amount)!,\n initialAmount: parseNumber(subscription.initial_amount)!,\n billingCycle: parseBillingCycle(subscription.billing_cycle),\n isActive: isActive,\n renewalDate: parseDateTime(subscription.next_payment),\n licenseId: idToString(subscription.license_id!),\n currency: parseCurrency(subscription.currency) ?? CURRENCY.USD,\n createdAt: parseDateTime(subscription.created) ?? new Date(),\n cancelledAt: subscription.canceled_at ? parseDateTime(subscription.canceled_at) : null,\n quota: pricings.get(subscription.pricing_id!)?.licenses ?? null,\n paymentMethod: parsePaymentMethod(subscription.gateway),\n isTrial: isTrial,\n trialEnds: isTrial ? trialEndsData : null,\n isFreeTrial: isFreeTrial,\n applyRenewalCancellationCouponUrl: this.isRenewalCancellationCouponApplicable(subscription)\n ? this.action.renewalCoupon.createAuthenticatedUrl(\n idToString(subscription.id!),\n idToString(subscription.user_id!),\n endpoint\n )\n : null,\n cancelRenewalUrl: this.action.cancelRenewal.createAuthenticatedUrl(\n idToString(subscription.id!),\n idToString(subscription.user_id!),\n endpoint\n ),\n };\n\n if (isActive) {\n portalSubscriptions.active.push(subscriptionData);\n } else {\n portalSubscriptions.past.push(subscriptionData);\n }\n\n if (isActive && primaryLicenseId && isIdsEqual(subscription.license_id!, primaryLicenseId)) {\n portalSubscriptions.primary = subscriptionData;\n }\n });\n\n // Sort subscriptions by created date, most recent first\n portalSubscriptions.active.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n portalSubscriptions.past.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n\n if (!portalSubscriptions.primary) {\n // If no primary subscription is set, use the first active or inactive subscription as primary\n portalSubscriptions.primary = portalSubscriptions.active[0] ?? portalSubscriptions.past[0] ?? null;\n }\n\n // @todo - Right now add the checkout upgrade authorization to the primary subscription if it exists. In the future our API itself can return this data.\n if (portalSubscriptions.primary) {\n portalSubscriptions.primary.checkoutUpgradeAuthorization =\n await this.api.license.retrieveCheckoutUpgradeAuthorization(portalSubscriptions.primary.licenseId);\n }\n\n return portalSubscriptions;\n }\n\n /**\n * Check if coupon application is impossible due to certain conditions.\n * This function can be used to determine if a coupon can be applied to a subscription.\n * Introduced initially for PayPal subscriptions with a renewal date less than 48 hours in the future.\n *\n * @author @DanieleAlessandra\n * @author @swashata (Ported to SDK)\n *\n * @returns boolean\n */\n isRenewalCancellationCouponApplicable(subscription: UserSubscriptionEntity): boolean {\n // If it was already applied, do not allow applying it again.\n if (subscription.has_subscription_cancellation_discount || (subscription.total_gross ?? 0) <= 0) {\n return false;\n }\n\n // The block is only for PayPal subscriptions\n if (subscription.gateway !== 'paypal') {\n return true;\n }\n\n // Convert next_payment to time.\n const nextPaymentTime = parseDateTime(subscription.next_payment)?.getTime() ?? 0;\n\n // Get the current time.\n const currentTime = new Date().getTime();\n\n // Check if the next payment is at least 48 hours in the future.\n const fortyEightHoursInMs = 48 * 60 * 60 * 1000;\n\n return nextPaymentTime <= currentTime + fortyEightHoursInMs;\n }\n}\n","import { PortalAction } from '../contracts/portal';\nimport { UserRetriever } from '../contracts/types';\nimport { PortalDataRepository } from './PortalDataRepository';\n\nexport class PortalDataRetriever implements PortalAction {\n constructor(\n private readonly repository: PortalDataRepository,\n private readonly getUser: UserRetriever,\n private readonly endpoint: string,\n private readonly isSandbox?: boolean\n ) {}\n\n createAuthenticatedUrl(): string {\n throw new Error('Method not implemented.');\n }\n\n verifyAuthentication(): boolean {\n return true;\n }\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return request.method === 'GET' && action === 'portal_data';\n }\n\n async processAction(): Promise<Response> {\n const user = await this.getUser();\n\n if (!user || !('id' in user)) {\n return Response.json(null);\n }\n\n return Response.json(\n await this.repository.retrievePortalDataByUserId({\n userId: user.id,\n endpoint: this.endpoint,\n primaryLicenseId: null,\n sandbox: this.isSandbox ?? false,\n })\n );\n }\n}\n","import { PortalAction } from '../contracts/portal';\nimport { UserRetriever } from '../contracts/types';\nimport { ActionError } from '../errors/ActionError';\nimport { PurchaseInfo } from '../models/PurchaseInfo';\nimport { PurchaseService } from '../services/PurchaseService';\n\nexport type RestoreCallback = (purchases: PurchaseInfo[]) => Promise<Response | void>;\n\nexport class PurchaseRestorer implements PortalAction {\n constructor(\n private readonly purchase: PurchaseService,\n private readonly user: UserRetriever,\n private readonly callback?: RestoreCallback,\n private readonly subscriptionOnly: boolean = false\n ) {}\n\n createAuthenticatedUrl(): string {\n throw new Error('Method not implemented.');\n }\n\n verifyAuthentication(): boolean {\n // This is handled later in the processAction method\n return true;\n }\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return request.method === 'POST' && action === 'restore_purchase';\n }\n\n async processAction(): Promise<Response> {\n let purchases: PurchaseInfo[] | null = null;\n const user = await this.user();\n\n if (!user) {\n throw ActionError.unauthorized('User not authenticated');\n }\n\n if (this.subscriptionOnly) {\n purchases =\n 'id' in user\n ? await this.purchase.retrieveSubscriptions(user.id)\n : await this.purchase.retrieveSubscriptionsByEmail(user.email);\n } else {\n purchases =\n 'id' in user\n ? await this.purchase.retrievePurchases(user.id)\n : await this.purchase.retrievePurchasesByEmail(user.email);\n }\n\n if (!purchases) {\n throw ActionError.notFound('No purchases found for the provided user');\n }\n\n if (this.callback) {\n const callbackResponse = await this.callback(purchases);\n\n if (callbackResponse) {\n return callbackResponse;\n }\n }\n\n return Response.json(purchases.map((p) => p.toData()));\n }\n}\n","import { PortalAction } from '../contracts/portal';\nimport { RequestProcessor, UserRetriever } from '../contracts/types';\nimport { ActionError } from '../errors/ActionError';\nimport { PurchaseService } from '../services/PurchaseService';\nimport { CustomerPortalActionService } from './CustomerPortalActionService';\nimport { PortalDataRepository } from './PortalDataRepository';\nimport { PortalDataRetriever } from './PortalDataRetriever';\nimport { PurchaseRestorer, RestoreCallback } from './PurchaseRestorer';\n\nexport type PortalRequestConfig = {\n getUser: UserRetriever;\n onRestore?: RestoreCallback;\n restoreSubscriptionsOnly?: boolean;\n isSandbox?: boolean;\n portalEndpoint: string;\n};\n\nexport class PortalRequestProcessor implements RequestProcessor<PortalRequestConfig> {\n constructor(\n private readonly repository: PortalDataRepository,\n private readonly action: CustomerPortalActionService,\n private readonly purchase: PurchaseService\n ) {}\n\n createProcessor(config: PortalRequestConfig): (request: Request) => Promise<Response> {\n return (request: Request) => this.process(config, request);\n }\n\n /**\n * Process actions done by the user in the customer portal.\n */\n async process(config: PortalRequestConfig, request: Request): Promise<Response> {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n if (!action) {\n return ActionError.badRequest('Action parameter is required').toResponse();\n }\n\n const actionHandlers: PortalAction[] = [\n new PortalDataRetriever(this.repository, config.getUser, config.portalEndpoint, config.isSandbox),\n ...this.action.getAllHandlers(),\n ];\n\n if (config.onRestore) {\n actionHandlers.push(\n new PurchaseRestorer(\n this.purchase,\n config.getUser,\n config.onRestore,\n config.restoreSubscriptionsOnly ?? false\n )\n );\n }\n\n try {\n for (const actionHandler of actionHandlers) {\n if (actionHandler.canHandle(request)) {\n if (actionHandler.verifyAuthentication(request)) {\n return await actionHandler.processAction(request);\n } else {\n throw ActionError.unauthorized('Invalid authentication token');\n }\n }\n }\n } catch (error) {\n if (error instanceof ActionError) {\n return error.toResponse();\n }\n\n console.error('Error processing action:', error);\n return ActionError.internalError('Internal Server Error').toResponse();\n }\n\n return ActionError.badRequest('Unsupported action').toResponse();\n }\n}\n","import { BillingUpdatePayload } from '../api/types';\nimport { PortalAction } from '../contracts/portal';\nimport { ApiService } from '../services/ApiService';\nimport { AuthService } from '../services/AuthService';\nimport { ActionError } from '../errors/ActionError';\nimport * as zod from 'zod';\n\n// Define Zod schema for billing request body\nconst schema = zod.object({\n business_name: zod.string().optional(),\n tax_id: zod.string().optional(),\n phone: zod.string().optional(),\n address_apt: zod.string().optional(),\n address_street: zod.string().optional(),\n address_city: zod.string().optional(),\n address_state: zod.string().optional(),\n address_country_code: zod.string().optional(),\n address_zip: zod.string().optional(),\n});\n\nexport type BillingRequest = zod.infer<typeof schema>;\n\nexport class BillingAction implements PortalAction {\n private readonly actionName = 'billing';\n\n constructor(\n private readonly api: ApiService,\n private readonly auth: AuthService\n ) {}\n\n private createAction(id: string): string {\n return `billing_${id}`;\n }\n\n createAuthenticatedUrl(id: string, userId: string, endpoint: string): string {\n const token = this.auth.createActionToken(this.createAction(id), userId);\n\n const url = new URL(endpoint);\n url.searchParams.set('action', this.actionName);\n url.searchParams.set('token', token);\n url.searchParams.set('billingId', id);\n url.searchParams.set('userId', userId);\n\n return url.href;\n }\n\n verifyAuthentication(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n const token = url.searchParams.get('token');\n const billingId = url.searchParams.get('billingId');\n const userId = url.searchParams.get('userId');\n\n if (!action || !token || !billingId || !userId) {\n return false;\n }\n\n return this.auth.verifyActionToken(token, this.createAction(billingId), userId);\n }\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return action === this.actionName;\n }\n\n async processAction(request: Request): Promise<Response> {\n // Check content type\n const contentType = request.headers.get('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n throw ActionError.badRequest('Invalid content type. Expected application/json');\n }\n\n // Parse request body\n let requestBody: unknown;\n try {\n requestBody = await request.json();\n } catch {\n throw ActionError.badRequest('Request body must be valid JSON');\n }\n\n // Validate request body with Zod schema\n const parseResult = schema.safeParse(requestBody);\n if (!parseResult.success) {\n throw ActionError.validationFailed('Invalid request body format', parseResult.error.issues);\n }\n\n const billingData = parseResult.data;\n\n // Extract URL parameters for billing ID and user ID\n const url = new URL(request.url);\n const billingId = url.searchParams.get('billingId');\n const userId = url.searchParams.get('userId');\n\n if (!billingId) {\n throw ActionError.badRequest('Missing required parameter: billingId');\n }\n\n if (!userId) {\n throw ActionError.badRequest('Missing required parameter: userId');\n }\n\n const payload: BillingUpdatePayload = {};\n\n if (billingData.business_name) {\n payload.business_name = billingData.business_name;\n }\n if (billingData.tax_id) {\n payload.tax_id = billingData.tax_id;\n }\n if (billingData.phone) {\n payload.phone = billingData.phone;\n }\n\n if (billingData.address_apt) {\n payload.address_apt = billingData.address_apt;\n }\n if (billingData.address_street) {\n payload.address_street = billingData.address_street;\n }\n if (billingData.address_city) {\n payload.address_city = billingData.address_city;\n }\n if (billingData.address_state) {\n payload.address_state = billingData.address_state;\n }\n if (billingData.address_country_code) {\n payload.address_country_code = billingData.address_country_code;\n }\n if (billingData.address_zip) {\n payload.address_zip = billingData.address_zip;\n }\n\n const response = await this.api.user.updateBilling(userId, payload);\n\n if (!response) {\n throw ActionError.internalError('Failed to update billing information');\n }\n\n return Response.json(response, { status: 200 });\n }\n}\n","import { PortalAction } from '../contracts/portal';\nimport { ApiService } from '../services/ApiService';\nimport * as zod from 'zod';\nimport { AuthService } from '../services/AuthService';\nimport { ActionError } from '../errors/ActionError';\n\nexport class InvoiceAction implements PortalAction {\n private readonly actionName = 'invoice';\n\n constructor(\n private readonly api: ApiService,\n private readonly auth: AuthService\n ) {}\n\n private createAction(id: string): string {\n return `invoice_${id}`;\n }\n\n createAuthenticatedUrl(id: string, userId: string, endpoint: string): string {\n const token = this.auth.createActionToken(this.createAction(id), userId);\n\n const url = new URL(endpoint);\n url.searchParams.set('action', this.actionName);\n url.searchParams.set('token', token);\n url.searchParams.set('invoiceId', id);\n url.searchParams.set('userId', userId);\n\n return url.href;\n }\n\n verifyAuthentication(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n const token = url.searchParams.get('token');\n const invoiceId = url.searchParams.get('invoiceId');\n const userId = url.searchParams.get('userId');\n\n if (!action || !token || !invoiceId || !userId) {\n return false;\n }\n\n return this.auth.verifyActionToken(token, this.createAction(invoiceId), userId);\n }\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return action === this.actionName;\n }\n\n async processAction(request: Request): Promise<Response> {\n // Verify the request that is has valid invoiceId and userId with zod schema validation\n const schema = zod.object({\n invoiceId: zod.string().min(1, 'Invoice ID is required'),\n userId: zod.string().min(1, 'User ID is required'),\n });\n\n const url = new URL(request.url);\n const invoiceId = url.searchParams.get('invoiceId');\n const userIdParam = url.searchParams.get('userId');\n\n const parseResult = schema.safeParse({ invoiceId, userId: userIdParam });\n\n if (!parseResult.success) {\n throw ActionError.validationFailed('Invalid request parameters', parseResult.error.issues);\n }\n\n const { invoiceId: validInvoiceId } = parseResult.data;\n\n try {\n const pdf = await this.api.payment.retrieveInvoice(validInvoiceId);\n\n if (pdf) {\n return new Response(pdf, {\n headers: {\n 'Content-Type': 'application/pdf',\n 'Content-Disposition': `inline; filename=\"invoice_${validInvoiceId}.pdf\"`,\n },\n });\n } else {\n throw ActionError.notFound('Invoice not found');\n }\n } catch (error) {\n if (error instanceof ActionError) {\n throw error;\n }\n\n throw ActionError.internalError('Failed to retrieve invoice');\n }\n }\n}\n","import * as zod from 'zod';\nimport { idToString } from '../api/parser';\nimport { FSId, SubscriptionCancellationReasonType } from '../api/types';\nimport { PortalAction } from '../contracts/portal';\nimport { ActionError } from '../errors/ActionError';\nimport { ApiService } from '../services/ApiService';\nimport { AuthService } from '../services/AuthService';\n\nconst schema = zod.object({\n feedback: zod.string().optional(),\n reason_ids: zod\n .array(zod.enum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15']))\n .optional(),\n});\n\nexport type SubscriptionCancellationRequest = zod.infer<typeof schema>;\n\nexport class SubscriptionCancellationAction implements PortalAction {\n private readonly actionName = 'subscription_cancellation';\n\n constructor(\n private readonly api: ApiService,\n private readonly auth: AuthService\n ) {}\n\n private createAction(id: string): string {\n return `cancellation_${id}`;\n }\n\n createAuthenticatedUrl(id: string, userId: FSId, endpoint: string): string {\n const token = this.auth.createActionToken(this.createAction(id), userId);\n\n const url = new URL(endpoint);\n url.searchParams.set('action', this.actionName);\n url.searchParams.set('token', token);\n url.searchParams.set('subscriptionId', id);\n url.searchParams.set('userId', idToString(userId));\n\n return url.href;\n }\n\n verifyAuthentication(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n const token = url.searchParams.get('token');\n const subscriptionId = url.searchParams.get('subscriptionId');\n const userId = url.searchParams.get('userId');\n\n if (!action || !token || !subscriptionId || !userId) {\n return false;\n }\n\n return this.auth.verifyActionToken(token, this.createAction(subscriptionId), userId);\n }\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return action === this.actionName && request.method === 'POST';\n }\n\n async processAction(request: Request): Promise<Response> {\n // Get feedback and reason_ids from the body\n\n // Check content type\n const contentType = request.headers.get('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n throw ActionError.badRequest('Invalid content type. Expected application/json');\n }\n\n let requestBody: unknown;\n try {\n requestBody = await request.json();\n } catch {\n throw ActionError.badRequest('Request body must be valid JSON');\n }\n\n // Validate request body with Zod schema\n const parseResult = schema.safeParse(requestBody);\n if (!parseResult.success) {\n throw ActionError.validationFailed('Invalid request body format', parseResult.error.issues);\n }\n\n // Get subscriptionId and userId from the URL\n const url = new URL(request.url);\n const subscriptionId = url.searchParams.get('subscriptionId');\n const userId = url.searchParams.get('userId');\n\n if (!subscriptionId || !userId) {\n throw ActionError.badRequest('Missing subscriptionId or userId');\n }\n\n const reasonIds = parseResult.data.reason_ids\n ? (parseResult.data.reason_ids.map((id) => parseInt(id, 10)) as SubscriptionCancellationReasonType[])\n : undefined;\n\n const result = await this.api.subscription.cancel(subscriptionId, parseResult.data.feedback, reasonIds);\n\n if (!result) {\n throw ActionError.internalError('Failed to cancel the subscription');\n }\n\n return Response.json(result, { status: 200 });\n }\n}\n","import { idToString } from '../api/parser';\nimport { FSId } from '../api/types';\nimport { PortalAction } from '../contracts/portal';\nimport { ActionError } from '../errors/ActionError';\nimport { ApiService } from '../services/ApiService';\nimport { AuthService } from '../services/AuthService';\nimport * as zod from 'zod';\n\nconst schema = zod.object({\n couponId: zod.string().min(1),\n});\n\nexport type ApplyRenewalCouponRequest = zod.infer<typeof schema>;\n\nexport class SubscriptionRenewalCouponAction implements PortalAction {\n private readonly actionName = 'subscription_cancellation_coupon';\n\n constructor(\n private readonly api: ApiService,\n private readonly auth: AuthService\n ) {}\n\n private createAction(id: string): string {\n return `renewal_coupon_${id}`;\n }\n\n createAuthenticatedUrl(id: string, userId: FSId, endpoint: string): string {\n const token = this.auth.createActionToken(this.createAction(id), userId);\n\n const url = new URL(endpoint);\n url.searchParams.set('action', this.actionName);\n url.searchParams.set('token', token);\n url.searchParams.set('subscriptionId', id);\n url.searchParams.set('userId', idToString(userId));\n\n return url.href;\n }\n\n verifyAuthentication(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n const token = url.searchParams.get('token');\n const subscriptionId = url.searchParams.get('subscriptionId');\n const userId = url.searchParams.get('userId');\n\n if (!action || !token || !subscriptionId || !userId) {\n return false;\n }\n\n return this.auth.verifyActionToken(token, this.createAction(subscriptionId), userId);\n }\n\n canHandle(request: Request): boolean {\n const url = new URL(request.url);\n const action = url.searchParams.get('action');\n\n return action === this.actionName && request.method === 'POST';\n }\n\n async processAction(request: Request): Promise<Response> {\n // Check content type\n const contentType = request.headers.get('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n throw ActionError.badRequest('Invalid content type. Expected application/json');\n }\n\n let requestBody: unknown;\n try {\n requestBody = await request.json();\n } catch {\n throw ActionError.badRequest('Request body must be valid JSON');\n }\n\n // Validate request body with Zod schema\n const parseResult = schema.safeParse(requestBody);\n if (!parseResult.success) {\n throw ActionError.validationFailed('Invalid request body format', parseResult.error.issues);\n }\n\n // Get the subscriptionId from the URL\n const url = new URL(request.url);\n const subscriptionId = url.searchParams.get('subscriptionId');\n if (!subscriptionId) {\n throw ActionError.badRequest('Missing subscriptionId in the request URL');\n }\n\n const result = await this.api.subscription.applyRenewalCoupon(subscriptionId, parseResult.data.couponId, true);\n\n if (!result) {\n throw ActionError.internalError('Failed to apply renewal coupon to the subscription');\n }\n\n return Response.json(result, { status: 200 });\n }\n}\n","import { PortalAction } from '../contracts/portal';\nimport { ApiService } from '../services/ApiService';\nimport { AuthService } from '../services/AuthService';\nimport { BillingAction } from './BillingAction';\nimport { InvoiceAction } from './InvoiceAction';\nimport { SubscriptionCancellationAction } from './SubscriptionCancellationAction';\nimport { SubscriptionRenewalCouponAction } from './SubscriptionRenewalCouponAction';\n\nexport class CustomerPortalActionService {\n public readonly invoice: InvoiceAction;\n public readonly billing: BillingAction;\n public readonly renewalCoupon: SubscriptionRenewalCouponAction;\n public readonly cancelRenewal: SubscriptionCancellationAction;\n\n constructor(\n private readonly api: ApiService,\n private readonly authService: AuthService\n ) {\n this.invoice = new InvoiceAction(this.api, this.authService);\n this.billing = new BillingAction(this.api, this.authService);\n this.renewalCoupon = new SubscriptionRenewalCouponAction(this.api, this.authService);\n this.cancelRenewal = new SubscriptionCancellationAction(this.api, this.authService);\n }\n\n getAllHandlers(): PortalAction[] {\n return [this.invoice, this.billing, this.renewalCoupon, this.cancelRenewal];\n }\n}\n","import { ApiService } from './ApiService';\nimport { PortalData } from '../contracts/portal';\nimport { CheckoutService } from './CheckoutService';\nimport { AuthService } from './AuthService';\nimport {\n CustomerPortalDataWithEmailOption,\n CustomerPortalDataWithUserOption,\n PortalDataRepository,\n} from '../customer-portal/PortalDataRepository';\nimport { PurchaseService } from './PurchaseService';\nimport { PortalRequestProcessor } from '../customer-portal/PortalRequestProcessor';\nimport { CustomerPortalActionService } from '../customer-portal/CustomerPortalActionService';\nimport { PurchaseInfo } from '../models/PurchaseInfo';\n\nexport class CustomerPortalService {\n private readonly repository: PortalDataRepository;\n\n public readonly action: CustomerPortalActionService;\n public readonly request: PortalRequestProcessor;\n\n constructor(\n private readonly api: ApiService,\n private readonly checkout: CheckoutService,\n private readonly authService: AuthService,\n private readonly purchase: PurchaseService\n ) {\n this.action = new CustomerPortalActionService(this.api, this.authService);\n\n this.repository = new PortalDataRepository(this.api, this.action, this.checkout);\n\n this.request = new PortalRequestProcessor(this.repository, this.action, this.purchase);\n }\n\n /**\n * Retrieves the customer portal data for a user, including subscriptions, billing, and payments.\n */\n async retrieveData(option: CustomerPortalDataWithUserOption): Promise<PortalData | null> {\n return this.repository.retrievePortalDataByUserId(option);\n }\n\n async retrieveDataByEmail(option: CustomerPortalDataWithEmailOption): Promise<PortalData | null> {\n return this.repository.retrievePortalDataByEmail(option);\n }\n\n /**\n * Creates a restorer function that processes an array of purchases by invoking the provided callback for each purchase.\n */\n createRestorer(callback: (purchase: PurchaseInfo) => Promise<void>): (purchases: PurchaseInfo[]) => Promise<void> {\n return async (purchases: PurchaseInfo[]) => {\n await Promise.all(purchases.map((purchase) => callback(purchase)));\n };\n }\n}\n","import crypto from 'crypto';\nimport { WebhookEventHandler, WebhookEventType, WebhookEvent } from './events';\nimport { EventEntity } from '../api/types';\nimport { WebhookAuthenticationMethod, WebhookListenerResponse } from '../contracts/webhook';\nimport { WebhookError } from '../errors/WebhookError';\nimport { ApiService } from '../services/ApiService';\n\nexport interface NormalizedRequest {\n headers: Record<string, string | string[] | undefined> | Headers;\n rawBody: string | Buffer; // MUST be the exact raw body Freemius sent\n}\n\nconst SIGNATURE_HEADER = 'x-signature';\n\nconst DEFAULT_ERROR_HANDLER = async (error: unknown) => {\n console.error('Webhook processing error:', error);\n};\n\n// @todo - Add a method `onAny` to listen to all events with a single handler\nexport class WebhookListener {\n private eventHandlers: Map<WebhookEventType, Set<WebhookEventHandler<WebhookEventType>>> = new Map();\n\n constructor(\n private readonly api: ApiService,\n private readonly secretKey: string,\n private readonly onError: (error: unknown) => Promise<void> = DEFAULT_ERROR_HANDLER,\n private readonly authenticationMethod: WebhookAuthenticationMethod = WebhookAuthenticationMethod.SignatureHeader\n ) {}\n\n // Overload for single event type\n on<T extends WebhookEventType>(type: T, handler: WebhookEventHandler<T>): this;\n // Overload for array of event types\n on<T extends WebhookEventType>(types: T[], handler: WebhookEventHandler<T>): this;\n // Implementation\n on<T extends WebhookEventType>(typeOrTypes: T | T[], handler: WebhookEventHandler<T>): this {\n const types = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes];\n\n for (const type of types) {\n if (!this.eventHandlers.has(type)) {\n this.eventHandlers.set(type, new Set());\n }\n\n const existingHandlers = this.eventHandlers.get(type)!;\n existingHandlers?.add(handler as WebhookEventHandler<WebhookEventType>);\n }\n\n return this;\n }\n\n // Overload for single event type\n off<T extends WebhookEventType>(type: T, handler: WebhookEventHandler<T>): this;\n // Overload for array of event types\n off<T extends WebhookEventType>(types: T[], handler: WebhookEventHandler<T>): this;\n // Implementation\n off<T extends WebhookEventType>(typeOrTypes: T | T[], handler: WebhookEventHandler<T>): this {\n const types = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes];\n\n for (const type of types) {\n const currentHandlers = this.eventHandlers.get(type);\n if (!currentHandlers) {\n continue;\n }\n\n // Set.delete() returns true if the element was in the set\n currentHandlers.delete(handler as WebhookEventHandler<WebhookEventType>);\n\n // Remove the entire entry if no handlers remain\n if (currentHandlers.size === 0) {\n this.eventHandlers.delete(type);\n }\n }\n\n return this;\n }\n\n // Remove all handlers for specific event type(s)\n removeAll<T extends WebhookEventType>(typeOrTypes: T | T[]): this {\n const types = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes];\n\n for (const type of types) {\n this.eventHandlers.delete(type);\n }\n\n return this;\n }\n\n // Get handler count for debugging\n getHandlerCount<T extends WebhookEventType>(type: T): number {\n return this.eventHandlers.get(type)?.size ?? 0;\n }\n\n // Get total number of event types with handlers\n getEventTypeCount(): number {\n return this.eventHandlers.size;\n }\n\n // Get all registered event types\n getRegisteredEventTypes(): WebhookEventType[] {\n return Array.from(this.eventHandlers.keys());\n }\n\n // Check if a specific event type has any handlers\n hasHandlers<T extends WebhookEventType>(type: T): boolean {\n const handlers = this.eventHandlers.get(type);\n return handlers !== undefined && handlers.size > 0;\n }\n\n // Check if a specific handler is registered for an event type\n hasHandler<T extends WebhookEventType>(type: T, handler: WebhookEventHandler<T>): boolean {\n const handlers = this.eventHandlers.get(type);\n return handlers ? handlers.has(handler as WebhookEventHandler<WebhookEventType>) : false;\n }\n\n // Get all handlers for a specific event type (useful for debugging)\n getHandlers<T extends WebhookEventType>(type: T): Set<WebhookEventHandler<WebhookEventType>> {\n return this.eventHandlers.get(type) || new Set();\n }\n\n // Get total count of all handlers across all event types\n getTotalHandlerCount(): number {\n let total = 0;\n for (const handlers of this.eventHandlers.values()) {\n total += handlers.size;\n }\n return total;\n }\n\n /**\n * Verify hex HMAC signature against the raw body.\n */\n verifySignature(rawBody: string | Buffer, signature: string | null): boolean {\n if (!signature) {\n return false;\n }\n\n const mac = crypto.createHmac('sha256', this.secretKey).update(rawBody).digest('hex');\n\n try {\n return crypto.timingSafeEqual(Buffer.from(mac, 'hex'), Buffer.from(signature, 'hex'));\n } catch {\n return false;\n }\n }\n\n /**\n * Process a normalized request.\n * Returns an object you can map to your framework's response easily.\n */\n async process(input: NormalizedRequest): Promise<WebhookListenerResponse> {\n try {\n const event =\n this.authenticationMethod === WebhookAuthenticationMethod.SignatureHeader\n ? await this.authenticateAndGetEventFromInput(input)\n : await this.authenticateAndGetEventFromApi(input);\n\n return this.processEvent(event);\n } catch (error) {\n if (error instanceof WebhookError) {\n return error.toResponse();\n }\n\n return { status: 500, success: false, error: 'Internal Server Error' };\n }\n }\n\n private async authenticateAndGetEventFromInput(input: NormalizedRequest): Promise<EventEntity> {\n const sig = this.getHeader(SIGNATURE_HEADER, input.headers);\n\n if (!this.verifySignature(input.rawBody, sig)) {\n throw new WebhookError('Invalid signature', 401);\n }\n\n return this.parseEventFromInput(input);\n }\n\n private async authenticateAndGetEventFromApi(input: NormalizedRequest): Promise<EventEntity> {\n const jsonPayload = this.parseEventFromInput(input);\n\n if (!jsonPayload.id) {\n throw new WebhookError('Invalid payload');\n }\n\n const event = await this.api.event.retrieve(jsonPayload.id);\n\n if (!event) {\n throw new WebhookError('Event not found', 404);\n }\n\n return event;\n }\n\n private parseEventFromInput(input: NormalizedRequest): EventEntity {\n try {\n const parsed = JSON.parse(\n typeof input.rawBody === 'string' ? input.rawBody : input.rawBody.toString('utf8')\n );\n\n if (!parsed || typeof parsed.type !== 'string') {\n throw new WebhookError('Invalid payload');\n }\n\n return parsed as EventEntity;\n } catch {\n throw new WebhookError('Malformed JSON');\n }\n }\n\n private async processEvent(event: EventEntity): Promise<WebhookListenerResponse> {\n const eventType = event.type as WebhookEventType;\n const eventHandlers = this.eventHandlers.get(eventType);\n\n if (!eventHandlers || eventHandlers.size === 0) {\n // Optionally log unhandled events\n console.warn(`No handlers registered for event type: ${eventType}`);\n }\n\n try {\n // Execute handlers with proper type casting\n const promises = Array.from(eventHandlers || []).map((handler) => {\n const typedHandler = handler as WebhookEventHandler<typeof eventType>;\n const typedEvent = event as WebhookEvent<typeof eventType>;\n return typedHandler(typedEvent);\n });\n\n // Execute handlers in parallel for better performance\n await Promise.all(promises);\n } catch (error) {\n await this.onError?.(error as Error);\n return { status: 500, success: false, error: 'Internal Server Error' };\n }\n\n return { status: 200, success: true };\n }\n\n private getHeader(name: string, headers: NormalizedRequest['headers']): string | null {\n const lname = name.toLowerCase();\n\n if (headers instanceof Headers) {\n return headers.get(lname);\n }\n\n const v = headers[lname] ?? headers[name];\n\n if (Array.isArray(v)) {\n return v[0] ?? null;\n }\n\n return (v as string | undefined) ?? null;\n }\n}\n","import { WebhookAuthenticationMethod } from '../contracts/webhook';\nimport { WebhookListener } from '../webhook/WebhookListener';\nimport { ApiService } from './ApiService';\n\nexport type WebhookListenerConfig = {\n onError?: (error: unknown) => Promise<void>;\n authenticationMethod?: WebhookAuthenticationMethod;\n};\n\nexport class WebhookService {\n constructor(\n private readonly api: ApiService,\n private readonly secretKey: string\n ) {}\n\n createListener(config: WebhookListenerConfig = {}): WebhookListener {\n const { onError, authenticationMethod } = config;\n\n return new WebhookListener(this.api, this.secretKey, onError, authenticationMethod);\n }\n\n createRequestProcessor(listener: WebhookListener): (request: Request) => Promise<Response> {\n return (request: Request) => this.processFetch(listener, request);\n }\n\n /**\n * WHATWG Fetch API adapter for modern JavaScript environments.\n *\n * Compatible with:\n * - Next.js App Router (route.ts files)\n * - Cloudflare Workers\n * - Deno\n * - Bun\n * - Vercel Edge Functions\n * - Any environment supporting the WHATWG Fetch API\n *\n * This method reads the request body as text and processes the webhook,\n * returning a standard Response object that can be directly returned\n * from your endpoint handler.\n *\n * @param listener - The webhook listener instance\n * @param request - The incoming Request object (WHATWG Fetch API)\n * @returns A Response object with the webhook processing result\n *\n * @example\n * ```typescript\n * // Next.js App Router (app/webhook/route.ts)\n * export async function POST(request: Request) {\n * const listener = webhookService.createListener();\n * return await webhookService.processFetch(listener, request);\n * }\n *\n * // Cloudflare Workers\n * export default {\n * async fetch(request: Request): Promise<Response> {\n * if (new URL(request.url).pathname === '/webhook') {\n * const listener = webhookService.createListener();\n * return await webhookService.processFetch(listener, request);\n * }\n * return new Response('Not Found', { status: 404 });\n * }\n * };\n * ```\n */\n async processFetch(listener: WebhookListener, request: Request): Promise<Response> {\n const rawBody = await request.text();\n const result = await listener.process({ headers: request.headers, rawBody });\n\n return new Response(JSON.stringify(result), {\n status: result.status,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n /**\n * Native Node.js HTTP server adapter.\n *\n * Reads the raw body from the request stream and writes the HTTP response directly.\n *\n * @example\n * ```typescript\n * import { createServer } from 'http';\n *\n * const server = createServer(async (req, res) => {\n * if (req.url === '/webhook') {\n * await freemius.webhook.processNodeHttp(listener, req, res);\n * }\n * });\n * ```\n */\n async processNodeHttp(\n listener: WebhookListener,\n req: import('http').IncomingMessage,\n res: import('http').ServerResponse\n ): Promise<void> {\n // Read the raw body from the request stream\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(chunk);\n }\n\n const rawBody = Buffer.concat(chunks);\n\n const result = await listener.process({ headers: req.headers, rawBody });\n\n res.statusCode = result.status;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify(result));\n }\n\n // @todo - Implement other adapters.\n\n // /**\n // * Express adapter.\n // * Route MUST use: express.raw({ type: '* / *' })\n // * Writes the HTTP response directly.\n // */\n // async fromExpress(\n // listener: WebhookListener,\n // req: import('express').Request,\n // res: import('express').Response\n // ): Promise<void> {\n // const { status, ...body } = await listener.process({ headers: req.headers as any, rawBody: req.body });\n // res.status(status).json(body);\n // }\n\n // /**\n // * Fastify adapter.\n // * Must register raw buffer parser:\n // * fastify.addContentTypeParser('* /*', { parseAs: 'buffer' }, (_req, body, done) => done(null, body))\n // * Writes the HTTP response directly.\n // */\n // async fromFastify(\n // listener: WebhookListener,\n // request: { headers: Record<string, string | string[] | undefined>; body: Buffer },\n // reply: { code: (n: number) => any; send: (payload: any) => any }\n // ): Promise<void> {\n // const { status, ...body } = await listener.process({ headers: request.headers, rawBody: request.body });\n // reply.code(status).send(body);\n // }\n}\n","import { createHmac, randomBytes, timingSafeEqual } from 'crypto';\nimport { FSId } from '../api/types';\nimport { idToString } from '../api/parser';\n\nexport interface TokenPayload {\n expiresAt: number;\n nonce: string;\n}\n\nexport class AuthService {\n private static readonly TOKEN_SEPARATOR = '::';\n private static readonly DEFAULT_EXPIRY_MINUTES = 60; // 1 hour default\n\n constructor(\n private readonly productId: FSId,\n private readonly secretKey: string\n ) {\n if (!secretKey || secretKey.length < 32) {\n throw new Error('Secret key must be at least 32 characters long');\n }\n }\n\n /**\n * Creates a secure token for a specific action that can be performed by a user.\n *\n * @param action The action identifier (e.g., 'download_invoice', 'update_billing')\n * @param userId The ID of the user who can perform this action\n * @param expiryMinutes Optional expiry time in minutes (default: 60 minutes)\n * @returns A secure token string\n */\n createActionToken(\n action: string,\n userId: FSId,\n expiryMinutes: number = AuthService.DEFAULT_EXPIRY_MINUTES\n ): string {\n if (!action || action.trim().length === 0) {\n throw new Error('Action cannot be empty');\n }\n\n const now = Date.now();\n const expiresAt = now + expiryMinutes * 60 * 1000;\n const nonce = randomBytes(16).toString('hex');\n\n // Create the minimal payload\n const payload = this.encodeTokenPayload({ expiresAt, nonce });\n\n // Create signature based on all the data\n const signature = this.signData(action.trim(), userId, expiresAt, nonce);\n\n return `${payload}${AuthService.TOKEN_SEPARATOR}${signature}`;\n }\n\n /**\n * Verifies and validates an action token.\n *\n * @param token The token to verify\n * @param action The expected action\n * @param userId The expected user ID\n * @returns true if valid, false otherwise\n */\n verifyActionToken(token: string, action: string, userId: FSId): boolean {\n try {\n if (!token || typeof token !== 'string' || !action || !userId) {\n return false;\n }\n\n const parts = token.split(AuthService.TOKEN_SEPARATOR);\n if (parts.length !== 2) {\n return false;\n }\n\n const [payloadPart, signature] = parts;\n\n if (!payloadPart || !signature) {\n return false;\n }\n\n // Decode the payload\n const payload = this.decodeTokenPayload(payloadPart);\n if (!payload) {\n return false;\n }\n\n // Check expiry\n const now = Date.now();\n if (payload.expiresAt <= now) {\n return false;\n }\n\n // Verify signature by recreating it with the provided action and userId\n const expectedSignature = this.signData(action.trim(), userId, payload.expiresAt, payload.nonce);\n\n return this.constantTimeEqual(signature, expectedSignature);\n } catch {\n return false;\n }\n }\n\n private encodeTokenPayload(payload: TokenPayload): string {\n const jsonString = JSON.stringify(payload);\n return Buffer.from(jsonString).toString('base64url');\n }\n\n private decodeTokenPayload(payloadPart: string): TokenPayload | null {\n try {\n const jsonString = Buffer.from(payloadPart, 'base64url').toString('utf8');\n const data = JSON.parse(jsonString);\n\n // Validate required fields\n if (typeof data.expiresAt !== 'number' || !data.nonce) {\n return null;\n }\n\n return data as TokenPayload;\n } catch {\n return null;\n }\n }\n\n private signData(action: string, userId: FSId, expiresAt: number, nonce: string): string {\n const data = `${action}:${idToString(userId)}:${idToString(this.productId)}:${expiresAt}:${nonce}`;\n\n return createHmac('sha256', this.secretKey).update(data).digest('hex');\n }\n\n private constantTimeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) {\n return false;\n }\n\n try {\n return timingSafeEqual(Buffer.from(a), Buffer.from(b));\n } catch {\n return false;\n }\n }\n}\n","import { isIdsEqual } from '../api/parser';\nimport { FSId, PricingEntity, SellingUnit } from '../api/types';\nimport { PricingData } from '../contracts/pricing';\nimport { PortalPlans } from '../contracts/portal';\nimport { ApiService } from '../services/ApiService';\n\nexport class PricingService {\n constructor(private readonly api: ApiService) {}\n\n async retrieve(topupPlanId?: FSId): Promise<PricingData> {\n const pricingData = await this.api.product.retrievePricingData();\n\n const plans: PricingData['plans'] =\n pricingData?.plans\n ?.filter((plan) => {\n return plan.pricing?.some((p) => this.isValidPricing(p));\n })\n .map((plan) => {\n return {\n ...plan,\n pricing: plan.pricing?.filter((p) => this.isValidPricing(p)) ?? [],\n };\n }) ?? [];\n\n return {\n plans: plans,\n topupPlan: this.findTopupPlan(plans, topupPlanId),\n sellingUnit: (pricingData?.plugin?.selling_unit_label as SellingUnit) ?? {\n singular: 'Unit',\n plural: 'Units',\n },\n };\n }\n\n findTopupPlan(plans?: PortalPlans, planId?: FSId): PortalPlans[number] | null {\n if (!plans) {\n return null;\n }\n\n const topupPlan = plans.find((plan) => {\n return (\n // Either search by the explicitly provided plan ID\n (isIdsEqual(plan.id!, planId ?? '') && !plan.is_hidden) ||\n // Or try to guess: A topup plan is where all pricing have one-off purchase set\n plan.pricing?.filter((p) => this.isValidPricing(p)).every((p) => p.lifetime_price)\n );\n });\n\n return topupPlan ?? null;\n }\n\n private isValidPricing(pricing: PricingEntity): boolean {\n return !!(pricing.monthly_price || pricing.annual_price || pricing.lifetime_price);\n }\n}\n","import { BILLING_CYCLE, CURRENCY } from '../contracts/types';\nimport {\n parseBillingCycle,\n parseNumber,\n idToString,\n isIdsEqual,\n parseDateTime,\n parseCurrency,\n parsePaymentMethod,\n} from '../api/parser';\nimport { PurchaseData, PurchaseEntitlementData } from '../contracts/purchase';\nimport { PricingTableData, FSId, UserEntity, LicenseEntity, SubscriptionEntity } from '../api/types';\n\nexport class PurchaseInfo implements PurchaseData {\n readonly email: string;\n readonly firstName: string;\n readonly lastName: string;\n readonly userId: string;\n readonly planId: string;\n readonly pricingId: string;\n readonly licenseId: string;\n readonly expiration: Date | null;\n readonly canceled: boolean;\n readonly subscriptionId: string | null;\n readonly billingCycle: BILLING_CYCLE | null;\n readonly quota: number | null;\n readonly initialAmount: number | null;\n readonly renewalAmount: number | null;\n readonly currency: CURRENCY | null;\n readonly renewalDate: Date | null;\n readonly paymentMethod: 'card' | 'paypal' | 'ideal' | null;\n readonly created: Date;\n\n constructor(user: UserEntity, license: LicenseEntity, subscription: SubscriptionEntity | null) {\n this.email = user.email!;\n this.firstName = user.first ?? '';\n this.lastName = user.last ?? '';\n this.userId = idToString(license.user_id!);\n this.canceled = license.is_cancelled ?? false;\n this.expiration = license.expiration ? parseDateTime(license.expiration) : null;\n this.licenseId = idToString(license.id!);\n this.planId = idToString(license.plan_id!);\n this.subscriptionId = null;\n this.billingCycle = null;\n this.quota = license.quota ?? null;\n this.pricingId = idToString(license.pricing_id!);\n this.initialAmount = null;\n this.renewalAmount = null;\n this.currency = null;\n this.renewalDate = null;\n this.paymentMethod = null;\n this.created = parseDateTime(license.created) ?? new Date();\n\n if (subscription) {\n this.subscriptionId = idToString(subscription.id!);\n this.billingCycle = parseBillingCycle(subscription.billing_cycle);\n this.initialAmount = parseNumber(subscription.initial_amount);\n this.renewalAmount = parseNumber(subscription.renewal_amount);\n this.currency = parseCurrency(subscription.currency);\n this.renewalDate = subscription.next_payment ? parseDateTime(subscription.next_payment) : null;\n this.paymentMethod = parsePaymentMethod(subscription.gateway);\n }\n }\n\n isPlan(planId: FSId): boolean {\n return this.planId === idToString(planId);\n }\n\n isFromPlans(planIds: FSId[]): boolean {\n return planIds.some((planId) => this.isPlan(planId));\n }\n\n toData(): PurchaseData {\n return {\n email: this.email,\n firstName: this.firstName,\n lastName: this.lastName,\n userId: this.userId,\n planId: this.planId,\n pricingId: this.pricingId,\n licenseId: this.licenseId,\n expiration: this.expiration,\n canceled: this.canceled,\n subscriptionId: this.subscriptionId,\n billingCycle: this.billingCycle,\n quota: this.quota,\n isActive: this.isActive,\n initialAmount: this.initialAmount,\n renewalAmount: this.renewalAmount,\n currency: this.currency,\n renewalDate: this.renewalDate,\n paymentMethod: this.paymentMethod,\n created: this.created,\n };\n }\n\n /**\n * A convenience method to convert the purchase info to a format suitable for database storage.\n */\n toEntitlementRecord<T extends Record<string, unknown>>(additionalData: T = {} as T): PurchaseEntitlementData & T {\n return {\n ...additionalData,\n fsLicenseId: this.licenseId,\n fsPlanId: this.planId,\n fsPricingId: this.pricingId,\n fsUserId: this.userId,\n type: this.isSubscription() ? 'subscription' : 'oneoff',\n expiration: this.expiration,\n createdAt: this.created,\n isCanceled: this.canceled,\n };\n }\n\n get isActive(): boolean {\n if (this.canceled) {\n return false;\n }\n\n if (this.expiration && this.expiration < new Date()) {\n return false;\n }\n\n return true;\n }\n\n isSubscription(): boolean {\n return this.subscriptionId !== null;\n }\n\n isAnnual(): boolean {\n return this.billingCycle === BILLING_CYCLE.YEARLY;\n }\n\n isMonthly(): boolean {\n return this.billingCycle === BILLING_CYCLE.MONTHLY;\n }\n\n isOneOff(): boolean {\n return this.billingCycle === BILLING_CYCLE.ONEOFF || this.billingCycle === null;\n }\n\n getPlanTitle(pricingData: PricingTableData | null): string {\n const plan = pricingData?.plans?.find((p) => isIdsEqual(p.id!, this.planId));\n\n return plan?.title ?? plan?.name ?? 'Deleted Plan';\n }\n}\n","import { isIdsEqual } from '../api/parser';\nimport { FSId, SubscriptionEntity, UserEntity } from '../api/types';\nimport { PurchaseData } from '../contracts/purchase';\nimport { PagingOptions } from '../contracts/types';\nimport { PurchaseInfo } from '../models/PurchaseInfo';\nimport { ApiService } from './ApiService';\n\nexport class PurchaseService {\n constructor(private readonly api: ApiService) {}\n\n /**\n * Retrieve purchase information from the Freemius API based on the license ID.\n *\n * The license is the primary entitlement for a purchase, and it may or may not be associated with a subscription.\n * With this method, you can retrieve detailed information about the purchase, including user details, plan, expiration, and more.\n */\n async retrievePurchase(licenseId: FSId): Promise<PurchaseInfo | null> {\n const [license, subscription] = await Promise.all([\n await this.api.license.retrieve(licenseId),\n await this.api.license.retrieveSubscription(licenseId),\n ]);\n\n if (!license) {\n return null;\n }\n\n const user = await this.api.user.retrieve(license.user_id!);\n\n if (!user) {\n return null;\n }\n\n return new PurchaseInfo(user, license, subscription);\n }\n\n /**\n * A helper method to retrieve raw purchase data instead of a full PurchaseInfo object.\n *\n * This is useful when passing data from server to client in frameworks like Next.js, where only serializable data should be sent.\n */\n async retrievePurchaseData(licenseId: FSId): Promise<PurchaseData | null> {\n const purchaseInfo = await this.retrievePurchase(licenseId);\n\n if (!purchaseInfo) {\n return null;\n }\n\n return purchaseInfo.toData();\n }\n\n async retrievePurchases(userOrEntity: FSId | UserEntity, pagination?: PagingOptions): Promise<PurchaseInfo[]> {\n const user = typeof userOrEntity === 'object' ? userOrEntity : await this.api.user.retrieve(userOrEntity);\n\n if (!user) {\n return [];\n }\n\n const licenses = await this.api.user.retrieveLicenses(user.id!, { type: 'active' }, pagination);\n\n if (!licenses || !licenses.length) {\n return [];\n }\n\n // @todo - Improve rate limiting by batching requests or using a more efficient method/ new API endpoint.\n const licenseSubscriptionPromises = licenses.map(async (license) => {\n const subscription =\n license.expiration !== null ? await this.api.license.retrieveSubscription(license.id!) : null;\n\n return new PurchaseInfo(user, license, subscription);\n });\n\n return await Promise.all(licenseSubscriptionPromises).then((results) =>\n results\n // Remove null values and sort by creation date, where the recent purchase is first.\n .filter((result): result is PurchaseInfo => result !== null)\n .sort((a, b) => b.created.getTime() - a.created.getTime())\n );\n }\n\n async retrievePurchasesData(userOrEntity: FSId | UserEntity, pagination?: PagingOptions): Promise<PurchaseData[]> {\n const purchaseInfos = await this.retrievePurchases(userOrEntity, pagination);\n\n return purchaseInfos.map((info) => info.toData());\n }\n\n /**\n * Retrieve a list of active subscriptions for a user. You can use this method to find or sync subscriptions from freemius to your system.\n */\n async retrieveSubscriptions(userOrEntity: FSId | UserEntity, pagination?: PagingOptions): Promise<PurchaseInfo[]> {\n const user = typeof userOrEntity === 'object' ? userOrEntity : await this.api.user.retrieve(userOrEntity);\n\n if (!user) {\n return [];\n }\n\n const subscriptions = await this.api.user.retrieveSubscriptions(\n user.id!,\n {\n filter: 'active',\n },\n pagination\n );\n\n if (!subscriptions || !subscriptions.length) {\n return [];\n }\n\n // @todo - Improve rate limiting by batching requests or using a more efficient method.\n const licenseSubscriptionPromises = subscriptions.map(async (subscription) => {\n const license = await this.api.license.retrieve(subscription.license_id!);\n\n if (!license) {\n return null;\n }\n\n return new PurchaseInfo(user, license, subscription);\n });\n\n return await Promise.all(licenseSubscriptionPromises).then((results) =>\n results\n // Remove null values and sort by creation date, where the recent purchase is first.\n .filter((result): result is PurchaseInfo => result !== null)\n .sort((a, b) => b.created.getTime() - a.created.getTime())\n );\n }\n\n /**\n * Retrieve a list of purchase data for a user.\n *\n * This is a convenience method that returns the purchase data in a format suitable for client-side rendering or serialization.\n */\n async retrieveSubscriptionsData(userId: FSId, pagination?: PagingOptions): Promise<PurchaseData[]> {\n const purchaseInfos = await this.retrieveSubscriptions(userId, pagination);\n\n return purchaseInfos.map((info) => info.toData());\n }\n\n async retrieveBySubscription(\n subscription: SubscriptionEntity,\n subscriptionUser?: UserEntity\n ): Promise<PurchaseInfo | null> {\n if (!subscription.license_id) {\n return null;\n }\n\n const license = await this.api.license.retrieve(subscription.license_id);\n\n if (!license) {\n return null;\n }\n\n const user =\n subscriptionUser && isIdsEqual(subscriptionUser.id!, license.user_id!)\n ? subscriptionUser\n : await this.api.user.retrieve(license.user_id!);\n\n if (!user) {\n return null;\n }\n\n return new PurchaseInfo(user, license, subscription);\n }\n\n async retrieveSubscriptionsByEmail(email: string, pagination?: PagingOptions): Promise<PurchaseInfo[]> {\n const user = await this.api.user.retrieveByEmail(email);\n\n if (!user) {\n return [];\n }\n\n return await this.retrieveSubscriptions(user.id!, pagination);\n }\n\n async retrievePurchasesByEmail(email: string, pagination?: PagingOptions): Promise<PurchaseInfo[]> {\n const user = await this.api.user.retrieveByEmail(email);\n\n if (!user) {\n return [];\n }\n\n return await this.retrievePurchases(user.id!, pagination);\n }\n}\n","import { parseDateTime } from '../api/parser';\nimport { PurchaseEntitlementData } from '../contracts/purchase';\nimport { UserRetriever } from '../contracts/types';\n\nexport class EntitlementService {\n /**\n * Get the active subscription entitlement from a list of entitlements stored in your own database.\n *\n * @param entitlements - Array of entitlements to filter\n * @returns The single active entitlement, or null if none found\n * @throws Error if multiple active entitlements are found\n */\n getActive<T extends PurchaseEntitlementData>(entitlements: T[] | null): T | null {\n const activeEntitlements = this.getActives(entitlements);\n\n if (!activeEntitlements || activeEntitlements.length === 0) {\n return null;\n }\n\n if (activeEntitlements.length > 1) {\n throw new Error(`Multiple active entitlements found: ${activeEntitlements.length} entitlements are active`);\n }\n\n return activeEntitlements[0]!;\n }\n\n /**\n * Get all active subscription entitlements from a list of entitlements stored in your own database.\n *\n * @param entitlements - Array of entitlements to filter\n * @returns Array of active entitlements, or null if none found\n */\n getActives<T extends PurchaseEntitlementData>(entitlements: T[] | null): T[] | null {\n if (!entitlements || entitlements.length === 0) {\n return null;\n }\n\n const activeEntitlements = entitlements.filter((entitlement) => {\n if (entitlement.type !== 'subscription') {\n return false;\n }\n\n if (entitlement.isCanceled) {\n return false;\n }\n\n if (entitlement.expiration === null) {\n return true;\n }\n\n const expiration =\n entitlement.expiration instanceof Date ? entitlement.expiration : parseDateTime(entitlement.expiration);\n\n if (expiration && expiration < new Date()) {\n return false;\n }\n\n return true;\n });\n\n return activeEntitlements.length > 0 ? activeEntitlements : null;\n }\n\n getFsUser(entitlement: PurchaseEntitlementData | null, email?: string): Awaited<ReturnType<UserRetriever>> {\n if (entitlement) {\n return {\n email,\n id: entitlement.fsUserId,\n primaryLicenseId: entitlement.fsLicenseId,\n } as Awaited<ReturnType<UserRetriever>>;\n }\n\n return email ? { email } : null;\n }\n\n /**\n * Calculates the number of complete months elapsed since the entitlement subscription was created.\n *\n * @param entitlement - The entitlement to check\n * @returns Number of complete months elapsed, or -1 if entitlement is null\n */\n getElapsedMonth(entitlement: PurchaseEntitlementData | null): number {\n if (!entitlement) {\n return -1;\n }\n\n const created =\n entitlement.createdAt instanceof Date ? entitlement.createdAt : parseDateTime(entitlement.createdAt);\n\n if (!created) {\n return -1;\n }\n\n const now = new Date();\n\n let months = (now.getFullYear() - created.getFullYear()) * 12 + (now.getMonth() - created.getMonth());\n\n // Adjust if current day is before the created day\n if (now.getDate() < created.getDate()) {\n months -= 1;\n }\n\n return months;\n }\n\n /**\n * Calculates the number of complete years elapsed since the entitlement subscription was created.\n *\n * @param entitlement - The entitlement to check\n * @returns Number of complete years elapsed, or -1 if entitlement is null\n */\n getElapsedYear(entitlement: PurchaseEntitlementData | null): number {\n if (!entitlement) {\n return -1;\n }\n\n const created =\n entitlement.createdAt instanceof Date ? entitlement.createdAt : parseDateTime(entitlement.createdAt);\n\n if (!created) {\n return -1;\n }\n\n const now = new Date();\n\n let years = now.getFullYear() - created.getFullYear();\n\n // Adjust if current month/day is before the created month/day\n if (\n now.getMonth() < created.getMonth() ||\n (now.getMonth() === created.getMonth() && now.getDate() < created.getDate())\n ) {\n years -= 1;\n }\n\n return years;\n }\n}\n","import { ApiService } from './services/ApiService';\nimport { CheckoutService } from './services/CheckoutService';\nimport { CustomerPortalService } from './services/CustomerPortalService';\nimport { FSId } from './api/types';\nimport { WebhookService } from './services/WebhookService';\nimport { AuthService } from './services/AuthService';\nimport { PricingService } from './services/PricingService';\nimport { PurchaseService } from './services/PurchaseService';\nimport { EntitlementService } from './services/EntitlementService';\n\nexport type FreemiusConfig = {\n productId: FSId;\n apiKey: string;\n secretKey: string;\n publicKey: string;\n};\n\nexport class Freemius {\n public readonly api: ApiService;\n\n public readonly checkout: CheckoutService;\n\n public readonly purchase: PurchaseService;\n\n public readonly customerPortal: CustomerPortalService;\n\n public readonly webhook: WebhookService;\n\n public readonly pricing: PricingService;\n\n public readonly entitlement = new EntitlementService();\n\n private readonly auth: AuthService;\n\n constructor(config: FreemiusConfig) {\n const { productId, apiKey, secretKey, publicKey } = config;\n\n this.api = new ApiService(productId, apiKey, secretKey, publicKey);\n this.auth = new AuthService(productId, secretKey);\n this.pricing = new PricingService(this.api);\n this.purchase = new PurchaseService(this.api);\n this.checkout = new CheckoutService(this.api, productId, publicKey, secretKey, this.purchase, this.pricing);\n this.customerPortal = new CustomerPortalService(this.api, this.checkout, this.auth, this.purchase);\n this.webhook = new WebhookService(this.api, secretKey);\n }\n}\n"],"mappings":"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"}
|