@parsrun/payments 0.1.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/README.md +184 -0
- package/dist/billing/index.d.ts +121 -0
- package/dist/billing/index.js +1082 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/billing-service-LsAFesou.d.ts +578 -0
- package/dist/dunning/index.d.ts +310 -0
- package/dist/dunning/index.js +2677 -0
- package/dist/dunning/index.js.map +1 -0
- package/dist/index.d.ts +185 -0
- package/dist/index.js +7698 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.js +1396 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/iyzico.d.ts +250 -0
- package/dist/providers/iyzico.js +469 -0
- package/dist/providers/iyzico.js.map +1 -0
- package/dist/providers/paddle.d.ts +66 -0
- package/dist/providers/paddle.js +437 -0
- package/dist/providers/paddle.js.map +1 -0
- package/dist/providers/stripe.d.ts +122 -0
- package/dist/providers/stripe.js +586 -0
- package/dist/providers/stripe.js.map +1 -0
- package/dist/schema-C5Zcju_j.d.ts +4191 -0
- package/dist/types.d.ts +388 -0
- package/dist/types.js +74 -0
- package/dist/types.js.map +1 -0
- package/dist/usage/index.d.ts +2674 -0
- package/dist/usage/index.js +2916 -0
- package/dist/usage/index.js.map +1 -0
- package/dist/webhooks/index.d.ts +89 -0
- package/dist/webhooks/index.js +188 -0
- package/dist/webhooks/index.js.map +1 -0
- package/package.json +91 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/usage/types.ts","../../src/usage/memory-storage.ts","../../src/usage/drizzle-storage.ts","../../src/usage/schema.ts","../../src/usage/quota-manager.ts","../../src/usage/usage-tracker.ts","../../src/usage/lifecycle-hooks.ts","../../src/usage/usage-service.ts","../../src/usage/billing-integration.ts","../../src/usage/usage-meter.ts"],"sourcesContent":["/**\n * @parsrun/payments - Usage Types\n * Types for usage-based billing\n */\n\nimport type { PaymentProviderType } from \"../types.js\";\nimport type { BillingSubscription, BillingLogger as BillingLoggerType } from \"../billing/types.js\";\n\n// Re-export BillingLogger for convenience\nexport type BillingLogger = BillingLoggerType;\n\n// ============================================================================\n// Plan & Features\n// ============================================================================\n\n/**\n * Plan tier levels\n */\nexport type PlanTier = 0 | 1 | 2 | 3 | 4; // free, starter, pro, enterprise, custom\n\n/**\n * Billing interval\n */\nexport type BillingInterval = \"month\" | \"year\";\n\n/**\n * Limit period for features\n */\nexport type LimitPeriod = \"hour\" | \"day\" | \"month\" | null;\n\n/**\n * Reset period for quotas\n * - monthly: Reset on the 1st of each calendar month\n * - billing_cycle: Reset on subscription renewal date\n */\nexport type ResetPeriod = \"monthly\" | \"billing_cycle\";\n\n/**\n * Customer access status\n * - active: Full access, subscription is current\n * - past_due: Limited access, payment failed but grace period active\n * - suspended: No access, multiple payment failures\n * - canceled: No access, subscription canceled\n * - unpaid: No access, subscription unpaid\n */\nexport type AccessStatus = \"active\" | \"past_due\" | \"suspended\" | \"canceled\" | \"unpaid\";\n\n/**\n * Access status info for a customer\n */\nexport interface AccessStatusInfo {\n status: AccessStatus;\n /** When the status was last updated */\n updatedAt: Date;\n /** Reason for current status */\n reason?: string;\n /** When access will be suspended (for past_due) */\n suspensionDate?: Date;\n /** Number of failed payment attempts */\n failedPaymentAttempts?: number;\n /** Grace period end date */\n gracePeriodEnd?: Date;\n}\n\n/**\n * Plan definition\n */\nexport interface Plan {\n id: string;\n name: string;\n displayName: string;\n description: string | null;\n tier: PlanTier;\n basePrice: number; // cents\n currency: string;\n billingInterval: BillingInterval;\n features: PlanFeature[];\n isActive: boolean;\n metadata?: Record<string, unknown>;\n createdAt: Date;\n updatedAt: Date;\n}\n\n/**\n * Plan feature with limits\n */\nexport interface PlanFeature {\n id: string;\n planId: string;\n featureKey: string;\n limitValue: number | null; // null = unlimited\n limitPeriod: LimitPeriod;\n isEnabled: boolean;\n metadata?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Usage Events\n// ============================================================================\n\n/**\n * Usage event record\n */\nexport interface UsageEvent {\n id: string;\n tenantId: string;\n customerId: string;\n subscriptionId?: string;\n featureKey: string;\n quantity: number;\n timestamp: Date;\n metadata?: Record<string, unknown>;\n idempotencyKey?: string;\n}\n\n/**\n * Options for tracking usage\n */\nexport interface TrackUsageOptions {\n tenantId: string;\n customerId: string;\n subscriptionId?: string;\n featureKey: string;\n quantity?: number;\n metadata?: Record<string, unknown>;\n idempotencyKey?: string;\n timestamp?: Date;\n}\n\n// ============================================================================\n// Usage Aggregates\n// ============================================================================\n\n/**\n * Period type for aggregation\n */\nexport type PeriodType = \"hour\" | \"day\" | \"month\";\n\n/**\n * Usage aggregate record\n */\nexport interface UsageAggregate {\n id: string;\n tenantId: string;\n customerId: string;\n subscriptionId?: string;\n featureKey: string;\n periodStart: Date;\n periodEnd: Date;\n periodType: PeriodType;\n totalQuantity: number;\n eventCount: number;\n lastUpdated: Date;\n}\n\n/**\n * Options for getting usage\n */\nexport interface GetUsageOptions {\n featureKey?: string;\n periodType?: PeriodType;\n startDate?: Date;\n endDate?: Date;\n subscriptionId?: string;\n}\n\n// ============================================================================\n// Quota\n// ============================================================================\n\n/**\n * Quota status for a feature\n */\nexport interface QuotaStatus {\n featureKey: string;\n limit: number | null; // null = unlimited\n used: number;\n remaining: number | null; // null = unlimited\n percentUsed: number | null;\n periodStart: Date;\n periodEnd: Date;\n isExceeded: boolean;\n isUnlimited: boolean;\n /** Overage amount (used - limit) when soft limits enabled */\n overage?: number;\n /** Whether overage is allowed for this feature */\n overageAllowed?: boolean;\n}\n\n/**\n * Quota check result\n */\nexport interface QuotaCheckResult {\n allowed: boolean;\n currentUsage: number;\n limit: number | null;\n remaining: number | null;\n wouldExceed: boolean;\n percentAfter: number | null;\n}\n\n// ============================================================================\n// Alerts\n// ============================================================================\n\n/**\n * Alert status\n */\nexport type AlertStatus = \"pending\" | \"triggered\" | \"acknowledged\" | \"resolved\";\n\n/**\n * Usage alert\n */\nexport interface UsageAlert {\n id: string;\n tenantId: string;\n customerId: string;\n subscriptionId?: string;\n featureKey: string;\n thresholdPercent: number;\n status: AlertStatus;\n currentUsage: number;\n limit: number;\n triggeredAt?: Date;\n acknowledgedAt?: Date;\n resolvedAt?: Date;\n metadata?: Record<string, unknown>;\n createdAt: Date;\n}\n\n// ============================================================================\n// Subscription Lifecycle\n// ============================================================================\n\n/**\n * Subscription lifecycle event types\n */\nexport type SubscriptionEventType =\n | \"subscription.created\"\n | \"subscription.activated\"\n | \"subscription.updated\"\n | \"subscription.plan_changed\"\n | \"subscription.canceled\"\n | \"subscription.expired\"\n | \"subscription.renewed\"\n | \"subscription.trial_started\"\n | \"subscription.trial_ended\"\n | \"subscription.payment_failed\"\n | \"subscription.payment_succeeded\"\n | \"subscription.period_reset\";\n\n/**\n * Subscription lifecycle event\n */\nexport interface SubscriptionEvent {\n type: SubscriptionEventType;\n subscription: BillingSubscription;\n previousPlan?: Plan;\n newPlan?: Plan;\n timestamp: Date;\n provider: PaymentProviderType;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Subscription event handler\n */\nexport type SubscriptionHandler = (event: SubscriptionEvent) => void | Promise<void>;\n\n// ============================================================================\n// Usage Webhook Events\n// ============================================================================\n\n/**\n * Usage-specific webhook event types\n */\nexport type UsageWebhookEventType =\n | \"usage.recorded\"\n | \"usage.threshold_reached\"\n | \"usage.limit_exceeded\"\n | \"usage.limit_reset\"\n | \"plan.upgraded\"\n | \"plan.downgraded\";\n\n// ============================================================================\n// Storage Interface\n// ============================================================================\n\n/**\n * Usage storage interface\n * Allows pluggable storage backends (database, in-memory, etc.)\n */\nexport interface UsageStorage {\n // Plans\n getPlan(planId: string): Promise<Plan | null>;\n getPlanByName(name: string): Promise<Plan | null>;\n listPlans(options?: { activeOnly?: boolean }): Promise<Plan[]>;\n\n // Plan Features\n getPlanFeatures(planId: string): Promise<PlanFeature[]>;\n getFeatureLimit(planId: string, featureKey: string): Promise<PlanFeature | null>;\n\n // Customer-Plan mapping\n getCustomerPlanId(customerId: string): Promise<string | null>;\n setCustomerPlanId(customerId: string, planId: string): Promise<void>;\n\n // Usage Events\n recordUsage(event: Omit<UsageEvent, \"id\">): Promise<UsageEvent>;\n recordUsageBatch(events: Omit<UsageEvent, \"id\">[]): Promise<UsageEvent[]>;\n getUsageEvents(customerId: string, options: GetUsageOptions): Promise<UsageEvent[]>;\n\n // Usage Aggregates\n getAggregate(customerId: string, featureKey: string, periodType: PeriodType, periodStart: Date): Promise<UsageAggregate | null>;\n upsertAggregate(aggregate: Omit<UsageAggregate, \"id\">): Promise<UsageAggregate>;\n getAggregates(customerId: string, options: GetUsageOptions): Promise<UsageAggregate[]>;\n\n // Current period usage (fast path)\n getCurrentPeriodUsage(customerId: string, featureKey: string, periodStart: Date): Promise<number>;\n\n // Alerts\n createAlert(alert: Omit<UsageAlert, \"id\" | \"createdAt\">): Promise<UsageAlert>;\n getActiveAlerts(customerId: string): Promise<UsageAlert[]>;\n updateAlertStatus(alertId: string, status: AlertStatus): Promise<void>;\n\n // Usage Reset\n /** Reset usage for a customer (all features or specific ones) */\n resetUsage(customerId: string, featureKeys?: string[], periodStart?: Date): Promise<void>;\n /** Reset all aggregates for a customer */\n resetAggregates(customerId: string, featureKeys?: string[]): Promise<void>;\n\n // Access Status\n /** Get customer access status */\n getAccessStatus(customerId: string): Promise<AccessStatusInfo | null>;\n /** Set customer access status */\n setAccessStatus(customerId: string, status: AccessStatusInfo): Promise<void>;\n\n // Billing Cycle (for billing_cycle reset period)\n /** Get customer's current billing cycle dates */\n getBillingCycle(customerId: string): Promise<{ start: Date; end: Date } | null>;\n /** Set customer's billing cycle dates */\n setBillingCycle(customerId: string, start: Date, end: Date): Promise<void>;\n}\n\n// ============================================================================\n// Service Configuration\n// ============================================================================\n\n/**\n * Usage service configuration\n */\nexport interface UsageServiceConfig {\n /** Storage backend */\n storage: UsageStorage;\n\n /** Logger */\n logger?: BillingLogger;\n\n /** Alert thresholds (default: [80, 100]) */\n alertThresholds?: number[];\n\n /** Aggregate usage immediately (default: false - async) */\n aggregateImmediately?: boolean;\n\n /**\n * Reset period for quotas\n * - monthly: Reset on the 1st of each calendar month (default)\n * - billing_cycle: Reset on subscription renewal date\n */\n resetPeriod?: ResetPeriod;\n\n /**\n * Auto-reset quotas on subscription renewal\n * Default: true\n */\n autoResetOnRenewal?: boolean;\n\n /**\n * Grace period in days before access is suspended after payment failure\n * Default: 3 days\n */\n paymentGraceDays?: number;\n\n /**\n * Number of failed payments before suspending access\n * Default: 3\n */\n maxFailedPayments?: number;\n\n /** Callback when threshold is reached */\n onThresholdReached?: (alert: UsageAlert) => void | Promise<void>;\n\n /** Callback when limit is exceeded */\n onLimitExceeded?: (quota: QuotaStatus, customerId: string) => void | Promise<void>;\n\n /** Callback when period resets */\n onPeriodReset?: (customerId: string, featureKey: string) => void | Promise<void>;\n\n /** Callback when access status changes */\n onAccessStatusChanged?: (customerId: string, status: AccessStatusInfo, previousStatus?: AccessStatus) => void | Promise<void>;\n}\n\n/**\n * Quota manager configuration\n */\nexport interface QuotaManagerConfig {\n /** Storage backend */\n storage: UsageStorage;\n\n /** Logger */\n logger?: BillingLogger;\n\n /** Soft limit behavior - warn but allow (default: false) */\n softLimits?: boolean;\n\n /** Grace period percentage over limit (default: 0) */\n gracePercent?: number;\n\n /**\n * Features that allow overage (soft limits)\n * If set, only these features will allow overage.\n * If not set and softLimits=true, all features allow overage.\n */\n overageAllowedFeatures?: string[];\n\n /**\n * Callback when overage is recorded\n * Called when usage exceeds limit for a soft-limit feature\n */\n onOverage?: (customerId: string, featureKey: string, overage: number, limit: number) => void | Promise<void>;\n}\n\n// ============================================================================\n// Errors\n// ============================================================================\n\n/**\n * Usage error codes\n */\nexport const UsageErrorCodes = {\n QUOTA_EXCEEDED: \"QUOTA_EXCEEDED\",\n PLAN_NOT_FOUND: \"PLAN_NOT_FOUND\",\n FEATURE_NOT_FOUND: \"FEATURE_NOT_FOUND\",\n CUSTOMER_NOT_FOUND: \"CUSTOMER_NOT_FOUND\",\n DUPLICATE_EVENT: \"DUPLICATE_EVENT\",\n STORAGE_ERROR: \"STORAGE_ERROR\",\n INVALID_PERIOD: \"INVALID_PERIOD\",\n} as const;\n\nexport type UsageErrorCode = keyof typeof UsageErrorCodes;\n\n/**\n * Usage error\n */\nexport class UsageError extends Error {\n constructor(\n message: string,\n public readonly code: UsageErrorCode,\n public readonly details?: Record<string, unknown>\n ) {\n super(message);\n this.name = \"UsageError\";\n }\n}\n\n/**\n * Quota exceeded error\n */\nexport class QuotaExceededError extends UsageError {\n constructor(\n public readonly featureKey: string,\n public readonly limit: number | null,\n public readonly currentUsage: number,\n public readonly requestedQuantity: number = 1\n ) {\n super(\n `Quota exceeded for feature \"${featureKey}\": ${currentUsage}/${limit ?? \"unlimited\"} used`,\n \"QUOTA_EXCEEDED\",\n { featureKey, limit, currentUsage, requestedQuantity }\n );\n this.name = \"QuotaExceededError\";\n }\n}\n","/**\n * @parsrun/payments - In-Memory Usage Storage\n * Default storage implementation for development and testing\n */\n\nimport type {\n UsageStorage,\n Plan,\n PlanFeature,\n UsageEvent,\n UsageAggregate,\n UsageAlert,\n AlertStatus,\n PeriodType,\n GetUsageOptions,\n AccessStatusInfo,\n} from \"./types.js\";\n\n/**\n * Generate unique ID\n */\nfunction generateId(): string {\n return `${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 9)}`;\n}\n\n/**\n * In-memory usage storage\n * For development, testing, and single-instance deployments\n */\nexport class MemoryUsageStorage implements UsageStorage {\n private plans = new Map<string, Plan>();\n private planFeatures = new Map<string, PlanFeature[]>();\n private customerPlans = new Map<string, string>();\n private usageEvents: UsageEvent[] = [];\n private usageAggregates = new Map<string, UsageAggregate>();\n private alerts = new Map<string, UsageAlert>();\n private idempotencyKeys = new Set<string>();\n private accessStatuses = new Map<string, AccessStatusInfo>();\n private billingCycles = new Map<string, { start: Date; end: Date }>();\n\n // ============================================================================\n // Plans\n // ============================================================================\n\n async getPlan(planId: string): Promise<Plan | null> {\n return this.plans.get(planId) ?? null;\n }\n\n async getPlanByName(name: string): Promise<Plan | null> {\n for (const plan of this.plans.values()) {\n if (plan.name === name) {\n return plan;\n }\n }\n return null;\n }\n\n async listPlans(options?: { activeOnly?: boolean }): Promise<Plan[]> {\n const plans = Array.from(this.plans.values());\n if (options?.activeOnly) {\n return plans.filter((p) => p.isActive);\n }\n return plans;\n }\n\n /**\n * Add a plan (for testing/setup)\n */\n addPlan(plan: Plan): void {\n this.plans.set(plan.id, plan);\n // Extract features\n if (plan.features.length > 0) {\n this.planFeatures.set(plan.id, plan.features);\n }\n }\n\n // ============================================================================\n // Plan Features\n // ============================================================================\n\n async getPlanFeatures(planId: string): Promise<PlanFeature[]> {\n return this.planFeatures.get(planId) ?? [];\n }\n\n async getFeatureLimit(planId: string, featureKey: string): Promise<PlanFeature | null> {\n const features = this.planFeatures.get(planId);\n if (!features) return null;\n return features.find((f) => f.featureKey === featureKey) ?? null;\n }\n\n /**\n * Add plan features (for testing/setup)\n */\n addPlanFeatures(planId: string, features: PlanFeature[]): void {\n this.planFeatures.set(planId, features);\n }\n\n // ============================================================================\n // Customer-Plan Mapping\n // ============================================================================\n\n async getCustomerPlanId(customerId: string): Promise<string | null> {\n return this.customerPlans.get(customerId) ?? null;\n }\n\n async setCustomerPlanId(customerId: string, planId: string): Promise<void> {\n this.customerPlans.set(customerId, planId);\n }\n\n // ============================================================================\n // Usage Events\n // ============================================================================\n\n async recordUsage(event: Omit<UsageEvent, \"id\">): Promise<UsageEvent> {\n // Check idempotency\n if (event.idempotencyKey) {\n if (this.idempotencyKeys.has(event.idempotencyKey)) {\n // Return existing event with same key\n const existing = this.usageEvents.find(\n (e) => e.idempotencyKey === event.idempotencyKey\n );\n if (existing) return existing;\n }\n this.idempotencyKeys.add(event.idempotencyKey);\n }\n\n const usageEvent: UsageEvent = {\n id: generateId(),\n ...event,\n timestamp: event.timestamp ?? new Date(),\n };\n\n this.usageEvents.push(usageEvent);\n return usageEvent;\n }\n\n async recordUsageBatch(events: Omit<UsageEvent, \"id\">[]): Promise<UsageEvent[]> {\n const results: UsageEvent[] = [];\n for (const event of events) {\n results.push(await this.recordUsage(event));\n }\n return results;\n }\n\n async getUsageEvents(customerId: string, options: GetUsageOptions): Promise<UsageEvent[]> {\n let events = this.usageEvents.filter((e) => e.customerId === customerId);\n\n if (options.featureKey) {\n events = events.filter((e) => e.featureKey === options.featureKey);\n }\n\n if (options.subscriptionId) {\n events = events.filter((e) => e.subscriptionId === options.subscriptionId);\n }\n\n if (options.startDate) {\n events = events.filter((e) => e.timestamp >= options.startDate!);\n }\n\n if (options.endDate) {\n events = events.filter((e) => e.timestamp <= options.endDate!);\n }\n\n return events;\n }\n\n // ============================================================================\n // Usage Aggregates\n // ============================================================================\n\n private getAggregateKey(\n customerId: string,\n featureKey: string,\n periodType: PeriodType,\n periodStart: Date\n ): string {\n return `${customerId}:${featureKey}:${periodType}:${periodStart.toISOString()}`;\n }\n\n async getAggregate(\n customerId: string,\n featureKey: string,\n periodType: PeriodType,\n periodStart: Date\n ): Promise<UsageAggregate | null> {\n const key = this.getAggregateKey(customerId, featureKey, periodType, periodStart);\n return this.usageAggregates.get(key) ?? null;\n }\n\n async upsertAggregate(aggregate: Omit<UsageAggregate, \"id\">): Promise<UsageAggregate> {\n const key = this.getAggregateKey(\n aggregate.customerId,\n aggregate.featureKey,\n aggregate.periodType,\n aggregate.periodStart\n );\n\n const existing = this.usageAggregates.get(key);\n const result: UsageAggregate = {\n id: existing?.id ?? generateId(),\n ...aggregate,\n lastUpdated: new Date(),\n };\n\n this.usageAggregates.set(key, result);\n return result;\n }\n\n async getAggregates(customerId: string, options: GetUsageOptions): Promise<UsageAggregate[]> {\n let aggregates = Array.from(this.usageAggregates.values()).filter(\n (a) => a.customerId === customerId\n );\n\n if (options.featureKey) {\n aggregates = aggregates.filter((a) => a.featureKey === options.featureKey);\n }\n\n if (options.periodType) {\n aggregates = aggregates.filter((a) => a.periodType === options.periodType);\n }\n\n if (options.subscriptionId) {\n aggregates = aggregates.filter((a) => a.subscriptionId === options.subscriptionId);\n }\n\n if (options.startDate) {\n aggregates = aggregates.filter((a) => a.periodStart >= options.startDate!);\n }\n\n if (options.endDate) {\n aggregates = aggregates.filter((a) => a.periodEnd <= options.endDate!);\n }\n\n return aggregates;\n }\n\n async getCurrentPeriodUsage(\n customerId: string,\n featureKey: string,\n periodStart: Date\n ): Promise<number> {\n // First check aggregate\n const aggregate = await this.getAggregate(customerId, featureKey, \"month\", periodStart);\n if (aggregate) {\n return aggregate.totalQuantity;\n }\n\n // Fall back to counting events\n const events = this.usageEvents.filter(\n (e) =>\n e.customerId === customerId &&\n e.featureKey === featureKey &&\n e.timestamp >= periodStart\n );\n\n return events.reduce((sum, e) => sum + e.quantity, 0);\n }\n\n // ============================================================================\n // Alerts\n // ============================================================================\n\n async createAlert(alert: Omit<UsageAlert, \"id\" | \"createdAt\">): Promise<UsageAlert> {\n const usageAlert: UsageAlert = {\n id: generateId(),\n ...alert,\n createdAt: new Date(),\n };\n\n this.alerts.set(usageAlert.id, usageAlert);\n return usageAlert;\n }\n\n async getActiveAlerts(customerId: string): Promise<UsageAlert[]> {\n return Array.from(this.alerts.values()).filter(\n (a) =>\n a.customerId === customerId &&\n (a.status === \"pending\" || a.status === \"triggered\")\n );\n }\n\n async updateAlertStatus(alertId: string, status: AlertStatus): Promise<void> {\n const alert = this.alerts.get(alertId);\n if (alert) {\n alert.status = status;\n if (status === \"triggered\") {\n alert.triggeredAt = new Date();\n } else if (status === \"acknowledged\") {\n alert.acknowledgedAt = new Date();\n } else if (status === \"resolved\") {\n alert.resolvedAt = new Date();\n }\n }\n }\n\n // ============================================================================\n // Usage Reset\n // ============================================================================\n\n async resetUsage(\n customerId: string,\n featureKeys?: string[],\n periodStart?: Date\n ): Promise<void> {\n // Remove events for this customer\n this.usageEvents = this.usageEvents.filter((e) => {\n if (e.customerId !== customerId) return true;\n if (featureKeys && !featureKeys.includes(e.featureKey)) return true;\n if (periodStart && e.timestamp < periodStart) return true;\n return false;\n });\n\n // Reset aggregates\n await this.resetAggregates(customerId, featureKeys);\n }\n\n async resetAggregates(customerId: string, featureKeys?: string[]): Promise<void> {\n const keysToDelete: string[] = [];\n\n for (const [key, aggregate] of this.usageAggregates) {\n if (aggregate.customerId !== customerId) continue;\n if (featureKeys && !featureKeys.includes(aggregate.featureKey)) continue;\n keysToDelete.push(key);\n }\n\n for (const key of keysToDelete) {\n this.usageAggregates.delete(key);\n }\n }\n\n // ============================================================================\n // Access Status\n // ============================================================================\n\n async getAccessStatus(customerId: string): Promise<AccessStatusInfo | null> {\n return this.accessStatuses.get(customerId) ?? null;\n }\n\n async setAccessStatus(customerId: string, status: AccessStatusInfo): Promise<void> {\n this.accessStatuses.set(customerId, status);\n }\n\n // ============================================================================\n // Billing Cycle\n // ============================================================================\n\n async getBillingCycle(customerId: string): Promise<{ start: Date; end: Date } | null> {\n return this.billingCycles.get(customerId) ?? null;\n }\n\n async setBillingCycle(customerId: string, start: Date, end: Date): Promise<void> {\n this.billingCycles.set(customerId, { start, end });\n }\n\n // ============================================================================\n // Utilities\n // ============================================================================\n\n /**\n * Clear all data (for testing)\n */\n clear(): void {\n this.plans.clear();\n this.planFeatures.clear();\n this.customerPlans.clear();\n this.usageEvents = [];\n this.usageAggregates.clear();\n this.alerts.clear();\n this.idempotencyKeys.clear();\n this.accessStatuses.clear();\n this.billingCycles.clear();\n }\n\n /**\n * Get stats (for debugging)\n */\n getStats(): {\n plans: number;\n customers: number;\n events: number;\n aggregates: number;\n alerts: number;\n accessStatuses: number;\n billingCycles: number;\n } {\n return {\n plans: this.plans.size,\n customers: this.customerPlans.size,\n events: this.usageEvents.length,\n aggregates: this.usageAggregates.size,\n alerts: this.alerts.size,\n accessStatuses: this.accessStatuses.size,\n billingCycles: this.billingCycles.size,\n };\n }\n}\n\n/**\n * Create in-memory usage storage\n */\nexport function createMemoryUsageStorage(): MemoryUsageStorage {\n return new MemoryUsageStorage();\n}\n","/**\n * @parsrun/payments - Drizzle Usage Storage\n * Database-backed storage implementation using Drizzle ORM\n *\n * @example\n * ```typescript\n * import { createDrizzleUsageStorage } from \"@parsrun/payments\";\n * import { drizzle } from \"drizzle-orm/postgres-js\";\n * import postgres from \"postgres\";\n *\n * const client = postgres(process.env.DATABASE_URL);\n * const db = drizzle(client);\n *\n * const storage = createDrizzleUsageStorage({ db });\n *\n * const usageService = createUsageService({ storage });\n * ```\n */\n\nimport { eq, and, gte, lte, sql, inArray } from \"drizzle-orm\";\nimport type { PostgresJsDatabase } from \"drizzle-orm/postgres-js\";\nimport type { NeonHttpDatabase } from \"drizzle-orm/neon-http\";\n\nimport type {\n UsageStorage,\n Plan,\n PlanFeature,\n UsageEvent,\n UsageAggregate,\n UsageAlert,\n AlertStatus,\n PeriodType,\n GetUsageOptions,\n AccessStatusInfo,\n LimitPeriod,\n BillingInterval,\n PlanTier,\n} from \"./types.js\";\n\nimport {\n plans,\n planFeatures,\n customerPlans,\n customerAccessStatus,\n customerBillingCycles,\n usageEvents,\n usageAggregates,\n usageAlerts,\n} from \"./schema.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Supported Drizzle database types\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type DrizzleDb = PostgresJsDatabase<any> | NeonHttpDatabase<any>;\n\n/**\n * Drizzle storage configuration\n */\nexport interface DrizzleUsageStorageConfig {\n /** Drizzle database instance */\n db: DrizzleDb;\n\n /** Table prefix (optional, for multi-tenant setups) */\n tablePrefix?: string;\n}\n\n/**\n * Generate unique ID\n */\nfunction generateId(): string {\n return `${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 9)}`;\n}\n\n// ============================================================================\n// Drizzle Usage Storage\n// ============================================================================\n\n/**\n * Drizzle-backed usage storage\n * Persists all usage data to PostgreSQL/Neon database\n */\nexport class DrizzleUsageStorage implements UsageStorage {\n private readonly db: DrizzleDb;\n\n constructor(config: DrizzleUsageStorageConfig) {\n this.db = config.db;\n }\n\n // ============================================================================\n // Plans\n // ============================================================================\n\n async getPlan(planId: string): Promise<Plan | null> {\n const rows = await this.db\n .select()\n .from(plans)\n .where(eq(plans.id, planId))\n .limit(1);\n\n const row = rows[0];\n if (!row) return null;\n\n const features = await this.getPlanFeatures(planId);\n\n return this.mapRowToPlan(row, features);\n }\n\n async getPlanByName(name: string): Promise<Plan | null> {\n const rows = await this.db\n .select()\n .from(plans)\n .where(eq(plans.name, name))\n .limit(1);\n\n const row = rows[0];\n if (!row) return null;\n\n const features = await this.getPlanFeatures(row.id);\n\n return this.mapRowToPlan(row, features);\n }\n\n async listPlans(options?: { activeOnly?: boolean }): Promise<Plan[]> {\n let query = this.db.select().from(plans);\n\n if (options?.activeOnly) {\n query = query.where(eq(plans.isActive, true)) as typeof query;\n }\n\n const rows = await query;\n\n // Fetch features for all plans\n const result: Plan[] = [];\n for (const row of rows) {\n const features = await this.getPlanFeatures(row.id);\n result.push(this.mapRowToPlan(row, features));\n }\n\n return result;\n }\n\n private mapRowToPlan(\n row: typeof plans.$inferSelect,\n features: PlanFeature[]\n ): Plan {\n const plan: Plan = {\n id: row.id,\n name: row.name,\n displayName: row.displayName,\n description: row.description,\n tier: row.tier as PlanTier,\n basePrice: row.basePrice,\n currency: row.currency,\n billingInterval: row.billingInterval as BillingInterval,\n features,\n isActive: row.isActive,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n\n if (row.metadata !== null) {\n plan.metadata = row.metadata;\n }\n\n return plan;\n }\n\n // ============================================================================\n // Plan Features\n // ============================================================================\n\n async getPlanFeatures(planId: string): Promise<PlanFeature[]> {\n const rows = await this.db\n .select()\n .from(planFeatures)\n .where(eq(planFeatures.planId, planId));\n\n return rows.map((row) => this.mapRowToPlanFeature(row));\n }\n\n async getFeatureLimit(\n planId: string,\n featureKey: string\n ): Promise<PlanFeature | null> {\n const rows = await this.db\n .select()\n .from(planFeatures)\n .where(\n and(\n eq(planFeatures.planId, planId),\n eq(planFeatures.featureKey, featureKey)\n )\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return null;\n\n return this.mapRowToPlanFeature(row);\n }\n\n private mapRowToPlanFeature(\n row: typeof planFeatures.$inferSelect\n ): PlanFeature {\n const feature: PlanFeature = {\n id: row.id,\n planId: row.planId,\n featureKey: row.featureKey,\n limitValue: row.limitValue,\n limitPeriod: row.limitPeriod as LimitPeriod,\n isEnabled: row.isEnabled,\n };\n\n if (row.metadata !== null) {\n feature.metadata = row.metadata;\n }\n\n return feature;\n }\n\n // ============================================================================\n // Customer-Plan Mapping\n // ============================================================================\n\n async getCustomerPlanId(customerId: string): Promise<string | null> {\n const rows = await this.db\n .select({ planId: customerPlans.planId })\n .from(customerPlans)\n .where(eq(customerPlans.customerId, customerId))\n .limit(1);\n\n const row = rows[0];\n return row ? row.planId : null;\n }\n\n async setCustomerPlanId(customerId: string, planId: string): Promise<void> {\n await this.db\n .insert(customerPlans)\n .values({\n customerId,\n planId,\n assignedAt: new Date(),\n })\n .onConflictDoUpdate({\n target: customerPlans.customerId,\n set: {\n planId,\n assignedAt: new Date(),\n },\n });\n }\n\n // ============================================================================\n // Usage Events\n // ============================================================================\n\n async recordUsage(event: Omit<UsageEvent, \"id\">): Promise<UsageEvent> {\n const id = generateId();\n\n // Check idempotency\n if (event.idempotencyKey) {\n const existing = await this.db\n .select()\n .from(usageEvents)\n .where(eq(usageEvents.idempotencyKey, event.idempotencyKey))\n .limit(1);\n\n const existingRow = existing[0];\n if (existingRow) {\n return this.mapRowToUsageEvent(existingRow);\n }\n }\n\n const insertData: typeof usageEvents.$inferInsert = {\n id,\n tenantId: event.tenantId,\n customerId: event.customerId,\n featureKey: event.featureKey,\n quantity: event.quantity,\n timestamp: event.timestamp ?? new Date(),\n };\n\n if (event.subscriptionId !== undefined) {\n insertData.subscriptionId = event.subscriptionId;\n }\n if (event.metadata !== undefined) {\n insertData.metadata = event.metadata;\n }\n if (event.idempotencyKey !== undefined) {\n insertData.idempotencyKey = event.idempotencyKey;\n }\n\n await this.db.insert(usageEvents).values(insertData);\n\n return {\n id,\n ...event,\n timestamp: event.timestamp ?? new Date(),\n };\n }\n\n async recordUsageBatch(\n events: Omit<UsageEvent, \"id\">[]\n ): Promise<UsageEvent[]> {\n const results: UsageEvent[] = [];\n for (const event of events) {\n results.push(await this.recordUsage(event));\n }\n return results;\n }\n\n async getUsageEvents(\n customerId: string,\n options: GetUsageOptions\n ): Promise<UsageEvent[]> {\n const conditions = [eq(usageEvents.customerId, customerId)];\n\n if (options.featureKey) {\n conditions.push(eq(usageEvents.featureKey, options.featureKey));\n }\n if (options.subscriptionId) {\n conditions.push(eq(usageEvents.subscriptionId, options.subscriptionId));\n }\n if (options.startDate) {\n conditions.push(gte(usageEvents.timestamp, options.startDate));\n }\n if (options.endDate) {\n conditions.push(lte(usageEvents.timestamp, options.endDate));\n }\n\n const rows = await this.db\n .select()\n .from(usageEvents)\n .where(and(...conditions))\n .orderBy(usageEvents.timestamp);\n\n return rows.map((row) => this.mapRowToUsageEvent(row));\n }\n\n private mapRowToUsageEvent(\n row: typeof usageEvents.$inferSelect\n ): UsageEvent {\n const event: UsageEvent = {\n id: row.id,\n tenantId: row.tenantId,\n customerId: row.customerId,\n featureKey: row.featureKey,\n quantity: row.quantity,\n timestamp: row.timestamp,\n };\n\n if (row.subscriptionId !== null) {\n event.subscriptionId = row.subscriptionId;\n }\n if (row.metadata !== null) {\n event.metadata = row.metadata;\n }\n if (row.idempotencyKey !== null) {\n event.idempotencyKey = row.idempotencyKey;\n }\n\n return event;\n }\n\n // ============================================================================\n // Usage Aggregates\n // ============================================================================\n\n async getAggregate(\n customerId: string,\n featureKey: string,\n periodType: PeriodType,\n periodStart: Date\n ): Promise<UsageAggregate | null> {\n const rows = await this.db\n .select()\n .from(usageAggregates)\n .where(\n and(\n eq(usageAggregates.customerId, customerId),\n eq(usageAggregates.featureKey, featureKey),\n eq(usageAggregates.periodType, periodType),\n eq(usageAggregates.periodStart, periodStart)\n )\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return null;\n\n return this.mapRowToUsageAggregate(row);\n }\n\n async upsertAggregate(\n aggregate: Omit<UsageAggregate, \"id\">\n ): Promise<UsageAggregate> {\n const id = generateId();\n\n const insertData: typeof usageAggregates.$inferInsert = {\n id,\n tenantId: aggregate.tenantId,\n customerId: aggregate.customerId,\n featureKey: aggregate.featureKey,\n periodStart: aggregate.periodStart,\n periodEnd: aggregate.periodEnd,\n periodType: aggregate.periodType,\n totalQuantity: aggregate.totalQuantity,\n eventCount: aggregate.eventCount,\n lastUpdated: new Date(),\n };\n\n if (aggregate.subscriptionId !== undefined) {\n insertData.subscriptionId = aggregate.subscriptionId;\n }\n\n await this.db\n .insert(usageAggregates)\n .values(insertData)\n .onConflictDoUpdate({\n target: [\n usageAggregates.customerId,\n usageAggregates.featureKey,\n usageAggregates.periodType,\n usageAggregates.periodStart,\n ],\n set: {\n totalQuantity: aggregate.totalQuantity,\n eventCount: aggregate.eventCount,\n lastUpdated: new Date(),\n },\n });\n\n return {\n id,\n ...aggregate,\n lastUpdated: new Date(),\n };\n }\n\n async getAggregates(\n customerId: string,\n options: GetUsageOptions\n ): Promise<UsageAggregate[]> {\n const conditions = [eq(usageAggregates.customerId, customerId)];\n\n if (options.featureKey) {\n conditions.push(eq(usageAggregates.featureKey, options.featureKey));\n }\n if (options.periodType) {\n conditions.push(eq(usageAggregates.periodType, options.periodType));\n }\n if (options.subscriptionId) {\n conditions.push(\n eq(usageAggregates.subscriptionId, options.subscriptionId)\n );\n }\n if (options.startDate) {\n conditions.push(gte(usageAggregates.periodStart, options.startDate));\n }\n if (options.endDate) {\n conditions.push(lte(usageAggregates.periodEnd, options.endDate));\n }\n\n const rows = await this.db\n .select()\n .from(usageAggregates)\n .where(and(...conditions))\n .orderBy(usageAggregates.periodStart);\n\n return rows.map((row) => this.mapRowToUsageAggregate(row));\n }\n\n async getCurrentPeriodUsage(\n customerId: string,\n featureKey: string,\n periodStart: Date\n ): Promise<number> {\n // First check aggregate\n const aggregate = await this.getAggregate(\n customerId,\n featureKey,\n \"month\",\n periodStart\n );\n\n if (aggregate) {\n return aggregate.totalQuantity;\n }\n\n // Fall back to summing events\n const result = await this.db\n .select({\n total: sql<number>`COALESCE(SUM(${usageEvents.quantity}), 0)`,\n })\n .from(usageEvents)\n .where(\n and(\n eq(usageEvents.customerId, customerId),\n eq(usageEvents.featureKey, featureKey),\n gte(usageEvents.timestamp, periodStart)\n )\n );\n\n return Number(result[0]?.total ?? 0);\n }\n\n private mapRowToUsageAggregate(\n row: typeof usageAggregates.$inferSelect\n ): UsageAggregate {\n const aggregate: UsageAggregate = {\n id: row.id,\n tenantId: row.tenantId,\n customerId: row.customerId,\n featureKey: row.featureKey,\n periodStart: row.periodStart,\n periodEnd: row.periodEnd,\n periodType: row.periodType as PeriodType,\n totalQuantity: row.totalQuantity,\n eventCount: row.eventCount,\n lastUpdated: row.lastUpdated,\n };\n\n if (row.subscriptionId !== null) {\n aggregate.subscriptionId = row.subscriptionId;\n }\n\n return aggregate;\n }\n\n // ============================================================================\n // Alerts\n // ============================================================================\n\n async createAlert(\n alert: Omit<UsageAlert, \"id\" | \"createdAt\">\n ): Promise<UsageAlert> {\n const id = generateId();\n const createdAt = new Date();\n\n const insertData: typeof usageAlerts.$inferInsert = {\n id,\n tenantId: alert.tenantId,\n customerId: alert.customerId,\n featureKey: alert.featureKey,\n thresholdPercent: alert.thresholdPercent,\n status: alert.status,\n currentUsage: alert.currentUsage,\n limit: alert.limit,\n createdAt,\n };\n\n if (alert.subscriptionId !== undefined) {\n insertData.subscriptionId = alert.subscriptionId;\n }\n if (alert.triggeredAt !== undefined) {\n insertData.triggeredAt = alert.triggeredAt;\n }\n if (alert.acknowledgedAt !== undefined) {\n insertData.acknowledgedAt = alert.acknowledgedAt;\n }\n if (alert.resolvedAt !== undefined) {\n insertData.resolvedAt = alert.resolvedAt;\n }\n if (alert.metadata !== undefined) {\n insertData.metadata = alert.metadata;\n }\n\n await this.db.insert(usageAlerts).values(insertData);\n\n return {\n id,\n ...alert,\n createdAt,\n };\n }\n\n async getActiveAlerts(customerId: string): Promise<UsageAlert[]> {\n const rows = await this.db\n .select()\n .from(usageAlerts)\n .where(\n and(\n eq(usageAlerts.customerId, customerId),\n inArray(usageAlerts.status, [\"pending\", \"triggered\"])\n )\n );\n\n return rows.map((row) => this.mapRowToUsageAlert(row));\n }\n\n async updateAlertStatus(alertId: string, status: AlertStatus): Promise<void> {\n const updateData: Partial<typeof usageAlerts.$inferInsert> = { status };\n\n if (status === \"triggered\") {\n updateData.triggeredAt = new Date();\n } else if (status === \"acknowledged\") {\n updateData.acknowledgedAt = new Date();\n } else if (status === \"resolved\") {\n updateData.resolvedAt = new Date();\n }\n\n await this.db\n .update(usageAlerts)\n .set(updateData)\n .where(eq(usageAlerts.id, alertId));\n }\n\n private mapRowToUsageAlert(row: typeof usageAlerts.$inferSelect): UsageAlert {\n const alert: UsageAlert = {\n id: row.id,\n tenantId: row.tenantId,\n customerId: row.customerId,\n featureKey: row.featureKey,\n thresholdPercent: row.thresholdPercent,\n status: row.status as AlertStatus,\n currentUsage: row.currentUsage,\n limit: row.limit,\n createdAt: row.createdAt,\n };\n\n if (row.subscriptionId !== null) {\n alert.subscriptionId = row.subscriptionId;\n }\n if (row.triggeredAt !== null) {\n alert.triggeredAt = row.triggeredAt;\n }\n if (row.acknowledgedAt !== null) {\n alert.acknowledgedAt = row.acknowledgedAt;\n }\n if (row.resolvedAt !== null) {\n alert.resolvedAt = row.resolvedAt;\n }\n if (row.metadata !== null) {\n alert.metadata = row.metadata;\n }\n\n return alert;\n }\n\n // ============================================================================\n // Usage Reset\n // ============================================================================\n\n async resetUsage(\n customerId: string,\n featureKeys?: string[],\n periodStart?: Date\n ): Promise<void> {\n // Build conditions\n const eventConditions = [eq(usageEvents.customerId, customerId)];\n const aggregateConditions = [eq(usageAggregates.customerId, customerId)];\n\n if (featureKeys && featureKeys.length > 0) {\n eventConditions.push(inArray(usageEvents.featureKey, featureKeys));\n aggregateConditions.push(\n inArray(usageAggregates.featureKey, featureKeys)\n );\n }\n\n if (periodStart) {\n eventConditions.push(gte(usageEvents.timestamp, periodStart));\n aggregateConditions.push(gte(usageAggregates.periodStart, periodStart));\n }\n\n // Delete events\n await this.db.delete(usageEvents).where(and(...eventConditions));\n\n // Delete aggregates\n await this.db.delete(usageAggregates).where(and(...aggregateConditions));\n }\n\n async resetAggregates(\n customerId: string,\n featureKeys?: string[]\n ): Promise<void> {\n const conditions = [eq(usageAggregates.customerId, customerId)];\n\n if (featureKeys && featureKeys.length > 0) {\n conditions.push(inArray(usageAggregates.featureKey, featureKeys));\n }\n\n await this.db.delete(usageAggregates).where(and(...conditions));\n }\n\n // ============================================================================\n // Access Status\n // ============================================================================\n\n async getAccessStatus(customerId: string): Promise<AccessStatusInfo | null> {\n const rows = await this.db\n .select()\n .from(customerAccessStatus)\n .where(eq(customerAccessStatus.customerId, customerId))\n .limit(1);\n\n const row = rows[0];\n if (!row) return null;\n\n const status: AccessStatusInfo = {\n status: row.status as AccessStatusInfo[\"status\"],\n updatedAt: row.updatedAt,\n };\n\n if (row.reason !== null) {\n status.reason = row.reason;\n }\n if (row.suspensionDate !== null) {\n status.suspensionDate = row.suspensionDate;\n }\n if (row.failedPaymentAttempts !== null) {\n status.failedPaymentAttempts = row.failedPaymentAttempts;\n }\n if (row.gracePeriodEnd !== null) {\n status.gracePeriodEnd = row.gracePeriodEnd;\n }\n\n return status;\n }\n\n async setAccessStatus(\n customerId: string,\n status: AccessStatusInfo\n ): Promise<void> {\n const insertData: typeof customerAccessStatus.$inferInsert = {\n customerId,\n status: status.status,\n updatedAt: status.updatedAt,\n };\n\n if (status.reason !== undefined) {\n insertData.reason = status.reason;\n }\n if (status.suspensionDate !== undefined) {\n insertData.suspensionDate = status.suspensionDate;\n }\n if (status.failedPaymentAttempts !== undefined) {\n insertData.failedPaymentAttempts = status.failedPaymentAttempts;\n }\n if (status.gracePeriodEnd !== undefined) {\n insertData.gracePeriodEnd = status.gracePeriodEnd;\n }\n\n await this.db\n .insert(customerAccessStatus)\n .values(insertData)\n .onConflictDoUpdate({\n target: customerAccessStatus.customerId,\n set: {\n status: status.status,\n reason: status.reason ?? null,\n suspensionDate: status.suspensionDate ?? null,\n failedPaymentAttempts: status.failedPaymentAttempts ?? null,\n gracePeriodEnd: status.gracePeriodEnd ?? null,\n updatedAt: status.updatedAt,\n },\n });\n }\n\n // ============================================================================\n // Billing Cycle\n // ============================================================================\n\n async getBillingCycle(\n customerId: string\n ): Promise<{ start: Date; end: Date } | null> {\n const rows = await this.db\n .select()\n .from(customerBillingCycles)\n .where(eq(customerBillingCycles.customerId, customerId))\n .limit(1);\n\n const row = rows[0];\n if (!row) return null;\n\n return {\n start: row.periodStart,\n end: row.periodEnd,\n };\n }\n\n async setBillingCycle(\n customerId: string,\n start: Date,\n end: Date\n ): Promise<void> {\n await this.db\n .insert(customerBillingCycles)\n .values({\n customerId,\n periodStart: start,\n periodEnd: end,\n updatedAt: new Date(),\n })\n .onConflictDoUpdate({\n target: customerBillingCycles.customerId,\n set: {\n periodStart: start,\n periodEnd: end,\n updatedAt: new Date(),\n },\n });\n }\n\n // ============================================================================\n // Admin Methods (for setup)\n // ============================================================================\n\n /**\n * Create or update a plan\n */\n async upsertPlan(plan: Omit<Plan, \"createdAt\" | \"updatedAt\">): Promise<Plan> {\n const now = new Date();\n\n await this.db\n .insert(plans)\n .values({\n id: plan.id,\n name: plan.name,\n displayName: plan.displayName,\n description: plan.description,\n tier: plan.tier,\n basePrice: plan.basePrice,\n currency: plan.currency,\n billingInterval: plan.billingInterval,\n isActive: plan.isActive,\n metadata: plan.metadata,\n createdAt: now,\n updatedAt: now,\n })\n .onConflictDoUpdate({\n target: plans.id,\n set: {\n name: plan.name,\n displayName: plan.displayName,\n description: plan.description,\n tier: plan.tier,\n basePrice: plan.basePrice,\n currency: plan.currency,\n billingInterval: plan.billingInterval,\n isActive: plan.isActive,\n metadata: plan.metadata,\n updatedAt: now,\n },\n });\n\n // Delete existing features and insert new ones\n await this.db\n .delete(planFeatures)\n .where(eq(planFeatures.planId, plan.id));\n\n for (const feature of plan.features) {\n await this.db.insert(planFeatures).values({\n id: feature.id || generateId(),\n planId: plan.id,\n featureKey: feature.featureKey,\n limitValue: feature.limitValue,\n limitPeriod: feature.limitPeriod,\n isEnabled: feature.isEnabled,\n metadata: feature.metadata,\n });\n }\n\n return {\n ...plan,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n /**\n * Delete a plan\n */\n async deletePlan(planId: string): Promise<void> {\n // Features are cascade deleted\n await this.db.delete(plans).where(eq(plans.id, planId));\n }\n}\n\n/**\n * Create Drizzle-backed usage storage\n */\nexport function createDrizzleUsageStorage(\n config: DrizzleUsageStorageConfig\n): DrizzleUsageStorage {\n return new DrizzleUsageStorage(config);\n}\n","/**\n * @parsrun/payments - Usage Database Schema\n * Drizzle ORM schema for usage-based billing tables\n *\n * These tables store:\n * - Plans and features with limits\n * - Customer plan assignments\n * - Usage events and aggregates\n * - Access status and billing cycles\n * - Usage alerts\n */\n\nimport {\n pgTable,\n text,\n integer,\n boolean,\n timestamp,\n jsonb,\n uniqueIndex,\n index,\n} from \"drizzle-orm/pg-core\";\n\n// ============================================================================\n// Plans & Features\n// ============================================================================\n\n/**\n * Plans table - defines billing plans with tiers\n */\nexport const plans = pgTable(\"usage_plans\", {\n id: text(\"id\").primaryKey(),\n name: text(\"name\").notNull().unique(),\n displayName: text(\"display_name\").notNull(),\n description: text(\"description\"),\n tier: integer(\"tier\").notNull().default(0), // 0=free, 1=starter, 2=pro, 3=enterprise, 4=custom\n basePrice: integer(\"base_price\").notNull().default(0), // cents\n currency: text(\"currency\").notNull().default(\"usd\"),\n billingInterval: text(\"billing_interval\").notNull().default(\"month\"), // month, year\n isActive: boolean(\"is_active\").notNull().default(true),\n metadata: jsonb(\"metadata\").$type<Record<string, unknown>>(),\n createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n updatedAt: timestamp(\"updated_at\").defaultNow().notNull(),\n});\n\n/**\n * Plan features table - defines feature limits for each plan\n */\nexport const planFeatures = pgTable(\n \"usage_plan_features\",\n {\n id: text(\"id\").primaryKey(),\n planId: text(\"plan_id\")\n .notNull()\n .references(() => plans.id, { onDelete: \"cascade\" }),\n featureKey: text(\"feature_key\").notNull(), // \"api_calls\", \"storage_gb\", \"team_members\"\n limitValue: integer(\"limit_value\"), // null = unlimited\n limitPeriod: text(\"limit_period\"), // \"hour\", \"day\", \"month\", null = total\n isEnabled: boolean(\"is_enabled\").notNull().default(true),\n metadata: jsonb(\"metadata\").$type<Record<string, unknown>>(),\n },\n (table) => ({\n planFeatureIdx: uniqueIndex(\"usage_plan_features_plan_feature_idx\").on(\n table.planId,\n table.featureKey\n ),\n })\n);\n\n// ============================================================================\n// Customer Data\n// ============================================================================\n\n/**\n * Customer plans table - maps customers to their current plan\n */\nexport const customerPlans = pgTable(\"usage_customer_plans\", {\n customerId: text(\"customer_id\").primaryKey(),\n planId: text(\"plan_id\")\n .notNull()\n .references(() => plans.id),\n assignedAt: timestamp(\"assigned_at\").defaultNow().notNull(),\n metadata: jsonb(\"metadata\").$type<Record<string, unknown>>(),\n});\n\n/**\n * Customer access status table - tracks access status for each customer\n */\nexport const customerAccessStatus = pgTable(\"usage_customer_access_status\", {\n customerId: text(\"customer_id\").primaryKey(),\n status: text(\"status\").notNull().default(\"active\"), // active, past_due, suspended, canceled, unpaid\n reason: text(\"reason\"),\n suspensionDate: timestamp(\"suspension_date\"),\n failedPaymentAttempts: integer(\"failed_payment_attempts\").default(0),\n gracePeriodEnd: timestamp(\"grace_period_end\"),\n updatedAt: timestamp(\"updated_at\").defaultNow().notNull(),\n});\n\n/**\n * Customer billing cycles table - tracks billing period for each customer\n */\nexport const customerBillingCycles = pgTable(\"usage_customer_billing_cycles\", {\n customerId: text(\"customer_id\").primaryKey(),\n periodStart: timestamp(\"period_start\").notNull(),\n periodEnd: timestamp(\"period_end\").notNull(),\n updatedAt: timestamp(\"updated_at\").defaultNow().notNull(),\n});\n\n// ============================================================================\n// Usage Events & Aggregates\n// ============================================================================\n\n/**\n * Usage events table - raw usage event log\n */\nexport const usageEvents = pgTable(\n \"usage_events\",\n {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n customerId: text(\"customer_id\").notNull(),\n subscriptionId: text(\"subscription_id\"),\n featureKey: text(\"feature_key\").notNull(),\n quantity: integer(\"quantity\").notNull().default(1),\n timestamp: timestamp(\"timestamp\").defaultNow().notNull(),\n metadata: jsonb(\"metadata\").$type<Record<string, unknown>>(),\n idempotencyKey: text(\"idempotency_key\"),\n },\n (table) => ({\n customerIdx: index(\"usage_events_customer_idx\").on(table.customerId),\n featureIdx: index(\"usage_events_feature_idx\").on(table.featureKey),\n timestampIdx: index(\"usage_events_timestamp_idx\").on(table.timestamp),\n idempotencyIdx: uniqueIndex(\"usage_events_idempotency_idx\").on(\n table.idempotencyKey\n ),\n // Composite index for common queries\n customerFeatureTimestampIdx: index(\n \"usage_events_customer_feature_timestamp_idx\"\n ).on(table.customerId, table.featureKey, table.timestamp),\n })\n);\n\n/**\n * Usage aggregates table - pre-computed usage summaries for performance\n */\nexport const usageAggregates = pgTable(\n \"usage_aggregates\",\n {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n customerId: text(\"customer_id\").notNull(),\n subscriptionId: text(\"subscription_id\"),\n featureKey: text(\"feature_key\").notNull(),\n periodStart: timestamp(\"period_start\").notNull(),\n periodEnd: timestamp(\"period_end\").notNull(),\n periodType: text(\"period_type\").notNull(), // \"hour\", \"day\", \"month\"\n totalQuantity: integer(\"total_quantity\").notNull().default(0),\n eventCount: integer(\"event_count\").notNull().default(0),\n lastUpdated: timestamp(\"last_updated\").defaultNow().notNull(),\n },\n (table) => ({\n // Unique constraint for upsert\n lookupIdx: uniqueIndex(\"usage_aggregates_lookup_idx\").on(\n table.customerId,\n table.featureKey,\n table.periodType,\n table.periodStart\n ),\n // Index for customer queries\n customerIdx: index(\"usage_aggregates_customer_idx\").on(table.customerId),\n })\n);\n\n// ============================================================================\n// Alerts\n// ============================================================================\n\n/**\n * Usage alerts table - tracks quota threshold alerts\n */\nexport const usageAlerts = pgTable(\n \"usage_alerts\",\n {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n customerId: text(\"customer_id\").notNull(),\n subscriptionId: text(\"subscription_id\"),\n featureKey: text(\"feature_key\").notNull(),\n thresholdPercent: integer(\"threshold_percent\").notNull(), // 80, 100, 120\n status: text(\"status\").notNull().default(\"pending\"), // pending, triggered, acknowledged, resolved\n currentUsage: integer(\"current_usage\").notNull(),\n limit: integer(\"limit\").notNull(),\n triggeredAt: timestamp(\"triggered_at\"),\n acknowledgedAt: timestamp(\"acknowledged_at\"),\n resolvedAt: timestamp(\"resolved_at\"),\n metadata: jsonb(\"metadata\").$type<Record<string, unknown>>(),\n createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n },\n (table) => ({\n customerIdx: index(\"usage_alerts_customer_idx\").on(table.customerId),\n statusIdx: index(\"usage_alerts_status_idx\").on(table.status),\n })\n);\n\n// ============================================================================\n// Type Exports\n// ============================================================================\n\nexport type Plan = typeof plans.$inferSelect;\nexport type NewPlan = typeof plans.$inferInsert;\n\nexport type PlanFeature = typeof planFeatures.$inferSelect;\nexport type NewPlanFeature = typeof planFeatures.$inferInsert;\n\nexport type CustomerPlan = typeof customerPlans.$inferSelect;\nexport type NewCustomerPlan = typeof customerPlans.$inferInsert;\n\nexport type CustomerAccessStatusRow = typeof customerAccessStatus.$inferSelect;\nexport type NewCustomerAccessStatus = typeof customerAccessStatus.$inferInsert;\n\nexport type CustomerBillingCycle = typeof customerBillingCycles.$inferSelect;\nexport type NewCustomerBillingCycle = typeof customerBillingCycles.$inferInsert;\n\nexport type UsageEventRow = typeof usageEvents.$inferSelect;\nexport type NewUsageEvent = typeof usageEvents.$inferInsert;\n\nexport type UsageAggregateRow = typeof usageAggregates.$inferSelect;\nexport type NewUsageAggregate = typeof usageAggregates.$inferInsert;\n\nexport type UsageAlertRow = typeof usageAlerts.$inferSelect;\nexport type NewUsageAlert = typeof usageAlerts.$inferInsert;\n","/**\n * @parsrun/payments - Quota Manager\n * Handles quota checking and enforcement\n */\n\nimport type {\n UsageStorage,\n QuotaStatus,\n QuotaCheckResult,\n QuotaManagerConfig,\n BillingLogger,\n} from \"./types.js\";\nimport { QuotaExceededError } from \"./types.js\";\n\n/**\n * Null logger\n */\nconst nullLogger: BillingLogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Get period boundaries based on limit period\n */\nfunction getPeriodBoundaries(limitPeriod: \"hour\" | \"day\" | \"month\" | null): {\n start: Date;\n end: Date;\n} {\n const now = new Date();\n\n switch (limitPeriod) {\n case \"hour\": {\n const start = new Date(now);\n start.setMinutes(0, 0, 0);\n const end = new Date(start);\n end.setHours(end.getHours() + 1);\n return { start, end };\n }\n\n case \"day\": {\n const start = new Date(now);\n start.setHours(0, 0, 0, 0);\n const end = new Date(start);\n end.setDate(end.getDate() + 1);\n return { start, end };\n }\n\n case \"month\":\n default: {\n const start = new Date(now.getFullYear(), now.getMonth(), 1);\n const end = new Date(now.getFullYear(), now.getMonth() + 1, 1);\n return { start, end };\n }\n }\n}\n\n/**\n * Quota Manager\n * Handles checking and enforcing usage quotas\n */\nexport class QuotaManager {\n private readonly storage: UsageStorage;\n private readonly logger: BillingLogger;\n private readonly softLimits: boolean;\n private readonly gracePercent: number;\n private readonly overageAllowedFeatures: string[] | null;\n private readonly onOverage?: (customerId: string, featureKey: string, overage: number, limit: number) => void | Promise<void>;\n\n constructor(config: QuotaManagerConfig) {\n this.storage = config.storage;\n this.logger = config.logger ?? nullLogger;\n this.softLimits = config.softLimits ?? false;\n this.gracePercent = config.gracePercent ?? 0;\n this.overageAllowedFeatures = config.overageAllowedFeatures ?? null;\n if (config.onOverage !== undefined) {\n this.onOverage = config.onOverage;\n }\n }\n\n /**\n * Check if overage is allowed for a feature\n */\n private isOverageAllowed(featureKey: string): boolean {\n if (!this.softLimits) return false;\n if (this.overageAllowedFeatures === null) return true; // All features allow overage\n return this.overageAllowedFeatures.includes(featureKey);\n }\n\n /**\n * Check if quota allows the requested quantity\n */\n async checkQuota(\n customerId: string,\n featureKey: string,\n requestedQuantity: number = 1\n ): Promise<QuotaCheckResult> {\n // Get customer's plan\n const planId = await this.storage.getCustomerPlanId(customerId);\n if (!planId) {\n // No plan = no limits\n return {\n allowed: true,\n currentUsage: 0,\n limit: null,\n remaining: null,\n wouldExceed: false,\n percentAfter: null,\n };\n }\n\n // Get feature limit\n const feature = await this.storage.getFeatureLimit(planId, featureKey);\n if (!feature) {\n // Feature not defined = unlimited\n return {\n allowed: true,\n currentUsage: 0,\n limit: null,\n remaining: null,\n wouldExceed: false,\n percentAfter: null,\n };\n }\n\n // Feature disabled\n if (!feature.isEnabled) {\n return {\n allowed: false,\n currentUsage: 0,\n limit: 0,\n remaining: 0,\n wouldExceed: true,\n percentAfter: 100,\n };\n }\n\n // Unlimited feature\n if (feature.limitValue === null) {\n return {\n allowed: true,\n currentUsage: 0,\n limit: null,\n remaining: null,\n wouldExceed: false,\n percentAfter: null,\n };\n }\n\n // Get current usage\n const { start } = getPeriodBoundaries(feature.limitPeriod);\n const currentUsage = await this.storage.getCurrentPeriodUsage(\n customerId,\n featureKey,\n start\n );\n\n // Calculate effective limit with grace\n const effectiveLimit = Math.ceil(feature.limitValue * (1 + this.gracePercent / 100));\n const usageAfter = currentUsage + requestedQuantity;\n const wouldExceed = usageAfter > effectiveLimit;\n const percentAfter = Math.round((usageAfter / feature.limitValue) * 100);\n\n // Check if overage is allowed for this specific feature\n const overageAllowed = this.isOverageAllowed(featureKey);\n const allowed = overageAllowed ? true : !wouldExceed;\n\n this.logger.debug(\"Quota check\", {\n customerId,\n featureKey,\n currentUsage,\n requestedQuantity,\n limit: feature.limitValue,\n effectiveLimit,\n allowed,\n wouldExceed,\n });\n\n return {\n allowed,\n currentUsage,\n limit: feature.limitValue,\n remaining: Math.max(0, feature.limitValue - currentUsage),\n wouldExceed,\n percentAfter,\n };\n }\n\n /**\n * Enforce quota - throws if exceeded\n */\n async enforceQuota(\n customerId: string,\n featureKey: string,\n requestedQuantity: number = 1\n ): Promise<void> {\n const result = await this.checkQuota(customerId, featureKey, requestedQuantity);\n\n if (!result.allowed) {\n this.logger.warn(\"Quota exceeded\", {\n customerId,\n featureKey,\n currentUsage: result.currentUsage,\n limit: result.limit,\n requestedQuantity,\n });\n\n throw new QuotaExceededError(\n featureKey,\n result.limit,\n result.currentUsage,\n requestedQuantity\n );\n }\n }\n\n /**\n * Get quota status for a feature\n */\n async getQuotaStatus(customerId: string, featureKey: string): Promise<QuotaStatus> {\n const planId = await this.storage.getCustomerPlanId(customerId);\n\n // No plan = unlimited\n if (!planId) {\n const now = new Date();\n return {\n featureKey,\n limit: null,\n used: 0,\n remaining: null,\n percentUsed: null,\n periodStart: new Date(now.getFullYear(), now.getMonth(), 1),\n periodEnd: new Date(now.getFullYear(), now.getMonth() + 1, 1),\n isExceeded: false,\n isUnlimited: true,\n };\n }\n\n const feature = await this.storage.getFeatureLimit(planId, featureKey);\n\n // Feature not defined = unlimited\n if (!feature) {\n const now = new Date();\n return {\n featureKey,\n limit: null,\n used: 0,\n remaining: null,\n percentUsed: null,\n periodStart: new Date(now.getFullYear(), now.getMonth(), 1),\n periodEnd: new Date(now.getFullYear(), now.getMonth() + 1, 1),\n isExceeded: false,\n isUnlimited: true,\n };\n }\n\n const { start, end } = getPeriodBoundaries(feature.limitPeriod);\n const used = await this.storage.getCurrentPeriodUsage(customerId, featureKey, start);\n\n const isUnlimited = feature.limitValue === null;\n const limit = feature.limitValue;\n const remaining = isUnlimited ? null : Math.max(0, limit! - used);\n const percentUsed = isUnlimited ? null : Math.round((used / limit!) * 100);\n const isExceeded = !isUnlimited && used >= limit!;\n const overageAllowed = this.isOverageAllowed(featureKey);\n const overage = isUnlimited ? undefined : (used > limit! ? used - limit! : undefined);\n\n // Trigger overage callback if exceeded and overage is allowed\n if (overage !== undefined && overage > 0 && overageAllowed && this.onOverage) {\n try {\n await this.onOverage(customerId, featureKey, overage, limit!);\n } catch (error) {\n this.logger.error(\"Overage callback failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n const result: QuotaStatus = {\n featureKey,\n limit,\n used,\n remaining,\n percentUsed,\n periodStart: start,\n periodEnd: end,\n isExceeded,\n isUnlimited,\n };\n\n // Add overage info only when relevant\n if (overage !== undefined) {\n result.overage = overage;\n }\n if (overageAllowed) {\n result.overageAllowed = true;\n }\n\n return result;\n }\n\n /**\n * Get all quota statuses for a customer\n */\n async getAllQuotas(customerId: string): Promise<QuotaStatus[]> {\n const planId = await this.storage.getCustomerPlanId(customerId);\n if (!planId) return [];\n\n const features = await this.storage.getPlanFeatures(planId);\n const quotas: QuotaStatus[] = [];\n\n for (const feature of features) {\n const status = await this.getQuotaStatus(customerId, feature.featureKey);\n quotas.push(status);\n }\n\n return quotas;\n }\n\n /**\n * Check if any quota is exceeded\n */\n async hasExceededQuotas(customerId: string): Promise<boolean> {\n const quotas = await this.getAllQuotas(customerId);\n return quotas.some((q) => q.isExceeded);\n }\n\n /**\n * Get exceeded quotas\n */\n async getExceededQuotas(customerId: string): Promise<QuotaStatus[]> {\n const quotas = await this.getAllQuotas(customerId);\n return quotas.filter((q) => q.isExceeded);\n }\n}\n\n/**\n * Create quota manager\n */\nexport function createQuotaManager(config: QuotaManagerConfig): QuotaManager {\n return new QuotaManager(config);\n}\n","/**\n * @parsrun/payments - Usage Tracker\n * Handles recording and tracking usage events\n */\n\nimport type {\n UsageStorage,\n UsageEvent,\n TrackUsageOptions,\n UsageAggregate,\n PeriodType,\n BillingLogger,\n UsageAlert,\n} from \"./types.js\";\n\n/**\n * Null logger\n */\nconst nullLogger: BillingLogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Get period boundaries for aggregation\n */\nfunction getPeriodStart(timestamp: Date, periodType: PeriodType): Date {\n const date = new Date(timestamp);\n\n switch (periodType) {\n case \"hour\":\n date.setMinutes(0, 0, 0);\n return date;\n\n case \"day\":\n date.setHours(0, 0, 0, 0);\n return date;\n\n case \"month\":\n return new Date(date.getFullYear(), date.getMonth(), 1);\n }\n}\n\n/**\n * Get period end from period start\n */\nfunction getPeriodEnd(periodStart: Date, periodType: PeriodType): Date {\n const date = new Date(periodStart);\n\n switch (periodType) {\n case \"hour\":\n date.setHours(date.getHours() + 1);\n return date;\n\n case \"day\":\n date.setDate(date.getDate() + 1);\n return date;\n\n case \"month\":\n return new Date(date.getFullYear(), date.getMonth() + 1, 1);\n }\n}\n\n/**\n * Usage tracker configuration\n */\nexport interface UsageTrackerConfig {\n storage: UsageStorage;\n logger?: BillingLogger;\n\n /** Update aggregates immediately on each event (default: true) */\n aggregateOnRecord?: boolean;\n\n /** Alert thresholds as percentages (default: [80, 100]) */\n alertThresholds?: number[];\n\n /** Callback when threshold is reached */\n onThresholdReached?: (alert: UsageAlert) => void | Promise<void>;\n}\n\n/**\n * Usage Tracker\n * Records usage events and maintains aggregates\n */\nexport class UsageTracker {\n private readonly storage: UsageStorage;\n private readonly logger: BillingLogger;\n private readonly aggregateOnRecord: boolean;\n private readonly alertThresholds: number[];\n private readonly onThresholdReached?: (alert: UsageAlert) => void | Promise<void>;\n\n constructor(config: UsageTrackerConfig) {\n this.storage = config.storage;\n this.logger = config.logger ?? nullLogger;\n this.aggregateOnRecord = config.aggregateOnRecord ?? true;\n this.alertThresholds = config.alertThresholds ?? [80, 100];\n if (config.onThresholdReached !== undefined) {\n this.onThresholdReached = config.onThresholdReached;\n }\n }\n\n /**\n * Track a usage event\n */\n async trackUsage(options: TrackUsageOptions): Promise<UsageEvent> {\n const timestamp = options.timestamp ?? new Date();\n const quantity = options.quantity ?? 1;\n\n this.logger.debug(\"Recording usage\", {\n customerId: options.customerId,\n featureKey: options.featureKey,\n quantity,\n });\n\n // Record the event - build object with only defined optional properties\n const eventData: Omit<UsageEvent, \"id\"> = {\n tenantId: options.tenantId,\n customerId: options.customerId,\n featureKey: options.featureKey,\n quantity,\n timestamp,\n };\n\n if (options.subscriptionId !== undefined) {\n eventData.subscriptionId = options.subscriptionId;\n }\n if (options.metadata !== undefined) {\n eventData.metadata = options.metadata;\n }\n if (options.idempotencyKey !== undefined) {\n eventData.idempotencyKey = options.idempotencyKey;\n }\n\n const event = await this.storage.recordUsage(eventData);\n\n // Update aggregates if enabled\n if (this.aggregateOnRecord) {\n await this.updateAggregates(event);\n }\n\n // Check thresholds and create alerts\n await this.checkThresholds(options.customerId, options.featureKey);\n\n return event;\n }\n\n /**\n * Track multiple usage events\n */\n async trackBatch(events: TrackUsageOptions[]): Promise<UsageEvent[]> {\n const results: UsageEvent[] = [];\n\n for (const event of events) {\n const result = await this.trackUsage(event);\n results.push(result);\n }\n\n return results;\n }\n\n /**\n * Update aggregates for an event\n */\n private async updateAggregates(event: UsageEvent): Promise<void> {\n const periodTypes: PeriodType[] = [\"hour\", \"day\", \"month\"];\n\n for (const periodType of periodTypes) {\n const periodStart = getPeriodStart(event.timestamp, periodType);\n const periodEnd = getPeriodEnd(periodStart, periodType);\n\n // Get existing aggregate\n const existing = await this.storage.getAggregate(\n event.customerId,\n event.featureKey,\n periodType,\n periodStart\n );\n\n // Upsert aggregate - build object with only defined optional properties\n const aggregateData: Omit<UsageAggregate, \"id\"> = {\n tenantId: event.tenantId,\n customerId: event.customerId,\n featureKey: event.featureKey,\n periodStart,\n periodEnd,\n periodType,\n totalQuantity: (existing?.totalQuantity ?? 0) + event.quantity,\n eventCount: (existing?.eventCount ?? 0) + 1,\n lastUpdated: new Date(),\n };\n\n if (event.subscriptionId !== undefined) {\n aggregateData.subscriptionId = event.subscriptionId;\n }\n\n await this.storage.upsertAggregate(aggregateData);\n }\n }\n\n /**\n * Check thresholds and create alerts if needed\n */\n private async checkThresholds(customerId: string, featureKey: string): Promise<void> {\n // Get customer's plan\n const planId = await this.storage.getCustomerPlanId(customerId);\n if (!planId) return;\n\n // Get feature limit\n const feature = await this.storage.getFeatureLimit(planId, featureKey);\n if (!feature || feature.limitValue === null) return;\n\n // Get current usage\n const periodStart = getPeriodStart(new Date(), \"month\");\n const currentUsage = await this.storage.getCurrentPeriodUsage(\n customerId,\n featureKey,\n periodStart\n );\n\n const percentUsed = Math.round((currentUsage / feature.limitValue) * 100);\n\n // Check active alerts to avoid duplicates\n const activeAlerts = await this.storage.getActiveAlerts(customerId);\n const alertedThresholds = new Set(\n activeAlerts\n .filter((a) => a.featureKey === featureKey)\n .map((a) => a.thresholdPercent)\n );\n\n // Check each threshold\n for (const threshold of this.alertThresholds) {\n if (percentUsed >= threshold && !alertedThresholds.has(threshold)) {\n const alert = await this.storage.createAlert({\n tenantId: \"\", // Will be filled from customer\n customerId,\n featureKey,\n thresholdPercent: threshold,\n status: \"triggered\",\n currentUsage,\n limit: feature.limitValue,\n triggeredAt: new Date(),\n });\n\n this.logger.info(\"Usage threshold reached\", {\n customerId,\n featureKey,\n threshold,\n percentUsed,\n currentUsage,\n limit: feature.limitValue,\n });\n\n // Notify callback\n if (this.onThresholdReached) {\n try {\n await this.onThresholdReached(alert);\n } catch (error) {\n this.logger.error(\"Threshold callback failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n }\n }\n\n /**\n * Get usage for a customer\n */\n async getUsage(\n customerId: string,\n featureKey: string,\n periodType: PeriodType = \"month\"\n ): Promise<number> {\n const periodStart = getPeriodStart(new Date(), periodType);\n return this.storage.getCurrentPeriodUsage(customerId, featureKey, periodStart);\n }\n\n /**\n * Get usage aggregates\n */\n async getAggregates(\n customerId: string,\n featureKey: string,\n periodType: PeriodType,\n startDate?: Date,\n endDate?: Date\n ): Promise<UsageAggregate[]> {\n const options: { featureKey: string; periodType: PeriodType; startDate?: Date; endDate?: Date } = {\n featureKey,\n periodType,\n };\n\n if (startDate !== undefined) {\n options.startDate = startDate;\n }\n if (endDate !== undefined) {\n options.endDate = endDate;\n }\n\n return this.storage.getAggregates(customerId, options);\n }\n\n /**\n * Force aggregate recalculation for a period\n */\n async recalculateAggregates(\n customerId: string,\n featureKey: string,\n periodType: PeriodType,\n periodStart: Date\n ): Promise<UsageAggregate> {\n const periodEnd = getPeriodEnd(periodStart, periodType);\n\n // Get all events in period\n const events = await this.storage.getUsageEvents(customerId, {\n featureKey,\n startDate: periodStart,\n endDate: periodEnd,\n });\n\n // Calculate totals\n const totalQuantity = events.reduce((sum, e) => sum + e.quantity, 0);\n const eventCount = events.length;\n\n // Get tenant ID from first event or empty\n const tenantId = events[0]?.tenantId ?? \"\";\n const subscriptionId = events[0]?.subscriptionId;\n\n // Upsert aggregate - build object with only defined optional properties\n const aggregateData: Omit<UsageAggregate, \"id\"> = {\n tenantId,\n customerId,\n featureKey,\n periodStart,\n periodEnd,\n periodType,\n totalQuantity,\n eventCount,\n lastUpdated: new Date(),\n };\n\n if (subscriptionId !== undefined) {\n aggregateData.subscriptionId = subscriptionId;\n }\n\n return this.storage.upsertAggregate(aggregateData);\n }\n}\n\n/**\n * Create usage tracker\n */\nexport function createUsageTracker(config: UsageTrackerConfig): UsageTracker {\n return new UsageTracker(config);\n}\n","/**\n * @parsrun/payments - Subscription Lifecycle Hooks\n * Event-driven subscription lifecycle management\n */\n\nimport type {\n SubscriptionEventType,\n SubscriptionEvent,\n SubscriptionHandler,\n BillingLogger,\n} from \"./types.js\";\n\n/**\n * Null logger\n */\nconst nullLogger: BillingLogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Subscription Lifecycle\n * Manages subscription event handlers\n */\nexport class SubscriptionLifecycle {\n private readonly handlers: Map<SubscriptionEventType | \"*\", SubscriptionHandler[]>;\n private readonly logger: BillingLogger;\n\n constructor(logger?: BillingLogger) {\n this.handlers = new Map();\n this.logger = logger ?? nullLogger;\n }\n\n /**\n * Register an event handler\n */\n on(event: SubscriptionEventType | \"*\", handler: SubscriptionHandler): this {\n const handlers = this.handlers.get(event) ?? [];\n handlers.push(handler);\n this.handlers.set(event, handlers);\n\n this.logger.debug(\"Lifecycle handler registered\", { event });\n return this;\n }\n\n /**\n * Remove an event handler\n */\n off(event: SubscriptionEventType | \"*\", handler: SubscriptionHandler): this {\n const handlers = this.handlers.get(event);\n if (handlers) {\n const index = handlers.indexOf(handler);\n if (index !== -1) {\n handlers.splice(index, 1);\n }\n }\n return this;\n }\n\n /**\n * Emit an event to all handlers\n */\n async emit(event: SubscriptionEvent): Promise<void> {\n this.logger.info(\"Lifecycle event\", {\n type: event.type,\n subscriptionId: event.subscription.id,\n provider: event.provider,\n });\n\n // Get specific handlers\n const specificHandlers = this.handlers.get(event.type) ?? [];\n\n // Get wildcard handlers\n const wildcardHandlers = this.handlers.get(\"*\") ?? [];\n\n // Combine all handlers\n const allHandlers = [...specificHandlers, ...wildcardHandlers];\n\n // Execute all handlers\n const results = await Promise.allSettled(\n allHandlers.map((handler) => handler(event))\n );\n\n // Log any failures\n for (const result of results) {\n if (result.status === \"rejected\") {\n this.logger.error(\"Lifecycle handler failed\", {\n type: event.type,\n error: result.reason instanceof Error ? result.reason.message : String(result.reason),\n });\n }\n }\n }\n\n /**\n * Check if there are handlers for an event\n */\n hasHandlers(event: SubscriptionEventType | \"*\"): boolean {\n const handlers = this.handlers.get(event);\n return handlers !== undefined && handlers.length > 0;\n }\n\n /**\n * Get handler count for an event\n */\n handlerCount(event: SubscriptionEventType | \"*\"): number {\n return this.handlers.get(event)?.length ?? 0;\n }\n\n /**\n * Clear all handlers\n */\n clear(): void {\n this.handlers.clear();\n }\n\n // ============================================================================\n // Convenience Methods\n // ============================================================================\n\n /**\n * Handle subscription created\n */\n onCreated(handler: SubscriptionHandler): this {\n return this.on(\"subscription.created\", handler);\n }\n\n /**\n * Handle subscription activated\n */\n onActivated(handler: SubscriptionHandler): this {\n return this.on(\"subscription.activated\", handler);\n }\n\n /**\n * Handle subscription updated\n */\n onUpdated(handler: SubscriptionHandler): this {\n return this.on(\"subscription.updated\", handler);\n }\n\n /**\n * Handle plan changed\n */\n onPlanChanged(handler: SubscriptionHandler): this {\n return this.on(\"subscription.plan_changed\", handler);\n }\n\n /**\n * Handle subscription canceled\n */\n onCanceled(handler: SubscriptionHandler): this {\n return this.on(\"subscription.canceled\", handler);\n }\n\n /**\n * Handle subscription expired\n */\n onExpired(handler: SubscriptionHandler): this {\n return this.on(\"subscription.expired\", handler);\n }\n\n /**\n * Handle subscription renewed\n */\n onRenewed(handler: SubscriptionHandler): this {\n return this.on(\"subscription.renewed\", handler);\n }\n\n /**\n * Handle trial started\n */\n onTrialStarted(handler: SubscriptionHandler): this {\n return this.on(\"subscription.trial_started\", handler);\n }\n\n /**\n * Handle trial ended\n */\n onTrialEnded(handler: SubscriptionHandler): this {\n return this.on(\"subscription.trial_ended\", handler);\n }\n\n /**\n * Handle payment failed\n */\n onPaymentFailed(handler: SubscriptionHandler): this {\n return this.on(\"subscription.payment_failed\", handler);\n }\n\n /**\n * Handle payment succeeded\n */\n onPaymentSucceeded(handler: SubscriptionHandler): this {\n return this.on(\"subscription.payment_succeeded\", handler);\n }\n\n /**\n * Handle period reset\n */\n onPeriodReset(handler: SubscriptionHandler): this {\n return this.on(\"subscription.period_reset\", handler);\n }\n\n /**\n * Handle all events\n */\n onAll(handler: SubscriptionHandler): this {\n return this.on(\"*\", handler);\n }\n}\n\n/**\n * Create subscription lifecycle manager\n */\nexport function createSubscriptionLifecycle(logger?: BillingLogger): SubscriptionLifecycle {\n return new SubscriptionLifecycle(logger);\n}\n","/**\n * @parsrun/payments - Usage Service\n * Main service for usage-based billing\n */\n\nimport type {\n UsageStorage,\n UsageServiceConfig,\n UsageEvent,\n TrackUsageOptions,\n UsageAggregate,\n QuotaStatus,\n QuotaCheckResult,\n Plan,\n PlanFeature,\n PeriodType,\n GetUsageOptions,\n SubscriptionEventType,\n SubscriptionHandler,\n BillingLogger,\n ResetPeriod,\n AccessStatus,\n AccessStatusInfo,\n} from \"./types.js\";\nimport { QuotaManager } from \"./quota-manager.js\";\nimport { UsageTracker } from \"./usage-tracker.js\";\nimport { SubscriptionLifecycle } from \"./lifecycle-hooks.js\";\n\n/**\n * Null logger\n */\nconst nullLogger: BillingLogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Usage Service\n *\n * High-level API for usage-based billing:\n * - Track usage events\n * - Check and enforce quotas\n * - Manage subscription lifecycle\n * - Generate usage reports\n *\n * @example\n * ```typescript\n * const usageService = createUsageService({\n * storage: createMemoryUsageStorage(),\n * alertThresholds: [80, 100],\n * onThresholdReached: async (alert) => {\n * await sendEmail(alert.customerId, \"usage-warning\");\n * },\n * });\n *\n * // Track usage\n * await usageService.trackUsage({\n * tenantId: \"tenant_123\",\n * customerId: \"cus_456\",\n * featureKey: \"api_calls\",\n * quantity: 1,\n * });\n *\n * // Check quota\n * const quota = await usageService.getQuotaStatus(\"cus_456\", \"api_calls\");\n * if (quota.isExceeded) {\n * throw new Error(\"Quota exceeded\");\n * }\n *\n * // Lifecycle hooks\n * usageService.onPlanChanged(async (event) => {\n * console.log(`Plan changed from ${event.previousPlan?.name} to ${event.newPlan?.name}`);\n * });\n * ```\n */\nexport class UsageService {\n private readonly storage: UsageStorage;\n private readonly logger: BillingLogger;\n private readonly quotaManager: QuotaManager;\n private readonly usageTracker: UsageTracker;\n private readonly lifecycle: SubscriptionLifecycle;\n private readonly limitExceededHandler?: (quota: QuotaStatus, customerId: string) => void | Promise<void>;\n private readonly resetPeriod: ResetPeriod;\n private readonly autoResetOnRenewal: boolean;\n private readonly paymentGraceDays: number;\n private readonly maxFailedPayments: number;\n private readonly accessStatusChangedHandler?: (customerId: string, status: AccessStatusInfo, previousStatus?: AccessStatus) => void | Promise<void>;\n private readonly periodResetHandler?: (customerId: string, featureKey: string) => void | Promise<void>;\n\n constructor(config: UsageServiceConfig) {\n this.storage = config.storage;\n this.logger = config.logger ?? nullLogger;\n this.resetPeriod = config.resetPeriod ?? \"monthly\";\n this.autoResetOnRenewal = config.autoResetOnRenewal ?? true;\n this.paymentGraceDays = config.paymentGraceDays ?? 3;\n this.maxFailedPayments = config.maxFailedPayments ?? 3;\n\n if (config.onAccessStatusChanged !== undefined) {\n this.accessStatusChangedHandler = config.onAccessStatusChanged;\n }\n if (config.onPeriodReset !== undefined) {\n this.periodResetHandler = config.onPeriodReset;\n }\n\n // Store handler with undefined check\n if (config.onLimitExceeded !== undefined) {\n this.limitExceededHandler = config.onLimitExceeded;\n }\n\n // Build quota manager config with only defined properties\n const quotaManagerConfig: { storage: UsageStorage; logger?: BillingLogger } = {\n storage: config.storage,\n };\n if (config.logger !== undefined) {\n quotaManagerConfig.logger = config.logger;\n }\n this.quotaManager = new QuotaManager(quotaManagerConfig);\n\n // Build usage tracker config with only defined properties\n const usageTrackerConfig: {\n storage: UsageStorage;\n logger?: BillingLogger;\n aggregateOnRecord?: boolean;\n alertThresholds?: number[];\n onThresholdReached?: (alert: import(\"./types.js\").UsageAlert) => void | Promise<void>;\n } = {\n storage: config.storage,\n aggregateOnRecord: config.aggregateImmediately ?? true,\n alertThresholds: config.alertThresholds ?? [80, 100],\n };\n if (config.logger !== undefined) {\n usageTrackerConfig.logger = config.logger;\n }\n if (config.onThresholdReached !== undefined) {\n usageTrackerConfig.onThresholdReached = config.onThresholdReached;\n }\n this.usageTracker = new UsageTracker(usageTrackerConfig);\n\n // Initialize lifecycle\n this.lifecycle = config.logger !== undefined\n ? new SubscriptionLifecycle(config.logger)\n : new SubscriptionLifecycle();\n\n this.logger.info(\"UsageService initialized\");\n }\n\n // ============================================================================\n // Usage Tracking\n // ============================================================================\n\n /**\n * Track a usage event\n */\n async trackUsage(options: TrackUsageOptions): Promise<UsageEvent> {\n return this.usageTracker.trackUsage(options);\n }\n\n /**\n * Track multiple usage events\n */\n async trackBatch(events: TrackUsageOptions[]): Promise<UsageEvent[]> {\n return this.usageTracker.trackBatch(events);\n }\n\n /**\n * Get current usage for a feature\n */\n async getUsage(\n customerId: string,\n featureKey: string,\n periodType: PeriodType = \"month\"\n ): Promise<number> {\n return this.usageTracker.getUsage(customerId, featureKey, periodType);\n }\n\n /**\n * Get usage aggregates\n */\n async getAggregates(\n customerId: string,\n options: GetUsageOptions = {}\n ): Promise<UsageAggregate[]> {\n return this.storage.getAggregates(customerId, options);\n }\n\n // ============================================================================\n // Quota Management\n // ============================================================================\n\n /**\n * Check if quota allows the requested quantity\n */\n async checkQuota(\n customerId: string,\n featureKey: string,\n quantity: number = 1\n ): Promise<QuotaCheckResult> {\n return this.quotaManager.checkQuota(customerId, featureKey, quantity);\n }\n\n /**\n * Enforce quota - throws if exceeded\n */\n async enforceQuota(\n customerId: string,\n featureKey: string,\n quantity: number = 1\n ): Promise<void> {\n return this.quotaManager.enforceQuota(customerId, featureKey, quantity);\n }\n\n /**\n * Get quota status for a feature\n */\n async getQuotaStatus(customerId: string, featureKey: string): Promise<QuotaStatus> {\n const status = await this.quotaManager.getQuotaStatus(customerId, featureKey);\n\n // Trigger limit exceeded callback if needed\n if (status.isExceeded && this.limitExceededHandler) {\n try {\n await this.limitExceededHandler(status, customerId);\n } catch (error) {\n this.logger.error(\"Limit exceeded callback failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return status;\n }\n\n /**\n * Get all quota statuses for a customer\n */\n async getAllQuotas(customerId: string): Promise<QuotaStatus[]> {\n return this.quotaManager.getAllQuotas(customerId);\n }\n\n /**\n * Check if any quota is exceeded\n */\n async hasExceededQuotas(customerId: string): Promise<boolean> {\n return this.quotaManager.hasExceededQuotas(customerId);\n }\n\n // ============================================================================\n // Plan Management\n // ============================================================================\n\n /**\n * Get customer's current plan\n */\n async getCustomerPlan(customerId: string): Promise<Plan | null> {\n const planId = await this.storage.getCustomerPlanId(customerId);\n if (!planId) return null;\n return this.storage.getPlan(planId);\n }\n\n /**\n * Set customer's plan\n */\n async setCustomerPlan(customerId: string, planId: string): Promise<void> {\n this.logger.info(\"Setting customer plan\", { customerId, planId });\n await this.storage.setCustomerPlanId(customerId, planId);\n }\n\n /**\n * Get plan by ID\n */\n async getPlan(planId: string): Promise<Plan | null> {\n return this.storage.getPlan(planId);\n }\n\n /**\n * Get plan by name\n */\n async getPlanByName(name: string): Promise<Plan | null> {\n return this.storage.getPlanByName(name);\n }\n\n /**\n * List all plans\n */\n async listPlans(options?: { activeOnly?: boolean }): Promise<Plan[]> {\n return this.storage.listPlans(options);\n }\n\n /**\n * Get plan features\n */\n async getPlanFeatures(planId: string): Promise<PlanFeature[]> {\n return this.storage.getPlanFeatures(planId);\n }\n\n // ============================================================================\n // Subscription Lifecycle\n // ============================================================================\n\n /**\n * Register a subscription event handler\n */\n on(event: SubscriptionEventType | \"*\", handler: SubscriptionHandler): this {\n this.lifecycle.on(event, handler);\n return this;\n }\n\n /**\n * Remove a subscription event handler\n */\n off(event: SubscriptionEventType | \"*\", handler: SubscriptionHandler): this {\n this.lifecycle.off(event, handler);\n return this;\n }\n\n /**\n * Handle subscription created\n */\n onSubscriptionCreated(handler: SubscriptionHandler): this {\n this.lifecycle.onCreated(handler);\n return this;\n }\n\n /**\n * Handle subscription updated\n */\n onSubscriptionUpdated(handler: SubscriptionHandler): this {\n this.lifecycle.onUpdated(handler);\n return this;\n }\n\n /**\n * Handle subscription canceled\n */\n onSubscriptionCanceled(handler: SubscriptionHandler): this {\n this.lifecycle.onCanceled(handler);\n return this;\n }\n\n /**\n * Handle plan changed\n */\n onPlanChanged(handler: SubscriptionHandler): this {\n this.lifecycle.onPlanChanged(handler);\n return this;\n }\n\n /**\n * Handle subscription renewed\n */\n onRenewed(handler: SubscriptionHandler): this {\n this.lifecycle.onRenewed(handler);\n return this;\n }\n\n /**\n * Handle payment failed\n */\n onPaymentFailed(handler: SubscriptionHandler): this {\n this.lifecycle.onPaymentFailed(handler);\n return this;\n }\n\n /**\n * Handle period reset\n */\n onPeriodReset(handler: SubscriptionHandler): this {\n this.lifecycle.onPeriodReset(handler);\n return this;\n }\n\n /**\n * Get the lifecycle manager for advanced usage\n */\n get lifecycleManager(): SubscriptionLifecycle {\n return this.lifecycle;\n }\n\n // ============================================================================\n // Alerts\n // ============================================================================\n\n /**\n * Get active alerts for a customer\n */\n async getActiveAlerts(customerId: string) {\n return this.storage.getActiveAlerts(customerId);\n }\n\n /**\n * Acknowledge an alert\n */\n async acknowledgeAlert(alertId: string): Promise<void> {\n await this.storage.updateAlertStatus(alertId, \"acknowledged\");\n }\n\n /**\n * Resolve an alert\n */\n async resolveAlert(alertId: string): Promise<void> {\n await this.storage.updateAlertStatus(alertId, \"resolved\");\n }\n\n // ============================================================================\n // Usage Reset\n // ============================================================================\n\n /**\n * Reset usage for a customer\n * Typically called on subscription renewal\n */\n async resetUsage(\n customerId: string,\n featureKeys?: string[]\n ): Promise<void> {\n this.logger.info(\"Resetting usage\", { customerId, featureKeys });\n\n // Get the period start based on reset period setting\n const periodStart = await this.getResetPeriodStart(customerId);\n\n await this.storage.resetUsage(customerId, featureKeys, periodStart);\n\n // Trigger callbacks\n if (this.periodResetHandler) {\n const features = featureKeys ?? await this.getCustomerFeatureKeys(customerId);\n for (const featureKey of features) {\n try {\n await this.periodResetHandler(customerId, featureKey);\n } catch (error) {\n this.logger.error(\"Period reset callback failed\", {\n customerId,\n featureKey,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n\n // Emit lifecycle event\n const plan = await this.getCustomerPlan(customerId);\n if (plan) {\n await this.lifecycle.emit({\n type: \"subscription.period_reset\",\n subscription: {\n id: \"\",\n customerId,\n status: \"active\",\n priceId: \"\",\n currentPeriodStart: periodStart,\n currentPeriodEnd: new Date(),\n cancelAtPeriodEnd: false,\n provider: \"stripe\", // Default, will be overridden by BillingIntegration\n },\n newPlan: plan,\n timestamp: new Date(),\n provider: \"stripe\",\n });\n }\n\n this.logger.info(\"Usage reset complete\", { customerId });\n }\n\n /**\n * Get the period start date based on reset period setting\n */\n private async getResetPeriodStart(customerId: string): Promise<Date> {\n if (this.resetPeriod === \"billing_cycle\") {\n // Use the customer's billing cycle\n const billingCycle = await this.storage.getBillingCycle(customerId);\n if (billingCycle) {\n return billingCycle.start;\n }\n }\n\n // Default to calendar month\n const now = new Date();\n return new Date(now.getFullYear(), now.getMonth(), 1);\n }\n\n /**\n * Get feature keys for a customer's plan\n */\n private async getCustomerFeatureKeys(customerId: string): Promise<string[]> {\n const planId = await this.storage.getCustomerPlanId(customerId);\n if (!planId) return [];\n\n const features = await this.storage.getPlanFeatures(planId);\n return features.map(f => f.featureKey);\n }\n\n /**\n * Check if auto-reset on renewal is enabled\n */\n get autoResetEnabled(): boolean {\n return this.autoResetOnRenewal;\n }\n\n /**\n * Get current reset period setting\n */\n get currentResetPeriod(): ResetPeriod {\n return this.resetPeriod;\n }\n\n // ============================================================================\n // Access Status\n // ============================================================================\n\n /**\n * Get customer access status\n */\n async getAccessStatus(customerId: string): Promise<AccessStatusInfo> {\n const status = await this.storage.getAccessStatus(customerId);\n if (status) {\n return status;\n }\n\n // Default to active\n return {\n status: \"active\",\n updatedAt: new Date(),\n };\n }\n\n /**\n * Set customer access status\n */\n async setAccessStatus(\n customerId: string,\n status: AccessStatus,\n options?: {\n reason?: string;\n suspensionDate?: Date;\n failedPaymentAttempts?: number;\n gracePeriodEnd?: Date;\n }\n ): Promise<void> {\n const previousStatusInfo = await this.storage.getAccessStatus(customerId);\n const previousStatus = previousStatusInfo?.status;\n\n const newStatus: AccessStatusInfo = {\n status,\n updatedAt: new Date(),\n };\n\n if (options?.reason !== undefined) {\n newStatus.reason = options.reason;\n }\n if (options?.suspensionDate !== undefined) {\n newStatus.suspensionDate = options.suspensionDate;\n }\n if (options?.failedPaymentAttempts !== undefined) {\n newStatus.failedPaymentAttempts = options.failedPaymentAttempts;\n }\n if (options?.gracePeriodEnd !== undefined) {\n newStatus.gracePeriodEnd = options.gracePeriodEnd;\n }\n\n await this.storage.setAccessStatus(customerId, newStatus);\n\n this.logger.info(\"Access status changed\", {\n customerId,\n previousStatus,\n newStatus: status,\n reason: options?.reason,\n });\n\n // Trigger callback\n if (this.accessStatusChangedHandler) {\n try {\n await this.accessStatusChangedHandler(customerId, newStatus, previousStatus);\n } catch (error) {\n this.logger.error(\"Access status change callback failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n\n /**\n * Handle payment failure - update access status\n */\n async handlePaymentFailure(customerId: string): Promise<void> {\n const currentStatus = await this.getAccessStatus(customerId);\n const failedAttempts = (currentStatus.failedPaymentAttempts ?? 0) + 1;\n\n if (failedAttempts >= this.maxFailedPayments) {\n // Suspend access\n await this.setAccessStatus(customerId, \"suspended\", {\n reason: `Payment failed ${failedAttempts} times`,\n failedPaymentAttempts: failedAttempts,\n });\n } else {\n // Set to past_due with grace period\n const gracePeriodEnd = new Date();\n gracePeriodEnd.setDate(gracePeriodEnd.getDate() + this.paymentGraceDays);\n\n await this.setAccessStatus(customerId, \"past_due\", {\n reason: `Payment failed (attempt ${failedAttempts}/${this.maxFailedPayments})`,\n failedPaymentAttempts: failedAttempts,\n gracePeriodEnd,\n suspensionDate: gracePeriodEnd,\n });\n }\n }\n\n /**\n * Handle successful payment - restore access status\n */\n async handlePaymentSuccess(customerId: string): Promise<void> {\n const currentStatus = await this.getAccessStatus(customerId);\n\n if (currentStatus.status !== \"active\") {\n await this.setAccessStatus(customerId, \"active\", {\n reason: \"Payment successful\",\n failedPaymentAttempts: 0,\n });\n }\n }\n\n /**\n * Check if customer has access (not suspended/canceled)\n */\n async hasAccess(customerId: string): Promise<boolean> {\n const status = await this.getAccessStatus(customerId);\n return status.status === \"active\" || status.status === \"past_due\";\n }\n\n /**\n * Check if customer is in grace period\n */\n async isInGracePeriod(customerId: string): Promise<boolean> {\n const status = await this.getAccessStatus(customerId);\n if (status.status !== \"past_due\") return false;\n if (!status.gracePeriodEnd) return false;\n return new Date() < status.gracePeriodEnd;\n }\n\n // ============================================================================\n // Billing Cycle\n // ============================================================================\n\n /**\n * Set customer billing cycle\n */\n async setBillingCycle(customerId: string, start: Date, end: Date): Promise<void> {\n await this.storage.setBillingCycle(customerId, start, end);\n this.logger.debug(\"Billing cycle set\", { customerId, start, end });\n }\n\n /**\n * Get customer billing cycle\n */\n async getBillingCycle(customerId: string): Promise<{ start: Date; end: Date } | null> {\n return this.storage.getBillingCycle(customerId);\n }\n\n // ============================================================================\n // Internal\n // ============================================================================\n\n /**\n * Get the underlying storage\n */\n get storageBackend(): UsageStorage {\n return this.storage;\n }\n\n /**\n * Get the quota manager\n */\n get quotas(): QuotaManager {\n return this.quotaManager;\n }\n\n /**\n * Get the usage tracker\n */\n get tracker(): UsageTracker {\n return this.usageTracker;\n }\n}\n\n/**\n * Create usage service\n */\nexport function createUsageService(config: UsageServiceConfig): UsageService {\n return new UsageService(config);\n}\n","/**\n * @parsrun/payments - Billing Integration\n * Connects BillingService webhooks to UsageService lifecycle\n */\n\nimport type { WebhookEvent, PaymentProviderType } from \"../types.js\";\nimport type { BillingService } from \"../billing/billing-service.js\";\nimport type { UsageService } from \"./usage-service.js\";\nimport type { SubscriptionEvent, Plan, BillingLogger } from \"./types.js\";\n\n/**\n * Null logger\n */\nconst nullLogger: BillingLogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Billing integration configuration\n */\nexport interface BillingIntegrationConfig {\n /** Billing service instance */\n billing: BillingService;\n\n /** Usage service instance */\n usage: UsageService;\n\n /** Logger */\n logger?: BillingLogger;\n\n /** Auto-initialize customer plan on subscription creation */\n autoInitializePlan?: boolean;\n\n /**\n * Auto-reset quotas on subscription renewal\n * Default: true (uses UsageService.autoResetOnRenewal setting)\n */\n autoResetOnRenewal?: boolean;\n\n /**\n * Auto-update access status based on payment events\n * Default: true\n */\n autoManageAccessStatus?: boolean;\n\n /** Plan ID resolver - maps payment provider price IDs to internal plan IDs */\n resolvePlanId?: (priceId: string, provider: PaymentProviderType) => string | Promise<string>;\n\n /** Custom event handlers */\n onSubscriptionCreated?: (event: SubscriptionEvent) => void | Promise<void>;\n onSubscriptionCanceled?: (event: SubscriptionEvent) => void | Promise<void>;\n onSubscriptionRenewed?: (event: SubscriptionEvent) => void | Promise<void>;\n onPlanChanged?: (event: SubscriptionEvent) => void | Promise<void>;\n onPaymentFailed?: (event: SubscriptionEvent) => void | Promise<void>;\n}\n\n/**\n * Billing Integration\n *\n * Connects BillingService webhooks to UsageService lifecycle events.\n * This enables automatic:\n * - Customer plan initialization on subscription creation\n * - Plan updates when subscription changes\n * - Usage reset on subscription renewal\n * - Lifecycle event emission\n *\n * @example\n * ```typescript\n * import { createBillingService, createUsageService, integrateBillingWithUsage } from \"@parsrun/payments\";\n *\n * const billing = createBillingService({\n * providers: { default: stripeProvider },\n * });\n *\n * const usage = createUsageService({\n * storage: createMemoryUsageStorage(),\n * });\n *\n * // Connect them\n * const integration = integrateBillingWithUsage({\n * billing,\n * usage,\n * autoInitializePlan: true,\n * resolvePlanId: (priceId) => {\n * // Map Stripe price IDs to your internal plan IDs\n * const mapping: Record<string, string> = {\n * \"price_starter\": \"starter\",\n * \"price_pro\": \"pro\",\n * \"price_enterprise\": \"enterprise\",\n * };\n * return mapping[priceId] ?? \"free\";\n * },\n * });\n *\n * // Now subscription webhooks automatically update usage quotas\n * ```\n */\nexport class BillingIntegration {\n private readonly billing: BillingService;\n private readonly usage: UsageService;\n private readonly logger: BillingLogger;\n private readonly autoInitializePlan: boolean;\n private readonly autoResetOnRenewal: boolean;\n private readonly autoManageAccessStatus: boolean;\n private readonly resolvePlanId: (priceId: string, provider: PaymentProviderType) => string | Promise<string>;\n private readonly config: BillingIntegrationConfig;\n\n constructor(config: BillingIntegrationConfig) {\n this.billing = config.billing;\n this.usage = config.usage;\n this.logger = config.logger ?? nullLogger;\n this.autoInitializePlan = config.autoInitializePlan ?? true;\n this.autoResetOnRenewal = config.autoResetOnRenewal ?? true;\n this.autoManageAccessStatus = config.autoManageAccessStatus ?? true;\n this.resolvePlanId = config.resolvePlanId ?? ((priceId) => priceId);\n this.config = config;\n\n // Register webhook handlers\n this.setupWebhookHandlers();\n }\n\n /**\n * Setup webhook handlers on billing service\n */\n private setupWebhookHandlers(): void {\n // Subscription created\n this.billing.onWebhook(\"subscription.created\", async (event) => {\n await this.handleSubscriptionCreated(event);\n });\n\n // Subscription updated (plan change)\n this.billing.onWebhook(\"subscription.updated\", async (event) => {\n await this.handleSubscriptionUpdated(event);\n });\n\n // Subscription deleted (canceled)\n this.billing.onWebhook(\"subscription.deleted\", async (event) => {\n await this.handleSubscriptionCanceled(event);\n });\n\n // Invoice paid (renewal)\n this.billing.onWebhook(\"invoice.paid\", async (event) => {\n await this.handleInvoicePaid(event);\n });\n\n // Payment failed\n this.billing.onWebhook(\"invoice.payment_failed\", async (event) => {\n await this.handlePaymentFailed(event);\n });\n\n this.logger.info(\"Billing integration initialized\");\n }\n\n /**\n * Handle subscription created webhook\n */\n private async handleSubscriptionCreated(event: WebhookEvent): Promise<void> {\n const subscription = event.data as {\n id: string;\n customerId: string;\n status: string;\n priceId?: string;\n currentPeriodStart?: Date;\n currentPeriodEnd?: Date;\n };\n\n this.logger.info(\"Subscription created\", {\n subscriptionId: subscription.id,\n customerId: subscription.customerId,\n provider: event.provider,\n });\n\n // Initialize customer plan if enabled\n if (this.autoInitializePlan && subscription.priceId) {\n const planId = await this.resolvePlanId(subscription.priceId, event.provider);\n await this.usage.setCustomerPlan(subscription.customerId, planId);\n\n this.logger.debug(\"Customer plan initialized\", {\n customerId: subscription.customerId,\n planId,\n });\n }\n\n // Set billing cycle for billing_cycle reset period\n if (subscription.currentPeriodStart && subscription.currentPeriodEnd) {\n await this.usage.setBillingCycle(\n subscription.customerId,\n subscription.currentPeriodStart,\n subscription.currentPeriodEnd\n );\n }\n\n // Initialize access status\n if (this.autoManageAccessStatus) {\n await this.usage.setAccessStatus(subscription.customerId, \"active\", {\n reason: \"Subscription created\",\n });\n }\n\n // Get plan for event\n const plan = subscription.priceId\n ? await this.usage.getPlan(await this.resolvePlanId(subscription.priceId, event.provider))\n : null;\n\n // Emit lifecycle event - build with only defined optional properties\n const lifecycleEvent: SubscriptionEvent = {\n type: \"subscription.created\",\n subscription: {\n id: subscription.id,\n customerId: subscription.customerId,\n status: subscription.status as \"active\" | \"canceled\" | \"past_due\" | \"trialing\" | \"paused\" | \"incomplete\",\n priceId: subscription.priceId ?? \"\",\n currentPeriodStart: subscription.currentPeriodStart ?? new Date(),\n currentPeriodEnd: subscription.currentPeriodEnd ?? new Date(),\n cancelAtPeriodEnd: false,\n provider: event.provider,\n },\n timestamp: new Date(),\n provider: event.provider,\n };\n\n if (plan !== null) {\n lifecycleEvent.newPlan = plan;\n }\n\n await this.usage.lifecycleManager.emit(lifecycleEvent);\n\n // Custom handler\n if (this.config.onSubscriptionCreated) {\n await this.config.onSubscriptionCreated(lifecycleEvent);\n }\n }\n\n /**\n * Handle subscription updated webhook\n */\n private async handleSubscriptionUpdated(event: WebhookEvent): Promise<void> {\n const subscription = event.data as {\n id: string;\n customerId: string;\n status: string;\n priceId?: string;\n previousPriceId?: string;\n currentPeriodStart?: Date;\n currentPeriodEnd?: Date;\n };\n\n this.logger.info(\"Subscription updated\", {\n subscriptionId: subscription.id,\n customerId: subscription.customerId,\n provider: event.provider,\n });\n\n // Check if plan changed\n const priceChanged = subscription.priceId !== subscription.previousPriceId;\n\n let previousPlan: Plan | null = null;\n let newPlan: Plan | null = null;\n\n if (priceChanged && subscription.priceId) {\n // Get previous plan\n if (subscription.previousPriceId) {\n const previousPlanId = await this.resolvePlanId(subscription.previousPriceId, event.provider);\n previousPlan = await this.usage.getPlan(previousPlanId);\n }\n\n // Update customer plan\n const newPlanId = await this.resolvePlanId(subscription.priceId, event.provider);\n await this.usage.setCustomerPlan(subscription.customerId, newPlanId);\n newPlan = await this.usage.getPlan(newPlanId);\n\n this.logger.info(\"Customer plan changed\", {\n customerId: subscription.customerId,\n previousPlanId: previousPlan?.id,\n newPlanId,\n });\n }\n\n // Emit lifecycle event - build with only defined optional properties\n const eventType = priceChanged ? \"subscription.plan_changed\" : \"subscription.updated\";\n const lifecycleEvent: SubscriptionEvent = {\n type: eventType,\n subscription: {\n id: subscription.id,\n customerId: subscription.customerId,\n status: subscription.status as \"active\" | \"canceled\" | \"past_due\" | \"trialing\" | \"paused\" | \"incomplete\",\n priceId: subscription.priceId ?? \"\",\n currentPeriodStart: subscription.currentPeriodStart ?? new Date(),\n currentPeriodEnd: subscription.currentPeriodEnd ?? new Date(),\n cancelAtPeriodEnd: false,\n provider: event.provider,\n },\n timestamp: new Date(),\n provider: event.provider,\n };\n\n if (previousPlan !== null) {\n lifecycleEvent.previousPlan = previousPlan;\n }\n if (newPlan !== null) {\n lifecycleEvent.newPlan = newPlan;\n }\n\n await this.usage.lifecycleManager.emit(lifecycleEvent);\n\n // Custom handler for plan change\n if (priceChanged && this.config.onPlanChanged) {\n await this.config.onPlanChanged(lifecycleEvent);\n }\n }\n\n /**\n * Handle subscription canceled webhook\n */\n private async handleSubscriptionCanceled(event: WebhookEvent): Promise<void> {\n const subscription = event.data as {\n id: string;\n customerId: string;\n status: string;\n priceId?: string;\n currentPeriodEnd?: Date;\n };\n\n this.logger.info(\"Subscription canceled\", {\n subscriptionId: subscription.id,\n customerId: subscription.customerId,\n provider: event.provider,\n });\n\n // Update access status to canceled\n if (this.autoManageAccessStatus) {\n await this.usage.setAccessStatus(subscription.customerId, \"canceled\", {\n reason: \"Subscription canceled\",\n });\n }\n\n // Get current plan before removing\n const currentPlan = await this.usage.getCustomerPlan(subscription.customerId);\n\n // Emit lifecycle event - build with only defined optional properties\n const lifecycleEvent: SubscriptionEvent = {\n type: \"subscription.canceled\",\n subscription: {\n id: subscription.id,\n customerId: subscription.customerId,\n status: \"canceled\",\n priceId: subscription.priceId ?? \"\",\n currentPeriodStart: new Date(),\n currentPeriodEnd: subscription.currentPeriodEnd ?? new Date(),\n cancelAtPeriodEnd: true,\n provider: event.provider,\n },\n timestamp: new Date(),\n provider: event.provider,\n };\n\n if (currentPlan !== null) {\n lifecycleEvent.previousPlan = currentPlan;\n }\n\n await this.usage.lifecycleManager.emit(lifecycleEvent);\n\n // Custom handler\n if (this.config.onSubscriptionCanceled) {\n await this.config.onSubscriptionCanceled(lifecycleEvent);\n }\n }\n\n /**\n * Handle invoice paid webhook (renewal)\n */\n private async handleInvoicePaid(event: WebhookEvent): Promise<void> {\n const invoice = event.data as {\n id: string;\n customerId: string;\n subscriptionId?: string;\n billingReason?: string;\n periodStart?: Date;\n periodEnd?: Date;\n };\n\n // Only handle subscription renewals, not initial payments\n if (invoice.billingReason !== \"subscription_cycle\") {\n return;\n }\n\n this.logger.info(\"Subscription renewed\", {\n invoiceId: invoice.id,\n customerId: invoice.customerId,\n subscriptionId: invoice.subscriptionId,\n provider: event.provider,\n });\n\n // Update billing cycle\n if (invoice.periodStart && invoice.periodEnd) {\n await this.usage.setBillingCycle(\n invoice.customerId,\n invoice.periodStart,\n invoice.periodEnd\n );\n }\n\n // Auto-reset quotas on renewal if enabled\n if (this.autoResetOnRenewal && this.usage.autoResetEnabled) {\n this.logger.info(\"Auto-resetting quotas on renewal\", {\n customerId: invoice.customerId,\n });\n await this.usage.resetUsage(invoice.customerId);\n }\n\n // Restore access status if payment succeeded\n if (this.autoManageAccessStatus) {\n await this.usage.handlePaymentSuccess(invoice.customerId);\n }\n\n // Get current plan\n const currentPlan = await this.usage.getCustomerPlan(invoice.customerId);\n\n // Emit lifecycle event - build with only defined optional properties\n const periodStart = invoice.periodStart ?? new Date();\n const periodEnd = invoice.periodEnd ?? new Date();\n\n const lifecycleEvent: SubscriptionEvent = {\n type: \"subscription.renewed\",\n subscription: {\n id: invoice.subscriptionId ?? invoice.id,\n customerId: invoice.customerId,\n status: \"active\",\n priceId: \"\",\n currentPeriodStart: periodStart,\n currentPeriodEnd: periodEnd,\n cancelAtPeriodEnd: false,\n provider: event.provider,\n },\n timestamp: new Date(),\n provider: event.provider,\n };\n\n if (currentPlan !== null) {\n lifecycleEvent.newPlan = currentPlan;\n }\n\n await this.usage.lifecycleManager.emit(lifecycleEvent);\n\n // Custom handler\n if (this.config.onSubscriptionRenewed) {\n await this.config.onSubscriptionRenewed(lifecycleEvent);\n }\n }\n\n /**\n * Handle payment failed webhook\n */\n private async handlePaymentFailed(event: WebhookEvent): Promise<void> {\n const invoice = event.data as {\n id: string;\n customerId: string;\n subscriptionId?: string;\n };\n\n this.logger.warn(\"Payment failed\", {\n invoiceId: invoice.id,\n customerId: invoice.customerId,\n subscriptionId: invoice.subscriptionId,\n provider: event.provider,\n });\n\n // Update access status based on payment failure\n if (this.autoManageAccessStatus) {\n await this.usage.handlePaymentFailure(invoice.customerId);\n }\n\n // Get current plan\n const currentPlan = await this.usage.getCustomerPlan(invoice.customerId);\n\n // Emit lifecycle event - build with only defined optional properties\n const lifecycleEvent: SubscriptionEvent = {\n type: \"subscription.payment_failed\",\n subscription: {\n id: invoice.subscriptionId ?? invoice.id,\n customerId: invoice.customerId,\n status: \"past_due\",\n priceId: \"\",\n currentPeriodStart: new Date(),\n currentPeriodEnd: new Date(),\n cancelAtPeriodEnd: false,\n provider: event.provider,\n },\n timestamp: new Date(),\n provider: event.provider,\n };\n\n if (currentPlan !== null) {\n lifecycleEvent.newPlan = currentPlan;\n }\n\n await this.usage.lifecycleManager.emit(lifecycleEvent);\n\n // Custom handler\n if (this.config.onPaymentFailed) {\n await this.config.onPaymentFailed(lifecycleEvent);\n }\n }\n\n /**\n * Manually sync customer plan from billing provider\n */\n async syncCustomerPlan(customerId: string, provider?: PaymentProviderType): Promise<Plan | null> {\n // Get subscriptions from billing - build options with only defined properties\n const subscriptionOptions: { customerId: string; provider?: PaymentProviderType } = {\n customerId,\n };\n if (provider !== undefined) {\n subscriptionOptions.provider = provider;\n }\n const subscriptions = await this.billing.getSubscriptions(subscriptionOptions);\n\n // Find active subscription\n const activeSubscription = subscriptions.find(\n (sub) => sub.status === \"active\" || sub.status === \"trialing\"\n );\n\n if (!activeSubscription) {\n this.logger.debug(\"No active subscription found\", { customerId });\n return null;\n }\n\n // Resolve and set plan\n const planId = await this.resolvePlanId(\n activeSubscription.priceId,\n activeSubscription.provider\n );\n await this.usage.setCustomerPlan(customerId, planId);\n\n const plan = await this.usage.getPlan(planId);\n\n this.logger.info(\"Customer plan synced\", {\n customerId,\n planId,\n subscriptionId: activeSubscription.id,\n });\n\n return plan;\n }\n}\n\n/**\n * Create billing integration\n *\n * Connects BillingService webhooks to UsageService lifecycle events.\n *\n * @example\n * ```typescript\n * const integration = integrateBillingWithUsage({\n * billing,\n * usage,\n * autoInitializePlan: true,\n * resolvePlanId: (priceId) => priceMapping[priceId] ?? \"free\",\n * });\n * ```\n */\nexport function integrateBillingWithUsage(config: BillingIntegrationConfig): BillingIntegration {\n return new BillingIntegration(config);\n}\n\n/**\n * Alias for integrateBillingWithUsage\n */\nexport const createBillingIntegration = integrateBillingWithUsage;\n","/**\n * @parsrun/payments - Usage Meter\n * Buffers usage events and syncs to payment provider\n *\n * This solves the problem of:\n * - Not hitting provider API limits with per-request calls\n * - Reducing latency by batching usage reports\n * - Ensuring reliable usage reporting with retry logic\n */\n\nimport type { PaymentProviderType } from \"../types.js\";\nimport type { BillingLogger } from \"./types.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Usage record to report to provider\n */\nexport interface UsageRecord {\n /** Subscription item ID (Stripe) or subscription ID (Paddle) */\n subscriptionItemId: string;\n /** Usage quantity */\n quantity: number;\n /** Timestamp of usage (defaults to now) */\n timestamp?: Date;\n /** Action: increment (add to existing) or set (replace) */\n action?: \"increment\" | \"set\";\n /** Idempotency key to prevent duplicates */\n idempotencyKey?: string;\n}\n\n/**\n * Buffered usage record with metadata\n */\ninterface BufferedRecord {\n record: UsageRecord;\n customerId: string;\n featureKey: string;\n addedAt: Date;\n attempts: number;\n}\n\n/**\n * Usage reporter interface - implemented by providers\n */\nexport interface UsageReporter {\n /**\n * Report usage to the payment provider\n */\n reportUsage(record: UsageRecord): Promise<void>;\n\n /**\n * Report multiple usage records (batch)\n */\n reportUsageBatch?(records: UsageRecord[]): Promise<void>;\n\n /**\n * Get subscription item ID for a subscription and price\n */\n getSubscriptionItemId?(subscriptionId: string, priceId: string): Promise<string | null>;\n}\n\n/**\n * Sync strategy configuration\n */\nexport type SyncStrategy =\n | { type: \"interval\"; intervalMs: number }\n | { type: \"threshold\"; maxRecords: number; maxAgeMs: number }\n | { type: \"manual\" };\n\n/**\n * Usage meter configuration\n */\nexport interface UsageMeterConfig {\n /** Usage reporter (provider with reportUsage method) */\n reporter: UsageReporter;\n\n /** Provider type for logging */\n providerType: PaymentProviderType;\n\n /** Sync strategy */\n strategy: SyncStrategy;\n\n /** Logger */\n logger?: BillingLogger;\n\n /** Max retry attempts for failed syncs */\n maxRetries?: number;\n\n /** Retry delay in ms */\n retryDelayMs?: number;\n\n /** Callback on successful sync */\n onSyncSuccess?: (count: number) => void;\n\n /** Callback on sync failure */\n onSyncError?: (error: Error, records: BufferedRecord[]) => void;\n\n /** Callback when buffer is getting full (>80%) */\n onBufferWarning?: (size: number, maxSize: number) => void;\n\n /** Maximum buffer size before forcing sync */\n maxBufferSize?: number;\n}\n\n/**\n * Null logger\n */\nconst nullLogger: BillingLogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n// ============================================================================\n// Usage Meter\n// ============================================================================\n\n/**\n * Usage Meter\n *\n * Buffers usage events and syncs to payment provider periodically.\n * Supports multiple sync strategies:\n * - interval: Sync every N milliseconds\n * - threshold: Sync when buffer reaches N records or records are older than N ms\n * - manual: Only sync when explicitly called\n *\n * @example\n * ```typescript\n * import { createUsageMeter } from \"@parsrun/payments\";\n *\n * const meter = createUsageMeter({\n * reporter: stripeProvider, // Provider must implement UsageReporter\n * providerType: \"stripe\",\n * strategy: { type: \"interval\", intervalMs: 60000 }, // Sync every minute\n * });\n *\n * // Record usage (buffered, not sent immediately)\n * meter.record({\n * customerId: \"cus_123\",\n * featureKey: \"api_calls\",\n * subscriptionItemId: \"si_xxx\",\n * quantity: 1,\n * });\n *\n * // Force immediate sync\n * await meter.flush();\n *\n * // Cleanup when done\n * meter.stop();\n * ```\n */\nexport class UsageMeter {\n private readonly reporter: UsageReporter;\n private readonly providerType: PaymentProviderType;\n private readonly logger: BillingLogger;\n private readonly strategy: SyncStrategy;\n private readonly maxRetries: number;\n private readonly retryDelayMs: number;\n private readonly maxBufferSize: number;\n private readonly config: UsageMeterConfig;\n\n private buffer: BufferedRecord[] = [];\n private intervalId: ReturnType<typeof setInterval> | null = null;\n private isSyncing = false;\n private isRunning = false;\n\n constructor(config: UsageMeterConfig) {\n this.reporter = config.reporter;\n this.providerType = config.providerType;\n this.logger = config.logger ?? nullLogger;\n this.strategy = config.strategy;\n this.maxRetries = config.maxRetries ?? 3;\n this.retryDelayMs = config.retryDelayMs ?? 1000;\n this.maxBufferSize = config.maxBufferSize ?? 10000;\n this.config = config;\n\n this.start();\n }\n\n /**\n * Start the meter (auto-sync based on strategy)\n */\n start(): void {\n if (this.isRunning) return;\n this.isRunning = true;\n\n if (this.strategy.type === \"interval\") {\n this.intervalId = setInterval(() => {\n this.flush().catch((err) => {\n this.logger.error(\"Auto-sync failed\", { error: err.message });\n });\n }, this.strategy.intervalMs);\n\n this.logger.info(\"Usage meter started\", {\n strategy: \"interval\",\n intervalMs: this.strategy.intervalMs,\n provider: this.providerType,\n });\n } else if (this.strategy.type === \"threshold\") {\n // Threshold strategy uses checkThreshold on each record\n this.logger.info(\"Usage meter started\", {\n strategy: \"threshold\",\n maxRecords: this.strategy.maxRecords,\n maxAgeMs: this.strategy.maxAgeMs,\n provider: this.providerType,\n });\n } else {\n this.logger.info(\"Usage meter started\", {\n strategy: \"manual\",\n provider: this.providerType,\n });\n }\n }\n\n /**\n * Stop the meter\n */\n stop(): void {\n this.isRunning = false;\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = null;\n }\n this.logger.info(\"Usage meter stopped\");\n }\n\n /**\n * Record usage (buffered)\n */\n record(options: {\n customerId: string;\n featureKey: string;\n subscriptionItemId: string;\n quantity: number;\n timestamp?: Date;\n action?: \"increment\" | \"set\";\n idempotencyKey?: string;\n }): void {\n // Build record with only defined optional properties\n const usageRecord: UsageRecord = {\n subscriptionItemId: options.subscriptionItemId,\n quantity: options.quantity,\n action: options.action ?? \"increment\",\n };\n\n if (options.timestamp !== undefined) {\n usageRecord.timestamp = options.timestamp;\n }\n if (options.idempotencyKey !== undefined) {\n usageRecord.idempotencyKey = options.idempotencyKey;\n }\n\n const record: BufferedRecord = {\n record: usageRecord,\n customerId: options.customerId,\n featureKey: options.featureKey,\n addedAt: new Date(),\n attempts: 0,\n };\n\n this.buffer.push(record);\n\n this.logger.debug(\"Usage recorded\", {\n customerId: options.customerId,\n featureKey: options.featureKey,\n quantity: options.quantity,\n bufferSize: this.buffer.length,\n });\n\n // Check buffer size warning\n if (this.buffer.length > this.maxBufferSize * 0.8) {\n this.config.onBufferWarning?.(this.buffer.length, this.maxBufferSize);\n }\n\n // Force sync if buffer is full\n if (this.buffer.length >= this.maxBufferSize) {\n this.logger.warn(\"Buffer full, forcing sync\", { size: this.buffer.length });\n this.flush().catch((err) => {\n this.logger.error(\"Forced sync failed\", { error: err.message });\n });\n return;\n }\n\n // Check threshold strategy\n if (this.strategy.type === \"threshold\") {\n this.checkThreshold();\n }\n }\n\n /**\n * Check threshold and trigger sync if needed\n */\n private checkThreshold(): void {\n if (this.strategy.type !== \"threshold\") return;\n if (this.isSyncing) return;\n\n const { maxRecords, maxAgeMs } = this.strategy;\n\n // Check record count\n if (this.buffer.length >= maxRecords) {\n this.logger.debug(\"Threshold reached (count)\", { count: this.buffer.length });\n this.flush().catch((err) => {\n this.logger.error(\"Threshold sync failed\", { error: err.message });\n });\n return;\n }\n\n // Check age of oldest record\n const oldestRecord = this.buffer[0];\n if (oldestRecord) {\n const age = Date.now() - oldestRecord.addedAt.getTime();\n if (age >= maxAgeMs) {\n this.logger.debug(\"Threshold reached (age)\", { ageMs: age });\n this.flush().catch((err) => {\n this.logger.error(\"Threshold sync failed\", {\n error: err instanceof Error ? err.message : String(err),\n });\n });\n }\n }\n }\n\n /**\n * Flush buffer to provider\n */\n async flush(): Promise<number> {\n if (this.isSyncing) {\n this.logger.debug(\"Sync already in progress, skipping\");\n return 0;\n }\n\n if (this.buffer.length === 0) {\n return 0;\n }\n\n this.isSyncing = true;\n\n try {\n // Take current buffer and clear it\n const toSync = [...this.buffer];\n this.buffer = [];\n\n this.logger.info(\"Syncing usage to provider\", {\n count: toSync.length,\n provider: this.providerType,\n });\n\n // Aggregate by subscription item ID\n const aggregated = this.aggregateRecords(toSync);\n\n // Sync to provider\n const failed: BufferedRecord[] = [];\n\n for (const [subscriptionItemId, records] of aggregated) {\n try {\n const totalQuantity = records.reduce((sum, r) => sum + r.record.quantity, 0);\n\n // Build sync record with only defined optional properties\n const syncRecord: UsageRecord = {\n subscriptionItemId,\n quantity: totalQuantity,\n action: \"increment\",\n };\n\n // Use first record's timestamp for the batch if available\n const firstTimestamp = records[0]?.record.timestamp;\n if (firstTimestamp !== undefined) {\n syncRecord.timestamp = firstTimestamp;\n }\n\n await this.syncWithRetry(syncRecord);\n\n this.logger.debug(\"Usage synced\", {\n subscriptionItemId,\n quantity: totalQuantity,\n recordCount: records.length,\n });\n } catch (error) {\n this.logger.error(\"Failed to sync usage\", {\n subscriptionItemId,\n error: error instanceof Error ? error.message : String(error),\n });\n\n // Mark records as failed and add back to buffer if retries left\n for (const record of records) {\n record.attempts++;\n if (record.attempts < this.maxRetries) {\n failed.push(record);\n } else {\n this.logger.error(\"Record exceeded max retries, dropping\", {\n customerId: record.customerId,\n featureKey: record.featureKey,\n attempts: record.attempts,\n });\n }\n }\n }\n }\n\n // Add failed records back to buffer\n if (failed.length > 0) {\n this.buffer.unshift(...failed);\n this.config.onSyncError?.(new Error(\"Some records failed to sync\"), failed);\n }\n\n const syncedCount = toSync.length - failed.length;\n\n if (syncedCount > 0) {\n this.config.onSyncSuccess?.(syncedCount);\n }\n\n return syncedCount;\n } finally {\n this.isSyncing = false;\n }\n }\n\n /**\n * Aggregate records by subscription item ID\n */\n private aggregateRecords(records: BufferedRecord[]): Map<string, BufferedRecord[]> {\n const map = new Map<string, BufferedRecord[]>();\n\n for (const record of records) {\n const key = record.record.subscriptionItemId;\n const existing = map.get(key) ?? [];\n existing.push(record);\n map.set(key, existing);\n }\n\n return map;\n }\n\n /**\n * Sync single record with retry\n */\n private async syncWithRetry(record: UsageRecord): Promise<void> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < this.maxRetries; attempt++) {\n try {\n await this.reporter.reportUsage(record);\n return;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n this.logger.warn(\"Sync attempt failed, retrying\", {\n attempt: attempt + 1,\n maxRetries: this.maxRetries,\n error: lastError.message,\n });\n\n if (attempt < this.maxRetries - 1) {\n await this.delay(this.retryDelayMs * (attempt + 1)); // Exponential backoff\n }\n }\n }\n\n throw lastError ?? new Error(\"Sync failed after retries\");\n }\n\n /**\n * Delay helper\n */\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Get buffer size\n */\n get bufferSize(): number {\n return this.buffer.length;\n }\n\n /**\n * Get sync status\n */\n get syncing(): boolean {\n return this.isSyncing;\n }\n\n /**\n * Get running status\n */\n get running(): boolean {\n return this.isRunning;\n }\n\n /**\n * Get buffer contents (for debugging)\n */\n getBuffer(): ReadonlyArray<BufferedRecord> {\n return this.buffer;\n }\n}\n\n/**\n * Create usage meter\n */\nexport function createUsageMeter(config: UsageMeterConfig): UsageMeter {\n return new UsageMeter(config);\n}\n\n// ============================================================================\n// Subscription Item Resolver\n// ============================================================================\n\n/**\n * Subscription item mapping\n * Maps (customerId, featureKey) to subscriptionItemId\n */\nexport interface SubscriptionItemMapping {\n customerId: string;\n featureKey: string;\n subscriptionId: string;\n subscriptionItemId: string;\n priceId: string;\n createdAt: Date;\n}\n\n/**\n * Subscription item resolver configuration\n */\nexport interface SubscriptionItemResolverConfig {\n /** Reporter to fetch subscription items */\n reporter: UsageReporter;\n\n /** Cache TTL in ms (default: 1 hour) */\n cacheTtlMs?: number;\n\n /** Logger */\n logger?: BillingLogger;\n}\n\n/**\n * Subscription Item Resolver\n *\n * Resolves and caches subscription item IDs.\n * Stripe requires subscription_item_id for metered billing,\n * this resolver handles the lookup and caching.\n */\nexport class SubscriptionItemResolver {\n private readonly reporter: UsageReporter;\n private readonly cacheTtlMs: number;\n private readonly logger: BillingLogger;\n private cache = new Map<string, { itemId: string; expiresAt: Date }>();\n\n constructor(config: SubscriptionItemResolverConfig) {\n this.reporter = config.reporter;\n this.cacheTtlMs = config.cacheTtlMs ?? 60 * 60 * 1000; // 1 hour default\n this.logger = config.logger ?? nullLogger;\n }\n\n /**\n * Get subscription item ID\n */\n async resolve(subscriptionId: string, priceId: string): Promise<string | null> {\n const cacheKey = `${subscriptionId}:${priceId}`;\n\n // Check cache\n const cached = this.cache.get(cacheKey);\n if (cached && cached.expiresAt > new Date()) {\n return cached.itemId;\n }\n\n // Fetch from provider\n if (!this.reporter.getSubscriptionItemId) {\n this.logger.warn(\"Reporter does not support getSubscriptionItemId\");\n return null;\n }\n\n const itemId = await this.reporter.getSubscriptionItemId(subscriptionId, priceId);\n\n if (itemId) {\n // Cache the result\n this.cache.set(cacheKey, {\n itemId,\n expiresAt: new Date(Date.now() + this.cacheTtlMs),\n });\n }\n\n return itemId;\n }\n\n /**\n * Set cache entry manually (useful when creating subscriptions)\n */\n setCache(subscriptionId: string, priceId: string, itemId: string): void {\n const cacheKey = `${subscriptionId}:${priceId}`;\n this.cache.set(cacheKey, {\n itemId,\n expiresAt: new Date(Date.now() + this.cacheTtlMs),\n });\n }\n\n /**\n * Clear cache\n */\n clearCache(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache size\n */\n get cacheSize(): number {\n return this.cache.size;\n }\n}\n\n/**\n * Create subscription item resolver\n */\nexport function createSubscriptionItemResolver(\n config: SubscriptionItemResolverConfig\n): SubscriptionItemResolver {\n return new SubscriptionItemResolver(config);\n}\n"],"mappings":"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","names":["plans","generateId","nullLogger","timestamp","nullLogger","index","nullLogger","nullLogger","nullLogger"]}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { WebhookEventType, WebhookHandler, WebhookEvent, PaymentProvider } from '../types.js';
|
|
2
|
+
import '@parsrun/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @parsrun/payments - Webhook Handlers
|
|
6
|
+
* Utilities for handling payment webhooks
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Webhook event handler registry
|
|
11
|
+
*/
|
|
12
|
+
declare class WebhookHandlerRegistry {
|
|
13
|
+
private handlers;
|
|
14
|
+
/**
|
|
15
|
+
* Register a handler for a specific event type
|
|
16
|
+
*/
|
|
17
|
+
on<T = unknown>(type: WebhookEventType | "*", handler: WebhookHandler<T>): this;
|
|
18
|
+
/**
|
|
19
|
+
* Register handlers for checkout events
|
|
20
|
+
*/
|
|
21
|
+
onCheckout(handler: WebhookHandler): this;
|
|
22
|
+
/**
|
|
23
|
+
* Register handlers for subscription events
|
|
24
|
+
*/
|
|
25
|
+
onSubscription(handler: WebhookHandler): this;
|
|
26
|
+
/**
|
|
27
|
+
* Register handlers for payment events
|
|
28
|
+
*/
|
|
29
|
+
onPayment(handler: WebhookHandler): this;
|
|
30
|
+
/**
|
|
31
|
+
* Register handlers for invoice events
|
|
32
|
+
*/
|
|
33
|
+
onInvoice(handler: WebhookHandler): this;
|
|
34
|
+
/**
|
|
35
|
+
* Register handlers for customer events
|
|
36
|
+
*/
|
|
37
|
+
onCustomer(handler: WebhookHandler): this;
|
|
38
|
+
/**
|
|
39
|
+
* Get handlers for an event type
|
|
40
|
+
*/
|
|
41
|
+
getHandlers(type: WebhookEventType): WebhookHandler[];
|
|
42
|
+
/**
|
|
43
|
+
* Execute all handlers for an event
|
|
44
|
+
*/
|
|
45
|
+
handle(event: WebhookEvent): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Webhook processor for handling incoming webhooks
|
|
49
|
+
*/
|
|
50
|
+
declare class WebhookProcessor {
|
|
51
|
+
private provider;
|
|
52
|
+
private registry;
|
|
53
|
+
constructor(provider: PaymentProvider, registry?: WebhookHandlerRegistry);
|
|
54
|
+
/**
|
|
55
|
+
* Get the handler registry
|
|
56
|
+
*/
|
|
57
|
+
get handlers(): WebhookHandlerRegistry;
|
|
58
|
+
/**
|
|
59
|
+
* Process a webhook request
|
|
60
|
+
*/
|
|
61
|
+
process(request: Request): Promise<WebhookProcessResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Process raw webhook payload
|
|
64
|
+
*/
|
|
65
|
+
processRaw(payload: string | Uint8Array, signature: string): Promise<WebhookProcessResult>;
|
|
66
|
+
private getSignature;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Webhook process result
|
|
70
|
+
*/
|
|
71
|
+
interface WebhookProcessResult {
|
|
72
|
+
success: boolean;
|
|
73
|
+
event?: WebhookEvent | undefined;
|
|
74
|
+
error?: string | undefined;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create a webhook processor
|
|
78
|
+
*/
|
|
79
|
+
declare function createWebhookProcessor(provider: PaymentProvider, registry?: WebhookHandlerRegistry): WebhookProcessor;
|
|
80
|
+
/**
|
|
81
|
+
* Create a webhook handler registry
|
|
82
|
+
*/
|
|
83
|
+
declare function createWebhookHandlerRegistry(): WebhookHandlerRegistry;
|
|
84
|
+
/**
|
|
85
|
+
* Create a Hono/Express-compatible webhook handler
|
|
86
|
+
*/
|
|
87
|
+
declare function createWebhookHandler(provider: PaymentProvider, handlers: Partial<Record<WebhookEventType | "*", WebhookHandler>>): (request: Request) => Promise<Response>;
|
|
88
|
+
|
|
89
|
+
export { WebhookHandlerRegistry, type WebhookProcessResult, WebhookProcessor, createWebhookHandler, createWebhookHandlerRegistry, createWebhookProcessor };
|