getName())->toBe('polar'); expect($provider->supportsRecurring())->toBeTrue(); expect($provider->supportsOneTime())->toBeTrue(); expect($provider->getSupportedCurrencies())->toBe(['USD']); }); test('polar provider is inactive without api key', function () { $provider = new PolarProvider; expect($provider->isActive())->toBeFalse(); }); test('polar provider is active with api key', function () { $provider = new PolarProvider(['api_key' => 'test-api-key']); expect($provider->isActive())->toBeTrue(); }); test('can calculate fees correctly', function () { $provider = new PolarProvider; $fees = $provider->calculateFees(100.00); expect($fees['fixed_fee'])->toBe(0); expect($fees['percentage_fee'])->toBe(6.0); // 6% fee expect($fees['total_fee'])->toBe(6.0); expect($fees['net_amount'])->toBe(94.0); }); test('can get configuration', function () { $config = [ 'api_key' => 'test-api-key', 'webhook_secret' => 'test-secret', ]; $provider = new PolarProvider($config); expect($provider->getConfiguration())->toHaveKey('api_key', 'test-api-key'); expect($provider->getConfiguration())->toHaveKey('webhook_secret', 'test-secret'); }); test('can validate webhook signature', function () { $provider = new PolarProvider(['webhook_secret' => 'test-secret']); $request = new \Illuminate\Http\Request([], [], [], [], [], [], 'test-payload'); $request->headers->set('Polar-Signature', 'test-signature'); expect($provider->validateWebhook($request))->toBeFalse(); // Invalid signature }); test('webhook methods return expected values', function () { $provider = new PolarProvider; $request = new \Illuminate\Http\Request([], [], [], [], [], [], 'test-payload'); $webhookResult = $provider->processWebhook($request); expect($webhookResult['event_type'])->toBe('unknown'); expect($webhookResult['processed'])->toBeFalse(); expect($provider->validateWebhook($request))->toBeFalse(); }); test('customer portal returns dashboard when no customer', function () { $provider = new PolarProvider; $mockUser = new class { public $id = 1; }; // Should throw exception when no customer exists expect($provider->createCustomerPortalSession($mockUser)) ->toThrow(\Exception::class); }); test('polar payments are not refundable through API', function () { $provider = new PolarProvider; expect($provider->processRefund('test_id', 100.00, 'User requested')) ->toThrow('Polar refunds must be processed through Polar dashboard or API directly'); }); test('can get supported currencies', function () { $provider = new PolarProvider; $currencies = $provider->getSupportedCurrencies(); expect($currencies)->toBe(['USD']); }); test('has correct method signatures', function () { $provider = new PolarProvider; // Test that all required methods exist and have correct signatures $reflection = new ReflectionClass($provider); $methods = [ 'getName' => 0, 'isActive' => 0, 'supportsRecurring' => 0, 'supportsOneTime' => 0, 'getSupportedCurrencies' => 0, 'calculateFees' => 1, 'createSubscription' => 3, 'cancelSubscription' => 2, 'updateSubscription' => 2, 'pauseSubscription' => 1, 'resumeSubscription' => 1, 'getSubscriptionDetails' => 1, 'createCheckoutSession' => 3, 'createCustomerPortalSession' => 1, 'processWebhook' => 1, 'validateWebhook' => 1, 'getConfiguration' => 0, 'syncSubscriptionStatus' => 1, 'getPaymentMethodDetails' => 1, 'processRefund' => 3, 'getTransactionHistory' => 2, 'getSubscriptionMetadata' => 1, 'updateSubscriptionMetadata' => 2, 'startTrial' => 2, 'applyCoupon' => 2, 'removeCoupon' => 1, 'getUpcomingInvoice' => 1, 'retryFailedPayment' => 1, 'canModifySubscription' => 1, 'getCancellationTerms' => 1, 'exportSubscriptionData' => 1, 'importSubscriptionData' => 2, ]; foreach ($methods as $methodName => $expectedParams) { expect($reflection->hasMethod($methodName))->toBeTrue(); expect($reflection->getMethod($methodName)->getNumberOfParameters())->toBe($expectedParams); } }); test('has correct cancellation terms', function () { $provider = new PolarProvider; $mockSubscription = new class { public $id = 1; }; $terms = $provider->getCancellationTerms($mockSubscription); expect($terms['immediate_cancellation'])->toBeTrue(); expect($terms['refund_policy'])->toBe('no_pro_rated_refunds'); expect($terms['cancellation_effective'])->toBe('immediately'); expect($terms['billing_cycle_proration'])->toBeFalse(); }); test('can export subscription data', function () { $provider = new PolarProvider; $mockSubscription = new class { public $id = 1; public $provider_subscription_id = 'test_id'; public $provider_data = ['test' => 'data']; }; $data = $provider->exportSubscriptionData($mockSubscription); expect($data['provider'])->toBe('polar'); expect($data['provider_subscription_id'])->toBe('test_id'); expect($data['data'])->toBe(['test' => 'data']); }); test('cannot import subscription data', function () { $provider = new PolarProvider; $mockUser = new class { public $id = 1; }; $subscriptionData = ['provider' => 'polar', 'data' => []]; expect(fn () => $provider->importSubscriptionData($mockUser, $subscriptionData)) ->toThrow('Import to Polar payments not implemented'); }); test('cannot start trial after subscription', function () { $provider = new PolarProvider; $mockSubscription = new class { public $id = 1; }; expect($provider->startTrial($mockSubscription, 7))->toBeFalse(); }); test('upcoming invoice returns empty data for non-existent subscription', function () { $provider = new PolarProvider; $mockSubscription = new class { public $id = 1; }; $invoice = $provider->getUpcomingInvoice($mockSubscription); expect($invoice['amount_due'])->toBe(0); expect($invoice['currency'])->toBe('USD'); expect($invoice['next_payment_date'])->toBeNull(); }); test('cannot remove coupon without subscription', function () { $provider = new PolarProvider; $mockSubscription = new class { public $id = 1; }; expect($provider->removeCoupon($mockSubscription))->toBeFalse(); }); test('cannot apply coupon without subscription', function () { $provider = new PolarProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->applyCoupon($mockSubscription, 'DISCOUNT10')) ->toThrow(\Throwable::class); }); test('cannot pause subscription without polar subscription', function () { $provider = new PolarProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->pauseSubscription($mockSubscription)) ->toThrow(\Throwable::class); }); test('cannot resume subscription without polar subscription', function () { $provider = new PolarProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->resumeSubscription($mockSubscription)) ->toThrow(\Throwable::class); }); test('cannot get subscription details without valid id', function () { $provider = new PolarProvider; expect(fn () => $provider->getSubscriptionDetails('invalid-id')) ->toThrow(\Exception::class); }); test('cannot cancel subscription without subscription', function () { $provider = new PolarProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->cancelSubscription($mockSubscription, 'test')) ->toThrow(\Throwable::class); }); test('cannot update subscription without subscription', function () { $provider = new PolarProvider; $mockSubscription = new class { public $id = 1; }; $mockPlan = new class { public $id = 1; }; expect(fn () => $provider->updateSubscription($mockSubscription, $mockPlan)) ->toThrow(\Throwable::class); }); test('cannot create subscription without plan', function () { $provider = new PolarProvider; $mockUser = new class { public $id = 1; }; expect(fn () => $provider->createSubscription($mockUser, null)) ->toThrow(\TypeError::class); });