@nehorai/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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/dist/config/index.cjs +116 -0
  3. package/dist/config/index.cjs.map +1 -0
  4. package/dist/config/index.d.cts +125 -0
  5. package/dist/config/index.d.ts +125 -0
  6. package/dist/config/index.js +83 -0
  7. package/dist/config/index.js.map +1 -0
  8. package/dist/factory.cjs +807 -0
  9. package/dist/factory.cjs.map +1 -0
  10. package/dist/factory.d.cts +96 -0
  11. package/dist/factory.d.ts +96 -0
  12. package/dist/factory.js +777 -0
  13. package/dist/factory.js.map +1 -0
  14. package/dist/index.cjs +1341 -0
  15. package/dist/index.cjs.map +1 -0
  16. package/dist/index.d.cts +40 -0
  17. package/dist/index.d.ts +40 -0
  18. package/dist/index.js +1260 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/payment-orchestrator-CPaLmDM5.d.ts +404 -0
  21. package/dist/payment-orchestrator-Co_X6T_V.d.cts +404 -0
  22. package/dist/payment-types-68W-PlGg.d.cts +211 -0
  23. package/dist/payment-types-68W-PlGg.d.ts +211 -0
  24. package/dist/providers/interfaces/index.cjs +19 -0
  25. package/dist/providers/interfaces/index.cjs.map +1 -0
  26. package/dist/providers/interfaces/index.d.cts +80 -0
  27. package/dist/providers/interfaces/index.d.ts +80 -0
  28. package/dist/providers/interfaces/index.js +1 -0
  29. package/dist/providers/interfaces/index.js.map +1 -0
  30. package/dist/repository/interfaces/index.cjs +19 -0
  31. package/dist/repository/interfaces/index.cjs.map +1 -0
  32. package/dist/repository/interfaces/index.d.cts +556 -0
  33. package/dist/repository/interfaces/index.d.ts +556 -0
  34. package/dist/repository/interfaces/index.js +1 -0
  35. package/dist/repository/interfaces/index.js.map +1 -0
  36. package/dist/routing-engine.interface-DJzGXor9.d.cts +194 -0
  37. package/dist/routing-engine.interface-h9_GmQ4b.d.ts +194 -0
  38. package/dist/services/index.cjs +806 -0
  39. package/dist/services/index.cjs.map +1 -0
  40. package/dist/services/index.d.cts +75 -0
  41. package/dist/services/index.d.ts +75 -0
  42. package/dist/services/index.js +763 -0
  43. package/dist/services/index.js.map +1 -0
  44. package/dist/state-machine-Cu6_qKnv.d.cts +109 -0
  45. package/dist/state-machine-Cu6_qKnv.d.ts +109 -0
  46. package/dist/types/index.cjs +173 -0
  47. package/dist/types/index.cjs.map +1 -0
  48. package/dist/types/index.d.cts +127 -0
  49. package/dist/types/index.d.ts +127 -0
  50. package/dist/types/index.js +130 -0
  51. package/dist/types/index.js.map +1 -0
  52. package/dist/utils/index.cjs +167 -0
  53. package/dist/utils/index.cjs.map +1 -0
  54. package/dist/utils/index.d.cts +102 -0
  55. package/dist/utils/index.d.ts +102 -0
  56. package/dist/utils/index.js +127 -0
  57. package/dist/utils/index.js.map +1 -0
  58. package/package.json +68 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/payment-orchestrator.ts","../../src/config/payment-config.ts","../../src/services/circuit-breaker-storage.interface.ts","../../src/services/in-memory-storage.ts","../../src/services/circuit-breaker.ts","../../src/services/routing-engine.ts"],"sourcesContent":["/**\r\n * @nehorai/payments - Payment Orchestrator Service\r\n *\r\n * Main entry point for payment operations. Coordinates between:\r\n * - Routing Engine (provider selection)\r\n * - Payment Providers (actual processing)\r\n * - Circuit Breaker (resilience)\r\n *\r\n * Supports full dependency injection for:\r\n * - Provider instances\r\n * - Routing engine\r\n * - Circuit breaker\r\n */\r\n\r\nimport { randomUUID } from 'crypto'\r\nimport type { PaymentProvider, PaymentAmount, TransactionStatus, PaymentMetadata } from '../types/index.js'\r\nimport type { IPaymentProvider, RoutingDecision } from '../providers/interfaces/index.js'\r\nimport { RoutingEngine, getRoutingEngine } from './routing-engine.js'\r\nimport { CircuitBreaker, getCircuitBreaker } from './circuit-breaker.js'\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface InitiatePaymentParams {\r\n userId: string\r\n amount: PaymentAmount\r\n transactionType: 'one_time_purchase' | 'subscription_initial' | 'subscription_renewal'\r\n description?: string\r\n metadata?: PaymentMetadata\r\n preferredProvider?: PaymentProvider\r\n cardBin?: string\r\n returnUrl: string\r\n /** If true, auto-capture immediately (no J5 hold) */\r\n autoCapture?: boolean\r\n}\r\n\r\nexport interface PaymentInitiationResult {\r\n success: boolean\r\n transactionId: string\r\n internalPaymentId: string\r\n provider: PaymentProvider\r\n clientSecret?: string\r\n redirectUrl?: string\r\n error?: string\r\n}\r\n\r\nexport interface ConfirmPaymentParams {\r\n transactionId: string\r\n internalPaymentId: string\r\n providerIntentId: string\r\n provider: PaymentProvider\r\n}\r\n\r\nexport interface PaymentConfirmationResult {\r\n success: boolean\r\n transactionId: string\r\n status: TransactionStatus\r\n error?: string\r\n}\r\n\r\nexport interface CapturePaymentParams {\r\n transactionId: string\r\n providerIntentId: string\r\n provider: PaymentProvider\r\n amount?: PaymentAmount\r\n}\r\n\r\nexport interface PaymentCaptureResult {\r\n success: boolean\r\n status: TransactionStatus\r\n capturedAmount?: PaymentAmount\r\n error?: string\r\n}\r\n\r\n/**\r\n * Payment orchestrator dependencies (for dependency injection)\r\n */\r\nexport interface PaymentOrchestratorDeps {\r\n /** Provider instances - required */\r\n providers: Map<PaymentProvider, IPaymentProvider>\r\n /** Routing engine (optional, defaults to singleton) */\r\n routingEngine?: RoutingEngine\r\n /** Circuit breaker (optional, defaults to singleton) */\r\n circuitBreaker?: CircuitBreaker\r\n}\r\n\r\n// ============================================================================\r\n// Orchestrator Service\r\n// ============================================================================\r\n\r\n/**\r\n * Payment Orchestrator\r\n *\r\n * Standalone service for payment orchestration.\r\n * Supports full dependency injection for testing and customization.\r\n */\r\nexport class PaymentOrchestrator {\r\n private providers: Map<PaymentProvider, IPaymentProvider>\r\n private routingEngine: RoutingEngine\r\n private circuitBreaker: CircuitBreaker\r\n\r\n constructor(deps: PaymentOrchestratorDeps) {\r\n this.providers = deps.providers\r\n this.routingEngine = deps.routingEngine ?? getRoutingEngine()\r\n this.circuitBreaker = deps.circuitBreaker ?? getCircuitBreaker()\r\n }\r\n\r\n /**\r\n * Initiate a new payment\r\n */\r\n async initiatePayment(params: InitiatePaymentParams): Promise<PaymentInitiationResult> {\r\n const internalPaymentId = `pay_${randomUUID()}`\r\n const idempotencyKey = `idem_${randomUUID()}`\r\n\r\n try {\r\n const routing = await this.routingEngine.route({\r\n userId: params.userId,\r\n amount: params.amount,\r\n cardBin: params.cardBin,\r\n preferredProvider: params.preferredProvider,\r\n isRecurring: params.transactionType !== 'one_time_purchase',\r\n })\r\n\r\n const provider = this.getProvider(routing.provider)\r\n if (!provider) {\r\n return this.tryFailover(params, routing, internalPaymentId)\r\n }\r\n\r\n if (!(await this.circuitBreaker.canExecute(routing.provider))) {\r\n return this.tryFailover(params, routing, internalPaymentId)\r\n }\r\n\r\n const result = await provider.createPaymentIntent({\r\n amount: params.amount,\r\n userId: params.userId,\r\n idempotencyKey,\r\n description: params.description,\r\n metadata: params.metadata,\r\n returnUrl: params.returnUrl,\r\n captureMethod: params.autoCapture ? 'automatic' : 'manual',\r\n })\r\n\r\n if (!result.success) {\r\n await this.circuitBreaker.recordFailure(routing.provider)\r\n return this.tryFailover(params, routing, internalPaymentId)\r\n }\r\n\r\n await this.circuitBreaker.recordSuccess(routing.provider)\r\n\r\n return {\r\n success: true,\r\n transactionId: result.providerIntentId!,\r\n internalPaymentId,\r\n provider: routing.provider,\r\n clientSecret: result.clientSecret,\r\n redirectUrl: result.redirectUrl,\r\n }\r\n } catch (error) {\r\n return {\r\n success: false,\r\n transactionId: '',\r\n internalPaymentId,\r\n provider: 'unknown',\r\n error: error instanceof Error ? error.message : 'Unknown error',\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Confirm a payment after user authorization\r\n */\r\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentConfirmationResult> {\r\n const provider = this.getProvider(params.provider)\r\n if (!provider) {\r\n return {\r\n success: false,\r\n transactionId: params.transactionId,\r\n status: 'failed',\r\n error: `Provider ${params.provider} not available`,\r\n }\r\n }\r\n\r\n const result = await provider.authorize({\r\n providerIntentId: params.providerIntentId,\r\n idempotencyKey: `auth_${params.internalPaymentId}`,\r\n })\r\n\r\n if (!result.success) {\r\n return {\r\n success: false,\r\n transactionId: params.transactionId,\r\n status: 'failed',\r\n error: result.error,\r\n }\r\n }\r\n\r\n return {\r\n success: true,\r\n transactionId: params.transactionId,\r\n status: (result.status as TransactionStatus) ?? 'authorized',\r\n }\r\n }\r\n\r\n /**\r\n * Capture an authorized payment (J5 completion)\r\n */\r\n async capturePayment(params: CapturePaymentParams): Promise<PaymentCaptureResult> {\r\n const provider = this.getProvider(params.provider)\r\n if (!provider) {\r\n return {\r\n success: false,\r\n status: 'failed',\r\n error: `Provider ${params.provider} not available`,\r\n }\r\n }\r\n\r\n const result = await provider.capture({\r\n providerIntentId: params.providerIntentId,\r\n authorizationCode: params.providerIntentId,\r\n amount: params.amount,\r\n idempotencyKey: `cap_${params.transactionId}`,\r\n })\r\n\r\n if (!result.success) {\r\n return {\r\n success: false,\r\n status: 'failed',\r\n error: result.error,\r\n }\r\n }\r\n\r\n return {\r\n success: true,\r\n status: 'captured',\r\n capturedAmount: result.capturedAmount,\r\n }\r\n }\r\n\r\n /**\r\n * Get the routing engine (for testing/debugging)\r\n */\r\n getRoutingEngine(): RoutingEngine {\r\n return this.routingEngine\r\n }\r\n\r\n /**\r\n * Get the circuit breaker (for testing/debugging)\r\n */\r\n getCircuitBreaker(): CircuitBreaker {\r\n return this.circuitBreaker\r\n }\r\n\r\n /**\r\n * Get available providers (for testing/debugging)\r\n */\r\n getProviders(): Map<PaymentProvider, IPaymentProvider> {\r\n return new Map(this.providers)\r\n }\r\n\r\n // ==========================================================================\r\n // Private Methods\r\n // ==========================================================================\r\n\r\n private getProvider(name: PaymentProvider): IPaymentProvider | undefined {\r\n return this.providers.get(name)\r\n }\r\n\r\n private async tryFailover(\r\n params: InitiatePaymentParams,\r\n routing: RoutingDecision,\r\n internalPaymentId: string\r\n ): Promise<PaymentInitiationResult> {\r\n for (const fallback of routing.fallbackProviders) {\r\n const provider = this.getProvider(fallback)\r\n if (!provider) continue\r\n\r\n if (!(await this.circuitBreaker.canExecute(fallback))) continue\r\n\r\n const result = await provider.createPaymentIntent({\r\n amount: params.amount,\r\n userId: params.userId,\r\n idempotencyKey: `idem_${randomUUID()}`,\r\n description: params.description,\r\n metadata: params.metadata,\r\n returnUrl: params.returnUrl,\r\n captureMethod: params.autoCapture ? 'automatic' : 'manual',\r\n })\r\n\r\n if (result.success) {\r\n await this.circuitBreaker.recordSuccess(fallback)\r\n return {\r\n success: true,\r\n transactionId: result.providerIntentId!,\r\n internalPaymentId,\r\n provider: fallback,\r\n clientSecret: result.clientSecret,\r\n }\r\n }\r\n\r\n await this.circuitBreaker.recordFailure(fallback)\r\n }\r\n\r\n return {\r\n success: false,\r\n transactionId: '',\r\n internalPaymentId,\r\n provider: routing.provider,\r\n error: 'All payment providers failed',\r\n }\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Factory Function\r\n// ============================================================================\r\n\r\n/**\r\n * Create a payment orchestrator with custom dependencies\r\n */\r\nexport function createPaymentOrchestrator(deps: PaymentOrchestratorDeps): PaymentOrchestrator {\r\n return new PaymentOrchestrator(deps)\r\n}\r\n","/**\r\n * @nehorai/payments - Injectable Configuration\r\n *\r\n * Provides a framework-agnostic configuration interface that can be\r\n * populated from any source (env vars, secrets manager, database, etc.).\r\n *\r\n * Usage:\r\n * ```typescript\r\n * // Option 1: Create from custom source\r\n * const config = createConfig({\r\n * providers: {\r\n * stripe: { secretKey: 'sk_test_...' },\r\n * },\r\n * environment: 'sandbox',\r\n * defaultCurrency: 'USD',\r\n * });\r\n *\r\n * // Option 2: Create from environment variables\r\n * const config = createConfigFromEnv({\r\n * stripeSecretKey: 'STRIPE_SECRET_KEY',\r\n * stripePublishableKey: 'STRIPE_PUBLISHABLE_KEY',\r\n * stripeWebhookSecret: 'STRIPE_WEBHOOK_SECRET',\r\n * environment: 'PAYMENT_ENVIRONMENT',\r\n * defaultCurrency: 'DEFAULT_CURRENCY',\r\n * });\r\n * ```\r\n */\r\n\r\n// ============================================================================\r\n// Provider Configuration Types\r\n// ============================================================================\r\n\r\n/**\r\n * Generic provider configuration - key-value pairs\r\n * Providers can have any config shape.\r\n */\r\nexport interface ProviderConfig {\r\n /** Provider-specific configuration values */\r\n [key: string]: unknown\r\n}\r\n\r\n/**\r\n * Provider configurations map.\r\n * Keys are provider names, values are provider-specific config.\r\n */\r\nexport interface ProvidersConfig {\r\n [providerName: string]: ProviderConfig | undefined\r\n}\r\n\r\n/**\r\n * Main payment configuration interface\r\n *\r\n * This is the injectable configuration that can be provided from any source.\r\n */\r\nexport interface PaymentConfig {\r\n /** Provider-specific configurations */\r\n providers: ProvidersConfig\r\n /** Environment mode */\r\n environment: 'sandbox' | 'production'\r\n /** Default currency for transactions */\r\n defaultCurrency: string\r\n}\r\n\r\n/**\r\n * Provider availability derived from configuration\r\n */\r\nexport interface ConfiguredProviderAvailability {\r\n [providerName: string]: boolean\r\n}\r\n\r\n// ============================================================================\r\n// Environment Variable Mapping\r\n// ============================================================================\r\n\r\n/**\r\n * Mapping of provider config keys to environment variable names.\r\n * Used by createConfigFromEnv().\r\n */\r\nexport interface EnvVarMapping {\r\n [envVarKey: string]: string\r\n}\r\n\r\n/**\r\n * Provider env var mapping: maps config keys to env var names\r\n */\r\nexport interface ProviderEnvMapping {\r\n providerName: string\r\n /** Map of config key -> env var name. Provider is considered configured if any key has a value. */\r\n keys: EnvVarMapping\r\n /** Keys that are all required for the provider to be considered configured */\r\n requiredKeys?: string[]\r\n}\r\n\r\n/**\r\n * Full env mapping configuration\r\n */\r\nexport interface EnvMappingConfig {\r\n providers: ProviderEnvMapping[]\r\n environmentVar?: string\r\n defaultCurrencyVar?: string\r\n}\r\n\r\n// ============================================================================\r\n// Configuration Factory Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Create payment configuration from environment variables\r\n *\r\n * @param mapping - Mapping of config keys to env var names\r\n * @returns PaymentConfig populated from process.env\r\n */\r\nexport function createConfigFromEnv(mapping?: EnvMappingConfig): PaymentConfig {\r\n if (!mapping) {\r\n return createPartialConfig({})\r\n }\r\n\r\n const providers: ProvidersConfig = {}\r\n\r\n for (const providerMapping of mapping.providers) {\r\n const config: ProviderConfig = {}\r\n let hasValue = false\r\n\r\n for (const [configKey, envVarName] of Object.entries(providerMapping.keys)) {\r\n const value = process.env[envVarName]\r\n if (value?.trim()) {\r\n config[configKey] = value\r\n hasValue = true\r\n }\r\n }\r\n\r\n // Check if required keys are all present\r\n if (providerMapping.requiredKeys) {\r\n const allRequired = providerMapping.requiredKeys.every(\r\n (key) => config[key] && typeof config[key] === 'string' && (config[key] as string).trim()\r\n )\r\n if (allRequired) {\r\n providers[providerMapping.providerName] = config\r\n }\r\n } else if (hasValue) {\r\n providers[providerMapping.providerName] = config\r\n }\r\n }\r\n\r\n const environment = (process.env[mapping.environmentVar ?? 'PAYMENT_ENVIRONMENT'] as 'sandbox' | 'production') ?? 'sandbox'\r\n const defaultCurrency = process.env[mapping.defaultCurrencyVar ?? 'DEFAULT_CURRENCY'] ?? 'USD'\r\n\r\n return {\r\n providers,\r\n environment,\r\n defaultCurrency,\r\n }\r\n}\r\n\r\n/**\r\n * Create payment configuration from a custom source\r\n *\r\n * @param config - Custom configuration object\r\n * @returns Validated PaymentConfig\r\n */\r\nexport function createConfig(config: PaymentConfig): PaymentConfig {\r\n validateConfig(config)\r\n return {\r\n providers: { ...config.providers },\r\n environment: config.environment,\r\n defaultCurrency: config.defaultCurrency,\r\n }\r\n}\r\n\r\n/**\r\n * Create a partial configuration (useful for testing)\r\n *\r\n * @param partial - Partial configuration\r\n * @returns Full configuration with defaults\r\n */\r\nexport function createPartialConfig(partial: Partial<PaymentConfig>): PaymentConfig {\r\n return {\r\n providers: partial.providers ?? {},\r\n environment: partial.environment ?? 'sandbox',\r\n defaultCurrency: partial.defaultCurrency ?? 'USD',\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Configuration Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * Check which providers are configured\r\n */\r\nexport function getConfiguredProviders(config: PaymentConfig): ConfiguredProviderAvailability {\r\n const availability: ConfiguredProviderAvailability = {}\r\n\r\n for (const [name, providerConfig] of Object.entries(config.providers)) {\r\n availability[name] = providerConfig !== undefined && Object.keys(providerConfig).length > 0\r\n }\r\n\r\n return availability\r\n}\r\n\r\n/**\r\n * Get list of configured provider names\r\n */\r\nexport function getConfiguredProviderList(config: PaymentConfig): string[] {\r\n const availability = getConfiguredProviders(config)\r\n return Object.entries(availability)\r\n .filter(([, isAvailable]) => isAvailable)\r\n .map(([name]) => name)\r\n}\r\n\r\n/**\r\n * Check if configuration is valid for production\r\n */\r\nexport function isProductionReady(config: PaymentConfig): boolean {\r\n const providers = getConfiguredProviderList(config)\r\n return config.environment === 'production' && providers.length > 0\r\n}\r\n\r\n/**\r\n * Validate configuration has at least one provider\r\n */\r\nexport function validateConfig(config: PaymentConfig): void {\r\n const providers = getConfiguredProviderList(config)\r\n\r\n if (providers.length === 0) {\r\n throw new Error(\r\n 'Payment configuration error: At least one payment provider must be configured'\r\n )\r\n }\r\n}\r\n","/**\r\n * @nehorai/payments - Circuit Breaker Storage Interface\r\n *\r\n * Abstracts storage for circuit breaker state, enabling:\r\n * - In-memory storage (default, for single-instance deployments)\r\n * - Database storage (for multi-instance/serverless deployments)\r\n * - Redis storage (for high-performance distributed systems)\r\n */\r\n\r\nimport type { PaymentProvider } from '../types/index.js'\r\n\r\n// ============================================================================\r\n// Circuit Breaker State (Storage-Agnostic)\r\n// ============================================================================\r\n\r\n/**\r\n * Circuit breaker states\r\n */\r\nexport type StoredCircuitState = 'closed' | 'open' | 'half_open'\r\n\r\n/**\r\n * Circuit breaker state record\r\n *\r\n * This is the state that gets persisted by storage implementations.\r\n */\r\nexport interface CircuitBreakerStateRecord {\r\n provider: PaymentProvider\r\n state: StoredCircuitState\r\n failureCount: number\r\n successCount: number\r\n lastFailure: Date | null\r\n openedAt: Date | null\r\n nextRetryAt: Date | null\r\n}\r\n\r\n// ============================================================================\r\n// Storage Interface\r\n// ============================================================================\r\n\r\n/**\r\n * Storage interface for circuit breaker state\r\n *\r\n * Implement this interface to provide custom storage backends:\r\n * - InMemoryCircuitBreakerStorage (default)\r\n * - Database-backed storage (uses provider_health table)\r\n * - Redis-backed storage (for high-performance needs)\r\n */\r\nexport interface ICircuitBreakerStorage {\r\n getState(provider: PaymentProvider): Promise<CircuitBreakerStateRecord | null>\r\n\r\n setState(provider: PaymentProvider, state: CircuitBreakerStateRecord): Promise<void>\r\n\r\n getAllStates(): Promise<Map<PaymentProvider, CircuitBreakerStateRecord>>\r\n\r\n deleteState(provider: PaymentProvider): Promise<void>\r\n\r\n getOpenCircuits(): Promise<PaymentProvider[]>\r\n\r\n isHealthy(): Promise<boolean>\r\n}\r\n\r\n// ============================================================================\r\n// Default State Factory\r\n// ============================================================================\r\n\r\n/**\r\n * Create default (closed) state for a provider\r\n */\r\nexport function createDefaultState(provider: PaymentProvider): CircuitBreakerStateRecord {\r\n return {\r\n provider,\r\n state: 'closed',\r\n failureCount: 0,\r\n successCount: 0,\r\n lastFailure: null,\r\n openedAt: null,\r\n nextRetryAt: null,\r\n }\r\n}\r\n\r\n/**\r\n * Check if state indicates circuit is open\r\n */\r\nexport function isCircuitOpen(state: CircuitBreakerStateRecord | null): boolean {\r\n return state?.state === 'open'\r\n}\r\n\r\n/**\r\n * Check if circuit should attempt half-open transition\r\n */\r\nexport function shouldAttemptHalfOpen(state: CircuitBreakerStateRecord | null): boolean {\r\n if (!state || state.state !== 'open') return false\r\n if (!state.nextRetryAt) return false\r\n\r\n return Date.now() >= state.nextRetryAt.getTime()\r\n}\r\n","/**\r\n * @nehorai/payments - In-Memory Circuit Breaker Storage\r\n *\r\n * Default storage implementation using a Map for circuit breaker state.\r\n * Suitable for single-instance deployments and development.\r\n *\r\n * Limitations:\r\n * - State is lost on process restart\r\n * - Not shared across multiple instances (not serverless-friendly)\r\n */\r\n\r\nimport type { PaymentProvider } from '../types/index.js'\r\nimport type {\r\n ICircuitBreakerStorage,\r\n CircuitBreakerStateRecord,\r\n} from './circuit-breaker-storage.interface.js'\r\nimport { createDefaultState } from './circuit-breaker-storage.interface.js'\r\n\r\n// ============================================================================\r\n// In-Memory Storage Implementation\r\n// ============================================================================\r\n\r\n/**\r\n * In-memory implementation of ICircuitBreakerStorage\r\n *\r\n * Uses a JavaScript Map for fast in-memory state storage.\r\n * This is the default storage and maintains backward compatibility.\r\n */\r\nexport class InMemoryCircuitBreakerStorage implements ICircuitBreakerStorage {\r\n protected internalStates: Map<PaymentProvider, CircuitBreakerStateRecord>\r\n\r\n constructor() {\r\n this.internalStates = new Map()\r\n }\r\n\r\n async getState(provider: PaymentProvider): Promise<CircuitBreakerStateRecord | null> {\r\n return this.internalStates.get(provider) ?? null\r\n }\r\n\r\n async setState(provider: PaymentProvider, state: CircuitBreakerStateRecord): Promise<void> {\r\n this.internalStates.set(provider, { ...state })\r\n }\r\n\r\n async getAllStates(): Promise<Map<PaymentProvider, CircuitBreakerStateRecord>> {\r\n return new Map(this.internalStates)\r\n }\r\n\r\n async deleteState(provider: PaymentProvider): Promise<void> {\r\n this.internalStates.delete(provider)\r\n }\r\n\r\n async getOpenCircuits(): Promise<PaymentProvider[]> {\r\n const open: PaymentProvider[] = []\r\n for (const [provider, state] of this.internalStates) {\r\n if (state.state === 'open') {\r\n open.push(provider)\r\n }\r\n }\r\n return open\r\n }\r\n\r\n async isHealthy(): Promise<boolean> {\r\n return true\r\n }\r\n\r\n /**\r\n * Clear all stored states (useful for testing)\r\n */\r\n clear(): void {\r\n this.internalStates.clear()\r\n }\r\n\r\n /**\r\n * Get current state count (useful for testing/debugging)\r\n */\r\n get size(): number {\r\n return this.internalStates.size\r\n }\r\n\r\n /**\r\n * Synchronous state access (for backward compatibility with CircuitBreaker.isOpen)\r\n *\r\n * Note: Only available on InMemoryCircuitBreakerStorage.\r\n * Database-backed storage cannot provide sync access.\r\n */\r\n getStateSync(provider: PaymentProvider): CircuitBreakerStateRecord | null {\r\n return this.internalStates.get(provider) ?? null\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Singleton Factory\r\n// ============================================================================\r\n\r\nlet defaultStorage: InMemoryCircuitBreakerStorage | null = null\r\n\r\n/**\r\n * Get or create the default in-memory storage instance\r\n */\r\nexport function getInMemoryStorage(): InMemoryCircuitBreakerStorage {\r\n if (!defaultStorage) {\r\n defaultStorage = new InMemoryCircuitBreakerStorage()\r\n }\r\n return defaultStorage\r\n}\r\n\r\n/**\r\n * Reset the default storage instance (useful for testing)\r\n */\r\nexport function resetInMemoryStorage(): void {\r\n if (defaultStorage) {\r\n defaultStorage.clear()\r\n }\r\n defaultStorage = null\r\n}\r\n\r\n// ============================================================================\r\n// Migration Helper\r\n// ============================================================================\r\n\r\n/**\r\n * Legacy state structure (for migration from old circuit breaker)\r\n */\r\nexport interface LegacyCircuitState {\r\n provider: PaymentProvider\r\n state: 'closed' | 'open' | 'half_open'\r\n failureCount: number\r\n successCount: number\r\n lastFailure: Date | null\r\n openedAt: Date | null\r\n nextRetryAt: Date | null\r\n}\r\n\r\n/**\r\n * Migrate from legacy Map storage to ICircuitBreakerStorage\r\n */\r\nexport async function migrateFromLegacyMap(\r\n legacyStates: Map<PaymentProvider, LegacyCircuitState>\r\n): Promise<InMemoryCircuitBreakerStorage> {\r\n const storage = new InMemoryCircuitBreakerStorage()\r\n\r\n for (const [provider, state] of legacyStates) {\r\n const record: CircuitBreakerStateRecord = {\r\n provider: state.provider,\r\n state: state.state,\r\n failureCount: state.failureCount,\r\n successCount: state.successCount,\r\n lastFailure: state.lastFailure,\r\n openedAt: state.openedAt,\r\n nextRetryAt: state.nextRetryAt,\r\n }\r\n await storage.setState(provider, record)\r\n }\r\n\r\n return storage\r\n}\r\n","/**\r\n * @nehorai/payments - Circuit Breaker Service\r\n *\r\n * Implements the circuit breaker pattern for payment provider resilience.\r\n * Prevents cascading failures by temporarily disabling unhealthy providers.\r\n *\r\n * States:\r\n * - CLOSED: Normal operation, all requests pass through\r\n * - OPEN: Provider disabled, all requests fail fast\r\n * - HALF_OPEN: Testing if provider recovered\r\n */\r\n\r\nimport type { PaymentProvider } from '../types/index.js'\r\nimport type {\r\n ICircuitBreakerStorage,\r\n CircuitBreakerStateRecord,\r\n} from './circuit-breaker-storage.interface.js'\r\nimport { createDefaultState } from './circuit-breaker-storage.interface.js'\r\nimport { InMemoryCircuitBreakerStorage } from './in-memory-storage.js'\r\n\r\n// ============================================================================\r\n// Configuration\r\n// ============================================================================\r\n\r\nexport interface CircuitBreakerConfig {\r\n /** Number of failures before opening circuit */\r\n failureThreshold: number\r\n /** Time in ms before attempting to close circuit */\r\n resetTimeoutMs: number\r\n /** Max requests allowed in half-open state */\r\n halfOpenMaxRequests: number\r\n}\r\n\r\nconst DEFAULT_CONFIG: CircuitBreakerConfig = {\r\n failureThreshold: 5,\r\n resetTimeoutMs: 60000, // 1 minute\r\n halfOpenMaxRequests: 3,\r\n}\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport type CircuitState = 'closed' | 'open' | 'half_open'\r\n\r\nexport interface CircuitBreakerState {\r\n provider: PaymentProvider\r\n state: CircuitState\r\n failureCount: number\r\n successCount: number\r\n lastFailure: Date | null\r\n openedAt: Date | null\r\n nextRetryAt: Date | null\r\n}\r\n\r\n/**\r\n * Circuit breaker dependencies (for dependency injection)\r\n */\r\nexport interface CircuitBreakerDeps {\r\n /** Storage implementation (optional, defaults to in-memory) */\r\n storage?: ICircuitBreakerStorage\r\n /** Configuration overrides */\r\n config?: Partial<CircuitBreakerConfig>\r\n}\r\n\r\n// ============================================================================\r\n// Circuit Breaker Service\r\n// ============================================================================\r\n\r\nexport class CircuitBreaker {\r\n private config: CircuitBreakerConfig\r\n private storage: ICircuitBreakerStorage\r\n\r\n constructor(deps: CircuitBreakerDeps = {}) {\r\n this.config = { ...DEFAULT_CONFIG, ...deps.config }\r\n this.storage = deps.storage ?? new InMemoryCircuitBreakerStorage()\r\n }\r\n\r\n /**\r\n * Check if a request can be executed for a provider\r\n */\r\n async canExecute(provider: PaymentProvider): Promise<boolean> {\r\n const state = await this.getState(provider)\r\n\r\n switch (state.state) {\r\n case 'closed':\r\n return true\r\n\r\n case 'open':\r\n if (state.nextRetryAt && Date.now() >= state.nextRetryAt.getTime()) {\r\n await this.transitionTo(provider, 'half_open')\r\n return true\r\n }\r\n return false\r\n\r\n case 'half_open':\r\n return state.failureCount < this.config.halfOpenMaxRequests\r\n }\r\n }\r\n\r\n /**\r\n * Record a successful request\r\n */\r\n async recordSuccess(provider: PaymentProvider): Promise<void> {\r\n const state = await this.getState(provider)\r\n\r\n if (state.state === 'half_open') {\r\n state.successCount++\r\n if (state.successCount >= this.config.halfOpenMaxRequests) {\r\n await this.transitionTo(provider, 'closed')\r\n return\r\n }\r\n } else if (state.state === 'closed') {\r\n state.failureCount = 0\r\n }\r\n\r\n await this.storage.setState(provider, state)\r\n }\r\n\r\n /**\r\n * Record a failed request\r\n */\r\n async recordFailure(provider: PaymentProvider): Promise<void> {\r\n const state = await this.getState(provider)\r\n\r\n state.failureCount++\r\n state.lastFailure = new Date()\r\n\r\n if (state.state === 'half_open') {\r\n await this.transitionTo(provider, 'open')\r\n } else if (state.state === 'closed') {\r\n if (state.failureCount >= this.config.failureThreshold) {\r\n await this.transitionTo(provider, 'open')\r\n } else {\r\n await this.storage.setState(provider, state)\r\n }\r\n } else {\r\n await this.storage.setState(provider, state)\r\n }\r\n }\r\n\r\n /**\r\n * Get current state for a provider\r\n */\r\n async getState(provider: PaymentProvider): Promise<CircuitBreakerState> {\r\n const stored = await this.storage.getState(provider)\r\n return stored ?? createDefaultState(provider)\r\n }\r\n\r\n /**\r\n * Check if circuit is open (provider unavailable)\r\n */\r\n isOpen(provider: PaymentProvider): boolean {\r\n return this.isOpenSync(provider)\r\n }\r\n\r\n /**\r\n * Async version of isOpen\r\n */\r\n async isOpenAsync(provider: PaymentProvider): Promise<boolean> {\r\n const state = await this.getState(provider)\r\n return state.state === 'open'\r\n }\r\n\r\n /**\r\n * Manually reset a provider's circuit\r\n */\r\n async reset(provider: PaymentProvider): Promise<void> {\r\n await this.storage.setState(provider, createDefaultState(provider))\r\n }\r\n\r\n /**\r\n * Get all providers with open circuits\r\n */\r\n async getOpenCircuits(): Promise<PaymentProvider[]> {\r\n return this.storage.getOpenCircuits()\r\n }\r\n\r\n /**\r\n * Get the storage instance (useful for testing)\r\n */\r\n getStorage(): ICircuitBreakerStorage {\r\n return this.storage\r\n }\r\n\r\n /**\r\n * Get configuration (useful for testing)\r\n */\r\n getConfig(): CircuitBreakerConfig {\r\n return { ...this.config }\r\n }\r\n\r\n // ==========================================================================\r\n // Private Methods\r\n // ==========================================================================\r\n\r\n private async transitionTo(provider: PaymentProvider, newState: CircuitState): Promise<void> {\r\n const state = await this.getState(provider)\r\n const now = new Date()\r\n\r\n state.state = newState\r\n\r\n if (newState === 'open') {\r\n state.openedAt = now\r\n state.nextRetryAt = new Date(now.getTime() + this.config.resetTimeoutMs)\r\n console.warn(\r\n `[CIRCUIT_BREAKER] Circuit OPENED for ${provider}. ` +\r\n `Retry at: ${state.nextRetryAt.toISOString()}`\r\n )\r\n } else if (newState === 'half_open') {\r\n state.failureCount = 0\r\n state.successCount = 0\r\n console.info(`[CIRCUIT_BREAKER] Circuit HALF_OPEN for ${provider}`)\r\n } else if (newState === 'closed') {\r\n state.failureCount = 0\r\n state.successCount = 0\r\n state.openedAt = null\r\n state.nextRetryAt = null\r\n console.info(`[CIRCUIT_BREAKER] Circuit CLOSED for ${provider}`)\r\n }\r\n\r\n await this.storage.setState(provider, state)\r\n }\r\n\r\n /**\r\n * Synchronous check for backward compatibility\r\n * Note: This always returns false for database-backed storage\r\n */\r\n private isOpenSync(provider: PaymentProvider): boolean {\r\n if (this.storage instanceof InMemoryCircuitBreakerStorage) {\r\n const state = this.storage.getStateSync(provider)\r\n return state?.state === 'open'\r\n }\r\n return false\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Singleton Pattern (Backward Compatible)\r\n// ============================================================================\r\n\r\nlet circuitBreakerInstance: CircuitBreaker | null = null\r\nlet defaultStorage: InMemoryCircuitBreakerStorage | null = null\r\n\r\n/**\r\n * Get or create singleton CircuitBreaker instance\r\n */\r\nexport function getCircuitBreaker(config?: Partial<CircuitBreakerConfig>): CircuitBreaker {\r\n if (!circuitBreakerInstance) {\r\n if (!defaultStorage) {\r\n defaultStorage = new InMemoryCircuitBreakerStorage()\r\n }\r\n circuitBreakerInstance = new CircuitBreaker({\r\n storage: defaultStorage,\r\n config,\r\n })\r\n }\r\n return circuitBreakerInstance\r\n}\r\n\r\n/**\r\n * Create a new CircuitBreaker with custom dependencies\r\n */\r\nexport function createCircuitBreaker(deps: CircuitBreakerDeps = {}): CircuitBreaker {\r\n return new CircuitBreaker(deps)\r\n}\r\n\r\n/**\r\n * Reset the singleton instance (useful for testing)\r\n */\r\nexport function resetCircuitBreaker(): void {\r\n circuitBreakerInstance = null\r\n if (defaultStorage) {\r\n defaultStorage.clear()\r\n }\r\n defaultStorage = null\r\n}\r\n","/**\r\n * @nehorai/payments - Routing Engine Service\r\n *\r\n * Intelligent payment routing based on configurable rules:\r\n * - Card BIN rules (match card ranges to preferred providers)\r\n * - Provider health (circuit breaker state)\r\n * - Transaction fees / provider priorities\r\n * - Currency support\r\n *\r\n * Unlike the Podcasto-specific version, this accepts generic RoutingRules\r\n * config instead of hardcoded Israeli card BIN ranges.\r\n */\r\n\r\nimport type { PaymentProvider } from '../types/index.js'\r\nimport type { IRoutingEngine, RoutingContext, RoutingDecision } from '../providers/interfaces/index.js'\r\nimport {\r\n type PaymentConfig,\r\n type ConfiguredProviderAvailability,\r\n getConfiguredProviders,\r\n createPartialConfig,\r\n} from '../config/payment-config.js'\r\nimport { getCircuitBreaker, type CircuitBreaker } from './circuit-breaker.js'\r\n\r\n// ============================================================================\r\n// Routing Rules Types\r\n// ============================================================================\r\n\r\n/**\r\n * Rule for matching card BIN ranges to preferred providers\r\n */\r\nexport interface CardBinRule {\r\n ranges: Array<{ start: string; end: string; issuer?: string; country?: string }>\r\n preferredProvider: string\r\n priority?: number\r\n}\r\n\r\n/**\r\n * Provider priority configuration\r\n */\r\nexport interface ProviderPriorityRule {\r\n provider: string\r\n priority: number\r\n maxFeePercent: number\r\n supportsCurrency: string[]\r\n supportsRecurring: boolean\r\n isLocalGateway?: boolean\r\n}\r\n\r\n/**\r\n * Currency-specific routing rule\r\n */\r\nexport interface CurrencyRule {\r\n currency: string\r\n preferredProvider: string\r\n}\r\n\r\n/**\r\n * Generic routing rules configuration.\r\n * Injected at construction time instead of hardcoded.\r\n */\r\nexport interface RoutingRules {\r\n cardBinRules?: CardBinRule[]\r\n providerPriorities?: ProviderPriorityRule[]\r\n currencyRules?: CurrencyRule[]\r\n}\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Routing engine dependencies (for dependency injection)\r\n */\r\nexport interface RoutingEngineDeps {\r\n /** Payment configuration (optional, defaults to empty config) */\r\n config?: PaymentConfig\r\n /** Circuit breaker instance (optional, defaults to singleton) */\r\n circuitBreaker?: CircuitBreaker\r\n /** Routing rules (optional, no rules means simple round-robin) */\r\n routingRules?: RoutingRules\r\n}\r\n\r\n// ============================================================================\r\n// Internal Routing Helpers\r\n// ============================================================================\r\n\r\nfunction matchCardBinToRule(\r\n bin: string,\r\n rules: CardBinRule[]\r\n): { rule: CardBinRule; issuer?: string; country?: string } | null {\r\n if (!bin || bin.length < 6) return null\r\n const binPrefix = bin.substring(0, 6)\r\n\r\n for (const rule of rules) {\r\n for (const range of rule.ranges) {\r\n if (binPrefix >= range.start && binPrefix <= range.end) {\r\n return { rule, issuer: range.issuer, country: range.country }\r\n }\r\n }\r\n }\r\n return null\r\n}\r\n\r\nfunction getOptimalProviderFromPriorities(\r\n matchedBin: boolean,\r\n currency: string,\r\n requiresRecurring: boolean,\r\n availableProviders: PaymentProvider[],\r\n priorities: ProviderPriorityRule[]\r\n): PaymentProvider | null {\r\n const candidates = priorities.filter((p) =>\r\n availableProviders.includes(p.provider)\r\n )\r\n if (candidates.length === 0) return availableProviders[0] ?? null\r\n\r\n const suitable = candidates.filter((p) => {\r\n if (!p.supportsCurrency.includes(currency)) return false\r\n if (requiresRecurring && !p.supportsRecurring) return false\r\n return true\r\n })\r\n\r\n if (suitable.length === 0) {\r\n return candidates.sort((a, b) => a.priority - b.priority)[0]?.provider ?? null\r\n }\r\n\r\n // If card matched a BIN rule, prefer local gateways\r\n if (matchedBin) {\r\n const localProviders = suitable.filter((p) => p.isLocalGateway)\r\n if (localProviders.length > 0) {\r\n return localProviders.sort((a, b) => a.priority - b.priority)[0].provider\r\n }\r\n }\r\n\r\n return suitable.sort((a, b) => a.priority - b.priority)[0].provider\r\n}\r\n\r\nfunction getFallbackProvidersFromPriorities(\r\n primaryProvider: PaymentProvider,\r\n availableProviders: PaymentProvider[],\r\n priorities: ProviderPriorityRule[]\r\n): PaymentProvider[] {\r\n return priorities\r\n .filter((p) =>\r\n p.provider !== primaryProvider &&\r\n availableProviders.includes(p.provider)\r\n )\r\n .sort((a, b) => a.priority - b.priority)\r\n .map((p) => p.provider)\r\n}\r\n\r\nfunction getProviderFeeFromPriorities(\r\n provider: PaymentProvider,\r\n priorities: ProviderPriorityRule[]\r\n): number {\r\n const config = priorities.find((p) => p.provider === provider)\r\n return config?.maxFeePercent ?? 3.0\r\n}\r\n\r\n// ============================================================================\r\n// Routing Engine Implementation\r\n// ============================================================================\r\n\r\n/**\r\n * Routing Engine Implementation\r\n *\r\n * Routes payments to optimal providers with automatic failover.\r\n * Uses injected RoutingRules instead of hardcoded locale-specific logic.\r\n */\r\nexport class RoutingEngine implements IRoutingEngine {\r\n private config: PaymentConfig\r\n private circuitBreaker: CircuitBreaker\r\n private routingRules: RoutingRules\r\n\r\n constructor(deps: RoutingEngineDeps = {}) {\r\n this.config = deps.config ?? createPartialConfig({})\r\n this.circuitBreaker = deps.circuitBreaker ?? getCircuitBreaker()\r\n this.routingRules = deps.routingRules ?? {}\r\n }\r\n\r\n /**\r\n * Determine optimal provider for a transaction\r\n */\r\n async route(context: RoutingContext): Promise<RoutingDecision> {\r\n const availability = getConfiguredProviders(this.config)\r\n const availableProviders = this.getProviderList(availability)\r\n\r\n if (availableProviders.length === 0) {\r\n throw new Error('No payment providers configured')\r\n }\r\n\r\n // If using saved payment method, must use same provider\r\n if (context.savedPaymentMethodId && context.savedPaymentMethodProvider) {\r\n return this.routeToSavedMethodProvider(context, availableProviders)\r\n }\r\n\r\n // Check card BIN against rules\r\n const binMatch = context.cardBin && this.routingRules.cardBinRules\r\n ? matchCardBinToRule(context.cardBin, this.routingRules.cardBinRules)\r\n : null\r\n\r\n // Check currency rules\r\n const currencyRule = this.routingRules.currencyRules?.find(\r\n (r) => r.currency === context.amount.currency\r\n )\r\n\r\n // Get healthy providers\r\n const healthyProviders = await this.getHealthyProviders(availableProviders)\r\n const availableHealthy = availableProviders.filter((p) => healthyProviders.includes(p))\r\n const effectiveProviders = availableHealthy.length > 0 ? availableHealthy : availableProviders\r\n\r\n // If BIN matched a rule and the preferred provider is available, use it\r\n if (binMatch && effectiveProviders.includes(binMatch.rule.preferredProvider)) {\r\n const provider = binMatch.rule.preferredProvider\r\n const fallbacks = this.getFallbackProviders(provider, availableProviders)\r\n const feePercent = this.getProviderFee(provider)\r\n\r\n return {\r\n provider,\r\n reason: binMatch.issuer\r\n ? `Card (${binMatch.issuer}) matched BIN rule, routed to preferred provider`\r\n : 'Card matched BIN rule, routed to preferred provider',\r\n fallbackProviders: fallbacks,\r\n estimatedFeePercent: feePercent,\r\n metadata: {\r\n matchedBinRule: true,\r\n cardIssuer: binMatch.issuer,\r\n cardCountry: binMatch.country,\r\n },\r\n }\r\n }\r\n\r\n // If currency rule matches and provider is available, use it\r\n if (currencyRule && effectiveProviders.includes(currencyRule.preferredProvider)) {\r\n const provider = currencyRule.preferredProvider\r\n const fallbacks = this.getFallbackProviders(provider, availableProviders)\r\n const feePercent = this.getProviderFee(provider)\r\n\r\n return {\r\n provider,\r\n reason: `Currency ${context.amount.currency} routed to preferred provider`,\r\n fallbackProviders: fallbacks,\r\n estimatedFeePercent: feePercent,\r\n }\r\n }\r\n\r\n // Use provider priorities if configured\r\n const priorities = this.routingRules.providerPriorities\r\n if (priorities && priorities.length > 0) {\r\n const provider = getOptimalProviderFromPriorities(\r\n !!binMatch,\r\n context.amount.currency,\r\n context.isRecurring,\r\n effectiveProviders,\r\n priorities\r\n )\r\n\r\n if (provider) {\r\n const fallbacks = getFallbackProvidersFromPriorities(provider, availableProviders, priorities)\r\n const feePercent = getProviderFeeFromPriorities(provider, priorities)\r\n\r\n return {\r\n provider,\r\n reason: `Selected ${provider} based on priority rules`,\r\n fallbackProviders: fallbacks,\r\n estimatedFeePercent: feePercent,\r\n metadata: {\r\n matchedBinRule: !!binMatch,\r\n cardIssuer: binMatch?.issuer,\r\n cardCountry: binMatch?.country,\r\n },\r\n }\r\n }\r\n }\r\n\r\n // Fallback: use first available provider\r\n const provider = effectiveProviders[0]\r\n const fallbacks = effectiveProviders.slice(1)\r\n\r\n return {\r\n provider,\r\n reason: `Default routing to ${provider}`,\r\n fallbackProviders: fallbacks,\r\n estimatedFeePercent: this.getProviderFee(provider),\r\n }\r\n }\r\n\r\n /**\r\n * Get next provider after a failure\r\n */\r\n async getFailoverProvider(\r\n failedProvider: PaymentProvider,\r\n _context: RoutingContext\r\n ): Promise<PaymentProvider | null> {\r\n const availability = getConfiguredProviders(this.config)\r\n const availableProviders = this.getProviderList(availability)\r\n const healthyProviders = await this.getHealthyProviders(availableProviders)\r\n\r\n const fallbacks = this.getFallbackProviders(failedProvider, availableProviders)\r\n\r\n for (const provider of fallbacks) {\r\n if (healthyProviders.includes(provider)) {\r\n return provider\r\n }\r\n }\r\n\r\n return fallbacks[0] ?? null\r\n }\r\n\r\n /**\r\n * Check if a card BIN matches any configured rule\r\n */\r\n matchCardBin(bin: string): boolean {\r\n if (!this.routingRules.cardBinRules) return false\r\n return matchCardBinToRule(bin, this.routingRules.cardBinRules) !== null\r\n }\r\n\r\n /**\r\n * Get all available (healthy) providers\r\n */\r\n async getAvailableProviders(): Promise<PaymentProvider[]> {\r\n const availability = getConfiguredProviders(this.config)\r\n const configured = this.getProviderList(availability)\r\n return this.getHealthyProviders(configured)\r\n }\r\n\r\n /**\r\n * Quick recommendation without full context\r\n */\r\n async getQuickRecommendation(\r\n currency: string,\r\n isRecurring: boolean\r\n ): Promise<PaymentProvider | null> {\r\n const availability = getConfiguredProviders(this.config)\r\n const available = this.getProviderList(availability)\r\n\r\n if (available.length === 0) return null\r\n\r\n // Check currency rules first\r\n const currencyRule = this.routingRules.currencyRules?.find(\r\n (r) => r.currency === currency\r\n )\r\n if (currencyRule && available.includes(currencyRule.preferredProvider)) {\r\n return currencyRule.preferredProvider\r\n }\r\n\r\n // Use provider priorities if configured\r\n const priorities = this.routingRules.providerPriorities\r\n if (priorities && priorities.length > 0) {\r\n return getOptimalProviderFromPriorities(false, currency, isRecurring, available, priorities)\r\n }\r\n\r\n return available[0] ?? null\r\n }\r\n\r\n /**\r\n * Get current configuration (for testing/debugging)\r\n */\r\n getConfig(): PaymentConfig {\r\n return this.config\r\n }\r\n\r\n /**\r\n * Get circuit breaker instance (for testing/debugging)\r\n */\r\n getCircuitBreaker(): CircuitBreaker {\r\n return this.circuitBreaker\r\n }\r\n\r\n /**\r\n * Get routing rules (for testing/debugging)\r\n */\r\n getRoutingRules(): RoutingRules {\r\n return this.routingRules\r\n }\r\n\r\n // ==========================================================================\r\n // Private Methods\r\n // ==========================================================================\r\n\r\n private getProviderList(availability: ConfiguredProviderAvailability): PaymentProvider[] {\r\n const providers: PaymentProvider[] = []\r\n for (const [name, isAvailable] of Object.entries(availability)) {\r\n if (isAvailable) providers.push(name)\r\n }\r\n return providers\r\n }\r\n\r\n private async getHealthyProviders(providers: PaymentProvider[]): Promise<PaymentProvider[]> {\r\n const healthy: PaymentProvider[] = []\r\n for (const provider of providers) {\r\n const isOpen = await this.circuitBreaker.isOpenAsync(provider)\r\n if (!isOpen) {\r\n healthy.push(provider)\r\n }\r\n }\r\n return healthy\r\n }\r\n\r\n private routeToSavedMethodProvider(\r\n context: RoutingContext,\r\n availableProviders: PaymentProvider[]\r\n ): RoutingDecision {\r\n const provider = context.savedPaymentMethodProvider!\r\n\r\n if (!availableProviders.includes(provider)) {\r\n throw new Error(`Saved payment method provider '${provider}' is not available`)\r\n }\r\n\r\n return {\r\n provider,\r\n reason: 'Using saved payment method provider',\r\n fallbackProviders: [],\r\n estimatedFeePercent: this.getProviderFee(provider),\r\n }\r\n }\r\n\r\n private getFallbackProviders(\r\n primaryProvider: PaymentProvider,\r\n availableProviders: PaymentProvider[]\r\n ): PaymentProvider[] {\r\n const priorities = this.routingRules.providerPriorities\r\n if (priorities && priorities.length > 0) {\r\n return getFallbackProvidersFromPriorities(primaryProvider, availableProviders, priorities)\r\n }\r\n return availableProviders.filter((p) => p !== primaryProvider)\r\n }\r\n\r\n private getProviderFee(provider: PaymentProvider): number {\r\n const priorities = this.routingRules.providerPriorities\r\n if (priorities && priorities.length > 0) {\r\n return getProviderFeeFromPriorities(provider, priorities)\r\n }\r\n return 3.0 // Default fee if no priorities configured\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Singleton Pattern (Backward Compatible)\r\n// ============================================================================\r\n\r\nlet routingEngineInstance: RoutingEngine | null = null\r\n\r\n/**\r\n * Get or create singleton RoutingEngine instance\r\n */\r\nexport function getRoutingEngine(): RoutingEngine {\r\n if (!routingEngineInstance) {\r\n routingEngineInstance = new RoutingEngine()\r\n }\r\n return routingEngineInstance\r\n}\r\n\r\n/**\r\n * Create a new RoutingEngine with custom dependencies\r\n */\r\nexport function createRoutingEngine(deps: RoutingEngineDeps = {}): RoutingEngine {\r\n return new RoutingEngine(deps)\r\n}\r\n\r\n/**\r\n * Reset the singleton instance (useful for testing)\r\n */\r\nexport function resetRoutingEngine(): void {\r\n routingEngineInstance = null\r\n}\r\n"],"mappings":"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","names":["defaultStorage","provider","fallbacks"]}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @nehorai/payments - Transaction State Machine
