Files
zemailnator/app/Http/Controllers/PaymentProviderController.php
idevakk 27ac13948c feat: implement comprehensive multi-provider payment processing system
- Add unified payment provider architecture with contract-based design
  - Implement 6 payment providers: Stripe, Lemon Squeezy, Polar, Oxapay, Crypto, Activation Keys
  - Create subscription management with lifecycle handling (create, cancel, pause, resume, update)
  - Add coupon system with usage tracking and trial extensions
  - Build Filament admin resources for payment providers, subscriptions, coupons, and trials
  - Implement payment orchestration service with provider registry and configuration management
  - Add comprehensive payment logging and webhook handling for all providers
  - Create customer analytics dashboard with revenue, churn, and lifetime value metrics
  - Add subscription migration service for provider switching
  - Include extensive test coverage for all payment functionality
2025-11-19 09:37:00 -08:00

298 lines
9.3 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\ActivationKey;
use App\Services\Payments\PaymentConfigurationManager;
use App\Services\Payments\PaymentOrchestrator;
use App\Services\Payments\Providers\ActivationKeyProvider;
use App\Services\Payments\Providers\CryptoProvider;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
class PaymentProviderController extends Controller
{
public function __construct(
private PaymentConfigurationManager $configManager,
private PaymentOrchestrator $orchestrator
) {}
/**
* Get all payment providers and their status
*/
public function index(): JsonResponse
{
try {
$status = $this->configManager->getProviderStatus();
$stats = $this->orchestrator->getRegistry()->getProviderStats();
return response()->json([
'providers' => $status,
'statistics' => $stats,
]);
} catch (\Exception $e) {
return response()->json([
'error' => 'Failed to retrieve provider status',
'message' => $e->getMessage(),
], 500);
}
}
/**
* Get specific provider details
*/
public function show(string $provider): JsonResponse
{
try {
$providerInstance = $this->orchestrator->getRegistry()->get($provider);
$config = $this->configManager->getProviderConfig($provider);
return response()->json([
'provider' => $provider,
'name' => $providerInstance->getName(),
'active' => $providerInstance->isActive(),
'configuration' => $this->configManager->sanitizeConfig($config),
'capabilities' => [
'supports_recurring' => $providerInstance->supportsRecurring(),
'supports_one_time' => $providerInstance->supportsOneTime(),
'supported_currencies' => $providerInstance->getSupportedCurrencies(),
],
]);
} catch (\Exception $e) {
return response()->json([
'error' => 'Provider not found',
'message' => $e->getMessage(),
], 404);
}
}
/**
* Test provider connectivity
*/
public function test(string $provider): JsonResponse
{
try {
$result = $this->configManager->testProviderConnectivity($provider);
return response()->json($result);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => 'Failed to test provider',
'message' => $e->getMessage(),
], 500);
}
}
/**
* Enable/disable a provider
*/
public function toggle(Request $request, string $provider): JsonResponse
{
$validated = $request->validate([
'enabled' => 'required|boolean',
]);
try {
$result = $this->configManager->toggleProvider($provider, $validated['enabled']);
if ($result) {
return response()->json([
'success' => true,
'message' => "Provider {$provider} has been ".
($validated['enabled'] ? 'enabled' : 'disabled'),
]);
}
return response()->json([
'success' => false,
'message' => "Failed to toggle provider {$provider}",
], 400);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => 'Failed to toggle provider',
'message' => $e->getMessage(),
], 500);
}
}
/**
* Update provider configuration
*/
public function updateConfig(Request $request, string $provider): JsonResponse
{
$config = $request->all();
// Validate configuration
$validation = $this->configManager->validateProviderConfig($provider, $config);
if (! $validation['valid']) {
throw ValidationException::withMessages([
'config' => $validation['errors'],
]);
}
try {
$this->configManager->updateProviderConfig($provider, $config);
return response()->json([
'success' => true,
'message' => "Configuration updated for provider {$provider}",
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => 'Failed to update configuration',
'message' => $e->getMessage(),
], 500);
}
}
/**
* Refresh provider configurations
*/
public function refresh(): JsonResponse
{
try {
$this->configManager->refreshConfigurations();
return response()->json([
'success' => true,
'message' => 'Provider configurations refreshed',
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => 'Failed to refresh configurations',
'message' => $e->getMessage(),
], 500);
}
}
/**
* Redeem an activation key
*/
public function redeemActivationKey(Request $request): JsonResponse
{
$validated = $request->validate([
'activation_key' => 'required|string',
]);
try {
$user = $request->user();
if (! $user) {
return response()->json([
'success' => false,
'error' => 'Authentication required',
], 401);
}
$provider = new ActivationKeyProvider;
$result = $provider->redeemActivationKey($validated['activation_key'], $user);
return response()->json([
'success' => true,
'data' => $result,
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => 'Failed to redeem activation key',
'message' => $e->getMessage(),
], 400);
}
}
/**
* Validate an activation key
*/
public function validateActivationKey(string $key): JsonResponse
{
try {
$activationKey = ActivationKey::where('activation_key', $key)->first();
if (! $activationKey) {
return response()->json([
'valid' => false,
'reason' => 'Activation key not found',
]);
}
return response()->json([
'valid' => true,
'is_activated' => $activationKey->is_activated,
'created_at' => $activationKey->created_at,
'plan_id' => $activationKey->price_id,
]);
} catch (\Exception $e) {
return response()->json([
'valid' => false,
'error' => 'Failed to validate activation key',
'message' => $e->getMessage(),
], 500);
}
}
/**
* Get cryptocurrency exchange rate
*/
public function getCryptoRate(string $crypto): JsonResponse
{
try {
$provider = new CryptoProvider;
// Test conversion with $1 USD to get rate
$amount = $provider->convertUsdToCrypto(1.00, strtoupper($crypto));
$rate = 1 / $amount; // Invert to get USD per crypto
return response()->json([
'crypto' => strtoupper($crypto),
'rate_usd_per_crypto' => $rate,
'rate_crypto_per_usd' => $amount,
'updated_at' => now()->toISOString(),
]);
} catch (\Exception $e) {
return response()->json([
'error' => 'Failed to get crypto rate',
'message' => $e->getMessage(),
], 400);
}
}
/**
* Convert USD to cryptocurrency
*/
public function convertUsdToCrypto(Request $request): JsonResponse
{
$validated = $request->validate([
'usd_amount' => 'required|numeric|min:0.01',
'crypto' => 'required|string|in:BTC,ETH,USDT,USDC,LTC',
]);
try {
$provider = new CryptoProvider;
$cryptoAmount = $provider->convertUsdToCrypto(
$validated['usd_amount'],
strtoupper($validated['crypto'])
);
$fees = $provider->calculateFees($validated['usd_amount']);
return response()->json([
'usd_amount' => $validated['usd_amount'],
'crypto' => strtoupper($validated['crypto']),
'crypto_amount' => $cryptoAmount,
'fees' => $fees,
'net_crypto_amount' => $cryptoAmount, // Crypto providers typically don't deduct fees from amount
'updated_at' => now()->toISOString(),
]);
} catch (\Exception $e) {
return response()->json([
'error' => 'Failed to convert USD to crypto',
'message' => $e->getMessage(),
], 400);
}
}
}