Separate webinars from meetings and add conference billing.
Deploy Ladill Meet / deploy (push) Successful in 41s

Add restart meeting actions, a dedicated webinar sidebar flow billed at GHS 0.30 per participant, and reserve speaker-line space with a placeholder to prevent layout shift.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-01 18:23:04 +00:00
co-authored by Cursor
parent 3585904618
commit d0a3361f37
24 changed files with 598 additions and 51 deletions
+43
View File
@@ -66,6 +66,49 @@ class MeetBillingTest extends TestCase
&& ($request->data()['source'] ?? '') === 'meet_streaming');
}
public function test_webinar_session_end_charges_per_participant(): void
{
$this->fakeAffordableBilling();
$organization = Organization::create([
'owner_ref' => 'owner-webinar',
'name' => 'Webinar Org',
'slug' => 'webinar-org',
'timezone' => 'UTC',
]);
$room = Room::create([
'owner_ref' => 'owner-webinar',
'organization_id' => $organization->id,
'host_user_ref' => 'owner-webinar',
'title' => 'Webinar Room',
'type' => 'webinar',
'status' => 'ended',
'settings' => config('meet.default_settings'),
]);
$session = Session::create([
'owner_ref' => 'owner-webinar',
'room_id' => $room->id,
'media_room_name' => 'webinar-test',
'status' => 'ended',
'started_at' => now()->subHour(),
'ended_at' => now(),
'peak_participants' => 5,
]);
app(UsageMeteringService::class)->recordSessionUsage($session);
$this->assertDatabaseHas('meet_usage_records', [
'organization_id' => $organization->id,
'metric' => 'participant_count',
'quantity' => 5,
]);
Http::assertSent(fn ($request) => str_contains($request->url(), '/debit')
&& ($request->data()['source'] ?? '') === 'meet_participants');
}
public function test_storage_renewal_enters_grace_when_wallet_is_empty(): void
{
$base = rtrim((string) config('billing.api_url'), '/');
+1 -2
View File
@@ -541,9 +541,8 @@ class MeetWebTest extends TestCase
public function test_host_can_create_webinar_room(): void
{
$this->actingAs($this->user)
->post('/meetings', [
->post('/webinars', [
'title' => 'Product launch',
'type' => 'webinar',
'scheduled_at' => now()->addDay()->format('Y-m-d\TH:i'),
'duration_minutes' => 60,
])
+10
View File
@@ -30,6 +30,16 @@ class MeetBillingServiceTest extends TestCase
$this->assertSame(60, $billing->amountMinorForStreamingHours(2));
}
public function test_participant_amount_minor_matches_rate(): void
{
config(['meet.billing.price_per_participant_ghs' => 0.30]);
$billing = app(MeetBillingService::class);
$this->assertSame(30, $billing->amountMinorForParticipants(1));
$this->assertSame(90, $billing->amountMinorForParticipants(3));
}
public function test_storage_amount_minor_matches_gb_monthly_rate(): void
{
config(['meet.billing.price_per_gb_month_ghs' => 0.30]);