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); $mockHttpClient = Mockery::mock(Client::class); $mockResponse = Mockery::mock(); $mockOrder = Mockery::mock(Order::class); $mockOrder->id = 'test-order-id'; $mockResponse->result = $mockOrder; $mockHttpClient->shouldReceive('execute')->andReturn($mockResponse); $mockPayPalClient = Mockery::mock(PayPalClient::class); $mockPayPalClient->shouldReceive('client')->andReturn($mockHttpClient); $this->app->instance(PayPalClient::class, $mockPayPalClient); $response = $this->postJson(route('api.packages.paypal-create'), [ 'package_id' => $paidPackage->id, ]); $response->assertStatus(200); $response->assertJson(['orderID' => 'test-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, ]); $mockHttpClient = Mockery::mock(Client::class); $mockResponse = Mockery::mock(); $mockCapture = Mockery::mock(Order::class); $mockCapture->status = 'COMPLETED'; $mockCapture->purchaseUnits = [ (object) ['custom_id' => $metadata] ]; $mockResponse->result = $mockCapture; $mockHttpClient->shouldReceive('execute')->andReturn($mockResponse); $mockPayPalClient = Mockery::mock(PayPalClient::class); $mockPayPalClient->shouldReceive('client')->andReturn($mockHttpClient); $this->app->instance(PayPalClient::class, $mockPayPalClient); $response = $this->postJson(route('api.packages.paypal-capture'), [ 'order_id' => 'test-order-id', ]); $response->assertStatus(200); $response->assertJson(['success' => true]); $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' => 'test-order-id', ]); $this->assertEquals('active', $tenant->fresh()->subscription_status); } public function test_wizard_complete_purchase_handles_stripe_success() { $package = 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->postJson(route('api.packages.purchase'), [ 'package_id' => $package->id, 'type' => 'endcustomer_event', 'payment_method' => 'stripe', 'payment_method_id' => 'pm_test_success', ]); $response->assertStatus(201); $this->assertDatabaseHas('package_purchases', [ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'provider_id' => 'pm_test_success', 'price' => 10, ]); $this->assertDatabaseHas('tenant_packages', [ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'active' => true, ]); } public function test_wizard_complete_purchase_handles_paypal_success() { $package = 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->postJson(route('api.packages.purchase'), [ 'package_id' => $package->id, 'type' => 'endcustomer_event', 'payment_method' => 'paypal', 'paypal_order_id' => 'order_test_success', ]); $response->assertStatus(201); $this->assertDatabaseHas('package_purchases', [ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'provider_id' => 'order_test_success', 'price' => 10, ]); $this->assertDatabaseHas('tenant_packages', [ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'active' => true, ]); } public function test_wizard_auth_error_handling_in_registration() { $package = Package::factory()->create(['price' => 0]); $existingUser = User::factory()->create(['email' => 'duplicate@example.com']); $response = $this->post('/de/register', [ 'name' => 'Duplicate User', 'username' => 'duplicate', 'email' => 'duplicate@example.com', 'password' => 'Password123!', 'password_confirmation' => 'Password123!', 'first_name' => 'Max', 'last_name' => 'Mustermann', 'address' => 'Musterstr. 1', 'phone' => '+49123456789', 'privacy_consent' => true, 'package_id' => $package->id, ]); $response->assertStatus(302); $response->assertSessionHasErrors(['email']); $this->assertDatabaseMissing('users', ['email' => 'duplicate@example.com']); // No duplicate created } public function test_wizard_login_error_handling() { $user = User::factory()->create(['email' => 'test@example.com', 'password' => bcrypt('wrongpass')]); $response = $this->post('/de/login', [ 'email' => 'test@example.com', 'password' => 'wrongpass', ]); $response->assertStatus(302); $response->assertSessionHasErrors(['email']); // Or custom message $this->assertGuest(); // Not logged in } public function test_wizard_trial_activation_for_first_reseller() { $resellerPackage = Package::factory()->create(['price' => 10, 'type' => 'reseller_subscription']); $user = User::factory()->create(['email_verified_at' => now()]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); // No active packages yet $this->assertEquals(0, TenantPackage::where('tenant_id', $tenant->id)->where('active', true)->count()); $response = $this->postJson(route('api.packages.purchase'), [ 'package_id' => $resellerPackage->id, 'type' => 'reseller_subscription', 'payment_method' => 'paypal', 'paypal_order_id' => 'trial_order_id', ]); $response->assertStatus(201); $tenantPackage = TenantPackage::where('tenant_id', $tenant->id)->where('package_id', $resellerPackage->id)->first(); $this->assertNotNull($tenantPackage->expires_at); $this->assertTrue($tenantPackage->expires_at->isFuture()); $this->assertTrue($tenantPackage->expires_at->diffInDays(now()) === 14); // Trial period } public function test_wizard_reseller_renewal_no_trial() { $resellerPackage = Package::factory()->create(['price' => 10, 'type' => 'reseller_subscription']); $user = User::factory()->create(['email_verified_at' => now()]); $tenant = Tenant::factory()->create(['user_id' => $user->id]); Auth::login($user); // Existing active package TenantPackage::create([ 'tenant_id' => $tenant->id, 'package_id' => $resellerPackage->id, 'active' => true, 'expires_at' => \Carbon\Carbon::now()->addYear(), ]); $response = $this->postJson(route('api.packages.purchase'), [ 'package_id' => $resellerPackage->id, 'type' => 'reseller_subscription', 'payment_method' => 'stripe', 'payment_method_id' => 'pm_renewal', ]); $response->assertStatus(201); $tenantPackage = TenantPackage::where('tenant_id', $tenant->id)->where('package_id', $resellerPackage->id)->first(); $this->assertTrue($tenantPackage->expires_at->diffInDays(now()) === 365); // Full year, no trial } 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); $mockHttpClient = Mockery::mock(Client::class); $mockHttpClient->shouldReceive('execute')->andThrow(new \Exception('Capture failed')); $mockPayPalClient = Mockery::mock(PayPalClient::class); $mockPayPalClient->shouldReceive('client')->andReturn($mockHttpClient); $this->app->instance(PayPalClient::class, $mockPayPalClient); $response = $this->postJson(route('api.packages.paypal-capture'), [ 'order_id' => 'failed-order-id', ]); $response->assertStatus(422); $response->assertJson(['success' => false]); $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, ]); } }