registry = $registry; $this->providerConfigs = $this->loadProviderConfigurations(); } /** * Initialize all configured payment providers */ public function initializeProviders(): void { try { $this->registerStripeProvider(); $this->registerLemonSqueezyProvider(); $this->registerPolarProvider(); $this->registerOxapayProvider(); $this->registerCryptoProvider(); $this->registerActivationKeyProvider(); Log::info('Payment providers initialized', [ 'providers' => array_keys($this->registry->getAllProviders()->toArray()), 'active_providers' => array_keys($this->registry->getActiveProviders()->toArray()), ]); } catch (\Exception $e) { Log::error('Failed to initialize payment providers', [ 'error' => $e->getMessage(), ]); throw $e; } } /** * Register Stripe provider if configured */ protected function registerStripeProvider(): void { $config = $this->providerConfigs['stripe'] ?? []; if (! empty($config['secret_key'])) { $provider = new StripeProvider($config); $this->registry->register('stripe', $provider); } } /** * Register Lemon Squeezy provider if configured */ protected function registerLemonSqueezyProvider(): void { $config = $this->providerConfigs['lemon_squeezy'] ?? []; if (! empty($config['api_key']) && ! empty($config['store_id'])) { $provider = new LemonSqueezyProvider($config); $this->registry->register('lemon_squeezy', $provider); } } /** * Register Polar provider if configured */ protected function registerPolarProvider(): void { $config = $this->providerConfigs['polar'] ?? []; if (! empty($config['api_key'])) { $provider = new PolarProvider($config); $this->registry->register('polar', $provider); } } /** * Register OxaPay provider if configured */ protected function registerOxapayProvider(): void { $config = $this->providerConfigs['oxapay'] ?? []; if (! empty($config['merchant_api_key'])) { $provider = new OxapayProvider($config); $this->registry->register('oxapay', $provider); } } /** * Register Crypto provider if enabled */ protected function registerCryptoProvider(): void { $config = $this->providerConfigs['crypto'] ?? []; if ($config['enabled'] ?? false) { $provider = new CryptoProvider($config); $this->registry->register('crypto', $provider); } } /** * Register Activation Key provider (always available) */ protected function registerActivationKeyProvider(): void { $config = $this->providerConfigs['activation_key'] ?? []; $provider = new ActivationKeyProvider($config); $this->registry->register('activation_key', $provider); } /** * Load provider configurations from config and cache */ protected function loadProviderConfigurations(): array { return Cache::remember('payment_provider_configs', now()->addHour(), function () { return [ 'stripe' => [ 'secret_key' => config('services.stripe.secret_key'), 'publishable_key' => config('services.stripe.publishable_key'), 'webhook_secret' => config('services.stripe.webhook_secret'), ], 'lemon_squeezy' => [ 'api_key' => config('services.lemon_squeezy.api_key'), 'store_id' => config('services.lemon_squeezy.store_id'), 'webhook_secret' => config('services.lemon_squeezy.webhook_secret'), ], 'polar' => [ 'api_key' => config('services.polar.api_key'), 'webhook_secret' => config('services.polar.webhook_secret'), ], 'oxapay' => [ 'merchant_api_key' => config('services.oxapay.merchant_api_key'), 'webhook_url' => config('services.oxapay.webhook_url'), 'success_url' => config('services.oxapay.success_url'), 'cancel_url' => config('services.oxapay.cancel_url'), 'sandbox' => config('services.oxapay.sandbox', false), ], 'crypto' => [ 'enabled' => config('payments.crypto.enabled', false), 'webhook_secret' => config('payments.crypto.webhook_secret'), 'confirmation_timeout_minutes' => config('payments.crypto.confirmation_timeout_minutes', 30), 'exchange_rate_provider' => config('payments.crypto.exchange_rate_provider', 'coingecko'), ], 'activation_key' => [ 'key_prefix' => config('payments.activation_key.prefix', 'AK-'), 'key_length' => config('payments.activation_key.length', 32), 'expiration_days' => config('payments.activation_key.expiration_days'), ], ]; }); } /** * Get provider configuration */ public function getProviderConfig(string $provider): array { return $this->providerConfigs[$provider] ?? []; } /** * Update provider configuration */ public function updateProviderConfig(string $provider, array $config): void { $this->providerConfigs[$provider] = array_merge( $this->providerConfigs[$provider] ?? [], $config ); // Clear cache to force reload Cache::forget('payment_provider_configs'); Log::info('Payment provider configuration updated', [ 'provider' => $provider, ]); } /** * Validate provider configuration */ public function validateProviderConfig(string $provider, array $config): array { $errors = []; switch ($provider) { case 'stripe': if (empty($config['secret_key'])) { $errors[] = 'Stripe secret key is required'; } if (empty($config['publishable_key'])) { $errors[] = 'Stripe publishable key is required'; } break; case 'lemon_squeezy': if (empty($config['api_key'])) { $errors[] = 'Lemon Squeezy API key is required'; } if (empty($config['store_id'])) { $errors[] = 'Lemon Squeezy store ID is required'; } break; case 'polar': if (empty($config['api_key'])) { $errors[] = 'Polar API key is required'; } break; case 'oxapay': if (empty($config['merchant_api_key'])) { $errors[] = 'OxaPay merchant API key is required'; } break; case 'crypto': if (empty($config['webhook_secret'])) { $errors[] = 'Crypto webhook secret is required'; } break; case 'activation_key': // Activation keys don't require specific configuration break; default: $errors[] = "Unknown provider: {$provider}"; } return [ 'valid' => empty($errors), 'errors' => $errors, ]; } /** * Get provider status and health information */ public function getProviderStatus(): array { $status = []; $providers = $this->registry->getAllProviders(); foreach ($providers as $name => $provider) { $status[$name] = [ 'name' => $provider->getName(), 'active' => $provider->isActive(), 'supports_recurring' => $provider->supportsRecurring(), 'supports_one_time' => $provider->supportsOneTime(), 'supported_currencies' => $provider->getSupportedCurrencies(), 'configured' => ! empty($this->providerConfigs[$name]), 'configuration' => $this->sanitizeConfig($this->providerConfigs[$name] ?? []), ]; } return $status; } /** * Sanitize configuration for display (remove sensitive data) */ protected function sanitizeConfig(array $config): array { $sensitiveKeys = ['secret_key', 'api_key', 'webhook_secret']; $sanitized = $config; foreach ($sensitiveKeys as $key) { if (isset($sanitized[$key]) && ! empty($sanitized[$key])) { $sanitized[$key] = '***'.substr($sanitized[$key], -4); } } return $sanitized; } /** * Enable/disable a provider */ public function toggleProvider(string $provider, bool $enabled): bool { try { if ($enabled && ! $this->registry->has($provider)) { // Register the provider if it doesn't exist $this->registerProviderByName($provider); } if (! $enabled && $this->registry->has($provider)) { // Unregister the provider $this->registry->unregister($provider); } $this->updateProviderConfig($provider, ['enabled' => $enabled]); Log::info('Payment provider toggled', [ 'provider' => $provider, 'enabled' => $enabled, ]); return true; } catch (\Exception $e) { Log::error('Failed to toggle payment provider', [ 'provider' => $provider, 'enabled' => $enabled, 'error' => $e->getMessage(), ]); return false; } } /** * Register a provider by name */ protected function registerProviderByName(string $provider): void { switch ($provider) { case 'stripe': $this->registerStripeProvider(); break; case 'lemon_squeezy': $this->registerLemonSqueezyProvider(); break; case 'polar': $this->registerPolarProvider(); break; case 'oxapay': $this->registerOxapayProvider(); break; case 'crypto': $this->registerCryptoProvider(); break; case 'activation_key': $this->registerActivationKeyProvider(); break; default: throw new \InvalidArgumentException("Unknown provider: {$provider}"); } } /** * Get default provider for a given plan type */ public function getDefaultProvider(?string $planType = null): string { // Priority order for providers $priority = [ 'stripe', // Most reliable 'lemon_squeezy', // Good for international 'polar', // Developer-focused MoR 'oxapay', // Crypto payment gateway 'crypto', // For crypto payments 'activation_key', // For manual activation ]; foreach ($priority as $provider) { if ($this->registry->has($provider) && $this->registry->get($provider)->isActive()) { return $provider; } } // Fallback to activation key (always available) return 'activation_key'; } /** * Test provider connectivity */ public function testProviderConnectivity(string $provider): array { try { if (! $this->registry->has($provider)) { return [ 'success' => false, 'error' => 'Provider not registered', ]; } $providerInstance = $this->registry->get($provider); // Basic connectivity test - check if provider is active $isActive = $providerInstance->isActive(); return [ 'success' => $isActive, 'message' => $isActive ? 'Provider is active and ready' : 'Provider is not active', 'details' => [ 'name' => $providerInstance->getName(), 'supports_recurring' => $providerInstance->supportsRecurring(), 'supports_one_time' => $providerInstance->supportsOneTime(), ], ]; } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage(), ]; } } /** * Refresh provider configurations */ public function refreshConfigurations(): void { Cache::forget('payment_provider_configs'); $this->providerConfigs = $this->loadProviderConfigurations(); Log::info('Payment provider configurations refreshed'); } }