131 lines
4.8 KiB
PHP
131 lines
4.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Feature\Academy;
|
|
|
|
use App\Models\AcademyBillingEvent;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Laravel\Cashier\Events\WebhookHandled;
|
|
use Laravel\Cashier\Events\WebhookReceived;
|
|
use Tests\TestCase;
|
|
|
|
final class AcademyStripeWebhookTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
config()->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));
|
|
}
|
|
} |