set('academy_billing.plans', [ 'creator_monthly' => [ 'label' => 'Creator Monthly', 'tier' => 'creator', 'interval' => 'monthly', 'stripe_price_id' => 'price_creator_month', 'featured' => false, ], 'pro_monthly' => [ 'label' => 'Pro Monthly', 'tier' => 'pro', 'interval' => 'monthly', 'stripe_price_id' => 'price_pro_month', 'featured' => true, ], ]); } public function test_webhook_listener_stores_safe_audit_summary_and_records_handling_outcome(): void { $user = User::factory()->create([ 'stripe_id' => 'cus_webhook_test', 'email_verified_at' => now(), ]); Cache::put('academy.billing.account.'.$user->id, 'stale', 300); event(new WebhookReceived([ 'id' => 'evt_academy_billing_test', 'type' => 'customer.subscription.updated', 'data' => [ 'object' => [ 'id' => 'sub_webhook_test', 'customer' => 'cus_webhook_test', 'status' => 'active', 'items' => [ 'data' => [[ 'price' => [ 'id' => 'price_creator_month', ], ]], ], 'metadata' => [ 'academy_plan' => 'creator_monthly', 'academy_tier' => 'creator', 'user_id' => (string) $user->id, ], ], ], ])); $subscription = $user->subscriptions()->create([ 'type' => 'academy', 'stripe_id' => 'sub_webhook_test', 'stripe_status' => 'active', 'stripe_price' => 'price_creator_month', 'quantity' => 1, ]); $subscription->items()->create([ 'stripe_id' => 'si_webhook_test', 'stripe_product' => 'prod_creator_month', 'stripe_price' => 'price_creator_month', 'quantity' => 1, ]); event(new WebhookHandled([ 'id' => 'evt_academy_billing_test', 'type' => 'customer.subscription.updated', 'data' => [ 'object' => [ 'id' => 'sub_webhook_test', 'customer' => 'cus_webhook_test', 'status' => 'active', 'items' => [ 'data' => [[ 'price' => [ 'id' => 'price_creator_month', 'product' => 'prod_creator_month', ], 'quantity' => 1, 'id' => 'si_webhook_test', ]], ], 'metadata' => [ 'academy_plan' => 'creator_monthly', 'academy_tier' => 'creator', 'user_id' => (string) $user->id, ], 'subscription' => 'sub_webhook_test', ], ], ])); $billingEvent = AcademyBillingEvent::query()->where('stripe_event_id', 'evt_academy_billing_test')->first(); $this->assertNotNull($billingEvent); $this->assertSame('customer.subscription.updated', $billingEvent->event_type); $this->assertSame('creator_monthly', $billingEvent->academy_plan); $this->assertSame('creator', $billingEvent->academy_tier); $this->assertSame('cus_webhook_test', $billingEvent->stripe_customer_id); $this->assertSame(['price_creator_month'], $billingEvent->payload_summary['price_ids'] ?? []); $this->assertTrue($billingEvent->payload_summary['received'] ?? false); $this->assertTrue($billingEvent->payload_summary['handled'] ?? false); $this->assertSame('local_subscription_synced', $billingEvent->payload_summary['outcome'] ?? null); $this->assertTrue($billingEvent->payload_summary['cache_cleared'] ?? false); $this->assertFalse(Cache::has('academy.billing.account.'.$user->id)); } }