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
This commit is contained in:
idevakk
2025-11-19 09:37:00 -08:00
parent 0560016f33
commit 27ac13948c
83 changed files with 15613 additions and 103 deletions

View File

@@ -0,0 +1,86 @@
<?php
use App\Models\PaymentEvent;
use App\Models\User;
use App\Services\Payments\PaymentLogger;
test('can log payment event', function () {
$logger = new PaymentLogger;
$logger->logEvent('test_subscription_created', [
'subscription_id' => 123,
'provider' => 'stripe',
'amount' => 10.00,
]);
$event = PaymentEvent::where('event_type', 'test_subscription_created')->first();
expect($event)->not->toBeNull();
expect($event->level)->toBe('info');
expect($event->data['subscription_id'])->toBe(123);
expect($event->data['provider'])->toBe('stripe');
expect($event->data['amount'])->toBe(10.00);
});
test('can log error event', function () {
$logger = new PaymentLogger;
$logger->logError('test_payment_failed', [
'subscription_id' => 456,
'error' => 'Payment declined',
]);
$event = PaymentEvent::where('event_type', 'test_payment_failed')->first();
expect($event)->not->toBeNull();
expect($event->level)->toBe('error');
expect($event->data['error'])->toBe('Payment declined');
});
test('can log security event', function () {
$logger = new PaymentLogger;
$logger->logSecurityEvent('suspicious_activity', [
'ip_address' => '192.168.1.1',
'user_agent' => 'Test Agent',
]);
$event = PaymentEvent::where('event_type', 'security_suspicious_activity')->first();
expect($event)->not->toBeNull();
expect($event->level)->toBe('warning');
expect($event->data['requires_review'])->toBeTrue();
});
test('can get user audit trail', function () {
$user = User::factory()->create();
$logger = new PaymentLogger;
// Log some events for the user
$logger->logEvent('test_event_1', ['user_id' => $user->id]);
$logger->logEvent('test_event_2', ['user_id' => $user->id]);
$trail = $logger->getUserAuditTrail($user->id);
expect($trail)->toHaveCount(2);
expect($trail[0]['event_type'])->toBe('test_event_2'); // Latest first
expect($trail[1]['event_type'])->toBe('test_event_1');
});
test('can generate compliance report', function () {
$logger = new PaymentLogger;
// Log some events
$logger->logEvent('compliance_data_access', ['user_id' => 1]);
$logger->logEvent('subscription_created', ['user_id' => 2]);
$logger->logError('payment_failed', ['user_id' => 3]);
$report = $logger->generateComplianceReport();
expect($report['total_events'])->toBe(3);
expect($report['events_by_type']['compliance_data_access'])->toBe(1);
expect($report['events_by_type']['subscription_created'])->toBe(1);
expect($report['events_by_type']['payment_failed'])->toBe(1);
expect($report['events_by_level']['info'])->toBe(2);
expect($report['events_by_level']['error'])->toBe(1);
});

View File

@@ -0,0 +1,77 @@
<?php
use App\Services\Payments\ProviderRegistry;
use App\Services\Payments\Providers\StripeProvider;
test('can register payment provider', function () {
$registry = new ProviderRegistry;
$provider = new StripeProvider;
$registry->register('stripe', $provider);
expect($registry->has('stripe'))->toBeTrue();
expect($registry->get('stripe'))->toBe($provider);
});
test('can get all providers', function () {
$registry = new ProviderRegistry;
$stripeProvider = new StripeProvider;
$registry->register('stripe', $stripeProvider);
$providers = $registry->getAllProviders();
expect($providers)->toHaveCount(1);
expect($providers->get('stripe'))->toBe($stripeProvider);
});
test('can get active providers only', function () {
$registry = new ProviderRegistry;
$stripeProvider = new StripeProvider;
$registry->register('stripe', $stripeProvider);
$activeProviders = $registry->getActiveProviders();
expect($activeProviders)->toHaveCount(0); // Stripe is inactive without API key
});
test('can unregister provider', function () {
$registry = new ProviderRegistry;
$provider = new StripeProvider;
$registry->register('stripe', $provider);
expect($registry->has('stripe'))->toBeTrue();
$result = $registry->unregister('stripe');
expect($result)->toBeTrue();
expect($registry->has('stripe'))->toBeFalse();
});
test('can validate providers', function () {
$registry = new ProviderRegistry;
$stripeProvider = new StripeProvider;
$registry->register('stripe', $stripeProvider);
$results = $registry->validateProviders();
expect($results)->toHaveKey('stripe');
expect($results['stripe']['active'])->toBeFalse();
expect($results['stripe']['supports_recurring'])->toBeTrue();
expect($results['stripe']['supports_one_time'])->toBeTrue();
});
test('can get provider statistics', function () {
$registry = new ProviderRegistry;
$stripeProvider = new StripeProvider;
$registry->register('stripe', $stripeProvider);
$stats = $registry->getProviderStats();
expect($stats['total_providers'])->toBe(1);
expect($stats['active_providers'])->toBe(0);
expect($stats['recurring_providers'])->toBe(0);
expect($stats['one_time_providers'])->toBe(0);
});