create(['price' => 10]); $response = $this->get(route('buy.packages', $package->id)); $response->assertRedirect('/register?package_id=' . $package->id); } public function test_unverified_buy_redirects_to_verification() { $package = Package::factory()->create(['price' => 10]); $user = User::factory()->create(['email_verified_at' => null]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); $response = $this->get(route('buy.packages', $package->id)); $response->assertRedirect(route('verification.notice')); } public function test_free_package_assigns_after_auth() { $freePackage = Package::factory()->create(['price' => 0, 'type' => 'endcustomer']); $user = User::factory()->create(['email_verified_at' => now()]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); $response = $this->get(route('buy.packages', $freePackage->id)); $response->assertRedirect('/admin'); $this->assertDatabaseHas('tenant_packages', [ 'tenant_id' => $tenant->id, 'package_id' => $freePackage->id, 'price' => 0, ]); $this->assertDatabaseHas('package_purchases', [ 'tenant_id' => $tenant->id, 'package_id' => $freePackage->id, 'provider_id' => 'free', 'price' => 0, 'type' => 'endcustomer_event', ]); } public function test_paid_package_creates_stripe_session() { $paidPackage = Package::factory()->create(['price' => 10]); $user = User::factory()->create(['email_verified_at' => now()]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); $response = $this->get(route('buy.packages', $paidPackage->id)); $response->assertStatus(302); // Redirect to Stripe $this->assertStringContainsString('checkout.stripe.com', $response->headers->get('Location')); } public function test_paypal_checkout_creates_order() { $paidPackage = Package::factory()->create(['price' => 10]); $user = User::factory()->create(['email_verified_at' => now()]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); $mockClient = Mockery::mock('PayPal\PayPalHttp\Client'); $mockOrders = Mockery::mock(); $mockOrders->shouldReceive('createOrder')->andReturn(new \stdClass()); // Simplified mock $mockClient->shouldReceive('orders')->andReturn($mockOrders); $this->app->instance('PayPal\PayPalHttp\Client', $mockClient); $response = $this->get(route('buy.packages', $paidPackage->id) . '?provider=paypal'); $response->assertStatus(302); $this->assertNotNull(session('paypal_order_id')); } public function test_paypal_success_captures_and_activates_package() { $paidPackage = Package::factory()->create(['price' => 10]); $user = User::factory()->create(['email_verified_at' => now()]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); $metadata = json_encode([ 'user_id' => $user->id, 'tenant_id' => $tenant->id, 'package_id' => $paidPackage->id, 'type' => $paidPackage->type, ]); $mockClient = Mockery::mock('PayPal\PayPalHttp\Client'); $mockOrders = Mockery::mock(); $mockCapture = new \stdClass(); $mockCapture->status = 'COMPLETED'; $mockCapture->purchaseUnits = [(object)['custom_id' => $metadata]]; $mockResponse = new \stdClass(); $mockResponse->result = $mockCapture; $mockOrders->shouldReceive('captureOrder')->andReturn($mockResponse); $mockClient->shouldReceive('orders')->andReturn($mockOrders); $this->app->instance('PayPal\PayPalHttp\Client', $mockClient); session(['paypal_order_id' => 'test-order-id']); $response = $this->get(route('marketing.success', $paidPackage->id)); $response->assertRedirect('/admin'); $this->assertDatabaseHas('tenant_packages', [ 'tenant_id' => $tenant->id, 'package_id' => $paidPackage->id, 'active' => true, ]); $this->assertDatabaseHas('package_purchases', [ 'tenant_id' => $tenant->id, 'package_id' => $paidPackage->id, 'provider_id' => 'paypal', ]); $this->assertNull(session('paypal_order_id')); $this->assertEquals('active', $tenant->fresh()->subscription_status); } public function test_purchase_fails_when_package_limit_reached() { $package = Package::factory()->create(['price' => 0, 'max_events' => 1]); // Assume max_events field $user = User::factory()->create(['email_verified_at' => now()]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); // Simulate limit reached: Create one event already Event::factory()->create(['tenant_id' => $tenant->id]); // Assign package first to set limit TenantPackage::create([ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'active' => true, ]); $response = $this->get(route('buy.packages', $package->id)); $response->assertStatus(403); $response->assertSee('Package limit exceeded'); // No new purchase $this->assertDatabaseMissing('package_purchases', [ 'tenant_id' => $tenant->id, 'package_id' => $package->id, ]); } public function test_stripe_payment_failure_does_not_assign_package() { $paidPackage = Package::factory()->create(['price' => 10]); $user = User::factory()->create(['email_verified_at' => now()]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); // Mock Stripe failure $this->mock(StripeClient::class, function ($mock) { $mock->shouldReceive('checkout->sessions->create')->andThrow(new CardException('Payment failed', 'card_declined', null, null)); }); $response = $this->get(route('buy.packages', $paidPackage->id)); $response->assertStatus(422); $response->assertSee('Payment failed'); $this->assertDatabaseMissing('tenant_packages', [ 'tenant_id' => $tenant->id, 'package_id' => $paidPackage->id, ]); } public function test_paypal_capture_failure_does_not_activate_package() { $paidPackage = Package::factory()->create(['price' => 10]); $user = User::factory()->create(['email_verified_at' => now()]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); $mockClient = Mockery::mock('PayPal\PayPalHttp\Client'); $mockOrders = Mockery::mock(); $mockResponse = new \stdClass(); $mockResponse->statusCode = 400; $mockOrders->shouldReceive('captureOrder')->andThrow(new \Exception('Capture failed')); $mockClient->shouldReceive('orders')->andReturn($mockOrders); $this->app->instance('PayPal\PayPalHttp\Client', $mockClient); session(['paypal_order_id' => 'failed-order-id']); $response = $this->get(route('marketing.success', $paidPackage->id)); $response->assertStatus(422); $response->assertSee('Payment capture failed'); $this->assertDatabaseMissing('tenant_packages', [ 'tenant_id' => $tenant->id, 'package_id' => $paidPackage->id, ]); } public function test_purchase_with_invalid_provider_redirects_to_stripe() { $paidPackage = Package::factory()->create(['price' => 10]); $user = User::factory()->create(['email_verified_at' => now()]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); $response = $this->get(route('buy.packages', $paidPackage->id) . '?provider=invalid'); $response->assertRedirect(); // Defaults to Stripe $this->assertStringContainsString('checkout.stripe.com', $response->headers->get('Location')); } public function test_refund_simulation_deactivates_package() { $paidPackage = Package::factory()->create(['price' => 10]); $user = User::factory()->create(['email_verified_at' => now()]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); // First, simulate successful purchase TenantPackage::create([ 'tenant_id' => $tenant->id, 'package_id' => $paidPackage->id, 'active' => true, ]); PackagePurchase::create([ 'user_id' => $user->id, 'tenant_id' => $tenant->id, 'package_id' => $paidPackage->id, 'provider_id' => 'stripe', 'status' => 'completed', ]); // Mock refund webhook or endpoint $this->mock(\Stripe\StripeClient::class, function ($mock) use ($tenant, $paidPackage) { $mock->shouldReceive('refunds->create')->andReturnSelf(); }); // Simulate refund call (assume route or event) $response = $this->post(route('purchase.refund', $paidPackage->id), [ 'purchase_id' => PackagePurchase::latest()->first()->id, ]); $response->assertStatus(200); $this->assertDatabaseHas('tenant_packages', [ 'tenant_id' => $tenant->id, 'package_id' => $paidPackage->id, 'active' => false, ]); $this->assertDatabaseHas('package_purchases', [ 'tenant_id' => $tenant->id, 'package_id' => $paidPackage->id, 'status' => 'refunded', ]); $this->assertEquals('cancelled', $tenant->fresh()->subscription_status); } public function test_multiple_purchases_overwrite_previous_package() { $firstPackage = Package::factory()->create(['price' => 0]); $secondPackage = Package::factory()->create(['price' => 5]); $user = User::factory()->create(['email_verified_at' => now()]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); // First purchase $this->get(route('buy.packages', $firstPackage->id)); $this->assertDatabaseHas('tenant_packages', [ 'tenant_id' => $tenant->id, 'package_id' => $firstPackage->id, 'active' => true, ]); // Second purchase $this->get(route('buy.packages', $secondPackage->id)); // First deactivated, second active $this->assertDatabaseHas('tenant_packages', [ 'tenant_id' => $tenant->id, 'package_id' => $firstPackage->id, 'active' => false, ]); $this->assertDatabaseHas('tenant_packages', [ 'tenant_id' => $tenant->id, 'package_id' => $secondPackage->id, 'active' => true, ]); } }