getName())->toBe('oxapay'); expect($provider->supportsRecurring())->toBeFalse(); expect($provider->supportsOneTime())->toBeTrue(); expect($provider->getSupportedCurrencies())->toBeArray(); }); test('oxapay provider is inactive without api key', function () { $provider = new OxapayProvider; expect($provider->isActive())->toBeFalse(); }); test('oxapay provider is active with merchant api key', function () { $provider = new OxapayProvider(['merchant_api_key' => 'test-api-key']); expect($provider->isActive())->toBeTrue(); }); test('can calculate fees correctly', function () { $provider = new OxapayProvider; $fees = $provider->calculateFees(100.00); expect($fees['fixed_fee'])->toBe(0); expect($fees['percentage_fee'])->toBe(0.5); // 0.5% fee expect($fees['total_fee'])->toBe(0.5); expect($fees['net_amount'])->toBe(99.5); }); test('can get configuration', function () { $config = [ 'merchant_api_key' => 'test-api-key', 'webhook_url' => 'https://example.com/webhook', 'sandbox' => true, ]; $provider = new OxapayProvider($config); expect($provider->getConfiguration())->toHaveKey('merchant_api_key', 'test-api-key'); expect($provider->getConfiguration())->toHaveKey('webhook_url', 'https://example.com/webhook'); expect($provider->getConfiguration())->toHaveKey('sandbox', true); }); test('can validate webhook signature', function () { $provider = new OxapayProvider(['merchant_api_key' => 'test-secret']); $request = new \Illuminate\Http\Request([], [], [], [], [], [], '{"test": "payload"}'); $signature = hash_hmac('sha512', '{"test": "payload"}', 'test-secret'); $request->headers->set('HMAC', $signature); expect($provider->validateWebhook($request))->toBeTrue(); }); test('webhook validation fails with invalid signature', function () { $provider = new OxapayProvider(['merchant_api_key' => 'test-secret']); $request = new \Illuminate\Http\Request([], [], [], [], [], [], '{"test": "payload"}'); $request->headers->set('HMAC', 'invalid-signature'); expect($provider->validateWebhook($request))->toBeFalse(); }); test('webhook validation fails without signature', function () { $provider = new OxapayProvider(['merchant_api_key' => 'test-secret']); $request = new \Illuminate\Http\Request([], [], [], [], [], [], '{"test": "payload"}'); expect($provider->validateWebhook($request))->toBeFalse(); }); test('can process webhook with valid signature', function () { $provider = new OxapayProvider(['merchant_api_key' => 'test-secret']); $payload = json_encode([ 'status' => 'Paid', 'track_id' => 'test-track-id', 'type' => 'payment', 'amount' => 100, 'currency' => 'USDT', ]); $request = new \Illuminate\Http\Request([], [], [], [], [], [], $payload); $signature = hash_hmac('sha512', $payload, 'test-secret'); $request->headers->set('HMAC', $signature); $result = $provider->processWebhook($request); expect($result['success'])->toBeTrue(); expect($result['event_type'])->toBe('Paid'); expect($result['provider_transaction_id'])->toBe('test-track-id'); expect($result['processed'])->toBeTrue(); expect($result['type'])->toBe('payment'); }); test('webhook processing fails with invalid signature', function () { $provider = new OxapayProvider(['merchant_api_key' => 'test-secret']); $payload = json_encode([ 'status' => 'Paid', 'track_id' => 'test-track-id', ]); $request = new \Illuminate\Http\Request([], [], [], [], [], [], $payload); $request->headers->set('HMAC', 'invalid-signature'); $result = $provider->processWebhook($request); expect($result['success'])->toBeFalse(); expect($result['processed'])->toBeFalse(); expect($result['error'])->toBe('Invalid webhook signature'); }); test('cannot create recurring subscription', function () { $provider = new OxapayProvider; $mockUser = new class { public $id = 1; }; $mockPlan = new class { public $id = 1; }; expect(fn () => $provider->createSubscription($mockUser, $mockPlan)) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot cancel subscription', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->cancelSubscription($mockSubscription, 'test')) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot update subscription', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; $mockPlan = new class { public $id = 1; }; expect(fn () => $provider->updateSubscription($mockSubscription, $mockPlan)) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot pause subscription', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->pauseSubscription($mockSubscription)) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot resume subscription', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->resumeSubscription($mockSubscription)) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot get subscription details', function () { $provider = new OxapayProvider; expect(fn () => $provider->getSubscriptionDetails('test-id')) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot create customer portal session', function () { $provider = new OxapayProvider; $mockUser = new class { public $id = 1; }; expect(fn () => $provider->createCustomerPortalSession($mockUser)) ->toThrow('OxaPay does not provide customer portal functionality'); }); test('cannot sync subscription status', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->syncSubscriptionStatus($mockSubscription)) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot process refunds through api', function () { $provider = new OxapayProvider; $result = $provider->processRefund('test-id', 100.00, 'User requested'); expect($result['success'])->toBeFalse(); expect($result['error'])->toBe('OxaPay refunds must be processed manually via payouts'); }); test('cannot apply coupon to subscription', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->applyCoupon($mockSubscription, 'DISCOUNT10')) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot remove coupon from subscription', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect($provider->removeCoupon($mockSubscription))->toBeFalse(); }); test('cannot get upcoming invoice', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->getUpcomingInvoice($mockSubscription)) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot retry failed payment', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->retryFailedPayment($mockSubscription)) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot start trial', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect($provider->startTrial($mockSubscription, 7))->toBeFalse(); }); test('cannot modify subscription', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect($provider->canModifySubscription($mockSubscription))->toBeFalse(); }); test('has correct cancellation terms', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; $terms = $provider->getCancellationTerms($mockSubscription); expect($terms['immediate_cancellation'])->toBeTrue(); expect($terms['refund_policy'])->toBe('no_refunds_crypto'); expect($terms['cancellation_effective'])->toBe('immediately'); expect($terms['billing_cycle_proration'])->toBeFalse(); }); test('cannot export subscription data', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->exportSubscriptionData($mockSubscription)) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot import subscription data', function () { $provider = new OxapayProvider; $mockUser = new class { public $id = 1; }; $subscriptionData = ['provider' => 'oxapay', 'data' => []]; expect(fn () => $provider->importSubscriptionData($mockUser, $subscriptionData)) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot get subscription metadata', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->getSubscriptionMetadata($mockSubscription)) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('cannot update subscription metadata', function () { $provider = new OxapayProvider; $mockSubscription = new class { public $id = 1; }; expect(fn () => $provider->updateSubscriptionMetadata($mockSubscription, ['test' => 'data'])) ->toThrow('OxaPay does not support recurring subscriptions'); }); test('has correct method signatures', function () { $provider = new OxapayProvider; // 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('uses correct base url for production', function () { $provider = new OxapayProvider(['sandbox' => false]); // Access the baseUrl property via reflection $reflection = new ReflectionClass($provider); $baseUrlProperty = $reflection->getProperty('baseUrl'); $baseUrlProperty->setAccessible(true); expect($baseUrlProperty->getValue($provider))->toBe('https://api.oxapay.com/v1'); }); test('uses correct base url for sandbox', function () { $provider = new OxapayProvider(['sandbox' => true]); // Access the baseUrl property via reflection $reflection = new ReflectionClass($provider); $baseUrlProperty = $reflection->getProperty('baseUrl'); $baseUrlProperty->setAccessible(true); expect($baseUrlProperty->getValue($provider))->toBe('https://api-sandbox.oxapay.com/v1'); });