@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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/dunning/dunning-sequence.ts","../../src/dunning/payment-retry.ts","../../src/dunning/dunning-manager.ts","../../src/dunning/dunning-scheduler.ts","../../src/dunning/memory-storage.ts","../../src/dunning/drizzle-storage.ts","../../src/dunning/schema.ts","../../src/dunning/email-templates.ts","../../src/dunning/email-integration.ts"],"sourcesContent":["/**\n * @parsrun/payments - Dunning Sequence\n * Default sequences and step builders for dunning automation\n */\n\nimport type {\n DunningSequence,\n DunningStep,\n DunningAction,\n NotificationChannel,\n DunningContext,\n} from \"./types.js\";\n\n// ============================================================================\n// Step Builder\n// ============================================================================\n\n/**\n * Fluent builder for dunning steps\n */\nexport class DunningStepBuilder {\n private step: Partial<DunningStep> = {\n actions: [],\n notificationChannels: [],\n };\n\n constructor(id: string, name: string) {\n this.step.id = id;\n this.step.name = name;\n }\n\n /**\n * Set days after initial failure\n */\n afterDays(days: number): this {\n this.step.daysAfterFailure = days;\n return this;\n }\n\n /**\n * Set hours offset within the day\n */\n atHour(hour: number): this {\n this.step.hoursOffset = hour;\n return this;\n }\n\n /**\n * Add actions to take\n */\n withActions(...actions: DunningAction[]): this {\n this.step.actions = actions;\n return this;\n }\n\n /**\n * Add notification channels\n */\n notify(...channels: NotificationChannel[]): this {\n this.step.notificationChannels = channels;\n return this;\n }\n\n /**\n * Set notification template\n */\n withTemplate(templateId: string): this {\n this.step.notificationTemplateId = templateId;\n return this;\n }\n\n /**\n * Enable payment retry in this step\n */\n retryPayment(retry = true): this {\n this.step.retryPayment = retry;\n if (retry && !this.step.actions?.includes(\"retry_payment\")) {\n this.step.actions = [...(this.step.actions || []), \"retry_payment\"];\n }\n return this;\n }\n\n /**\n * Set access level for this step\n */\n setAccessLevel(level: \"full\" | \"limited\" | \"read_only\" | \"none\"): this {\n this.step.accessLevel = level;\n if (!this.step.actions?.includes(\"limit_features\")) {\n this.step.actions = [...(this.step.actions || []), \"limit_features\"];\n }\n return this;\n }\n\n /**\n * Mark this as the final step\n */\n final(isFinal = true): this {\n this.step.isFinal = isFinal;\n return this;\n }\n\n /**\n * Add custom action handler\n */\n withCustomAction(handler: (context: DunningContext) => Promise<void>): this {\n this.step.customAction = handler;\n if (!this.step.actions?.includes(\"custom\")) {\n this.step.actions = [...(this.step.actions || []), \"custom\"];\n }\n return this;\n }\n\n /**\n * Add condition for this step\n */\n when(condition: (context: DunningContext) => boolean | Promise<boolean>): this {\n this.step.condition = condition;\n return this;\n }\n\n /**\n * Add metadata\n */\n withMetadata(metadata: Record<string, unknown>): this {\n this.step.metadata = metadata;\n return this;\n }\n\n /**\n * Build the step\n */\n build(): DunningStep {\n if (!this.step.id || !this.step.name || this.step.daysAfterFailure === undefined) {\n throw new Error(\"DunningStep requires id, name, and daysAfterFailure\");\n }\n\n const result: DunningStep = {\n id: this.step.id,\n name: this.step.name,\n daysAfterFailure: this.step.daysAfterFailure,\n actions: this.step.actions || [],\n };\n\n // Only add optional properties if they have values\n if (this.step.hoursOffset !== undefined) result.hoursOffset = this.step.hoursOffset;\n if (this.step.notificationChannels !== undefined)\n result.notificationChannels = this.step.notificationChannels;\n if (this.step.notificationTemplateId !== undefined)\n result.notificationTemplateId = this.step.notificationTemplateId;\n if (this.step.retryPayment !== undefined) result.retryPayment = this.step.retryPayment;\n if (this.step.accessLevel !== undefined) result.accessLevel = this.step.accessLevel;\n if (this.step.isFinal !== undefined) result.isFinal = this.step.isFinal;\n if (this.step.customAction !== undefined) result.customAction = this.step.customAction;\n if (this.step.condition !== undefined) result.condition = this.step.condition;\n if (this.step.metadata !== undefined) result.metadata = this.step.metadata;\n\n return result;\n }\n}\n\n/**\n * Create a new dunning step builder\n */\nexport function step(id: string, name: string): DunningStepBuilder {\n return new DunningStepBuilder(id, name);\n}\n\n// ============================================================================\n// Sequence Builder\n// ============================================================================\n\n/**\n * Fluent builder for dunning sequences\n */\nexport class DunningSequenceBuilder {\n private sequence: Partial<DunningSequence> = {\n steps: [],\n isActive: true,\n };\n\n constructor(id: string, name: string) {\n this.sequence.id = id;\n this.sequence.name = name;\n }\n\n /**\n * Set description\n */\n describe(description: string): this {\n this.sequence.description = description;\n return this;\n }\n\n /**\n * Add steps\n */\n withSteps(...steps: DunningStep[]): this {\n this.sequence.steps = steps;\n return this;\n }\n\n /**\n * Set maximum duration before auto-cancel\n */\n maxDays(days: number): this {\n this.sequence.maxDurationDays = days;\n return this;\n }\n\n /**\n * Set active status\n */\n active(isActive = true): this {\n this.sequence.isActive = isActive;\n return this;\n }\n\n /**\n * Add metadata\n */\n withMetadata(metadata: Record<string, unknown>): this {\n this.sequence.metadata = metadata;\n return this;\n }\n\n /**\n * Build the sequence\n */\n build(): DunningSequence {\n if (!this.sequence.id || !this.sequence.name || !this.sequence.maxDurationDays) {\n throw new Error(\"DunningSequence requires id, name, and maxDurationDays\");\n }\n\n // Sort steps by daysAfterFailure\n const sortedSteps = [...(this.sequence.steps || [])].sort(\n (a, b) => a.daysAfterFailure - b.daysAfterFailure\n );\n\n const result: DunningSequence = {\n id: this.sequence.id,\n name: this.sequence.name,\n steps: sortedSteps,\n maxDurationDays: this.sequence.maxDurationDays,\n isActive: this.sequence.isActive ?? true,\n };\n\n // Only add optional properties if they have values\n if (this.sequence.description !== undefined) result.description = this.sequence.description;\n if (this.sequence.metadata !== undefined) result.metadata = this.sequence.metadata;\n\n return result;\n }\n}\n\n/**\n * Create a new dunning sequence builder\n */\nexport function sequence(id: string, name: string): DunningSequenceBuilder {\n return new DunningSequenceBuilder(id, name);\n}\n\n// ============================================================================\n// Default Sequences\n// ============================================================================\n\n/**\n * Standard SaaS dunning sequence (28 days)\n *\n * Day 0: Immediate retry + email notification\n * Day 1: Retry + email reminder\n * Day 3: Retry + email warning\n * Day 7: Retry + email + in-app notification, limit features\n * Day 14: Retry + email, suspend account\n * Day 21: Final warning email\n * Day 28: Cancel subscription\n */\nexport const standardSaasSequence: DunningSequence = sequence(\"standard-saas\", \"Standard SaaS Dunning\")\n .describe(\"Standard 28-day dunning sequence for SaaS applications\")\n .maxDays(28)\n .withSteps(\n step(\"immediate-retry\", \"Immediate Retry\")\n .afterDays(0)\n .withActions(\"retry_payment\", \"notify\")\n .notify(\"email\")\n .withTemplate(\"dunning-payment-failed\")\n .retryPayment()\n .build(),\n\n step(\"day-1-reminder\", \"Day 1 Reminder\")\n .afterDays(1)\n .atHour(10) // 10 AM\n .withActions(\"retry_payment\", \"notify\")\n .notify(\"email\")\n .withTemplate(\"dunning-reminder\")\n .retryPayment()\n .build(),\n\n step(\"day-3-warning\", \"Day 3 Warning\")\n .afterDays(3)\n .atHour(10)\n .withActions(\"retry_payment\", \"notify\")\n .notify(\"email\")\n .withTemplate(\"dunning-warning\")\n .retryPayment()\n .build(),\n\n step(\"day-7-limit\", \"Day 7 Feature Limit\")\n .afterDays(7)\n .atHour(10)\n .withActions(\"retry_payment\", \"notify\", \"limit_features\")\n .notify(\"email\", \"in_app\")\n .withTemplate(\"dunning-feature-limit\")\n .retryPayment()\n .setAccessLevel(\"limited\")\n .build(),\n\n step(\"day-14-suspend\", \"Day 14 Suspension\")\n .afterDays(14)\n .atHour(10)\n .withActions(\"retry_payment\", \"notify\", \"suspend\")\n .notify(\"email\", \"in_app\")\n .withTemplate(\"dunning-suspension\")\n .retryPayment()\n .setAccessLevel(\"read_only\")\n .build(),\n\n step(\"day-21-final-warning\", \"Day 21 Final Warning\")\n .afterDays(21)\n .atHour(10)\n .withActions(\"notify\")\n .notify(\"email\")\n .withTemplate(\"dunning-final-warning\")\n .build(),\n\n step(\"day-28-cancel\", \"Day 28 Cancellation\")\n .afterDays(28)\n .atHour(10)\n .withActions(\"notify\", \"cancel\")\n .notify(\"email\")\n .withTemplate(\"dunning-canceled\")\n .final()\n .setAccessLevel(\"none\")\n .build()\n )\n .build();\n\n/**\n * Aggressive dunning sequence (14 days)\n * For lower-tier plans or high-risk customers\n */\nexport const aggressiveSequence: DunningSequence = sequence(\"aggressive\", \"Aggressive Dunning\")\n .describe(\"Aggressive 14-day dunning sequence\")\n .maxDays(14)\n .withSteps(\n step(\"immediate\", \"Immediate Retry\")\n .afterDays(0)\n .withActions(\"retry_payment\", \"notify\")\n .notify(\"email\")\n .withTemplate(\"dunning-payment-failed\")\n .retryPayment()\n .build(),\n\n step(\"day-1\", \"Day 1\")\n .afterDays(1)\n .withActions(\"retry_payment\", \"notify\")\n .notify(\"email\", \"sms\")\n .withTemplate(\"dunning-urgent\")\n .retryPayment()\n .build(),\n\n step(\"day-3-limit\", \"Day 3 Limit\")\n .afterDays(3)\n .withActions(\"retry_payment\", \"notify\", \"limit_features\")\n .notify(\"email\", \"in_app\")\n .withTemplate(\"dunning-feature-limit\")\n .retryPayment()\n .setAccessLevel(\"limited\")\n .build(),\n\n step(\"day-7-suspend\", \"Day 7 Suspend\")\n .afterDays(7)\n .withActions(\"retry_payment\", \"notify\", \"suspend\")\n .notify(\"email\", \"sms\", \"in_app\")\n .withTemplate(\"dunning-suspension\")\n .retryPayment()\n .setAccessLevel(\"read_only\")\n .build(),\n\n step(\"day-14-cancel\", \"Day 14 Cancel\")\n .afterDays(14)\n .withActions(\"notify\", \"cancel\")\n .notify(\"email\")\n .withTemplate(\"dunning-canceled\")\n .final()\n .setAccessLevel(\"none\")\n .build()\n )\n .build();\n\n/**\n * Lenient dunning sequence (45 days)\n * For enterprise or high-value customers\n */\nexport const lenientSequence: DunningSequence = sequence(\"lenient\", \"Lenient Dunning\")\n .describe(\"Lenient 45-day dunning sequence for enterprise customers\")\n .maxDays(45)\n .withSteps(\n step(\"immediate\", \"Immediate Retry\")\n .afterDays(0)\n .withActions(\"retry_payment\", \"notify\")\n .notify(\"email\")\n .withTemplate(\"dunning-payment-failed-enterprise\")\n .retryPayment()\n .build(),\n\n step(\"day-3\", \"Day 3 Reminder\")\n .afterDays(3)\n .withActions(\"retry_payment\", \"notify\")\n .notify(\"email\")\n .withTemplate(\"dunning-reminder-enterprise\")\n .retryPayment()\n .build(),\n\n step(\"day-7\", \"Day 7 Reminder\")\n .afterDays(7)\n .withActions(\"retry_payment\", \"notify\")\n .notify(\"email\")\n .withTemplate(\"dunning-reminder-enterprise\")\n .retryPayment()\n .build(),\n\n step(\"day-14\", \"Day 14 Warning\")\n .afterDays(14)\n .withActions(\"retry_payment\", \"notify\")\n .notify(\"email\", \"in_app\")\n .withTemplate(\"dunning-warning-enterprise\")\n .retryPayment()\n .build(),\n\n step(\"day-21-limit\", \"Day 21 Feature Limit\")\n .afterDays(21)\n .withActions(\"retry_payment\", \"notify\", \"limit_features\")\n .notify(\"email\", \"in_app\")\n .withTemplate(\"dunning-feature-limit-enterprise\")\n .retryPayment()\n .setAccessLevel(\"limited\")\n .build(),\n\n step(\"day-30-suspend\", \"Day 30 Suspension\")\n .afterDays(30)\n .withActions(\"retry_payment\", \"notify\", \"suspend\")\n .notify(\"email\", \"in_app\")\n .withTemplate(\"dunning-suspension-enterprise\")\n .retryPayment()\n .setAccessLevel(\"read_only\")\n .build(),\n\n step(\"day-40-final\", \"Day 40 Final Warning\")\n .afterDays(40)\n .withActions(\"notify\")\n .notify(\"email\")\n .withTemplate(\"dunning-final-warning-enterprise\")\n .build(),\n\n step(\"day-45-cancel\", \"Day 45 Cancel\")\n .afterDays(45)\n .withActions(\"notify\", \"cancel\")\n .notify(\"email\")\n .withTemplate(\"dunning-canceled-enterprise\")\n .final()\n .setAccessLevel(\"none\")\n .build()\n )\n .build();\n\n/**\n * Minimal dunning sequence (7 days)\n * For free-to-paid conversions or trials\n */\nexport const minimalSequence: DunningSequence = sequence(\"minimal\", \"Minimal Dunning\")\n .describe(\"Minimal 7-day dunning sequence\")\n .maxDays(7)\n .withSteps(\n step(\"immediate\", \"Immediate Retry\")\n .afterDays(0)\n .withActions(\"retry_payment\", \"notify\")\n .notify(\"email\")\n .withTemplate(\"dunning-payment-failed\")\n .retryPayment()\n .build(),\n\n step(\"day-3\", \"Day 3\")\n .afterDays(3)\n .withActions(\"retry_payment\", \"notify\")\n .notify(\"email\")\n .withTemplate(\"dunning-reminder\")\n .retryPayment()\n .build(),\n\n step(\"day-7-cancel\", \"Day 7 Cancel\")\n .afterDays(7)\n .withActions(\"notify\", \"cancel\")\n .notify(\"email\")\n .withTemplate(\"dunning-canceled\")\n .final()\n .setAccessLevel(\"none\")\n .build()\n )\n .build();\n\n// ============================================================================\n// Sequence Registry\n// ============================================================================\n\n/**\n * Default sequences by tier\n */\nexport const defaultSequences: Record<string, DunningSequence> = {\n standard: standardSaasSequence,\n aggressive: aggressiveSequence,\n lenient: lenientSequence,\n minimal: minimalSequence,\n};\n\n/**\n * Get sequence by tier level\n *\n * @param tier - Plan tier (0=free, 1=starter, 2=pro, 3=enterprise)\n * @returns Appropriate dunning sequence\n */\nexport function getSequenceByTier(tier: number): DunningSequence {\n if (tier >= 3) return lenientSequence; // Enterprise\n if (tier >= 2) return standardSaasSequence; // Pro\n if (tier >= 1) return aggressiveSequence; // Starter\n return minimalSequence; // Free/Trial\n}\n","/**\n * @parsrun/payments - Payment Retry Strategy\n * Smart retry logic based on payment failure codes\n */\n\nimport type {\n PaymentFailure,\n PaymentFailureCategory,\n RetryStrategy,\n RetryResult,\n DunningContext,\n DunningLogger,\n} from \"./types.js\";\n\n// ============================================================================\n// Error Code Mapping\n// ============================================================================\n\n/**\n * Map provider error codes to failure categories\n */\nexport interface ErrorCodeMapping {\n provider: string;\n codes: Record<string, PaymentFailureCategory>;\n}\n\n/**\n * Stripe error code mappings\n */\nexport const stripeErrorCodes: ErrorCodeMapping = {\n provider: \"stripe\",\n codes: {\n // Card declined\n card_declined: \"card_declined\",\n generic_decline: \"card_declined\",\n do_not_honor: \"card_declined\",\n transaction_not_allowed: \"card_declined\",\n\n // Insufficient funds\n insufficient_funds: \"insufficient_funds\",\n\n // Card expired/invalid\n expired_card: \"card_expired\",\n invalid_expiry_month: \"card_expired\",\n invalid_expiry_year: \"card_expired\",\n invalid_number: \"invalid_card\",\n invalid_cvc: \"invalid_card\",\n incorrect_number: \"invalid_card\",\n incorrect_cvc: \"invalid_card\",\n\n // Processing errors (retry immediately)\n processing_error: \"processing_error\",\n try_again_later: \"processing_error\",\n bank_not_supported: \"processing_error\",\n\n // Authentication required\n authentication_required: \"authentication_required\",\n card_not_supported: \"authentication_required\",\n\n // Fraud\n fraudulent: \"fraud_suspected\",\n merchant_blacklist: \"fraud_suspected\",\n stolen_card: \"fraud_suspected\",\n lost_card: \"fraud_suspected\",\n\n // Rate limits\n rate_limit: \"velocity_exceeded\",\n },\n};\n\n/**\n * Paddle error code mappings\n */\nexport const paddleErrorCodes: ErrorCodeMapping = {\n provider: \"paddle\",\n codes: {\n declined: \"card_declined\",\n insufficient_funds: \"insufficient_funds\",\n card_expired: \"card_expired\",\n invalid_card: \"invalid_card\",\n processing_error: \"processing_error\",\n authentication_required: \"authentication_required\",\n fraud: \"fraud_suspected\",\n },\n};\n\n/**\n * iyzico error code mappings\n */\nexport const iyzicoErrorCodes: ErrorCodeMapping = {\n provider: \"iyzico\",\n codes: {\n // Turkish bank error codes\n \"10051\": \"insufficient_funds\", // Yetersiz bakiye\n \"10054\": \"card_expired\", // Süresi dolmuş kart\n \"10057\": \"card_declined\", // İşlem onaylanmadı\n \"10005\": \"invalid_card\", // Geçersiz kart\n \"10012\": \"invalid_card\", // Geçersiz işlem\n \"10041\": \"fraud_suspected\", // Kayıp kart\n \"10043\": \"fraud_suspected\", // Çalıntı kart\n \"10058\": \"card_declined\", // Terminal işlem yapma yetkisi yok\n \"10034\": \"fraud_suspected\", // Dolandırıcılık şüphesi\n },\n};\n\n/**\n * All provider mappings\n */\nexport const allErrorCodeMappings: ErrorCodeMapping[] = [\n stripeErrorCodes,\n paddleErrorCodes,\n iyzicoErrorCodes,\n];\n\n// ============================================================================\n// Default Retry Strategies\n// ============================================================================\n\n/**\n * Default retry strategies by failure category\n */\nexport const defaultRetryStrategies: RetryStrategy[] = [\n {\n category: \"card_declined\",\n shouldRetry: true,\n initialDelayHours: 24, // Wait a day\n maxRetries: 4,\n backoffMultiplier: 2,\n maxDelayHours: 168, // 1 week max\n optimalRetryHours: [10, 14, 18], // Business hours\n optimalRetryDays: [1, 2, 3, 4, 5], // Weekdays\n },\n {\n category: \"insufficient_funds\",\n shouldRetry: true,\n initialDelayHours: 72, // Wait until likely payday\n maxRetries: 4,\n backoffMultiplier: 1.5,\n maxDelayHours: 168,\n // Optimal times: end of month, mid-month (paydays)\n optimalRetryDays: [0, 1, 15, 16, 28, 29, 30, 31].map((d) => d % 7), // Around paydays\n },\n {\n category: \"card_expired\",\n shouldRetry: false, // Don't retry - needs card update\n initialDelayHours: 0,\n maxRetries: 0,\n backoffMultiplier: 1,\n maxDelayHours: 0,\n },\n {\n category: \"invalid_card\",\n shouldRetry: false, // Don't retry - needs card update\n initialDelayHours: 0,\n maxRetries: 0,\n backoffMultiplier: 1,\n maxDelayHours: 0,\n },\n {\n category: \"processing_error\",\n shouldRetry: true,\n initialDelayHours: 1, // Retry soon\n maxRetries: 5,\n backoffMultiplier: 2,\n maxDelayHours: 24,\n },\n {\n category: \"authentication_required\",\n shouldRetry: false, // Needs customer action (3DS)\n initialDelayHours: 0,\n maxRetries: 0,\n backoffMultiplier: 1,\n maxDelayHours: 0,\n },\n {\n category: \"fraud_suspected\",\n shouldRetry: false, // Never retry fraud\n initialDelayHours: 0,\n maxRetries: 0,\n backoffMultiplier: 1,\n maxDelayHours: 0,\n },\n {\n category: \"velocity_exceeded\",\n shouldRetry: true,\n initialDelayHours: 6, // Wait for rate limit reset\n maxRetries: 3,\n backoffMultiplier: 2,\n maxDelayHours: 48,\n },\n {\n category: \"unknown\",\n shouldRetry: true, // Cautious retry\n initialDelayHours: 24,\n maxRetries: 2,\n backoffMultiplier: 2,\n maxDelayHours: 72,\n },\n];\n\n// ============================================================================\n// Retry Calculator\n// ============================================================================\n\n/**\n * Payment retry calculator\n * Determines optimal retry timing based on failure category\n */\nexport class PaymentRetryCalculator {\n private strategies: Map<PaymentFailureCategory, RetryStrategy>;\n private errorMappings: Map<string, Map<string, PaymentFailureCategory>>;\n private logger?: DunningLogger;\n\n constructor(\n strategies: RetryStrategy[] = defaultRetryStrategies,\n errorMappings: ErrorCodeMapping[] = allErrorCodeMappings,\n logger?: DunningLogger\n ) {\n this.strategies = new Map(strategies.map((s) => [s.category, s]));\n this.errorMappings = new Map();\n if (logger) {\n this.logger = logger;\n }\n\n // Build error code lookup\n for (const mapping of errorMappings) {\n this.errorMappings.set(mapping.provider, new Map(Object.entries(mapping.codes)));\n }\n }\n\n /**\n * Map error code to failure category\n */\n categorizeError(provider: string, errorCode: string): PaymentFailureCategory {\n const providerMapping = this.errorMappings.get(provider.toLowerCase());\n if (providerMapping) {\n const category = providerMapping.get(errorCode.toLowerCase());\n if (category) return category;\n }\n return \"unknown\";\n }\n\n /**\n * Get retry strategy for failure category\n */\n getStrategy(category: PaymentFailureCategory): RetryStrategy {\n return (\n this.strategies.get(category) ?? {\n category: \"unknown\",\n shouldRetry: true,\n initialDelayHours: 24,\n maxRetries: 2,\n backoffMultiplier: 2,\n maxDelayHours: 72,\n }\n );\n }\n\n /**\n * Check if a failure should be retried\n */\n shouldRetry(failure: PaymentFailure): boolean {\n const strategy = this.getStrategy(failure.category);\n\n // Check if strategy allows retry\n if (!strategy.shouldRetry) {\n this.logger?.debug(\"Retry not allowed for category\", {\n category: failure.category,\n failureId: failure.id,\n });\n return false;\n }\n\n // Check retry count\n if (failure.retryCount >= strategy.maxRetries) {\n this.logger?.debug(\"Max retries reached\", {\n category: failure.category,\n retryCount: failure.retryCount,\n maxRetries: strategy.maxRetries,\n });\n return false;\n }\n\n return true;\n }\n\n /**\n * Calculate next retry time\n */\n calculateNextRetry(failure: PaymentFailure): Date | null {\n if (!this.shouldRetry(failure)) {\n return null;\n }\n\n const strategy = this.getStrategy(failure.category);\n\n // Calculate delay with exponential backoff\n const baseDelay = strategy.initialDelayHours;\n const multiplier = Math.pow(strategy.backoffMultiplier, failure.retryCount);\n let delayHours = Math.min(baseDelay * multiplier, strategy.maxDelayHours);\n\n // Calculate base retry time\n let retryTime = new Date(failure.failedAt.getTime() + delayHours * 60 * 60 * 1000);\n\n // Optimize for best retry time\n retryTime = this.optimizeRetryTime(retryTime, strategy);\n\n this.logger?.debug(\"Calculated next retry time\", {\n failureId: failure.id,\n category: failure.category,\n retryCount: failure.retryCount,\n delayHours,\n nextRetry: retryTime.toISOString(),\n });\n\n return retryTime;\n }\n\n /**\n * Optimize retry time based on strategy\n */\n private optimizeRetryTime(baseTime: Date, strategy: RetryStrategy): Date {\n const optimalHours = strategy.optimalRetryHours;\n const optimalDays = strategy.optimalRetryDays;\n\n if (!optimalHours?.length && !optimalDays?.length) {\n return baseTime;\n }\n\n let optimizedTime = new Date(baseTime);\n\n // Adjust to optimal hour if specified\n if (optimalHours?.length) {\n const currentHour = optimizedTime.getHours();\n const nearestOptimalHour = this.findNearestValue(currentHour, optimalHours);\n\n if (nearestOptimalHour !== currentHour) {\n optimizedTime.setHours(nearestOptimalHour, 0, 0, 0);\n\n // If we moved to an earlier hour, add a day\n if (nearestOptimalHour < currentHour) {\n optimizedTime.setDate(optimizedTime.getDate() + 1);\n }\n }\n }\n\n // Adjust to optimal day if specified\n if (optimalDays?.length) {\n const currentDay = optimizedTime.getDay();\n const nearestOptimalDay = this.findNearestValue(currentDay, optimalDays);\n\n if (nearestOptimalDay !== currentDay) {\n const daysToAdd = (nearestOptimalDay - currentDay + 7) % 7;\n optimizedTime.setDate(optimizedTime.getDate() + (daysToAdd || 7));\n }\n }\n\n // Don't move earlier than base time\n if (optimizedTime < baseTime) {\n return baseTime;\n }\n\n return optimizedTime;\n }\n\n /**\n * Find nearest value in array\n */\n private findNearestValue(current: number, values: number[]): number {\n const firstValue = values[0];\n if (firstValue === undefined) {\n return current;\n }\n\n let nearest = firstValue;\n let minDiff = Math.abs(current - nearest);\n\n for (const value of values) {\n const diff = Math.abs(current - value);\n if (diff < minDiff) {\n minDiff = diff;\n nearest = value;\n }\n }\n\n return nearest;\n }\n\n /**\n * Check if failure is recoverable (can be retried eventually)\n */\n isRecoverable(category: PaymentFailureCategory): boolean {\n const strategy = this.getStrategy(category);\n return strategy.shouldRetry;\n }\n\n /**\n * Get recommendation message for failure category\n */\n getRecommendation(category: PaymentFailureCategory): string {\n switch (category) {\n case \"card_declined\":\n return \"The payment was declined. We'll retry automatically.\";\n case \"insufficient_funds\":\n return \"There were insufficient funds. We'll retry around payday.\";\n case \"card_expired\":\n return \"Your card has expired. Please update your payment method.\";\n case \"invalid_card\":\n return \"The card information is invalid. Please update your payment method.\";\n case \"processing_error\":\n return \"A temporary processing error occurred. We'll retry shortly.\";\n case \"authentication_required\":\n return \"Additional authentication is required. Please complete the payment manually.\";\n case \"fraud_suspected\":\n return \"The payment was flagged. Please contact your bank or use a different card.\";\n case \"velocity_exceeded\":\n return \"Too many payment attempts. We'll retry later.\";\n default:\n return \"An error occurred. We'll retry the payment.\";\n }\n }\n}\n\n// ============================================================================\n// Payment Retrier\n// ============================================================================\n\n/**\n * Payment retry handler configuration\n */\nexport interface PaymentRetrierConfig {\n /** Retry calculator */\n calculator?: PaymentRetryCalculator;\n\n /** Function to actually retry the payment */\n retryPayment: (context: DunningContext) => Promise<RetryResult>;\n\n /** Logger */\n logger?: DunningLogger;\n\n /** Maximum retries per dunning session */\n maxSessionRetries?: number;\n}\n\n/**\n * Payment retrier\n * Handles intelligent payment retry logic\n */\nexport class PaymentRetrier {\n private calculator: PaymentRetryCalculator;\n private retryPayment: (context: DunningContext) => Promise<RetryResult>;\n private logger?: DunningLogger;\n private maxSessionRetries: number;\n\n constructor(config: PaymentRetrierConfig) {\n this.calculator =\n config.calculator ?? new PaymentRetryCalculator(undefined, undefined, config.logger);\n this.retryPayment = config.retryPayment;\n if (config.logger) {\n this.logger = config.logger;\n }\n this.maxSessionRetries = config.maxSessionRetries ?? 10;\n }\n\n /**\n * Attempt to retry a payment\n */\n async retry(context: DunningContext): Promise<RetryResult> {\n const failure = context.latestFailure;\n\n // Check if we should retry\n if (!this.calculator.shouldRetry(failure)) {\n this.logger?.info(\"Payment retry skipped - not recoverable\", {\n failureId: failure.id,\n category: failure.category,\n reason: this.calculator.getRecommendation(failure.category),\n });\n\n return {\n success: false,\n failure,\n attemptedAt: new Date(),\n };\n }\n\n // Check session retry limit\n if (context.state.totalRetryAttempts >= this.maxSessionRetries) {\n this.logger?.warn(\"Payment retry skipped - session limit reached\", {\n customerId: context.customer.id,\n totalAttempts: context.state.totalRetryAttempts,\n maxAttempts: this.maxSessionRetries,\n });\n\n return {\n success: false,\n failure,\n attemptedAt: new Date(),\n };\n }\n\n // Attempt retry\n this.logger?.info(\"Attempting payment retry\", {\n failureId: failure.id,\n customerId: context.customer.id,\n amount: failure.amount,\n retryCount: failure.retryCount,\n });\n\n try {\n const result = await this.retryPayment(context);\n\n if (result.success) {\n this.logger?.info(\"Payment retry successful\", {\n failureId: failure.id,\n transactionId: result.transactionId,\n });\n } else {\n this.logger?.info(\"Payment retry failed\", {\n failureId: failure.id,\n newFailure: result.failure,\n });\n }\n\n return result;\n } catch (error) {\n this.logger?.error(\"Payment retry error\", {\n failureId: failure.id,\n error: error instanceof Error ? error.message : String(error),\n });\n\n return {\n success: false,\n failure,\n attemptedAt: new Date(),\n };\n }\n }\n\n /**\n * Get next retry time for a failure\n */\n getNextRetryTime(failure: PaymentFailure): Date | null {\n return this.calculator.calculateNextRetry(failure);\n }\n\n /**\n * Check if failure is recoverable\n */\n isRecoverable(failure: PaymentFailure): boolean {\n return this.calculator.isRecoverable(failure.category);\n }\n\n /**\n * Categorize an error code\n */\n categorizeError(provider: string, errorCode: string): PaymentFailureCategory {\n return this.calculator.categorizeError(provider, errorCode);\n }\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create a payment retry calculator\n */\nexport function createPaymentRetryCalculator(\n strategies?: RetryStrategy[],\n errorMappings?: ErrorCodeMapping[],\n logger?: DunningLogger\n): PaymentRetryCalculator {\n return new PaymentRetryCalculator(strategies, errorMappings, logger);\n}\n\n/**\n * Create a payment retrier\n */\nexport function createPaymentRetrier(config: PaymentRetrierConfig): PaymentRetrier {\n return new PaymentRetrier(config);\n}\n","/**\n * @parsrun/payments - Dunning Manager\n * Main orchestrator for dunning state management and step execution\n */\n\nimport type {\n DunningManagerConfig,\n DunningSequence,\n DunningStep,\n DunningState,\n DunningStatus,\n DunningContext,\n DunningEvent,\n DunningEventHandler,\n PaymentFailure,\n ExecutedStep,\n DunningNotification,\n NotificationResult,\n DunningStorage,\n DunningLogger,\n} from \"./types.js\";\nimport { standardSaasSequence } from \"./dunning-sequence.js\";\n\n// ============================================================================\n// Dunning Manager\n// ============================================================================\n\n/**\n * Dunning Manager\n * Orchestrates the dunning process including state management and step execution\n */\nexport class DunningManager {\n private config: DunningManagerConfig;\n private storage: DunningStorage;\n private eventHandlers: DunningEventHandler[] = [];\n private logger?: DunningLogger;\n\n constructor(config: DunningManagerConfig, storage: DunningStorage) {\n this.config = config;\n this.storage = storage;\n if (config.logger) {\n this.logger = config.logger;\n }\n\n // Register event handler from config\n if (config.onEvent) {\n this.eventHandlers.push(config.onEvent);\n }\n }\n\n // ============================================================================\n // Dunning Lifecycle\n // ============================================================================\n\n /**\n * Start dunning process for a payment failure\n */\n async startDunning(failure: PaymentFailure): Promise<DunningState> {\n // Check if dunning already exists\n const existingState = await this.storage.getDunningState(failure.customerId);\n if (existingState && existingState.status === \"active\") {\n this.logger?.info(\"Dunning already active, adding failure\", {\n customerId: failure.customerId,\n dunningId: existingState.id,\n });\n\n // Add new failure to existing state\n existingState.failures.push(failure);\n await this.storage.updateDunningState(existingState.id, {\n failures: existingState.failures,\n });\n\n return existingState;\n }\n\n // Get appropriate sequence\n const sequence = await this.getSequenceForCustomer(failure.customerId);\n const firstStep = sequence.steps[0];\n\n if (!firstStep) {\n throw new Error(`Dunning sequence ${sequence.id} has no steps`);\n }\n\n // Create new dunning state\n const state: DunningState = {\n id: this.generateId(),\n customerId: failure.customerId,\n subscriptionId: failure.subscriptionId,\n sequenceId: sequence.id,\n currentStepIndex: 0,\n currentStepId: firstStep.id,\n status: \"active\",\n initialFailure: failure,\n failures: [failure],\n executedSteps: [],\n startedAt: new Date(),\n nextStepAt: this.calculateStepTime(firstStep, failure.failedAt),\n totalRetryAttempts: 0,\n };\n\n // Save state\n await this.storage.saveDunningState(state);\n\n // Record failure\n await this.storage.recordPaymentFailure(failure);\n\n // Emit event\n await this.emitEvent({\n type: \"dunning.started\",\n customerId: state.customerId,\n subscriptionId: state.subscriptionId,\n dunningStateId: state.id,\n timestamp: new Date(),\n data: {\n sequenceId: sequence.id,\n initialFailure: failure,\n },\n });\n\n this.logger?.info(\"Dunning started\", {\n customerId: state.customerId,\n dunningId: state.id,\n sequenceId: sequence.id,\n });\n\n return state;\n }\n\n /**\n * Execute the next step in dunning sequence\n */\n async executeStep(stateId: string): Promise<ExecutedStep | null> {\n const state = await this.getDunningStateById(stateId);\n if (!state || state.status !== \"active\") {\n this.logger?.warn(\"Cannot execute step - invalid state\", {\n stateId,\n status: state?.status,\n });\n return null;\n }\n\n const sequence = this.getSequence(state.sequenceId);\n const step = sequence.steps[state.currentStepIndex];\n\n if (!step) {\n this.logger?.warn(\"No step found at index\", {\n stateId,\n stepIndex: state.currentStepIndex,\n });\n return null;\n }\n\n // Build context\n const context = await this.buildContext(state, step);\n\n // Check step condition\n if (step.condition) {\n const shouldExecute = await step.condition(context);\n if (!shouldExecute) {\n this.logger?.info(\"Step condition not met, skipping\", {\n stateId,\n stepId: step.id,\n });\n return this.advanceToNextStep(state);\n }\n }\n\n // Execute step\n const executedStep = await this.performStepActions(context, step);\n\n // Update state\n state.executedSteps.push(executedStep);\n state.lastStepAt = executedStep.executedAt;\n state.totalRetryAttempts += executedStep.paymentRetried ? 1 : 0;\n\n // Check if payment recovered\n if (executedStep.paymentSucceeded) {\n await this.recoverDunning(state, \"payment_recovered\");\n return executedStep;\n }\n\n // Check if final step\n if (step.isFinal) {\n await this.exhaustDunning(state);\n return executedStep;\n }\n\n // Advance to next step\n await this.advanceToNextStep(state);\n\n return executedStep;\n }\n\n /**\n * Recover from dunning (payment successful)\n */\n async recoverDunning(\n stateOrId: DunningState | string,\n reason: \"payment_recovered\" = \"payment_recovered\"\n ): Promise<void> {\n const state =\n typeof stateOrId === \"string\" ? await this.getDunningStateById(stateOrId) : stateOrId;\n\n if (!state) return;\n\n state.status = \"recovered\";\n state.endedAt = new Date();\n state.endReason = reason;\n\n await this.storage.updateDunningState(state.id, {\n status: state.status,\n endedAt: state.endedAt,\n endReason: state.endReason,\n });\n\n // Restore full access\n if (this.config.onAccessUpdate) {\n await this.config.onAccessUpdate(state.customerId, \"full\");\n }\n\n await this.emitEvent({\n type: \"dunning.payment_recovered\",\n customerId: state.customerId,\n subscriptionId: state.subscriptionId,\n dunningStateId: state.id,\n timestamp: new Date(),\n data: { reason },\n });\n\n this.logger?.info(\"Dunning recovered\", {\n dunningId: state.id,\n customerId: state.customerId,\n });\n }\n\n /**\n * Pause dunning process\n */\n async pauseDunning(stateId: string): Promise<void> {\n await this.storage.updateDunningState(stateId, {\n status: \"paused\",\n });\n\n const state = await this.getDunningStateById(stateId);\n if (state) {\n await this.emitEvent({\n type: \"dunning.paused\",\n customerId: state.customerId,\n subscriptionId: state.subscriptionId,\n dunningStateId: state.id,\n timestamp: new Date(),\n data: {},\n });\n }\n }\n\n /**\n * Resume paused dunning\n */\n async resumeDunning(stateId: string): Promise<void> {\n const state = await this.getDunningStateById(stateId);\n if (!state || state.status !== \"paused\") return;\n\n const sequence = this.getSequence(state.sequenceId);\n const step = sequence.steps[state.currentStepIndex];\n\n const updates: Partial<DunningState> = { status: \"active\" };\n if (step) {\n updates.nextStepAt = this.calculateStepTime(step, new Date());\n }\n await this.storage.updateDunningState(stateId, updates);\n\n await this.emitEvent({\n type: \"dunning.resumed\",\n customerId: state.customerId,\n subscriptionId: state.subscriptionId,\n dunningStateId: state.id,\n timestamp: new Date(),\n data: {},\n });\n }\n\n /**\n * Cancel dunning manually\n */\n async cancelDunning(stateId: string, reason?: string): Promise<void> {\n const state = await this.getDunningStateById(stateId);\n if (!state) return;\n\n state.status = \"canceled\";\n state.endedAt = new Date();\n state.endReason = \"manually_canceled\";\n\n await this.storage.updateDunningState(stateId, {\n status: state.status,\n endedAt: state.endedAt,\n endReason: state.endReason,\n metadata: { ...state.metadata, cancelReason: reason },\n });\n\n await this.emitEvent({\n type: \"dunning.canceled\",\n customerId: state.customerId,\n subscriptionId: state.subscriptionId,\n dunningStateId: state.id,\n timestamp: new Date(),\n data: { reason },\n });\n }\n\n // ============================================================================\n // State Queries\n // ============================================================================\n\n /**\n * Get dunning state by customer ID\n */\n async getDunningState(customerId: string): Promise<DunningState | null> {\n return this.storage.getDunningState(customerId);\n }\n\n /**\n * Get dunning state by ID\n */\n async getDunningStateById(stateId: string): Promise<DunningState | null> {\n // Storage should support this - for now iterate active states\n const states = await this.storage.getActiveDunningStates();\n return states.find((s) => s.id === stateId) ?? null;\n }\n\n /**\n * Get all active dunning states\n */\n async getActiveDunningStates(): Promise<DunningState[]> {\n return this.storage.getActiveDunningStates();\n }\n\n /**\n * Get dunning states by status\n */\n async getDunningStatesByStatus(status: DunningStatus): Promise<DunningState[]> {\n return this.storage.getDunningStatesByStatus(status);\n }\n\n /**\n * Get scheduled steps due for execution\n */\n async getScheduledSteps(\n before: Date\n ): Promise<Array<{ stateId: string; stepId: string; scheduledAt: Date }>> {\n return this.storage.getScheduledSteps(before);\n }\n\n // ============================================================================\n // Events\n // ============================================================================\n\n /**\n * Register event handler\n */\n onEvent(handler: DunningEventHandler): this {\n this.eventHandlers.push(handler);\n return this;\n }\n\n /**\n * Emit dunning event\n */\n private async emitEvent(event: DunningEvent): Promise<void> {\n for (const handler of this.eventHandlers) {\n try {\n await handler(event);\n } catch (error) {\n this.logger?.error(\"Event handler error\", {\n eventType: event.type,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n\n // ============================================================================\n // Internal Methods\n // ============================================================================\n\n /**\n * Perform step actions\n */\n private async performStepActions(\n context: DunningContext,\n step: DunningStep\n ): Promise<ExecutedStep> {\n const executed: ExecutedStep = {\n stepId: step.id,\n stepName: step.name,\n executedAt: new Date(),\n actionsTaken: [],\n paymentRetried: false,\n notificationsSent: [],\n };\n\n for (const action of step.actions) {\n try {\n switch (action) {\n case \"notify\":\n const notifyResults = await this.sendNotifications(context, step);\n executed.notificationsSent = notifyResults\n .filter((r) => r.success)\n .map((r) => r.channel);\n executed.actionsTaken.push(\"notify\");\n break;\n\n case \"retry_payment\":\n if (this.config.onRetryPayment) {\n const retryResult = await this.config.onRetryPayment(context);\n executed.paymentRetried = true;\n executed.paymentSucceeded = retryResult.success;\n executed.actionsTaken.push(\"retry_payment\");\n\n await this.emitEvent({\n type: \"dunning.payment_retried\",\n customerId: context.customer.id,\n subscriptionId: context.subscription.id,\n dunningStateId: context.state.id,\n timestamp: new Date(),\n data: {\n success: retryResult.success,\n transactionId: retryResult.transactionId,\n },\n });\n\n // If retry succeeded, stop processing other actions\n if (retryResult.success) {\n return executed;\n }\n }\n break;\n\n case \"limit_features\":\n if (this.config.onAccessUpdate && step.accessLevel) {\n await this.config.onAccessUpdate(context.customer.id, step.accessLevel);\n executed.actionsTaken.push(\"limit_features\");\n\n await this.emitEvent({\n type: \"dunning.access_limited\",\n customerId: context.customer.id,\n subscriptionId: context.subscription.id,\n dunningStateId: context.state.id,\n timestamp: new Date(),\n data: { accessLevel: step.accessLevel },\n });\n }\n break;\n\n case \"suspend\":\n if (this.config.onAccessUpdate) {\n await this.config.onAccessUpdate(context.customer.id, \"read_only\");\n executed.actionsTaken.push(\"suspend\");\n\n await this.emitEvent({\n type: \"dunning.suspended\",\n customerId: context.customer.id,\n subscriptionId: context.subscription.id,\n dunningStateId: context.state.id,\n timestamp: new Date(),\n data: {},\n });\n }\n break;\n\n case \"cancel\":\n if (this.config.onCancelSubscription) {\n await this.config.onCancelSubscription(\n context.subscription.id,\n \"dunning_exhausted\"\n );\n executed.actionsTaken.push(\"cancel\");\n }\n break;\n\n case \"custom\":\n if (step.customAction) {\n await step.customAction(context);\n executed.actionsTaken.push(\"custom\");\n }\n break;\n }\n } catch (error) {\n this.logger?.error(\"Step action failed\", {\n stepId: step.id,\n action,\n error: error instanceof Error ? error.message : String(error),\n });\n executed.error = error instanceof Error ? error.message : String(error);\n }\n }\n\n // Emit step executed event\n await this.emitEvent({\n type: \"dunning.step_executed\",\n customerId: context.customer.id,\n subscriptionId: context.subscription.id,\n dunningStateId: context.state.id,\n timestamp: new Date(),\n data: {\n stepId: step.id,\n stepName: step.name,\n actionsTaken: executed.actionsTaken,\n },\n });\n\n return executed;\n }\n\n /**\n * Send notifications for a step\n */\n private async sendNotifications(\n context: DunningContext,\n step: DunningStep\n ): Promise<NotificationResult[]> {\n if (!step.notificationChannels?.length || !this.config.onNotification) {\n return [];\n }\n\n const results: NotificationResult[] = [];\n\n for (const channel of step.notificationChannels) {\n // Build recipient with only defined properties\n const recipient: DunningNotification[\"recipient\"] = {\n customerId: context.customer.id,\n };\n if (context.customer.email) {\n recipient.email = context.customer.email;\n }\n\n // Build variables with only defined properties\n const variables: DunningNotification[\"variables\"] = {\n amount: context.amountOwed,\n currency: context.currency,\n daysSinceFailure: context.daysSinceFailure,\n };\n if (context.customer.name) {\n variables.customerName = context.customer.name;\n }\n if (this.config.urls?.updatePayment) {\n variables.updatePaymentUrl = this.config.urls.updatePayment;\n }\n if (this.config.urls?.viewInvoice) {\n variables.invoiceUrl = this.config.urls.viewInvoice;\n }\n if (this.config.urls?.support) {\n variables.supportUrl = this.config.urls.support;\n }\n\n const notification: DunningNotification = {\n channel,\n templateId: step.notificationTemplateId ?? `dunning-${step.id}`,\n recipient,\n variables,\n context,\n };\n\n try {\n const result = await this.config.onNotification(notification);\n results.push(result);\n\n if (result.success) {\n await this.emitEvent({\n type: \"dunning.notification_sent\",\n customerId: context.customer.id,\n subscriptionId: context.subscription.id,\n dunningStateId: context.state.id,\n timestamp: new Date(),\n data: {\n channel,\n templateId: notification.templateId,\n },\n });\n }\n } catch (error) {\n results.push({\n success: false,\n channel,\n error: error instanceof Error ? error.message : String(error),\n sentAt: new Date(),\n });\n }\n }\n\n return results;\n }\n\n /**\n * Advance to next step\n */\n private async advanceToNextStep(state: DunningState): Promise<ExecutedStep | null> {\n const sequence = this.getSequence(state.sequenceId);\n const nextIndex = state.currentStepIndex + 1;\n\n if (nextIndex >= sequence.steps.length) {\n await this.exhaustDunning(state);\n return null;\n }\n\n const nextStep = sequence.steps[nextIndex];\n if (!nextStep) {\n await this.exhaustDunning(state);\n return null;\n }\n\n const nextStepTime = this.calculateStepTime(nextStep, state.startedAt);\n\n await this.storage.updateDunningState(state.id, {\n currentStepIndex: nextIndex,\n currentStepId: nextStep.id,\n nextStepAt: nextStepTime,\n });\n\n // Schedule step\n await this.storage.scheduleStep(state.id, nextStep.id, nextStepTime);\n\n return null;\n }\n\n /**\n * Mark dunning as exhausted (all steps completed without recovery)\n */\n private async exhaustDunning(state: DunningState): Promise<void> {\n state.status = \"exhausted\";\n state.endedAt = new Date();\n state.endReason = \"max_retries\";\n\n await this.storage.updateDunningState(state.id, {\n status: state.status,\n endedAt: state.endedAt,\n endReason: state.endReason,\n });\n\n await this.emitEvent({\n type: \"dunning.exhausted\",\n customerId: state.customerId,\n subscriptionId: state.subscriptionId,\n dunningStateId: state.id,\n timestamp: new Date(),\n data: {\n totalRetries: state.totalRetryAttempts,\n stepsExecuted: state.executedSteps.length,\n },\n });\n\n this.logger?.info(\"Dunning exhausted\", {\n dunningId: state.id,\n customerId: state.customerId,\n });\n }\n\n /**\n * Build dunning context for step execution\n */\n private async buildContext(state: DunningState, step: DunningStep): Promise<DunningContext> {\n const latestFailure = state.failures[state.failures.length - 1] ?? state.initialFailure;\n\n // Build customer object with only defined properties\n const customer: DunningContext[\"customer\"] = {\n id: state.customerId,\n };\n const customerEmail = state.metadata?.[\"customerEmail\"];\n if (typeof customerEmail === \"string\") {\n customer.email = customerEmail;\n }\n const customerName = state.metadata?.[\"customerName\"];\n if (typeof customerName === \"string\") {\n customer.name = customerName;\n }\n const customerMetadata = state.metadata?.[\"customer\"];\n if (customerMetadata && typeof customerMetadata === \"object\") {\n customer.metadata = customerMetadata as Record<string, unknown>;\n }\n\n // Build subscription object with only defined properties\n const subscription: DunningContext[\"subscription\"] = {\n id: state.subscriptionId,\n status: \"past_due\",\n };\n const planId = state.metadata?.[\"planId\"];\n if (typeof planId === \"string\") {\n subscription.planId = planId;\n }\n\n return {\n state,\n step,\n latestFailure,\n customer,\n subscription,\n daysSinceFailure: Math.floor(\n (Date.now() - state.initialFailure.failedAt.getTime()) / (1000 * 60 * 60 * 24)\n ),\n amountOwed: state.failures.reduce((sum, f) => sum + f.amount, 0),\n currency: latestFailure.currency,\n };\n }\n\n /**\n * Calculate when a step should execute\n */\n private calculateStepTime(step: DunningStep, baseTime: Date): Date {\n const time = new Date(baseTime);\n time.setDate(time.getDate() + step.daysAfterFailure);\n\n if (step.hoursOffset !== undefined) {\n time.setHours(step.hoursOffset, 0, 0, 0);\n }\n\n return time;\n }\n\n /**\n * Get sequence for a customer (by tier if configured)\n */\n private async getSequenceForCustomer(_customerId: string): Promise<DunningSequence> {\n // TODO: Could lookup customer tier and return appropriate sequence\n // For now, return default sequence\n return this.config.defaultSequence;\n }\n\n /**\n * Get sequence by ID\n */\n private getSequence(sequenceId: string): DunningSequence {\n if (sequenceId === this.config.defaultSequence.id) {\n return this.config.defaultSequence;\n }\n\n // Check tier sequences\n if (this.config.sequencesByPlanTier) {\n for (const seq of Object.values(this.config.sequencesByPlanTier)) {\n if (seq.id === sequenceId) return seq;\n }\n }\n\n return this.config.defaultSequence;\n }\n\n /**\n * Generate unique ID\n */\n private generateId(): string {\n return `dun_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;\n }\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create a dunning manager\n */\nexport function createDunningManager(\n config: DunningManagerConfig,\n storage: DunningStorage\n): DunningManager {\n return new DunningManager(config, storage);\n}\n\n/**\n * Create default dunning config\n */\nexport function createDefaultDunningConfig(\n overrides?: Partial<DunningManagerConfig>\n): DunningManagerConfig {\n return {\n defaultSequence: standardSaasSequence,\n ...overrides,\n };\n}\n","/**\n * @parsrun/payments - Dunning Scheduler\n * Automated scheduling and execution of dunning steps\n */\n\nimport type { DunningLogger } from \"./types.js\";\nimport { DunningManager } from \"./dunning-manager.js\";\n\n// ============================================================================\n// Scheduler Configuration\n// ============================================================================\n\n/**\n * Dunning scheduler configuration\n */\nexport interface DunningSchedulerConfig {\n /** Dunning manager instance */\n manager: DunningManager;\n\n /** Poll interval in milliseconds (default: 60000 = 1 minute) */\n pollInterval?: number;\n\n /** Batch size for processing (default: 50) */\n batchSize?: number;\n\n /** Maximum concurrent executions (default: 5) */\n maxConcurrent?: number;\n\n /** Timezone for scheduling (default: UTC) */\n timezone?: string;\n\n /** Logger */\n logger?: DunningLogger;\n\n /** Error handler */\n onError?: (error: Error, stateId: string) => void | Promise<void>;\n\n /** Before step execution hook */\n beforeStep?: (stateId: string, stepId: string) => void | Promise<void>;\n\n /** After step execution hook */\n afterStep?: (stateId: string, stepId: string, success: boolean) => void | Promise<void>;\n}\n\n// ============================================================================\n// Dunning Scheduler\n// ============================================================================\n\n/**\n * Dunning Scheduler\n * Handles automated execution of scheduled dunning steps\n */\nexport class DunningScheduler {\n private manager: DunningManager;\n private pollInterval: number;\n private batchSize: number;\n private maxConcurrent: number;\n private logger?: DunningLogger;\n private onError?: (error: Error, stateId: string) => void | Promise<void>;\n private beforeStep?: (stateId: string, stepId: string) => void | Promise<void>;\n private afterStep?: (stateId: string, stepId: string, success: boolean) => void | Promise<void>;\n\n private isRunning = false;\n private pollTimer?: ReturnType<typeof setTimeout>;\n private processingStates: Set<string> = new Set();\n\n /** Timezone for scheduling (reserved for future use) */\n readonly timezone: string;\n\n constructor(config: DunningSchedulerConfig) {\n this.manager = config.manager;\n this.pollInterval = config.pollInterval ?? 60000; // 1 minute\n this.batchSize = config.batchSize ?? 50;\n this.maxConcurrent = config.maxConcurrent ?? 5;\n this.timezone = config.timezone ?? \"UTC\";\n if (config.logger) {\n this.logger = config.logger;\n }\n if (config.onError) {\n this.onError = config.onError;\n }\n if (config.beforeStep) {\n this.beforeStep = config.beforeStep;\n }\n if (config.afterStep) {\n this.afterStep = config.afterStep;\n }\n }\n\n // ============================================================================\n // Lifecycle\n // ============================================================================\n\n /**\n * Start the scheduler\n */\n start(): void {\n if (this.isRunning) {\n this.logger?.warn(\"Scheduler already running\");\n return;\n }\n\n this.isRunning = true;\n this.logger?.info(\"Dunning scheduler started\", {\n pollInterval: this.pollInterval,\n batchSize: this.batchSize,\n maxConcurrent: this.maxConcurrent,\n });\n\n // Start polling\n this.poll();\n }\n\n /**\n * Stop the scheduler\n */\n stop(): void {\n this.isRunning = false;\n\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n delete this.pollTimer;\n }\n\n this.logger?.info(\"Dunning scheduler stopped\");\n }\n\n /**\n * Check if scheduler is running\n */\n get running(): boolean {\n return this.isRunning;\n }\n\n /**\n * Get current processing count\n */\n get processingCount(): number {\n return this.processingStates.size;\n }\n\n // ============================================================================\n // Processing\n // ============================================================================\n\n /**\n * Poll for scheduled steps\n */\n private async poll(): Promise<void> {\n if (!this.isRunning) return;\n\n try {\n await this.processScheduledSteps();\n } catch (error) {\n this.logger?.error(\"Poll error\", {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n\n // Schedule next poll\n if (this.isRunning) {\n this.pollTimer = setTimeout(() => this.poll(), this.pollInterval);\n }\n }\n\n /**\n * Process all scheduled steps that are due\n */\n async processScheduledSteps(): Promise<number> {\n const now = new Date();\n const scheduled = await this.manager.getScheduledSteps(now);\n\n if (scheduled.length === 0) {\n return 0;\n }\n\n this.logger?.debug(\"Found scheduled steps\", {\n count: scheduled.length,\n before: now.toISOString(),\n });\n\n // Filter out already processing\n const toProcess = scheduled\n .filter((s) => !this.processingStates.has(s.stateId))\n .slice(0, this.batchSize);\n\n if (toProcess.length === 0) {\n return 0;\n }\n\n // Process in batches respecting maxConcurrent\n let processed = 0;\n const batches = this.chunk(toProcess, this.maxConcurrent);\n\n for (const batch of batches) {\n const results = await Promise.allSettled(\n batch.map((item) => this.executeScheduledStep(item.stateId, item.stepId))\n );\n\n processed += results.filter((r) => r.status === \"fulfilled\").length;\n }\n\n return processed;\n }\n\n /**\n * Execute a single scheduled step\n */\n private async executeScheduledStep(stateId: string, stepId: string): Promise<void> {\n // Mark as processing\n this.processingStates.add(stateId);\n\n try {\n // Before hook\n if (this.beforeStep) {\n await this.beforeStep(stateId, stepId);\n }\n\n this.logger?.debug(\"Executing scheduled step\", { stateId, stepId });\n\n // Execute step\n const result = await this.manager.executeStep(stateId);\n const success = result !== null;\n\n this.logger?.info(\"Scheduled step executed\", {\n stateId,\n stepId,\n success,\n actionsTaken: result?.actionsTaken,\n });\n\n // After hook\n if (this.afterStep) {\n await this.afterStep(stateId, stepId, success);\n }\n } catch (error) {\n this.logger?.error(\"Step execution failed\", {\n stateId,\n stepId,\n error: error instanceof Error ? error.message : String(error),\n });\n\n if (this.onError) {\n await this.onError(error instanceof Error ? error : new Error(String(error)), stateId);\n }\n } finally {\n // Remove from processing\n this.processingStates.delete(stateId);\n }\n }\n\n /**\n * Manually trigger processing (for testing or cron jobs)\n */\n async trigger(): Promise<number> {\n return this.processScheduledSteps();\n }\n\n /**\n * Process a specific dunning state immediately\n */\n async processNow(stateId: string): Promise<boolean> {\n const state = await this.manager.getDunningState(stateId);\n if (!state) {\n this.logger?.warn(\"State not found for immediate processing\", { stateId });\n return false;\n }\n\n try {\n await this.executeScheduledStep(state.id, state.currentStepId);\n return true;\n } catch (error) {\n this.logger?.error(\"Immediate processing failed\", {\n stateId,\n error: error instanceof Error ? error.message : String(error),\n });\n return false;\n }\n }\n\n // ============================================================================\n // Utilities\n // ============================================================================\n\n /**\n * Split array into chunks\n */\n private chunk<T>(array: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < array.length; i += size) {\n chunks.push(array.slice(i, i + size));\n }\n return chunks;\n }\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create a dunning scheduler\n */\nexport function createDunningScheduler(config: DunningSchedulerConfig): DunningScheduler {\n return new DunningScheduler(config);\n}\n\n// ============================================================================\n// Cron Integration\n// ============================================================================\n\n/**\n * Create a cron-compatible handler for dunning processing\n *\n * @example\n * ```typescript\n * // In a cron job or serverless function\n * export async function handler() {\n * const processor = createDunningCronHandler(manager);\n * const result = await processor();\n * console.log(`Processed ${result.processed} dunning steps`);\n * }\n * ```\n */\nexport function createDunningCronHandler(\n manager: DunningManager,\n options?: {\n batchSize?: number;\n maxConcurrent?: number;\n logger?: DunningLogger;\n }\n): () => Promise<{ processed: number; errors: number }> {\n return async () => {\n const config: DunningSchedulerConfig = {\n manager,\n batchSize: options?.batchSize ?? 100,\n maxConcurrent: options?.maxConcurrent ?? 10,\n };\n if (options?.logger) {\n config.logger = options.logger;\n }\n\n const scheduler = new DunningScheduler(config);\n\n let errors = 0;\n const originalOnError = scheduler[\"onError\"];\n scheduler[\"onError\"] = async (error, stateId) => {\n errors++;\n if (originalOnError) await originalOnError(error, stateId);\n };\n\n const processed = await scheduler.trigger();\n\n return { processed, errors };\n };\n}\n\n// ============================================================================\n// Edge/Serverless Handler\n// ============================================================================\n\n/**\n * Create a handler for edge/serverless environments\n * Processes dunning in a single invocation\n *\n * @example\n * ```typescript\n * // In a Cloudflare Worker or similar\n * export default {\n * async scheduled(event, env, ctx) {\n * const handler = createDunningEdgeHandler(manager, { maxDurationMs: 25000 });\n * const result = await handler();\n * console.log(result);\n * }\n * }\n * ```\n */\nexport function createDunningEdgeHandler(\n manager: DunningManager,\n options?: {\n maxDurationMs?: number;\n batchSize?: number;\n logger?: DunningLogger;\n }\n): () => Promise<{\n processed: number;\n errors: number;\n duration: number;\n timedOut: boolean;\n}> {\n const maxDuration = options?.maxDurationMs ?? 30000; // 30 seconds default\n const batchSize = options?.batchSize ?? 25;\n\n return async () => {\n const startTime = Date.now();\n let processed = 0;\n let errors = 0;\n let timedOut = false;\n\n const now = new Date();\n const scheduled = await manager.getScheduledSteps(now);\n\n for (let i = 0; i < scheduled.length; i += batchSize) {\n // Check timeout\n if (Date.now() - startTime > maxDuration) {\n timedOut = true;\n options?.logger?.warn(\"Edge handler timed out\", {\n processed,\n remaining: scheduled.length - i,\n });\n break;\n }\n\n const batch = scheduled.slice(i, i + batchSize);\n\n const results = await Promise.allSettled(\n batch.map(async (item) => {\n try {\n await manager.executeStep(item.stateId);\n return true;\n } catch (error) {\n options?.logger?.error(\"Step execution error\", {\n stateId: item.stateId,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n })\n );\n\n for (const result of results) {\n if (result.status === \"fulfilled\") {\n processed++;\n } else {\n errors++;\n }\n }\n }\n\n const duration = Date.now() - startTime;\n\n return { processed, errors, duration, timedOut };\n };\n}\n","/**\n * @parsrun/payments - Dunning Memory Storage\n * In-memory storage implementation for development and testing\n */\n\nimport type {\n DunningStorage,\n DunningState,\n DunningStatus,\n PaymentFailure,\n} from \"./types.js\";\n\n// ============================================================================\n// Memory Storage Implementation\n// ============================================================================\n\n/**\n * In-memory dunning storage implementation\n * Use only for development/testing - data is lost on restart\n */\nexport class MemoryDunningStorage implements DunningStorage {\n private states: Map<string, DunningState> = new Map();\n private failures: Map<string, PaymentFailure[]> = new Map();\n private scheduledSteps: Array<{\n stateId: string;\n stepId: string;\n scheduledAt: Date;\n }> = [];\n\n // ============================================================================\n // Dunning State Methods\n // ============================================================================\n\n async getDunningState(customerId: string): Promise<DunningState | null> {\n for (const state of this.states.values()) {\n if (state.customerId === customerId) {\n return state;\n }\n }\n return null;\n }\n\n async getActiveDunningStates(): Promise<DunningState[]> {\n return Array.from(this.states.values()).filter((s) => s.status === \"active\");\n }\n\n async getDunningStatesByStatus(status: DunningStatus): Promise<DunningState[]> {\n return Array.from(this.states.values()).filter((s) => s.status === status);\n }\n\n async saveDunningState(state: DunningState): Promise<void> {\n this.states.set(state.id, { ...state });\n }\n\n async updateDunningState(id: string, updates: Partial<DunningState>): Promise<void> {\n const state = this.states.get(id);\n if (state) {\n this.states.set(id, { ...state, ...updates });\n }\n }\n\n // ============================================================================\n // Payment Failure Methods\n // ============================================================================\n\n async recordPaymentFailure(failure: PaymentFailure): Promise<void> {\n const customerFailures = this.failures.get(failure.customerId) ?? [];\n customerFailures.push({ ...failure });\n this.failures.set(failure.customerId, customerFailures);\n }\n\n async getPaymentFailures(customerId: string, limit = 50): Promise<PaymentFailure[]> {\n const customerFailures = this.failures.get(customerId) ?? [];\n return customerFailures\n .sort((a, b) => b.failedAt.getTime() - a.failedAt.getTime())\n .slice(0, limit);\n }\n\n // ============================================================================\n // Scheduled Steps Methods\n // ============================================================================\n\n async getScheduledSteps(\n before: Date\n ): Promise<Array<{ stateId: string; stepId: string; scheduledAt: Date }>> {\n return this.scheduledSteps.filter((s) => s.scheduledAt <= before);\n }\n\n async scheduleStep(stateId: string, stepId: string, scheduledAt: Date): Promise<void> {\n // Remove existing schedule for this step\n await this.removeScheduledStep(stateId, stepId);\n\n this.scheduledSteps.push({ stateId, stepId, scheduledAt });\n }\n\n async removeScheduledStep(stateId: string, stepId: string): Promise<void> {\n this.scheduledSteps = this.scheduledSteps.filter(\n (s) => !(s.stateId === stateId && s.stepId === stepId)\n );\n }\n\n // ============================================================================\n // Utility Methods\n // ============================================================================\n\n /**\n * Clear all data (for testing)\n */\n clear(): void {\n this.states.clear();\n this.failures.clear();\n this.scheduledSteps = [];\n }\n\n /**\n * Get state by ID\n */\n getStateById(id: string): DunningState | undefined {\n return this.states.get(id);\n }\n\n /**\n * Get all states (for debugging)\n */\n getAllStates(): DunningState[] {\n return Array.from(this.states.values());\n }\n\n /**\n * Get all scheduled steps (for debugging)\n */\n getAllScheduledSteps(): Array<{ stateId: string; stepId: string; scheduledAt: Date }> {\n return [...this.scheduledSteps];\n }\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create an in-memory dunning storage instance\n */\nexport function createMemoryDunningStorage(): MemoryDunningStorage {\n return new MemoryDunningStorage();\n}\n","/**\n * @parsrun/payments - Dunning Drizzle Storage\n * Persistent storage implementation using Drizzle ORM\n */\n\nimport { eq, and, lt, desc } from \"drizzle-orm\";\nimport type {\n DunningStorage,\n DunningState,\n DunningStatus,\n PaymentFailure,\n PaymentFailureCategory,\n ExecutedStep,\n DunningAction,\n NotificationChannel,\n} from \"./types.js\";\nimport {\n dunningStates,\n paymentFailures,\n scheduledSteps,\n executedSteps as executedStepsTable,\n type DunningStateRow,\n type PaymentFailureRow,\n} from \"./schema.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype QueryBuilder = Promise<unknown[]> & {\n where: (condition: unknown) => QueryBuilder;\n orderBy: (...order: unknown[]) => QueryBuilder;\n limit: (n: number) => QueryBuilder;\n};\n\n/**\n * Drizzle database type (compatible with various drivers)\n */\nexport type DrizzleDb = {\n select: () => {\n from: (table: unknown) => QueryBuilder;\n };\n insert: (table: unknown) => {\n values: (values: unknown) => {\n onConflictDoUpdate: (config: unknown) => Promise<unknown>;\n returning: () => Promise<unknown[]>;\n } & Promise<unknown>;\n };\n update: (table: unknown) => {\n set: (values: unknown) => {\n where: (condition: unknown) => Promise<unknown>;\n };\n };\n delete: (table: unknown) => {\n where: (condition: unknown) => Promise<unknown>;\n };\n};\n\n/**\n * Configuration for Drizzle dunning storage\n */\nexport interface DrizzleDunningStorageConfig {\n /** Drizzle database instance */\n db: DrizzleDb;\n}\n\n// ============================================================================\n// Storage Implementation\n// ============================================================================\n\n/**\n * Drizzle-based dunning storage implementation\n */\nexport class DrizzleDunningStorage implements DunningStorage {\n private db: DrizzleDb;\n\n constructor(config: DrizzleDunningStorageConfig) {\n this.db = config.db;\n }\n\n // ============================================================================\n // Dunning State Methods\n // ============================================================================\n\n async getDunningState(customerId: string): Promise<DunningState | null> {\n const rows = (await this.db\n .select()\n .from(dunningStates)\n .where(eq(dunningStates.customerId, customerId))) as DunningStateRow[];\n\n const row = rows[0];\n if (!row) return null;\n\n return this.mapRowToState(row);\n }\n\n async getActiveDunningStates(): Promise<DunningState[]> {\n const rows = (await this.db\n .select()\n .from(dunningStates)\n .where(eq(dunningStates.status, \"active\"))) as DunningStateRow[];\n\n return Promise.all(rows.map((row) => this.mapRowToState(row)));\n }\n\n async getDunningStatesByStatus(status: DunningStatus): Promise<DunningState[]> {\n const rows = (await this.db\n .select()\n .from(dunningStates)\n .where(eq(dunningStates.status, status))) as DunningStateRow[];\n\n return Promise.all(rows.map((row) => this.mapRowToState(row)));\n }\n\n async saveDunningState(state: DunningState): Promise<void> {\n await this.db.insert(dunningStates).values({\n id: state.id,\n customerId: state.customerId,\n subscriptionId: state.subscriptionId,\n sequenceId: state.sequenceId,\n currentStepIndex: state.currentStepIndex,\n currentStepId: state.currentStepId,\n status: state.status,\n initialFailureId: state.initialFailure.id,\n failureIds: state.failures.map((f) => f.id),\n startedAt: state.startedAt,\n lastStepAt: state.lastStepAt,\n nextStepAt: state.nextStepAt,\n endedAt: state.endedAt,\n endReason: state.endReason,\n totalRetryAttempts: state.totalRetryAttempts,\n metadata: state.metadata,\n });\n\n // Save executed steps\n for (const step of state.executedSteps) {\n await this.saveExecutedStep(state.id, step);\n }\n }\n\n async updateDunningState(id: string, updates: Partial<DunningState>): Promise<void> {\n const setValues: Record<string, unknown> = {};\n\n if (updates[\"currentStepIndex\"] !== undefined)\n setValues[\"currentStepIndex\"] = updates[\"currentStepIndex\"];\n if (updates[\"currentStepId\"] !== undefined)\n setValues[\"currentStepId\"] = updates[\"currentStepId\"];\n if (updates[\"status\"] !== undefined) setValues[\"status\"] = updates[\"status\"];\n if (updates[\"lastStepAt\"] !== undefined) setValues[\"lastStepAt\"] = updates[\"lastStepAt\"];\n if (updates[\"nextStepAt\"] !== undefined) setValues[\"nextStepAt\"] = updates[\"nextStepAt\"];\n if (updates[\"endedAt\"] !== undefined) setValues[\"endedAt\"] = updates[\"endedAt\"];\n if (updates[\"endReason\"] !== undefined) setValues[\"endReason\"] = updates[\"endReason\"];\n if (updates[\"totalRetryAttempts\"] !== undefined)\n setValues[\"totalRetryAttempts\"] = updates[\"totalRetryAttempts\"];\n if (updates[\"metadata\"] !== undefined) setValues[\"metadata\"] = updates[\"metadata\"];\n if (updates[\"failures\"] !== undefined)\n setValues[\"failureIds\"] = updates[\"failures\"].map((f) => f.id);\n\n if (Object.keys(setValues).length > 0) {\n await this.db.update(dunningStates).set(setValues).where(eq(dunningStates.id, id));\n }\n }\n\n // ============================================================================\n // Payment Failure Methods\n // ============================================================================\n\n async recordPaymentFailure(failure: PaymentFailure): Promise<void> {\n await this.db.insert(paymentFailures).values({\n id: failure.id,\n customerId: failure.customerId,\n subscriptionId: failure.subscriptionId,\n invoiceId: failure.invoiceId,\n amount: failure.amount,\n currency: failure.currency,\n category: failure.category,\n errorCode: failure.errorCode,\n errorMessage: failure.errorMessage,\n provider: failure.provider,\n failedAt: failure.failedAt,\n retryCount: failure.retryCount,\n nextRetryAt: failure.nextRetryAt,\n isRecoverable: failure.isRecoverable,\n metadata: failure.metadata,\n });\n }\n\n async getPaymentFailures(customerId: string, limit = 50): Promise<PaymentFailure[]> {\n const rows = (await this.db\n .select()\n .from(paymentFailures)\n .where(eq(paymentFailures.customerId, customerId))\n .orderBy(desc(paymentFailures.failedAt))\n .limit(limit)) as PaymentFailureRow[];\n\n return rows.map((row) => this.mapRowToFailure(row));\n }\n\n // ============================================================================\n // Scheduled Steps Methods\n // ============================================================================\n\n async getScheduledSteps(\n before: Date\n ): Promise<Array<{ stateId: string; stepId: string; scheduledAt: Date }>> {\n const rows = (await this.db\n .select()\n .from(scheduledSteps)\n .where(lt(scheduledSteps.scheduledAt, before))) as Array<{\n dunningStateId: string;\n stepId: string;\n scheduledAt: Date;\n }>;\n\n return rows.map((row) => ({\n stateId: row.dunningStateId,\n stepId: row.stepId,\n scheduledAt: row.scheduledAt,\n }));\n }\n\n async scheduleStep(stateId: string, stepId: string, scheduledAt: Date): Promise<void> {\n const id = `sched_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;\n\n await this.db.insert(scheduledSteps).values({\n id,\n dunningStateId: stateId,\n stepId,\n scheduledAt,\n });\n }\n\n async removeScheduledStep(stateId: string, stepId: string): Promise<void> {\n await this.db\n .delete(scheduledSteps)\n .where(\n and(eq(scheduledSteps.dunningStateId, stateId), eq(scheduledSteps.stepId, stepId))\n );\n }\n\n // ============================================================================\n // Helper Methods\n // ============================================================================\n\n private async saveExecutedStep(stateId: string, step: ExecutedStep): Promise<void> {\n const id = `exec_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;\n\n await this.db.insert(executedStepsTable).values({\n id,\n dunningStateId: stateId,\n stepId: step.stepId,\n stepName: step.stepName,\n executedAt: step.executedAt,\n actionsTaken: step.actionsTaken,\n paymentRetried: step.paymentRetried,\n paymentSucceeded: step.paymentSucceeded,\n notificationsSent: step.notificationsSent,\n error: step.error,\n });\n }\n\n private async mapRowToState(row: DunningStateRow): Promise<DunningState> {\n // Get initial failure\n const failureRows = (await this.db\n .select()\n .from(paymentFailures)\n .where(eq(paymentFailures.id, row.initialFailureId))) as PaymentFailureRow[];\n\n const initialFailureRow = failureRows[0];\n if (!initialFailureRow) {\n throw new Error(`Initial failure not found: ${row.initialFailureId}`);\n }\n\n const initialFailure = this.mapRowToFailure(initialFailureRow);\n\n // Get all failures\n const failures: PaymentFailure[] = [initialFailure];\n const failureIds = (row.failureIds as string[]) || [];\n for (const failureId of failureIds) {\n if (failureId !== row.initialFailureId) {\n const additionalRows = (await this.db\n .select()\n .from(paymentFailures)\n .where(eq(paymentFailures.id, failureId))) as PaymentFailureRow[];\n\n const additionalRow = additionalRows[0];\n if (additionalRow) {\n failures.push(this.mapRowToFailure(additionalRow));\n }\n }\n }\n\n // Get executed steps\n const execStepRows = (await this.db\n .select()\n .from(executedStepsTable)\n .where(eq(executedStepsTable.dunningStateId, row.id))\n .orderBy(executedStepsTable.executedAt)) as Array<{\n stepId: string;\n stepName: string;\n executedAt: Date;\n actionsTaken: string[] | null;\n paymentRetried: boolean;\n paymentSucceeded: boolean | null;\n notificationsSent: string[] | null;\n error: string | null;\n }>;\n\n const executedStepsList: ExecutedStep[] = execStepRows.map((es) => {\n const step: ExecutedStep = {\n stepId: es.stepId,\n stepName: es.stepName,\n executedAt: es.executedAt,\n actionsTaken: (es.actionsTaken || []) as DunningAction[],\n paymentRetried: es.paymentRetried,\n notificationsSent: (es.notificationsSent || []) as NotificationChannel[],\n };\n if (es.paymentSucceeded !== null) step.paymentSucceeded = es.paymentSucceeded;\n if (es.error !== null) step.error = es.error;\n return step;\n });\n\n const result: DunningState = {\n id: row.id,\n customerId: row.customerId,\n subscriptionId: row.subscriptionId,\n sequenceId: row.sequenceId,\n currentStepIndex: row.currentStepIndex,\n currentStepId: row.currentStepId,\n status: row.status as DunningStatus,\n initialFailure,\n failures,\n executedSteps: executedStepsList,\n startedAt: row.startedAt,\n totalRetryAttempts: row.totalRetryAttempts,\n };\n if (row.lastStepAt) result.lastStepAt = row.lastStepAt;\n if (row.nextStepAt) result.nextStepAt = row.nextStepAt;\n if (row.endedAt) result.endedAt = row.endedAt;\n const endReason = row.endReason;\n if (\n endReason === \"payment_recovered\" ||\n endReason === \"max_retries\" ||\n endReason === \"manually_canceled\" ||\n endReason === \"subscription_canceled\"\n ) {\n result.endReason = endReason;\n }\n if (row.metadata) result.metadata = row.metadata as Record<string, unknown>;\n\n return result;\n }\n\n private mapRowToFailure(row: PaymentFailureRow): PaymentFailure {\n const result: PaymentFailure = {\n id: row.id,\n customerId: row.customerId,\n subscriptionId: row.subscriptionId,\n amount: row.amount,\n currency: row.currency,\n category: row.category as PaymentFailureCategory,\n errorCode: row.errorCode,\n errorMessage: row.errorMessage,\n provider: row.provider as PaymentFailure[\"provider\"],\n failedAt: row.failedAt,\n retryCount: row.retryCount,\n isRecoverable: row.isRecoverable,\n };\n if (row.invoiceId) result.invoiceId = row.invoiceId;\n if (row.nextRetryAt) result.nextRetryAt = row.nextRetryAt;\n if (row.metadata) result.metadata = row.metadata as Record<string, unknown>;\n\n return result;\n }\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create a Drizzle dunning storage instance\n */\nexport function createDrizzleDunningStorage(\n config: DrizzleDunningStorageConfig\n): DrizzleDunningStorage {\n return new DrizzleDunningStorage(config);\n}\n","/**\n * @parsrun/payments - Dunning Database Schema\n * Drizzle ORM schema for dunning automation tables\n *\n * These tables store:\n * - Dunning sequences and steps\n * - Dunning states for active processes\n * - Payment failures\n * - Executed steps history\n * - Scheduled steps for automation\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// Dunning Sequences\n// ============================================================================\n\n/**\n * Dunning sequences table - defines dunning workflows\n */\nexport const dunningSequences = pgTable(\"dunning_sequences\", {\n id: text(\"id\").primaryKey(),\n name: text(\"name\").notNull().unique(),\n description: text(\"description\"),\n maxDurationDays: integer(\"max_duration_days\").notNull().default(28),\n isActive: boolean(\"is_active\").notNull().default(true),\n isDefault: boolean(\"is_default\").notNull().default(false),\n planTier: integer(\"plan_tier\"), // null = default for all, otherwise specific tier\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 * Dunning steps table - defines steps within sequences\n */\nexport const dunningSteps = pgTable(\n \"dunning_steps\",\n {\n id: text(\"id\").primaryKey(),\n sequenceId: text(\"sequence_id\")\n .notNull()\n .references(() => dunningSequences.id, { onDelete: \"cascade\" }),\n name: text(\"name\").notNull(),\n stepOrder: integer(\"step_order\").notNull(), // Order within sequence\n daysAfterFailure: integer(\"days_after_failure\").notNull().default(0),\n hoursOffset: integer(\"hours_offset\"), // Hour of day to execute (0-23)\n actions: jsonb(\"actions\").$type<string[]>().notNull().default([]), // Array of action types\n notificationChannels: jsonb(\"notification_channels\").$type<string[]>(), // email, sms, in_app, webhook, push\n notificationTemplateId: text(\"notification_template_id\"),\n accessLevel: text(\"access_level\"), // full, limited, read_only, none\n isFinal: boolean(\"is_final\").notNull().default(false),\n metadata: jsonb(\"metadata\").$type<Record<string, unknown>>(),\n },\n (table) => ({\n sequenceOrderIdx: uniqueIndex(\"dunning_steps_sequence_order_idx\").on(\n table.sequenceId,\n table.stepOrder\n ),\n sequenceIdx: index(\"dunning_steps_sequence_idx\").on(table.sequenceId),\n })\n);\n\n// ============================================================================\n// Payment Failures\n// ============================================================================\n\n/**\n * Payment failures table - records all payment failures\n */\nexport const paymentFailures = pgTable(\n \"dunning_payment_failures\",\n {\n id: text(\"id\").primaryKey(),\n customerId: text(\"customer_id\").notNull(),\n subscriptionId: text(\"subscription_id\").notNull(),\n invoiceId: text(\"invoice_id\"),\n amount: integer(\"amount\").notNull(), // cents\n currency: text(\"currency\").notNull().default(\"usd\"),\n category: text(\"category\").notNull(), // card_declined, insufficient_funds, etc.\n errorCode: text(\"error_code\").notNull(),\n errorMessage: text(\"error_message\").notNull(),\n provider: text(\"provider\").notNull(), // stripe, paddle, iyzico\n failedAt: timestamp(\"failed_at\").notNull(),\n retryCount: integer(\"retry_count\").notNull().default(0),\n nextRetryAt: timestamp(\"next_retry_at\"),\n isRecoverable: boolean(\"is_recoverable\").notNull().default(true),\n metadata: jsonb(\"metadata\").$type<Record<string, unknown>>(),\n },\n (table) => ({\n customerIdx: index(\"dunning_payment_failures_customer_idx\").on(table.customerId),\n subscriptionIdx: index(\"dunning_payment_failures_subscription_idx\").on(\n table.subscriptionId\n ),\n failedAtIdx: index(\"dunning_payment_failures_failed_at_idx\").on(table.failedAt),\n categoryIdx: index(\"dunning_payment_failures_category_idx\").on(table.category),\n })\n);\n\n// ============================================================================\n// Dunning States\n// ============================================================================\n\n/**\n * Dunning states table - tracks active dunning processes\n */\nexport const dunningStates = pgTable(\n \"dunning_states\",\n {\n id: text(\"id\").primaryKey(),\n customerId: text(\"customer_id\").notNull(),\n subscriptionId: text(\"subscription_id\").notNull(),\n sequenceId: text(\"sequence_id\")\n .notNull()\n .references(() => dunningSequences.id),\n currentStepIndex: integer(\"current_step_index\").notNull().default(0),\n currentStepId: text(\"current_step_id\").notNull(),\n status: text(\"status\").notNull().default(\"active\"), // active, recovered, exhausted, canceled, paused\n initialFailureId: text(\"initial_failure_id\")\n .notNull()\n .references(() => paymentFailures.id),\n failureIds: jsonb(\"failure_ids\").$type<string[]>().notNull().default([]), // All failure IDs\n startedAt: timestamp(\"started_at\").notNull(),\n lastStepAt: timestamp(\"last_step_at\"),\n nextStepAt: timestamp(\"next_step_at\"),\n endedAt: timestamp(\"ended_at\"),\n endReason: text(\"end_reason\"), // payment_recovered, max_retries, manually_canceled, subscription_canceled\n totalRetryAttempts: integer(\"total_retry_attempts\").notNull().default(0),\n metadata: jsonb(\"metadata\").$type<Record<string, unknown>>(),\n },\n (table) => ({\n customerIdx: uniqueIndex(\"dunning_states_customer_idx\").on(table.customerId),\n statusIdx: index(\"dunning_states_status_idx\").on(table.status),\n nextStepIdx: index(\"dunning_states_next_step_idx\").on(table.nextStepAt),\n subscriptionIdx: index(\"dunning_states_subscription_idx\").on(table.subscriptionId),\n })\n);\n\n// ============================================================================\n// Executed Steps\n// ============================================================================\n\n/**\n * Executed steps table - history of executed dunning steps\n */\nexport const executedSteps = pgTable(\n \"dunning_executed_steps\",\n {\n id: text(\"id\").primaryKey(),\n dunningStateId: text(\"dunning_state_id\")\n .notNull()\n .references(() => dunningStates.id, { onDelete: \"cascade\" }),\n stepId: text(\"step_id\").notNull(),\n stepName: text(\"step_name\").notNull(),\n executedAt: timestamp(\"executed_at\").notNull(),\n actionsTaken: jsonb(\"actions_taken\").$type<string[]>().notNull().default([]),\n paymentRetried: boolean(\"payment_retried\").notNull().default(false),\n paymentSucceeded: boolean(\"payment_succeeded\"),\n notificationsSent: jsonb(\"notifications_sent\").$type<string[]>().notNull().default([]),\n error: text(\"error\"),\n metadata: jsonb(\"metadata\").$type<Record<string, unknown>>(),\n },\n (table) => ({\n stateIdx: index(\"dunning_executed_steps_state_idx\").on(table.dunningStateId),\n executedAtIdx: index(\"dunning_executed_steps_executed_at_idx\").on(table.executedAt),\n })\n);\n\n// ============================================================================\n// Scheduled Steps\n// ============================================================================\n\n/**\n * Scheduled steps table - steps pending execution\n */\nexport const scheduledSteps = pgTable(\n \"dunning_scheduled_steps\",\n {\n id: text(\"id\").primaryKey(),\n dunningStateId: text(\"dunning_state_id\")\n .notNull()\n .references(() => dunningStates.id, { onDelete: \"cascade\" }),\n stepId: text(\"step_id\").notNull(),\n scheduledAt: timestamp(\"scheduled_at\").notNull(),\n createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n },\n (table) => ({\n stateStepIdx: uniqueIndex(\"dunning_scheduled_steps_state_step_idx\").on(\n table.dunningStateId,\n table.stepId\n ),\n scheduledAtIdx: index(\"dunning_scheduled_steps_scheduled_at_idx\").on(\n table.scheduledAt\n ),\n })\n);\n\n// ============================================================================\n// Dunning Events Log\n// ============================================================================\n\n/**\n * Dunning events table - audit log for all dunning events\n */\nexport const dunningEvents = pgTable(\n \"dunning_events\",\n {\n id: text(\"id\").primaryKey(),\n type: text(\"type\").notNull(), // dunning.started, dunning.step_executed, etc.\n customerId: text(\"customer_id\").notNull(),\n subscriptionId: text(\"subscription_id\").notNull(),\n dunningStateId: text(\"dunning_state_id\")\n .notNull()\n .references(() => dunningStates.id, { onDelete: \"cascade\" }),\n timestamp: timestamp(\"timestamp\").notNull(),\n data: jsonb(\"data\").$type<Record<string, unknown>>().notNull().default({}),\n },\n (table) => ({\n typeIdx: index(\"dunning_events_type_idx\").on(table.type),\n customerIdx: index(\"dunning_events_customer_idx\").on(table.customerId),\n timestampIdx: index(\"dunning_events_timestamp_idx\").on(table.timestamp),\n stateIdx: index(\"dunning_events_state_idx\").on(table.dunningStateId),\n })\n);\n\n// ============================================================================\n// Retry Strategies (Optional - for custom strategies)\n// ============================================================================\n\n/**\n * Custom retry strategies table - overrides for default strategies\n */\nexport const retryStrategies = pgTable(\n \"dunning_retry_strategies\",\n {\n id: text(\"id\").primaryKey(),\n category: text(\"category\").notNull().unique(), // failure category\n shouldRetry: boolean(\"should_retry\").notNull().default(true),\n initialDelayHours: integer(\"initial_delay_hours\").notNull().default(24),\n maxRetries: integer(\"max_retries\").notNull().default(4),\n backoffMultiplier: integer(\"backoff_multiplier\").notNull().default(2), // stored as x100 for decimals\n maxDelayHours: integer(\"max_delay_hours\").notNull().default(168),\n optimalRetryHours: jsonb(\"optimal_retry_hours\").$type<number[]>(),\n optimalRetryDays: jsonb(\"optimal_retry_days\").$type<number[]>(),\n metadata: jsonb(\"metadata\").$type<Record<string, unknown>>(),\n updatedAt: timestamp(\"updated_at\").defaultNow().notNull(),\n },\n (table) => ({\n categoryIdx: uniqueIndex(\"dunning_retry_strategies_category_idx\").on(table.category),\n })\n);\n\n// ============================================================================\n// Type Exports\n// ============================================================================\n\nexport type DunningSequenceRow = typeof dunningSequences.$inferSelect;\nexport type NewDunningSequence = typeof dunningSequences.$inferInsert;\n\nexport type DunningStepRow = typeof dunningSteps.$inferSelect;\nexport type NewDunningStep = typeof dunningSteps.$inferInsert;\n\nexport type PaymentFailureRow = typeof paymentFailures.$inferSelect;\nexport type NewPaymentFailure = typeof paymentFailures.$inferInsert;\n\nexport type DunningStateRow = typeof dunningStates.$inferSelect;\nexport type NewDunningState = typeof dunningStates.$inferInsert;\n\nexport type ExecutedStepRow = typeof executedSteps.$inferSelect;\nexport type NewExecutedStep = typeof executedSteps.$inferInsert;\n\nexport type ScheduledStepRow = typeof scheduledSteps.$inferSelect;\nexport type NewScheduledStep = typeof scheduledSteps.$inferInsert;\n\nexport type DunningEventRow = typeof dunningEvents.$inferSelect;\nexport type NewDunningEvent = typeof dunningEvents.$inferInsert;\n\nexport type RetryStrategyRow = typeof retryStrategies.$inferSelect;\nexport type NewRetryStrategy = typeof retryStrategies.$inferInsert;\n\n// ============================================================================\n// Schema Export\n// ============================================================================\n\n/**\n * All dunning schema tables\n */\nexport const dunningSchema = {\n dunningSequences,\n dunningSteps,\n paymentFailures,\n dunningStates,\n executedSteps,\n scheduledSteps,\n dunningEvents,\n retryStrategies,\n};\n","/**\n * @parsrun/payments - Dunning Email Templates\n * Pre-built email templates for dunning notifications\n */\n\nimport type { DunningContext } from \"./types.js\";\n\n// ============================================================================\n// Template Types\n// ============================================================================\n\n/**\n * Dunning email template data\n */\nexport interface DunningEmailData {\n /** Customer name */\n customerName?: string;\n /** Amount owed in display format */\n amount: string;\n /** Currency code */\n currency: string;\n /** Days since initial failure */\n daysSinceFailure: number;\n /** Days until features are limited */\n daysUntilLimit?: number;\n /** Days until suspension */\n daysUntilSuspension?: number;\n /** Days until cancellation */\n daysUntilCancellation?: number;\n /** URL to update payment method */\n updatePaymentUrl?: string;\n /** URL to view invoice */\n invoiceUrl?: string;\n /** URL for support */\n supportUrl?: string;\n /** Brand name */\n brandName?: string;\n /** Brand color */\n brandColor?: string;\n /** Last 4 digits of card */\n cardLast4?: string;\n /** Card brand (Visa, Mastercard, etc.) */\n cardBrand?: string;\n}\n\n/**\n * Rendered email template\n */\nexport interface RenderedEmail {\n subject: string;\n html: string;\n text: string;\n}\n\n/**\n * Dunning email template\n */\nexport interface DunningEmailTemplate {\n /** Template ID */\n id: string;\n /** Template name */\n name: string;\n /** Subject template */\n subject: string;\n /** HTML template */\n html: string;\n /** Plain text template */\n text: string;\n}\n\n// ============================================================================\n// Template Renderer\n// ============================================================================\n\n/**\n * Simple template engine - replaces {{key}} with values\n */\nfunction renderTemplate(template: string, data: Record<string, unknown>): string {\n return template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key: string) => {\n const value = data[key];\n return value !== undefined && value !== null ? String(value) : \"\";\n });\n}\n\n/**\n * Format currency amount for display\n */\nexport function formatAmount(amountCents: number, currency: string): string {\n const amount = amountCents / 100;\n return new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: currency.toUpperCase(),\n }).format(amount);\n}\n\n// ============================================================================\n// Default Templates\n// ============================================================================\n\n/**\n * Payment failed - immediate notification\n */\nexport const paymentFailedTemplate: DunningEmailTemplate = {\n id: \"dunning-payment-failed\",\n name: \"Payment Failed\",\n subject: \"Action required: Your payment failed\",\n html: `\n<h1>Your payment didn't go through</h1>\n<p>Hi{{customerName}},</p>\n<p>We weren't able to process your payment of <strong>{{amount}}</strong> for your subscription.</p>\n{{cardInfo}}\n<p>Don't worry - we'll automatically retry your payment. In the meantime, please make sure your payment information is up to date.</p>\n<div style=\"text-align: center; margin: 24px 0;\">\n <a href=\"{{updatePaymentUrl}}\" style=\"display: inline-block; background: {{brandColor}}; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 6px; font-weight: 600;\">Update Payment Method</a>\n</div>\n<p style=\"color: #666; font-size: 14px;\">If you have any questions, please <a href=\"{{supportUrl}}\">contact our support team</a>.</p>\n`,\n text: `Your payment didn't go through\n\nHi{{customerName}},\n\nWe weren't able to process your payment of {{amount}} for your subscription.\n\nDon't worry - we'll automatically retry your payment. In the meantime, please make sure your payment information is up to date.\n\nUpdate your payment method: {{updatePaymentUrl}}\n\nIf you have any questions, please contact our support team: {{supportUrl}}`,\n};\n\n/**\n * Payment reminder - gentle reminder\n */\nexport const paymentReminderTemplate: DunningEmailTemplate = {\n id: \"dunning-reminder\",\n name: \"Payment Reminder\",\n subject: \"Reminder: Please update your payment method\",\n html: `\n<h1>Friendly reminder about your payment</h1>\n<p>Hi{{customerName}},</p>\n<p>We wanted to remind you that we were unable to process your payment of <strong>{{amount}}</strong>. It's been {{daysSinceFailure}} days since we first tried.</p>\n<p>To keep your subscription active, please update your payment information.</p>\n<div style=\"text-align: center; margin: 24px 0;\">\n <a href=\"{{updatePaymentUrl}}\" style=\"display: inline-block; background: {{brandColor}}; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 6px; font-weight: 600;\">Update Payment Method</a>\n</div>\n<p style=\"color: #666; font-size: 14px;\">Need help? <a href=\"{{supportUrl}}\">Contact support</a></p>\n`,\n text: `Friendly reminder about your payment\n\nHi{{customerName}},\n\nWe wanted to remind you that we were unable to process your payment of {{amount}}. It's been {{daysSinceFailure}} days since we first tried.\n\nTo keep your subscription active, please update your payment information.\n\nUpdate your payment method: {{updatePaymentUrl}}\n\nNeed help? Contact support: {{supportUrl}}`,\n};\n\n/**\n * Payment warning - features may be limited\n */\nexport const paymentWarningTemplate: DunningEmailTemplate = {\n id: \"dunning-warning\",\n name: \"Payment Warning\",\n subject: \"Urgent: Your account access may be limited\",\n html: `\n<h1>Your account access may be limited soon</h1>\n<p>Hi{{customerName}},</p>\n<p>We've been trying to process your payment of <strong>{{amount}}</strong> for {{daysSinceFailure}} days without success.</p>\n<p style=\"background: #fff3cd; border: 1px solid #ffc107; border-radius: 6px; padding: 16px; margin: 16px 0;\">\n <strong>⚠️ Important:</strong> If we don't receive payment within {{daysUntilLimit}} days, some features will be limited.\n</p>\n<p>Please update your payment method to avoid any service interruption.</p>\n<div style=\"text-align: center; margin: 24px 0;\">\n <a href=\"{{updatePaymentUrl}}\" style=\"display: inline-block; background: {{brandColor}}; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 6px; font-weight: 600;\">Update Payment Now</a>\n</div>\n<p style=\"color: #666; font-size: 14px;\">Having trouble? <a href=\"{{supportUrl}}\">We're here to help</a></p>\n`,\n text: `Your account access may be limited soon\n\nHi{{customerName}},\n\nWe've been trying to process your payment of {{amount}} for {{daysSinceFailure}} days without success.\n\n⚠️ Important: If we don't receive payment within {{daysUntilLimit}} days, some features will be limited.\n\nPlease update your payment method to avoid any service interruption.\n\nUpdate your payment method: {{updatePaymentUrl}}\n\nHaving trouble? We're here to help: {{supportUrl}}`,\n};\n\n/**\n * Features limited notification\n */\nexport const featuresLimitedTemplate: DunningEmailTemplate = {\n id: \"dunning-feature-limit\",\n name: \"Features Limited\",\n subject: \"Some features have been limited on your account\",\n html: `\n<h1>Some features have been limited</h1>\n<p>Hi{{customerName}},</p>\n<p>Due to the outstanding payment of <strong>{{amount}}</strong>, we've had to limit some features on your account.</p>\n<p style=\"background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 6px; padding: 16px; margin: 16px 0;\">\n <strong>Current status:</strong> Limited access<br>\n <strong>Outstanding amount:</strong> {{amount}}<br>\n <strong>Days until suspension:</strong> {{daysUntilSuspension}}\n</p>\n<p>To restore full access, please update your payment method immediately.</p>\n<div style=\"text-align: center; margin: 24px 0;\">\n <a href=\"{{updatePaymentUrl}}\" style=\"display: inline-block; background: #dc3545; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 6px; font-weight: 600;\">Restore Full Access</a>\n</div>\n`,\n text: `Some features have been limited\n\nHi{{customerName}},\n\nDue to the outstanding payment of {{amount}}, we've had to limit some features on your account.\n\nCurrent status: Limited access\nOutstanding amount: {{amount}}\nDays until suspension: {{daysUntilSuspension}}\n\nTo restore full access, please update your payment method immediately.\n\nRestore full access: {{updatePaymentUrl}}`,\n};\n\n/**\n * Account suspension notification\n */\nexport const accountSuspendedTemplate: DunningEmailTemplate = {\n id: \"dunning-suspension\",\n name: \"Account Suspended\",\n subject: \"Your account has been suspended\",\n html: `\n<h1>Your account has been suspended</h1>\n<p>Hi{{customerName}},</p>\n<p>We've suspended your account due to an outstanding payment of <strong>{{amount}}</strong>.</p>\n<p style=\"background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 6px; padding: 16px; margin: 16px 0;\">\n <strong>⛔ Your account is now suspended</strong><br>\n You have read-only access to your data.<br>\n <strong>Days until cancellation:</strong> {{daysUntilCancellation}}\n</p>\n<p>To reactivate your account and regain full access, please pay the outstanding balance.</p>\n<div style=\"text-align: center; margin: 24px 0;\">\n <a href=\"{{updatePaymentUrl}}\" style=\"display: inline-block; background: #dc3545; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 6px; font-weight: 600;\">Reactivate Account</a>\n</div>\n<p style=\"color: #666; font-size: 14px;\">If you need to discuss payment options, please <a href=\"{{supportUrl}}\">contact us</a>.</p>\n`,\n text: `Your account has been suspended\n\nHi{{customerName}},\n\nWe've suspended your account due to an outstanding payment of {{amount}}.\n\n⛔ Your account is now suspended\nYou have read-only access to your data.\nDays until cancellation: {{daysUntilCancellation}}\n\nTo reactivate your account and regain full access, please pay the outstanding balance.\n\nReactivate account: {{updatePaymentUrl}}\n\nIf you need to discuss payment options, please contact us: {{supportUrl}}`,\n};\n\n/**\n * Final warning before cancellation\n */\nexport const finalWarningTemplate: DunningEmailTemplate = {\n id: \"dunning-final-warning\",\n name: \"Final Warning\",\n subject: \"Final notice: Your subscription will be canceled\",\n html: `\n<h1>Final notice before cancellation</h1>\n<p>Hi{{customerName}},</p>\n<p>This is your final notice. Your subscription will be <strong>automatically canceled</strong> in {{daysUntilCancellation}} days due to an unpaid balance of <strong>{{amount}}</strong>.</p>\n<p style=\"background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 6px; padding: 16px; margin: 16px 0;\">\n <strong>🚨 Action required immediately</strong><br>\n After cancellation, your data may be permanently deleted according to our data retention policy.\n</p>\n<p>Please pay now to keep your account and data.</p>\n<div style=\"text-align: center; margin: 24px 0;\">\n <a href=\"{{updatePaymentUrl}}\" style=\"display: inline-block; background: #dc3545; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 6px; font-weight: 600;\">Pay Now - Prevent Cancellation</a>\n</div>\n<p style=\"color: #666; font-size: 14px;\">Questions? <a href=\"{{supportUrl}}\">Contact us immediately</a></p>\n`,\n text: `Final notice before cancellation\n\nHi{{customerName}},\n\nThis is your final notice. Your subscription will be automatically canceled in {{daysUntilCancellation}} days due to an unpaid balance of {{amount}}.\n\n🚨 Action required immediately\nAfter cancellation, your data may be permanently deleted according to our data retention policy.\n\nPlease pay now to keep your account and data.\n\nPay now: {{updatePaymentUrl}}\n\nQuestions? Contact us immediately: {{supportUrl}}`,\n};\n\n/**\n * Subscription canceled\n */\nexport const subscriptionCanceledTemplate: DunningEmailTemplate = {\n id: \"dunning-canceled\",\n name: \"Subscription Canceled\",\n subject: \"Your subscription has been canceled\",\n html: `\n<h1>Your subscription has been canceled</h1>\n<p>Hi{{customerName}},</p>\n<p>Your subscription has been canceled due to non-payment of <strong>{{amount}}</strong>.</p>\n<p style=\"background: #f8f9fa; border-radius: 6px; padding: 16px; margin: 16px 0;\">\n Your data will be retained for 30 days. After that, it may be permanently deleted.\n</p>\n<p>If you'd like to resubscribe, you can do so at any time:</p>\n<div style=\"text-align: center; margin: 24px 0;\">\n <a href=\"{{updatePaymentUrl}}\" style=\"display: inline-block; background: {{brandColor}}; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 6px; font-weight: 600;\">Resubscribe</a>\n</div>\n<p>We're sorry to see you go. If there's anything we can do to help, please <a href=\"{{supportUrl}}\">let us know</a>.</p>\n`,\n text: `Your subscription has been canceled\n\nHi{{customerName}},\n\nYour subscription has been canceled due to non-payment of {{amount}}.\n\nYour data will be retained for 30 days. After that, it may be permanently deleted.\n\nIf you'd like to resubscribe, you can do so at any time: {{updatePaymentUrl}}\n\nWe're sorry to see you go. If there's anything we can do to help, please let us know: {{supportUrl}}`,\n};\n\n/**\n * Payment recovered - success notification\n */\nexport const paymentRecoveredTemplate: DunningEmailTemplate = {\n id: \"dunning-recovered\",\n name: \"Payment Recovered\",\n subject: \"Good news! Your payment was successful\",\n html: `\n<h1>Your payment was successful! 🎉</h1>\n<p>Hi{{customerName}},</p>\n<p>Great news! We've successfully processed your payment of <strong>{{amount}}</strong>.</p>\n<p style=\"background: #d4edda; border: 1px solid #c3e6cb; border-radius: 6px; padding: 16px; margin: 16px 0;\">\n <strong>✓ Payment received:</strong> {{amount}}<br>\n <strong>✓ Account status:</strong> Active\n</p>\n<p>Your subscription is now fully active. Thank you for being a valued customer!</p>\n<p style=\"color: #666; font-size: 14px;\">If you have any questions, we're always here to help at <a href=\"{{supportUrl}}\">support</a>.</p>\n`,\n text: `Your payment was successful! 🎉\n\nHi{{customerName}},\n\nGreat news! We've successfully processed your payment of {{amount}}.\n\n✓ Payment received: {{amount}}\n✓ Account status: Active\n\nYour subscription is now fully active. Thank you for being a valued customer!\n\nIf you have any questions, we're always here to help: {{supportUrl}}`,\n};\n\n// ============================================================================\n// Template Registry\n// ============================================================================\n\n/**\n * All default dunning templates\n */\nexport const dunningEmailTemplates: Record<string, DunningEmailTemplate> = {\n \"dunning-payment-failed\": paymentFailedTemplate,\n \"dunning-reminder\": paymentReminderTemplate,\n \"dunning-warning\": paymentWarningTemplate,\n \"dunning-feature-limit\": featuresLimitedTemplate,\n \"dunning-suspension\": accountSuspendedTemplate,\n \"dunning-final-warning\": finalWarningTemplate,\n \"dunning-canceled\": subscriptionCanceledTemplate,\n \"dunning-recovered\": paymentRecoveredTemplate,\n};\n\n// ============================================================================\n// Template Renderer\n// ============================================================================\n\n/**\n * Render a dunning email template\n */\nexport function renderDunningEmail(\n templateId: string,\n data: DunningEmailData,\n customTemplates?: Record<string, DunningEmailTemplate>\n): RenderedEmail {\n // Look up template\n const templates = { ...dunningEmailTemplates, ...customTemplates };\n const template = templates[templateId];\n\n if (!template) {\n throw new Error(`Dunning email template not found: ${templateId}`);\n }\n\n // Prepare data with defaults\n const templateData: Record<string, unknown> = {\n ...data,\n customerName: data.customerName ? ` ${data.customerName}` : \"\",\n brandColor: data.brandColor ?? \"#0070f3\",\n brandName: data.brandName ?? \"Your Service\",\n };\n\n // Add card info if available\n if (data.cardLast4 && data.cardBrand) {\n templateData[\"cardInfo\"] = `<p style=\"color: #666;\">Card ending in ${data.cardLast4} (${data.cardBrand})</p>`;\n } else {\n templateData[\"cardInfo\"] = \"\";\n }\n\n return {\n subject: renderTemplate(template.subject, templateData),\n html: renderTemplate(template.html, templateData),\n text: renderTemplate(template.text, templateData),\n };\n}\n\n/**\n * Build template data from dunning context\n */\nexport function buildTemplateData(\n context: DunningContext,\n options?: {\n brandName?: string;\n brandColor?: string;\n updatePaymentUrl?: string;\n invoiceUrl?: string;\n supportUrl?: string;\n daysUntilLimit?: number;\n daysUntilSuspension?: number;\n daysUntilCancellation?: number;\n }\n): DunningEmailData {\n // Build result with required fields\n const result: DunningEmailData = {\n amount: formatAmount(context.amountOwed, context.currency),\n currency: context.currency,\n daysSinceFailure: context.daysSinceFailure,\n };\n\n // Add optional fields only if they have values\n if (context.customer.name) result.customerName = context.customer.name;\n if (options?.daysUntilLimit !== undefined) result.daysUntilLimit = options.daysUntilLimit;\n if (options?.daysUntilSuspension !== undefined) result.daysUntilSuspension = options.daysUntilSuspension;\n if (options?.daysUntilCancellation !== undefined) result.daysUntilCancellation = options.daysUntilCancellation;\n if (options?.updatePaymentUrl) result.updatePaymentUrl = options.updatePaymentUrl;\n if (options?.invoiceUrl) result.invoiceUrl = options.invoiceUrl;\n if (options?.supportUrl) result.supportUrl = options.supportUrl;\n if (options?.brandName) result.brandName = options.brandName;\n if (options?.brandColor) result.brandColor = options.brandColor;\n\n return result;\n}\n","/**\n * @parsrun/payments - Dunning Email Integration\n * Built-in integration with @parsrun/email for sending dunning notifications\n */\n\nimport type {\n DunningNotification,\n NotificationResult,\n DunningContext,\n DunningLogger,\n} from \"./types.js\";\nimport {\n renderDunningEmail,\n buildTemplateData,\n type DunningEmailTemplate,\n type DunningEmailData,\n} from \"./email-templates.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Email service interface (compatible with @parsrun/email)\n * This allows using @parsrun/email or any compatible email service\n */\nexport interface EmailServiceLike {\n send(options: {\n to: string | string[];\n subject: string;\n html: string;\n text?: string;\n replyTo?: string;\n tags?: string[];\n }): Promise<{ success: boolean; messageId?: string; error?: string }>;\n}\n\n/**\n * Configuration for dunning email integration\n */\nexport interface DunningEmailIntegrationConfig {\n /** Email service instance (from @parsrun/email or compatible) */\n emailService: EmailServiceLike;\n\n /** Brand configuration */\n brand?: {\n name?: string;\n color?: string;\n logoUrl?: string;\n };\n\n /** URLs for dunning emails */\n urls?: {\n updatePayment?: string | ((customerId: string) => string);\n viewInvoice?: string | ((invoiceId: string) => string);\n support?: string;\n unsubscribe?: string | ((customerId: string) => string);\n };\n\n /** Reply-to email address */\n replyTo?: string;\n\n /** Email tags for tracking */\n tags?: string[];\n\n /** Custom email templates (override defaults) */\n customTemplates?: Record<string, DunningEmailTemplate>;\n\n /** Data enrichment function */\n enrichData?: (data: DunningEmailData, context: DunningContext) => DunningEmailData;\n\n /** Logger */\n logger?: DunningLogger;\n\n /** Skip sending to specific customers */\n skip?: (notification: DunningNotification) => boolean;\n}\n\n/**\n * Dunning email notification handler result\n */\nexport interface DunningEmailHandler {\n /** Notification handler to use in DunningManagerConfig */\n handler: (notification: DunningNotification) => Promise<NotificationResult>;\n\n /** Send a one-off dunning email */\n sendEmail: (templateId: string, to: string, context: DunningContext) => Promise<NotificationResult>;\n\n /** Send recovery success email */\n sendRecoveryEmail: (to: string, context: DunningContext) => Promise<NotificationResult>;\n}\n\n// ============================================================================\n// Email Integration\n// ============================================================================\n\n/**\n * Create a dunning email notification handler\n *\n * @example\n * ```typescript\n * import { createEmailService } from '@parsrun/email';\n * import { createDunningManager, createDunningEmailHandler } from '@parsrun/payments/dunning';\n *\n * const emailService = createEmailService({\n * provider: 'resend',\n * apiKey: process.env.RESEND_API_KEY,\n * fromEmail: 'billing@example.com',\n * fromName: 'Billing',\n * });\n *\n * const dunningEmail = createDunningEmailHandler({\n * emailService,\n * brand: { name: 'MyApp', color: '#0070f3' },\n * urls: {\n * updatePayment: (customerId) => `https://app.example.com/billing?customer=${customerId}`,\n * support: 'https://app.example.com/support',\n * },\n * });\n *\n * const dunningManager = createDunningManager({\n * defaultSequence: standardSaasSequence,\n * storage,\n * onNotification: dunningEmail.handler,\n * });\n * ```\n */\nexport function createDunningEmailHandler(\n config: DunningEmailIntegrationConfig\n): DunningEmailHandler {\n const { emailService, logger } = config;\n\n /**\n * Build URLs from config\n */\n const buildUrls = (\n notification: DunningNotification\n ): {\n updatePaymentUrl?: string;\n invoiceUrl?: string;\n supportUrl?: string;\n } => {\n const { urls } = config;\n if (!urls) return {};\n\n const result: {\n updatePaymentUrl?: string;\n invoiceUrl?: string;\n supportUrl?: string;\n } = {};\n\n const customerId = notification.recipient.customerId;\n const invoiceId = notification.context.state.initialFailure.invoiceId;\n\n // Update payment URL\n const updatePaymentUrl =\n typeof urls.updatePayment === \"function\"\n ? urls.updatePayment(customerId)\n : urls.updatePayment;\n if (updatePaymentUrl) result.updatePaymentUrl = updatePaymentUrl;\n\n // Invoice URL\n if (invoiceId && typeof urls.viewInvoice === \"function\") {\n result.invoiceUrl = urls.viewInvoice(invoiceId);\n } else if (typeof urls.viewInvoice === \"string\") {\n result.invoiceUrl = urls.viewInvoice;\n }\n\n // Support URL\n if (urls.support) result.supportUrl = urls.support;\n\n return result;\n };\n\n /**\n * Calculate days until events based on sequence\n */\n const calculateDaysUntil = (\n context: DunningContext\n ): {\n daysUntilLimit?: number;\n daysUntilSuspension?: number;\n daysUntilCancellation?: number;\n } => {\n const result: {\n daysUntilLimit?: number;\n daysUntilSuspension?: number;\n daysUntilCancellation?: number;\n } = {};\n\n // This would ideally come from sequence analysis\n // For now, use step metadata if available\n const stepMeta = context.step.metadata as Record<string, unknown> | undefined;\n if (stepMeta) {\n if (typeof stepMeta[\"daysUntilLimit\"] === \"number\") {\n result.daysUntilLimit = stepMeta[\"daysUntilLimit\"] as number;\n }\n if (typeof stepMeta[\"daysUntilSuspension\"] === \"number\") {\n result.daysUntilSuspension = stepMeta[\"daysUntilSuspension\"] as number;\n }\n if (typeof stepMeta[\"daysUntilCancellation\"] === \"number\") {\n result.daysUntilCancellation = stepMeta[\"daysUntilCancellation\"] as number;\n }\n }\n\n return result;\n };\n\n /**\n * Send a dunning email\n */\n const sendEmail = async (\n templateId: string,\n to: string,\n context: DunningContext\n ): Promise<NotificationResult> => {\n try {\n // Build template data\n const urls = buildUrls({\n channel: \"email\",\n templateId,\n recipient: { customerId: context.customer.id, email: to },\n variables: {\n amount: context.amountOwed,\n currency: context.currency,\n daysSinceFailure: context.daysSinceFailure,\n },\n context,\n });\n\n const daysUntil = calculateDaysUntil(context);\n\n // Build options for template data (only include defined values)\n const templateOptions: {\n brandName?: string;\n brandColor?: string;\n updatePaymentUrl?: string;\n invoiceUrl?: string;\n supportUrl?: string;\n daysUntilLimit?: number;\n daysUntilSuspension?: number;\n daysUntilCancellation?: number;\n } = {};\n\n if (config.brand?.name) templateOptions.brandName = config.brand.name;\n if (config.brand?.color) templateOptions.brandColor = config.brand.color;\n if (urls.updatePaymentUrl) templateOptions.updatePaymentUrl = urls.updatePaymentUrl;\n if (urls.invoiceUrl) templateOptions.invoiceUrl = urls.invoiceUrl;\n if (urls.supportUrl) templateOptions.supportUrl = urls.supportUrl;\n if (daysUntil.daysUntilLimit !== undefined) templateOptions.daysUntilLimit = daysUntil.daysUntilLimit;\n if (daysUntil.daysUntilSuspension !== undefined) templateOptions.daysUntilSuspension = daysUntil.daysUntilSuspension;\n if (daysUntil.daysUntilCancellation !== undefined) templateOptions.daysUntilCancellation = daysUntil.daysUntilCancellation;\n\n let emailData = buildTemplateData(context, templateOptions);\n\n // Allow data enrichment\n if (config.enrichData) {\n emailData = config.enrichData(emailData, context);\n }\n\n // Render email\n const rendered = renderDunningEmail(templateId, emailData, config.customTemplates);\n\n // Build send options (only include defined values)\n const sendOptions: {\n to: string | string[];\n subject: string;\n html: string;\n text?: string;\n replyTo?: string;\n tags?: string[];\n } = {\n to,\n subject: rendered.subject,\n html: rendered.html,\n text: rendered.text,\n tags: config.tags ?? [\"dunning\"],\n };\n if (config.replyTo) sendOptions.replyTo = config.replyTo;\n\n // Send via email service\n const result = await emailService.send(sendOptions);\n\n logger?.debug(\"Dunning email sent\", {\n templateId,\n to,\n success: result.success,\n messageId: result.messageId,\n });\n\n // Build result (only include defined values)\n const notificationResult: NotificationResult = {\n success: result.success,\n channel: \"email\",\n sentAt: new Date(),\n };\n if (result.messageId) notificationResult.externalId = result.messageId;\n if (result.error) notificationResult.error = result.error;\n\n return notificationResult;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n logger?.error(\"Failed to send dunning email\", {\n templateId,\n to,\n error: errorMessage,\n });\n\n return {\n success: false,\n channel: \"email\",\n error: errorMessage,\n sentAt: new Date(),\n };\n }\n };\n\n /**\n * Main notification handler\n */\n const handler = async (notification: DunningNotification): Promise<NotificationResult> => {\n // Only handle email channel\n if (notification.channel !== \"email\") {\n return {\n success: false,\n channel: notification.channel,\n error: `Channel ${notification.channel} not supported by email integration`,\n sentAt: new Date(),\n };\n }\n\n // Check skip condition\n if (config.skip?.(notification)) {\n logger?.debug(\"Skipping dunning notification\", {\n customerId: notification.recipient.customerId,\n templateId: notification.templateId,\n });\n\n return {\n success: true,\n channel: \"email\",\n sentAt: new Date(),\n };\n }\n\n // Get recipient email\n const email = notification.recipient.email;\n if (!email) {\n logger?.warn(\"No email address for dunning notification\", {\n customerId: notification.recipient.customerId,\n });\n\n return {\n success: false,\n channel: \"email\",\n error: \"No email address available\",\n sentAt: new Date(),\n };\n }\n\n return sendEmail(notification.templateId, email, notification.context);\n };\n\n /**\n * Send recovery success email\n */\n const sendRecoveryEmail = async (\n to: string,\n context: DunningContext\n ): Promise<NotificationResult> => {\n return sendEmail(\"dunning-recovered\", to, context);\n };\n\n return {\n handler,\n sendEmail,\n sendRecoveryEmail,\n };\n}\n\n// ============================================================================\n// Multi-Channel Integration\n// ============================================================================\n\n/**\n * SMS service interface (for future SMS integration)\n */\nexport interface SmsServiceLike {\n send(options: {\n to: string;\n message: string;\n }): Promise<{ success: boolean; messageId?: string; error?: string }>;\n}\n\n/**\n * Configuration for multi-channel dunning notifications\n */\nexport interface MultiChannelDunningConfig {\n /** Email service */\n email?: DunningEmailIntegrationConfig;\n\n /** SMS service (future) */\n sms?: {\n service: SmsServiceLike;\n templates?: Record<string, string>;\n };\n\n /** In-app notification handler */\n inApp?: (notification: DunningNotification) => Promise<NotificationResult>;\n\n /** Webhook handler */\n webhook?: (notification: DunningNotification) => Promise<NotificationResult>;\n\n /** Push notification handler */\n push?: (notification: DunningNotification) => Promise<NotificationResult>;\n\n /** Logger */\n logger?: DunningLogger;\n}\n\n/**\n * Create a multi-channel notification handler\n *\n * @example\n * ```typescript\n * const notificationHandler = createMultiChannelHandler({\n * email: {\n * emailService,\n * brand: { name: 'MyApp' },\n * },\n * inApp: async (notification) => {\n * await pushToInAppNotifications(notification);\n * return { success: true, channel: 'in_app', sentAt: new Date() };\n * },\n * });\n *\n * const dunningManager = createDunningManager({\n * defaultSequence: standardSaasSequence,\n * storage,\n * onNotification: notificationHandler,\n * });\n * ```\n */\nexport function createMultiChannelHandler(\n config: MultiChannelDunningConfig\n): (notification: DunningNotification) => Promise<NotificationResult> {\n const emailHandler = config.email\n ? createDunningEmailHandler(config.email)\n : undefined;\n\n return async (notification: DunningNotification): Promise<NotificationResult> => {\n const { channel } = notification;\n\n switch (channel) {\n case \"email\":\n if (!emailHandler) {\n config.logger?.warn(\"Email channel not configured\", {\n templateId: notification.templateId,\n });\n return {\n success: false,\n channel: \"email\",\n error: \"Email channel not configured\",\n sentAt: new Date(),\n };\n }\n return emailHandler.handler(notification);\n\n case \"sms\":\n if (!config.sms) {\n return {\n success: false,\n channel: \"sms\",\n error: \"SMS channel not configured\",\n sentAt: new Date(),\n };\n }\n // SMS implementation placeholder\n const phone = notification.recipient.phone;\n if (!phone) {\n return {\n success: false,\n channel: \"sms\",\n error: \"No phone number available\",\n sentAt: new Date(),\n };\n }\n const template = config.sms.templates?.[notification.templateId];\n if (!template) {\n return {\n success: false,\n channel: \"sms\",\n error: `SMS template not found: ${notification.templateId}`,\n sentAt: new Date(),\n };\n }\n const smsResult = await config.sms.service.send({\n to: phone,\n message: template, // TODO: render template\n });\n const smsNotificationResult: NotificationResult = {\n success: smsResult.success,\n channel: \"sms\",\n sentAt: new Date(),\n };\n if (smsResult.messageId) smsNotificationResult.externalId = smsResult.messageId;\n if (smsResult.error) smsNotificationResult.error = smsResult.error;\n return smsNotificationResult;\n\n case \"in_app\":\n if (!config.inApp) {\n return {\n success: false,\n channel: \"in_app\",\n error: \"In-app channel not configured\",\n sentAt: new Date(),\n };\n }\n return config.inApp(notification);\n\n case \"webhook\":\n if (!config.webhook) {\n return {\n success: false,\n channel: \"webhook\",\n error: \"Webhook channel not configured\",\n sentAt: new Date(),\n };\n }\n return config.webhook(notification);\n\n case \"push\":\n if (!config.push) {\n return {\n success: false,\n channel: \"push\",\n error: \"Push channel not configured\",\n sentAt: new Date(),\n };\n }\n return config.push(notification);\n\n default:\n return {\n success: false,\n channel,\n error: `Unknown channel: ${channel}`,\n sentAt: new Date(),\n };\n }\n };\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Create a simple email-only notification handler\n * Convenience wrapper for common use case\n *\n * @example\n * ```typescript\n * import { createEmailService } from '@parsrun/email';\n * import { createDunningManager, simpleEmailHandler } from '@parsrun/payments/dunning';\n *\n * const emailService = createEmailService({ ... });\n *\n * const dunningManager = createDunningManager({\n * defaultSequence: standardSaasSequence,\n * storage,\n * onNotification: simpleEmailHandler(emailService, {\n * brandName: 'MyApp',\n * updatePaymentUrl: 'https://app.example.com/billing',\n * }),\n * });\n * ```\n */\nexport function simpleEmailHandler(\n emailService: EmailServiceLike,\n options?: {\n brandName?: string;\n brandColor?: string;\n updatePaymentUrl?: string;\n supportUrl?: string;\n replyTo?: string;\n }\n): (notification: DunningNotification) => Promise<NotificationResult> {\n // Build config object with only defined values\n const config: DunningEmailIntegrationConfig = {\n emailService,\n };\n\n // Add brand if any option is set\n if (options?.brandName || options?.brandColor) {\n const brand: { name?: string; color?: string } = {};\n if (options.brandName) brand.name = options.brandName;\n if (options.brandColor) brand.color = options.brandColor;\n config.brand = brand;\n }\n\n // Add urls if any option is set\n if (options?.updatePaymentUrl || options?.supportUrl) {\n const urls: { updatePayment?: string; support?: string } = {};\n if (options.updatePaymentUrl) urls.updatePayment = options.updatePaymentUrl;\n if (options.supportUrl) urls.support = options.supportUrl;\n config.urls = urls;\n }\n\n // Add replyTo if set\n if (options?.replyTo) {\n config.replyTo = options.replyTo;\n }\n\n const handler = createDunningEmailHandler(config);\n return handler.handler;\n}\n"],"mappings":"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","names":["sequence","step","step"]}