3
+ *
4
+ * Defines the strict state transitions for payment transactions.
5
+ * Implements the J5 (Two-Phase Commit) pattern for authorize/capture flows.
6
+ *
7
+ * State Flow:
8
+ * +-------------------------------------------------------------+
9
+ * | CREATED --------------------------------------------------+ |
10
+ * | | | |
11
+ * | v v |
12
+ * | PENDING_AUTHORIZATION -----------------------------> FAILED |
13
+ * | | ^ |
14
+ * | v | |
15
+ * | AUTHORIZED ---------> VOIDED | |
16
+ * | | | |
17
+ * | +----------------> EXPIRED ------------------------+ |
18
+ * | | | |
19
+ * | v | |
20
+ * | CAPTURING --------------------------------------------+ |
21
+ * | | |
22
+ * | v |
23
+ * | CAPTURED ------> PARTIALLY_REFUNDED ------> FULLY_REFUNDED |
24
+ * +-------------------------------------------------------------+
25
+ */
26
+ /**
27
+ * All possible transaction states
28
+ */
29
+ type TransactionStatus = 'created' | 'pending_authorization' | 'authorized' | 'capturing' | 'captured' | 'voided' | 'failed' | 'expired' | 'partially_refunded' | 'fully_refunded';
30
+ /**
31
+ * Terminal states - no further transitions possible
32
+ */
33
+ declare const TERMINAL_STATES: readonly TransactionStatus[];
34
+ /**
35
+ * States that indicate successful completion
36
+ */
37
+ declare const SUCCESS_STATES: readonly TransactionStatus[];
38
+ /**
39
+ * States where funds are held but not captured
40
+ */
41
+ declare const HOLD_STATES: readonly TransactionStatus[];
42
+ /**
43
+ * Events that trigger state transitions
44
+ */
45
+ type TransactionEvent = 'INITIATE' | 'AUTHORIZE_PENDING' | 'AUTHORIZE_SUCCESS' | 'AUTHORIZE_FAILED' | 'CAPTURE_STARTED' | 'CAPTURE_SUCCESS' | 'CAPTURE_FAILED' | 'VOID_SUCCESS' | 'VOID_FAILED' | 'EXPIRED' | 'PARTIAL_REFUND' | 'FULL_REFUND';
46
+ /**
47
+ * Valid transitions from each state
48
+ */
49
+ declare const VALID_TRANSITIONS: Record<TransactionStatus, TransactionEvent[]>;
50
+ /**
51
+ * Check if a transition is valid
52
+ */
53
+ declare function canTransition(currentStatus: TransactionStatus, event: TransactionEvent): boolean;
54
+ /**
55
+ * Get the next state after an event, or null if transition is invalid
56
+ */
57
+ declare function getNextStatus(currentStatus: TransactionStatus, event: TransactionEvent): TransactionStatus | null;
58
+ /**
59
+ * Check if a state is terminal (no further transitions)
60
+ */
61
+ declare function isTerminalState(status: TransactionStatus): boolean;
62
+ /**
63
+ * Check if a state represents a successful payment
64
+ */
65
+ declare function isSuccessState(status: TransactionStatus): boolean;
66
+ /**
67
+ * Check if funds are currently held (authorized but not captured)
68
+ */
69
+ declare function isHoldState(status: TransactionStatus): boolean;
70
+ /**
71
+ * Check if a refund is possible from current state
72
+ */
73
+ declare function canRefund(status: TransactionStatus): boolean;
74
+ /**
75
+ * Check if capture is possible from current state
76
+ */
77
+ declare function canCapture(status: TransactionStatus): boolean;
78
+ /**
79
+ * Check if void is possible from current state
80
+ */
81
+ declare function canVoid(status: TransactionStatus): boolean;
82
+ /**
83
+ * Result of a state transition attempt
84
+ */
85
+ interface StateTransitionResult {
86
+ success: boolean;
87
+ previousStatus: TransactionStatus;
88
+ newStatus: TransactionStatus;
89
+ event: TransactionEvent;
90
+ error?: string;
91
+ }
92
+ /**
93
+ * Attempt a state transition with validation
94
+ */
95
+ declare function attemptTransition(currentStatus: TransactionStatus, event: TransactionEvent): StateTransitionResult;
96
+ /**
97
+ * Default authorization hold period (7 days for most providers)
98
+ */
99
+ declare const DEFAULT_AUTH_HOLD_DAYS = 7;
100
+ /**
101
+ * Calculate capture deadline from authorization time
102
+ */
103
+ declare function calculateCaptureDeadline(authorizedAt: Date, holdDays?: number): Date;
104
+ /**
105
+ * Check if authorization has expired
106
+ */
107
+ declare function isAuthorizationExpired(captureDeadline: Date): boolean;
108
+
109
+ export { DEFAULT_AUTH_HOLD_DAYS as D, HOLD_STATES as H, SUCCESS_STATES as S, TERMINAL_STATES as T, VALID_TRANSITIONS as V, type StateTransitionResult as a, type TransactionEvent as b, type TransactionStatus as c, attemptTransition as d, calculateCaptureDeadline as e, canCapture as f, canRefund as g, canTransition as h, canVoid as i, getNextStatus as j, isAuthorizationExpired as k, isHoldState as l, isSuccessState as m, isTerminalState as n };
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @nehorai/payments - Transaction State Machine
3
+ *
4
+ * Defines the strict state transitions for payment transactions.
5
+ * Implements the J5 (Two-Phase Commit) pattern for authorize/capture flows.
6
+ *
7
+ * State Flow:
8
+ * +-------------------------------------------------------------+
9
+ * | CREATED --------------------------------------------------+ |
10
+ * | | | |
11
+ * | v v |
12
+ * | PENDING_AUTHORIZATION -----------------------------> FAILED |
13
+ * | | ^ |
14
+ * | v | |
15
+ * | AUTHORIZED ---------> VOIDED | |
16
+ * | | | |
17
+ * | +----------------> EXPIRED ------------------------+ |
18
+ * | | | |
19
+ * | v | |
20
+ * | CAPTURING --------------------------------------------+ |
21
+ * | | |
22
+ * | v |
23
+ * | CAPTURED ------> PARTIALLY_REFUNDED ------> FULLY_REFUNDED |
24
+ * +-------------------------------------------------------------+
25
+ */
26
+ /**
27
+ * All possible transaction states
28
+ */
29
+ type TransactionStatus = 'created' | 'pending_authorization' | 'authorized' | 'capturing' | 'captured' | 'voided' | 'failed' | 'expired' | 'partially_refunded' | 'fully_refunded';
30
+ /**
31
+ * Terminal states - no further transitions possible
32
+ */
33
+ declare const TERMINAL_STATES: readonly TransactionStatus[];
34
+ /**
35
+ * States that indicate successful completion
36
+ */
37
+ declare const SUCCESS_STATES: readonly TransactionStatus[];
38
+ /**
39
+ * States where funds are held but not captured
40
+ */
41
+ declare const HOLD_STATES: readonly TransactionStatus[];
42
+ /**
43
+ * Events that trigger state transitions
44
+ */
45
+ type TransactionEvent = 'INITIATE' | 'AUTHORIZE_PENDING' | 'AUTHORIZE_SUCCESS' | 'AUTHORIZE_FAILED' | 'CAPTURE_STARTED' | 'CAPTURE_SUCCESS' | 'CAPTURE_FAILED' | 'VOID_SUCCESS' | 'VOID_FAILED' | 'EXPIRED' | 'PARTIAL_REFUND' | 'FULL_REFUND';
46
+ /**
47
+ * Valid transitions from each state
48
+ */
49
+ declare const VALID_TRANSITIONS: Record<TransactionStatus, TransactionEvent[]>;
50
+ /**
51
+ * Check if a transition is valid
52
+ */
53
+ declare function canTransition(currentStatus: TransactionStatus, event: TransactionEvent): boolean;
54
+ /**
55
+ * Get the next state after an event, or null if transition is invalid
56
+ */
57
+ declare function getNextStatus(currentStatus: TransactionStatus, event: TransactionEvent): TransactionStatus | null;
58
+ /**
59
+ * Check if a state is terminal (no further transitions)
60
+ */
61
+ declare function isTerminalState(status: TransactionStatus): boolean;
62
+ /**
63
+ * Check if a state represents a successful payment
64
+ */
65
+ declare function isSuccessState(status: TransactionStatus): boolean;
66
+ /**
67
+ * Check if funds are currently held (authorized but not captured)
68
+ */
69
+ declare function isHoldState(status: TransactionStatus): boolean;
70
+ /**
71
+ * Check if a refund is possible from current state
72
+ */
73
+ declare function canRefund(status: TransactionStatus): boolean;
74
+ /**
75
+ * Check if capture is possible from current state
76
+ */
77
+ declare function canCapture(status: TransactionStatus): boolean;
78
+ /**
79
+ * Check if void is possible from current state
80
+ */
81
+ declare function canVoid(status: TransactionStatus): boolean;
82
+ /**
83
+ * Result of a state transition attempt
84
+ */
85
+ interface StateTransitionResult {
86
+ success: boolean;
87
+ previousStatus: TransactionStatus;
88
+ newStatus: TransactionStatus;
89
+ event: TransactionEvent;
90
+ error?: string;
91
+ }
92
+ /**
93
+ * Attempt a state transition with validation
94
+ */
95
+ declare function attemptTransition(currentStatus: TransactionStatus, event: TransactionEvent): StateTransitionResult;
96
+ /**
97
+ * Default authorization hold period (7 days for most providers)
98
+ */
99
+ declare const DEFAULT_AUTH_HOLD_DAYS = 7;
100
+ /**
101
+ * Calculate capture deadline from authorization time
102
+ */
103
+ declare function calculateCaptureDeadline(authorizedAt: Date, holdDays?: number): Date;
104
+ /**
105
+ * Check if authorization has expired
106
+ */
107
+ declare function isAuthorizationExpired(captureDeadline: Date): boolean;
108
+
109
+ export { DEFAULT_AUTH_HOLD_DAYS as D, HOLD_STATES as H, SUCCESS_STATES as S, TERMINAL_STATES as T, VALID_TRANSITIONS as V, type StateTransitionResult as a, type TransactionEvent as b, type TransactionStatus as c, attemptTransition as d, calculateCaptureDeadline as e, canCapture as f, canRefund as g, canTransition as h, canVoid as i, getNextStatus as j, isAuthorizationExpired as k, isHoldState as l, isSuccessState as m, isTerminalState as n };
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/types/index.ts
21
+ var types_exports = {};
22
+ __export(types_exports, {
23
+ DEFAULT_AUTH_HOLD_DAYS: () => DEFAULT_AUTH_HOLD_DAYS,
24
+ HOLD_STATES: () => HOLD_STATES,
25
+ STRIPE_EVENT_TO_STATUS: () => STRIPE_EVENT_TO_STATUS,
26
+ SUCCESS_STATES: () => SUCCESS_STATES,
27
+ TERMINAL_STATES: () => TERMINAL_STATES,
28
+ VALID_TRANSITIONS: () => VALID_TRANSITIONS,
29
+ attemptTransition: () => attemptTransition,
30
+ calculateCaptureDeadline: () => calculateCaptureDeadline,
31
+ canCapture: () => canCapture,
32
+ canRefund: () => canRefund,
33
+ canTransition: () => canTransition,
34
+ canVoid: () => canVoid,
35
+ getNextStatus: () => getNextStatus,
36
+ isAuthorizationExpired: () => isAuthorizationExpired,
37
+ isHoldState: () => isHoldState,
38
+ isSuccessState: () => isSuccessState,
39
+ isTerminalState: () => isTerminalState
40
+ });
41
+ module.exports = __toCommonJS(types_exports);
42
+
43
+ // src/types/state-machine.ts
44
+ var TERMINAL_STATES = [
45
+ "voided",
46
+ "failed",
47
+ "expired",
48
+ "fully_refunded"
49
+ ];
50
+ var SUCCESS_STATES = [
51
+ "captured",
52
+ "partially_refunded",
53
+ "fully_refunded"
54
+ ];
55
+ var HOLD_STATES = [
56
+ "authorized"
57
+ ];
58
+ var VALID_TRANSITIONS = {
59
+ created: ["INITIATE", "AUTHORIZE_PENDING", "AUTHORIZE_FAILED"],
60
+ pending_authorization: ["AUTHORIZE_SUCCESS", "AUTHORIZE_FAILED", "EXPIRED"],
61
+ authorized: ["CAPTURE_STARTED", "VOID_SUCCESS", "VOID_FAILED", "EXPIRED"],
62
+ capturing: ["CAPTURE_SUCCESS", "CAPTURE_FAILED"],
63
+ captured: ["PARTIAL_REFUND", "FULL_REFUND"],
64
+ voided: [],
65
+ // Terminal state
66
+ failed: [],
67
+ // Terminal state
68
+ expired: [],
69
+ // Terminal state
70
+ partially_refunded: ["PARTIAL_REFUND", "FULL_REFUND"],
71
+ fully_refunded: []
72
+ // Terminal state
73
+ };
74
+ var EVENT_TO_STATE = {
75
+ INITIATE: "pending_authorization",
76
+ AUTHORIZE_PENDING: "pending_authorization",
77
+ AUTHORIZE_SUCCESS: "authorized",
78
+ AUTHORIZE_FAILED: "failed",
79
+ CAPTURE_STARTED: "capturing",
80
+ CAPTURE_SUCCESS: "captured",
81
+ CAPTURE_FAILED: "failed",
82
+ VOID_SUCCESS: "voided",
83
+ VOID_FAILED: "authorized",
84
+ // Remain authorized if void fails
85
+ EXPIRED: "expired",
86
+ PARTIAL_REFUND: "partially_refunded",
87
+ FULL_REFUND: "fully_refunded"
88
+ };
89
+ function canTransition(currentStatus, event) {
90
+ return VALID_TRANSITIONS[currentStatus].includes(event);
91
+ }
92
+ function getNextStatus(currentStatus, event) {
93
+ if (!canTransition(currentStatus, event)) {
94
+ return null;
95
+ }
96
+ return EVENT_TO_STATE[event];
97
+ }
98
+ function isTerminalState(status) {
99
+ return TERMINAL_STATES.includes(status);
100
+ }
101
+ function isSuccessState(status) {
102
+ return SUCCESS_STATES.includes(status);
103
+ }
104
+ function isHoldState(status) {
105
+ return HOLD_STATES.includes(status);
106
+ }
107
+ function canRefund(status) {
108
+ return status === "captured" || status === "partially_refunded";
109
+ }
110
+ function canCapture(status) {
111
+ return status === "authorized";
112
+ }
113
+ function canVoid(status) {
114
+ return status === "authorized";
115
+ }
116
+ function attemptTransition(currentStatus, event) {
117
+ const nextStatus = getNextStatus(currentStatus, event);
118
+ if (nextStatus === null) {
119
+ return {
120
+ success: false,
121
+ previousStatus: currentStatus,
122
+ newStatus: currentStatus,
123
+ event,
124
+ error: `Invalid transition: ${currentStatus} -> ${event}`
125
+ };
126
+ }
127
+ return {
128
+ success: true,
129
+ previousStatus: currentStatus,
130
+ newStatus: nextStatus,
131
+ event
132
+ };
133
+ }
134
+ var DEFAULT_AUTH_HOLD_DAYS = 7;
135
+ function calculateCaptureDeadline(authorizedAt, holdDays = DEFAULT_AUTH_HOLD_DAYS) {
136
+ const deadline = new Date(authorizedAt);
137
+ deadline.setDate(deadline.getDate() + holdDays);
138
+ return deadline;
139
+ }
140
+ function isAuthorizationExpired(captureDeadline) {
141
+ return /* @__PURE__ */ new Date() > captureDeadline;
142
+ }
143
+
144
+ // src/types/webhook-types.ts
145
+ var STRIPE_EVENT_TO_STATUS = {
146
+ "payment_intent.succeeded": "captured",
147
+ "payment_intent.payment_failed": "failed",
148
+ "payment_intent.canceled": "voided",
149
+ "payment_intent.amount_capturable_updated": "authorized",
150
+ "charge.refunded": "partially_refunded"
151
+ // or fully_refunded based on amount
152
+ };
153
+ // Annotate the CommonJS export names for ESM import in node:
154
+ 0 && (module.exports = {
155
+ DEFAULT_AUTH_HOLD_DAYS,
156
+ HOLD_STATES,
157
+ STRIPE_EVENT_TO_STATUS,
158
+ SUCCESS_STATES,
159
+ TERMINAL_STATES,
160
+ VALID_TRANSITIONS,
161
+ attemptTransition,
162
+ calculateCaptureDeadline,
163
+ canCapture,
164
+ canRefund,
165
+ canTransition,
166
+ canVoid,
167
+ getNextStatus,
168
+ isAuthorizationExpired,
169
+ isHoldState,
170
+ isSuccessState,
171
+ isTerminalState
172
+ });
173
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types/index.ts","../../src/types/state-machine.ts","../../src/types/webhook-types.ts"],"sourcesContent":["/**\r\n * @nehorai/payments - Type Exports\r\n *\r\n * Central export for all payment system types.\r\n */\r\n\r\n// Core payment types\r\nexport type {\r\n PaymentProvider,\r\n TransactionType,\r\n TaxInvoiceStatus,\r\n PaymentMethodType,\r\n CardBrand,\r\n PaymentAmount,\r\n CurrencyConversion,\r\n CreatePaymentIntentParams,\r\n PaymentIntentResult,\r\n AuthorizePaymentParams,\r\n AuthorizationResult,\r\n CapturePaymentParams,\r\n CaptureResult,\r\n VoidPaymentParams,\r\n VoidResult,\r\n RefundParams,\r\n RefundResult,\r\n PaymentMetadata,\r\n ProviderMetadata,\r\n ProviderHealthStatus,\r\n PaymentErrorCode,\r\n PaymentError,\r\n} from './payment-types.js';\r\n\r\n// State machine types\r\nexport type {\r\n TransactionStatus,\r\n TransactionEvent,\r\n StateTransitionResult,\r\n} from './state-machine.js';\r\n\r\nexport {\r\n TERMINAL_STATES,\r\n SUCCESS_STATES,\r\n HOLD_STATES,\r\n VALID_TRANSITIONS,\r\n DEFAULT_AUTH_HOLD_DAYS,\r\n canTransition,\r\n getNextStatus,\r\n isTerminalState,\r\n isSuccessState,\r\n isHoldState,\r\n canRefund,\r\n canCapture,\r\n canVoid,\r\n attemptTransition,\r\n calculateCaptureDeadline,\r\n isAuthorizationExpired,\r\n} from './state-machine.js';\r\n\r\n// Webhook types\r\nexport type {\r\n WebhookStatus,\r\n WebhookEvent,\r\n WebhookProcessingResult,\r\n WebhookAction,\r\n StripeEventType,\r\n WebhookVerificationParams,\r\n WebhookVerificationResult,\r\n WebhookQueueMessage,\r\n QueueProcessingResult,\r\n ReconciliationResult,\r\n ReconciliationStrategy,\r\n} from './webhook-types.js';\r\n\r\nexport { STRIPE_EVENT_TO_STATUS } from './webhook-types.js';\r\n","/**\r\n * @nehorai/payments - Transaction State Machine\r\n *\r\n * Defines the strict state transitions for payment transactions.\r\n * Implements the J5 (Two-Phase Commit) pattern for authorize/capture flows.\r\n *\r\n * State Flow:\r\n * +-------------------------------------------------------------+\r\n * | CREATED --------------------------------------------------+ |\r\n * | | | |\r\n * | v v |\r\n * | PENDING_AUTHORIZATION -----------------------------> FAILED |\r\n * | | ^ |\r\n * | v | |\r\n * | AUTHORIZED ---------> VOIDED | |\r\n * | | | |\r\n * | +----------------> EXPIRED ------------------------+ |\r\n * | | | |\r\n * | v | |\r\n * | CAPTURING --------------------------------------------+ |\r\n * | | |\r\n * | v |\r\n * | CAPTURED ------> PARTIALLY_REFUNDED ------> FULLY_REFUNDED |\r\n * +-------------------------------------------------------------+\r\n */\r\n\r\n// ============================================================================\r\n// Transaction States\r\n// ============================================================================\r\n\r\n/**\r\n * All possible transaction states\r\n */\r\nexport type TransactionStatus =\r\n | 'created'\r\n | 'pending_authorization'\r\n | 'authorized'\r\n | 'capturing'\r\n | 'captured'\r\n | 'voided'\r\n | 'failed'\r\n | 'expired'\r\n | 'partially_refunded'\r\n | 'fully_refunded';\r\n\r\n/**\r\n * Terminal states - no further transitions possible\r\n */\r\nexport const TERMINAL_STATES: readonly TransactionStatus[] = [\r\n 'voided',\r\n 'failed',\r\n 'expired',\r\n 'fully_refunded',\r\n] as const;\r\n\r\n/**\r\n * States that indicate successful completion\r\n */\r\nexport const SUCCESS_STATES: readonly TransactionStatus[] = [\r\n 'captured',\r\n 'partially_refunded',\r\n 'fully_refunded',\r\n] as const;\r\n\r\n/**\r\n * States where funds are held but not captured\r\n */\r\nexport const HOLD_STATES: readonly TransactionStatus[] = [\r\n 'authorized',\r\n] as const;\r\n\r\n// ============================================================================\r\n// Transaction Events\r\n// ============================================================================\r\n\r\n/**\r\n * Events that trigger state transitions\r\n */\r\nexport type TransactionEvent =\r\n | 'INITIATE'\r\n | 'AUTHORIZE_PENDING'\r\n | 'AUTHORIZE_SUCCESS'\r\n | 'AUTHORIZE_FAILED'\r\n | 'CAPTURE_STARTED'\r\n | 'CAPTURE_SUCCESS'\r\n | 'CAPTURE_FAILED'\r\n | 'VOID_SUCCESS'\r\n | 'VOID_FAILED'\r\n | 'EXPIRED'\r\n | 'PARTIAL_REFUND'\r\n | 'FULL_REFUND';\r\n\r\n// ============================================================================\r\n// State Transition Map\r\n// ============================================================================\r\n\r\n/**\r\n * Valid transitions from each state\r\n */\r\nexport const VALID_TRANSITIONS: Record<TransactionStatus, TransactionEvent[]> = {\r\n created: ['INITIATE', 'AUTHORIZE_PENDING', 'AUTHORIZE_FAILED'],\r\n pending_authorization: ['AUTHORIZE_SUCCESS', 'AUTHORIZE_FAILED', 'EXPIRED'],\r\n authorized: ['CAPTURE_STARTED', 'VOID_SUCCESS', 'VOID_FAILED', 'EXPIRED'],\r\n capturing: ['CAPTURE_SUCCESS', 'CAPTURE_FAILED'],\r\n captured: ['PARTIAL_REFUND', 'FULL_REFUND'],\r\n voided: [], // Terminal state\r\n failed: [], // Terminal state\r\n expired: [], // Terminal state\r\n partially_refunded: ['PARTIAL_REFUND', 'FULL_REFUND'],\r\n fully_refunded: [], // Terminal state\r\n};\r\n\r\n/**\r\n * Event to next state mapping\r\n */\r\nconst EVENT_TO_STATE: Record<TransactionEvent, TransactionStatus> = {\r\n INITIATE: 'pending_authorization',\r\n AUTHORIZE_PENDING: 'pending_authorization',\r\n AUTHORIZE_SUCCESS: 'authorized',\r\n AUTHORIZE_FAILED: 'failed',\r\n CAPTURE_STARTED: 'capturing',\r\n CAPTURE_SUCCESS: 'captured',\r\n CAPTURE_FAILED: 'failed',\r\n VOID_SUCCESS: 'voided',\r\n VOID_FAILED: 'authorized', // Remain authorized if void fails\r\n EXPIRED: 'expired',\r\n PARTIAL_REFUND: 'partially_refunded',\r\n FULL_REFUND: 'fully_refunded',\r\n};\r\n\r\n// ============================================================================\r\n// State Machine Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Check if a transition is valid\r\n */\r\nexport function canTransition(\r\n currentStatus: TransactionStatus,\r\n event: TransactionEvent\r\n): boolean {\r\n return VALID_TRANSITIONS[currentStatus].includes(event);\r\n}\r\n\r\n/**\r\n * Get the next state after an event, or null if transition is invalid\r\n */\r\nexport function getNextStatus(\r\n currentStatus: TransactionStatus,\r\n event: TransactionEvent\r\n): TransactionStatus | null {\r\n if (!canTransition(currentStatus, event)) {\r\n return null;\r\n }\r\n return EVENT_TO_STATE[event];\r\n}\r\n\r\n/**\r\n * Check if a state is terminal (no further transitions)\r\n */\r\nexport function isTerminalState(status: TransactionStatus): boolean {\r\n return TERMINAL_STATES.includes(status);\r\n}\r\n\r\n/**\r\n * Check if a state represents a successful payment\r\n */\r\nexport function isSuccessState(status: TransactionStatus): boolean {\r\n return SUCCESS_STATES.includes(status);\r\n}\r\n\r\n/**\r\n * Check if funds are currently held (authorized but not captured)\r\n */\r\nexport function isHoldState(status: TransactionStatus): boolean {\r\n return HOLD_STATES.includes(status);\r\n}\r\n\r\n/**\r\n * Check if a refund is possible from current state\r\n */\r\nexport function canRefund(status: TransactionStatus): boolean {\r\n return status === 'captured' || status === 'partially_refunded';\r\n}\r\n\r\n/**\r\n * Check if capture is possible from current state\r\n */\r\nexport function canCapture(status: TransactionStatus): boolean {\r\n return status === 'authorized';\r\n}\r\n\r\n/**\r\n * Check if void is possible from current state\r\n */\r\nexport function canVoid(status: TransactionStatus): boolean {\r\n return status === 'authorized';\r\n}\r\n\r\n// ============================================================================\r\n// State Transition Result Types\r\n// ============================================================================\r\n\r\n/**\r\n * Result of a state transition attempt\r\n */\r\nexport interface StateTransitionResult {\r\n success: boolean;\r\n previousStatus: TransactionStatus;\r\n newStatus: TransactionStatus;\r\n event: TransactionEvent;\r\n error?: string;\r\n}\r\n\r\n/**\r\n * Attempt a state transition with validation\r\n */\r\nexport function attemptTransition(\r\n currentStatus: TransactionStatus,\r\n event: TransactionEvent\r\n): StateTransitionResult {\r\n const nextStatus = getNextStatus(currentStatus, event);\r\n\r\n if (nextStatus === null) {\r\n return {\r\n success: false,\r\n previousStatus: currentStatus,\r\n newStatus: currentStatus,\r\n event,\r\n error: `Invalid transition: ${currentStatus} -> ${event}`,\r\n };\r\n }\r\n\r\n return {\r\n success: true,\r\n previousStatus: currentStatus,\r\n newStatus: nextStatus,\r\n event,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Authorization Expiry\r\n// ============================================================================\r\n\r\n/**\r\n * Default authorization hold period (7 days for most providers)\r\n */\r\nexport const DEFAULT_AUTH_HOLD_DAYS = 7;\r\n\r\n/**\r\n * Calculate capture deadline from authorization time\r\n */\r\nexport function calculateCaptureDeadline(\r\n authorizedAt: Date,\r\n holdDays: number = DEFAULT_AUTH_HOLD_DAYS\r\n): Date {\r\n const deadline = new Date(authorizedAt);\r\n deadline.setDate(deadline.getDate() + holdDays);\r\n return deadline;\r\n}\r\n\r\n/**\r\n * Check if authorization has expired\r\n */\r\nexport function isAuthorizationExpired(captureDeadline: Date): boolean {\r\n return new Date() > captureDeadline;\r\n}\r\n","/**\r\n * @nehorai/payments - Webhook Types\r\n *\r\n * Types for processing incoming webhooks from payment providers.\r\n * Includes signature verification and idempotent event handling.\r\n */\r\n\r\nimport type { PaymentProvider } from './payment-types.js';\r\nimport type { TransactionStatus } from './state-machine.js';\r\n\r\n// ============================================================================\r\n// Webhook Event Types\r\n// ============================================================================\r\n\r\n/**\r\n * Status of webhook processing\r\n */\r\nexport type WebhookStatus =\r\n | 'pending'\r\n | 'processing'\r\n | 'processed'\r\n | 'failed'\r\n | 'ignored';\r\n\r\n/**\r\n * Incoming webhook event from any provider\r\n */\r\nexport interface WebhookEvent {\r\n /** Source payment provider */\r\n provider: PaymentProvider;\r\n /** Provider's unique event ID (for idempotency) */\r\n eventId: string;\r\n /** Type of event (e.g., 'payment_intent.succeeded') */\r\n eventType: string;\r\n /** When the event occurred */\r\n timestamp: Date;\r\n /** Raw event payload */\r\n payload: Record<string, unknown>;\r\n /** Signature from provider for verification */\r\n signature: string;\r\n}\r\n\r\n/**\r\n * Result of processing a webhook event\r\n */\r\nexport interface WebhookProcessingResult {\r\n success: boolean;\r\n /** Associated transaction ID if applicable */\r\n transactionId?: string;\r\n /** What action was taken */\r\n action?: WebhookAction;\r\n /** Error message if failed */\r\n error?: string;\r\n /** Whether this was a duplicate event */\r\n wasDuplicate?: boolean;\r\n}\r\n\r\n/**\r\n * Actions taken when processing webhooks\r\n */\r\nexport type WebhookAction =\r\n | 'transaction_created'\r\n | 'status_updated'\r\n | 'refund_processed'\r\n | 'dispute_created'\r\n | 'skipped_duplicate'\r\n | 'ignored_event_type'\r\n | 'no_action_needed';\r\n\r\n// ============================================================================\r\n// Provider-Specific Event Types\r\n// ============================================================================\r\n\r\n/**\r\n * Stripe webhook event types we handle\r\n */\r\nexport type StripeEventType =\r\n | 'payment_intent.created'\r\n | 'payment_intent.processing'\r\n | 'payment_intent.succeeded'\r\n | 'payment_intent.payment_failed'\r\n | 'payment_intent.canceled'\r\n | 'payment_intent.amount_capturable_updated'\r\n | 'charge.succeeded'\r\n | 'charge.failed'\r\n | 'charge.refunded'\r\n | 'charge.dispute.created'\r\n | 'charge.dispute.closed'\r\n | 'customer.subscription.created'\r\n | 'customer.subscription.updated'\r\n | 'customer.subscription.deleted'\r\n | 'invoice.paid'\r\n | 'invoice.payment_failed';\r\n\r\n/**\r\n * Map of Stripe events to transaction status updates\r\n */\r\nexport const STRIPE_EVENT_TO_STATUS: Partial<Record<StripeEventType, TransactionStatus>> = {\r\n 'payment_intent.succeeded': 'captured',\r\n 'payment_intent.payment_failed': 'failed',\r\n 'payment_intent.canceled': 'voided',\r\n 'payment_intent.amount_capturable_updated': 'authorized',\r\n 'charge.refunded': 'partially_refunded', // or fully_refunded based on amount\r\n};\r\n\r\n// ============================================================================\r\n// Webhook Signature Verification\r\n// ============================================================================\r\n\r\n/**\r\n * Parameters for verifying webhook signature\r\n */\r\nexport interface WebhookVerificationParams {\r\n payload: string;\r\n signature: string;\r\n secret: string;\r\n /** Tolerance in seconds for timestamp validation */\r\n tolerance?: number;\r\n}\r\n\r\n/**\r\n * Result of webhook signature verification\r\n */\r\nexport interface WebhookVerificationResult {\r\n valid: boolean;\r\n error?: string;\r\n}\r\n\r\n// ============================================================================\r\n// Webhook Queue Types (for SQS processing)\r\n// ============================================================================\r\n\r\n/**\r\n * Message format for webhook queue\r\n */\r\nexport interface WebhookQueueMessage {\r\n /** Message ID for deduplication */\r\n messageId: string;\r\n /** Provider that sent the webhook */\r\n provider: PaymentProvider;\r\n /** Provider's event ID */\r\n eventId: string;\r\n /** Event type */\r\n eventType: string;\r\n /** Raw payload (JSON string) */\r\n payload: string;\r\n /** Signature for verification */\r\n signature: string;\r\n /** When received by our API */\r\n receivedAt: string;\r\n /** Number of processing attempts */\r\n attemptCount: number;\r\n}\r\n\r\n/**\r\n * Result of queue message processing\r\n */\r\nexport interface QueueProcessingResult {\r\n success: boolean;\r\n messageId: string;\r\n action?: WebhookAction;\r\n shouldDelete: boolean;\r\n shouldRetry: boolean;\r\n error?: string;\r\n}\r\n\r\n// ============================================================================\r\n// Reconciliation Types\r\n// ============================================================================\r\n\r\n/**\r\n * Result of reconciling payment state\r\n * Handles race condition between redirect and webhook\r\n */\r\nexport interface ReconciliationResult {\r\n reconciled: boolean;\r\n /** Final determined status */\r\n finalStatus: TransactionStatus;\r\n /** Source of truth (redirect callback vs webhook) */\r\n source: 'redirect' | 'webhook' | 'provider_query';\r\n /** Whether status was updated */\r\n statusChanged: boolean;\r\n}\r\n\r\n/**\r\n * Reconciliation strategy when redirect and webhook conflict\r\n */\r\nexport type ReconciliationStrategy =\r\n | 'prefer_webhook' // Wait for webhook (more reliable)\r\n | 'prefer_redirect' // Use redirect result immediately\r\n | 'query_provider'; // Query provider API directly\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACgDO,IAAM,kBAAgD;AAAA,EAC3D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKO,IAAM,iBAA+C;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AACF;AAKO,IAAM,cAA4C;AAAA,EACvD;AACF;AA8BO,IAAM,oBAAmE;AAAA,EAC9E,SAAS,CAAC,YAAY,qBAAqB,kBAAkB;AAAA,EAC7D,uBAAuB,CAAC,qBAAqB,oBAAoB,SAAS;AAAA,EAC1E,YAAY,CAAC,mBAAmB,gBAAgB,eAAe,SAAS;AAAA,EACxE,WAAW,CAAC,mBAAmB,gBAAgB;AAAA,EAC/C,UAAU,CAAC,kBAAkB,aAAa;AAAA,EAC1C,QAAQ,CAAC;AAAA;AAAA,EACT,QAAQ,CAAC;AAAA;AAAA,EACT,SAAS,CAAC;AAAA;AAAA,EACV,oBAAoB,CAAC,kBAAkB,aAAa;AAAA,EACpD,gBAAgB,CAAC;AAAA;AACnB;AAKA,IAAM,iBAA8D;AAAA,EAClE,UAAU;AAAA,EACV,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,aAAa;AAAA;AAAA,EACb,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,aAAa;AACf;AASO,SAAS,cACd,eACA,OACS;AACT,SAAO,kBAAkB,aAAa,EAAE,SAAS,KAAK;AACxD;AAKO,SAAS,cACd,eACA,OAC0B;AAC1B,MAAI,CAAC,cAAc,eAAe,KAAK,GAAG;AACxC,WAAO;AAAA,EACT;AACA,SAAO,eAAe,KAAK;AAC7B;AAKO,SAAS,gBAAgB,QAAoC;AAClE,SAAO,gBAAgB,SAAS,MAAM;AACxC;AAKO,SAAS,eAAe,QAAoC;AACjE,SAAO,eAAe,SAAS,MAAM;AACvC;AAKO,SAAS,YAAY,QAAoC;AAC9D,SAAO,YAAY,SAAS,MAAM;AACpC;AAKO,SAAS,UAAU,QAAoC;AAC5D,SAAO,WAAW,cAAc,WAAW;AAC7C;AAKO,SAAS,WAAW,QAAoC;AAC7D,SAAO,WAAW;AACpB;AAKO,SAAS,QAAQ,QAAoC;AAC1D,SAAO,WAAW;AACpB;AAoBO,SAAS,kBACd,eACA,OACuB;AACvB,QAAM,aAAa,cAAc,eAAe,KAAK;AAErD,MAAI,eAAe,MAAM;AACvB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX;AAAA,MACA,OAAO,uBAAuB,aAAa,OAAO,KAAK;AAAA,IACzD;AAAA,EACF;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX;AAAA,EACF;AACF;AASO,IAAM,yBAAyB;AAK/B,SAAS,yBACd,cACA,WAAmB,wBACb;AACN,QAAM,WAAW,IAAI,KAAK,YAAY;AACtC,WAAS,QAAQ,SAAS,QAAQ,IAAI,QAAQ;AAC9C,SAAO;AACT;AAKO,SAAS,uBAAuB,iBAAgC;AACrE,SAAO,oBAAI,KAAK,IAAI;AACtB;;;AC1KO,IAAM,yBAA8E;AAAA,EACzF,4BAA4B;AAAA,EAC5B,iCAAiC;AAAA,EACjC,2BAA2B;AAAA,EAC3B,4CAA4C;AAAA,EAC5C,mBAAmB;AAAA;AACrB;","names":[]}