fixed event join token handling in the event admin. created new seeders with new tenants and package purchases. added new playwright test scenarios.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('event_packages', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('event_packages', 'gallery_expires_at')) {
|
||||
$table->timestamp('gallery_expires_at')->nullable();
|
||||
}
|
||||
|
||||
if (! Schema::hasColumn('event_packages', 'used_guests')) {
|
||||
$table->integer('used_guests')->default(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('event_packages', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('event_packages', 'gallery_expires_at')) {
|
||||
$table->dropColumn('gallery_expires_at');
|
||||
}
|
||||
|
||||
if (Schema::hasColumn('event_packages', 'used_guests')) {
|
||||
$table->dropColumn('used_guests');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -2,22 +2,32 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\{Event, EventType};
|
||||
use App\Models\Event;
|
||||
use App\Models\EventPackage;
|
||||
use App\Models\EventType;
|
||||
use App\Models\Package;
|
||||
use App\Models\PackagePurchase;
|
||||
use App\Models\Tenant;
|
||||
use App\Services\EventJoinTokenService;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class DemoEventSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$type = EventType::where('slug','wedding')->first();
|
||||
if(!$type){ return; }
|
||||
$demoTenant = \App\Models\Tenant::where('slug', 'demo')->first();
|
||||
if (!$demoTenant) { return; }
|
||||
$event = Event::updateOrCreate(['slug'=>'demo-wedding-2025'], [
|
||||
$type = EventType::where('slug', 'wedding')->first();
|
||||
if (! $type) {
|
||||
return;
|
||||
}
|
||||
$demoTenant = Tenant::where('slug', 'demo-tenant')->first();
|
||||
if (! $demoTenant) {
|
||||
return;
|
||||
}
|
||||
$event = Event::updateOrCreate(['slug' => 'demo-wedding-2025'], [
|
||||
'tenant_id' => $demoTenant->id,
|
||||
'name' => ['de'=>'Demo Hochzeit 2025','en'=>'Demo Wedding 2025'],
|
||||
'description' => ['de'=>'Demo-Event','en'=>'Demo event'],
|
||||
'name' => ['de' => 'Demo Hochzeit 2025', 'en' => 'Demo Wedding 2025'],
|
||||
'description' => ['de' => 'Demo-Event', 'en' => 'Demo event'],
|
||||
'date' => now()->addMonths(3)->toDateString(),
|
||||
'event_type_id' => $type->id,
|
||||
'status' => 'published',
|
||||
@@ -33,5 +43,43 @@ class DemoEventSeeder extends Seeder
|
||||
'label' => 'Demo QR',
|
||||
]);
|
||||
}
|
||||
|
||||
$package = Package::where('slug', 'standard')->first();
|
||||
if (! $package) {
|
||||
$package = Package::where('type', 'endcustomer')->orderBy('price')->first();
|
||||
}
|
||||
|
||||
if ($package) {
|
||||
$eventPackageData = [
|
||||
'purchased_price' => $package->price,
|
||||
'purchased_at' => now()->subDays(7),
|
||||
];
|
||||
|
||||
if (Schema::hasColumn('event_packages', 'used_photos')) {
|
||||
$eventPackageData['used_photos'] = 0;
|
||||
}
|
||||
if (Schema::hasColumn('event_packages', 'used_guests')) {
|
||||
$eventPackageData['used_guests'] = 0;
|
||||
}
|
||||
if (Schema::hasColumn('event_packages', 'gallery_expires_at')) {
|
||||
$eventPackageData['gallery_expires_at'] = now()->addDays($package->gallery_days ?? 30);
|
||||
}
|
||||
|
||||
EventPackage::updateOrCreate(
|
||||
[
|
||||
'event_id' => $event->id,
|
||||
'package_id' => $package->id,
|
||||
],
|
||||
$eventPackageData
|
||||
);
|
||||
|
||||
PackagePurchase::query()
|
||||
->where('tenant_id', $demoTenant->id)
|
||||
->where('package_id', $package->id)
|
||||
->where('provider_id', 'demo-seed')
|
||||
->update([
|
||||
'event_id' => $event->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
404
database/seeders/DemoLifecycleSeeder.php
Normal file
404
database/seeders/DemoLifecycleSeeder.php
Normal file
@@ -0,0 +1,404 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Event;
|
||||
use App\Models\EventPackage;
|
||||
use App\Models\EventType;
|
||||
use App\Models\OAuthClient;
|
||||
use App\Models\Package;
|
||||
use App\Models\PackagePurchase;
|
||||
use App\Models\Tenant;
|
||||
use App\Models\TenantPackage;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class DemoLifecycleSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
[$standard, $premium, $reseller] = $this->ensurePackages();
|
||||
[$weddingType, $corporateType] = $this->ensureEventTypes();
|
||||
|
||||
$this->seedOnboardingTenant();
|
||||
$this->seedActiveTenant($standard, $premium, $weddingType, $corporateType);
|
||||
$this->seedResellerTenant($reseller, $standard, $weddingType);
|
||||
$this->seedDormantTenant();
|
||||
}
|
||||
|
||||
private function ensurePackages(): array
|
||||
{
|
||||
$standard = Package::firstOrCreate(
|
||||
['slug' => 'standard'],
|
||||
[
|
||||
'type' => 'endcustomer',
|
||||
'name' => 'Standard',
|
||||
'name_translations' => ['de' => 'Standard', 'en' => 'Standard'],
|
||||
'price' => 79,
|
||||
'max_photos' => 1500,
|
||||
'max_guests' => 400,
|
||||
'gallery_days' => 60,
|
||||
'features' => [
|
||||
'basic_uploads' => true,
|
||||
'unlimited_sharing' => true,
|
||||
'no_watermark' => true,
|
||||
'custom_tasks' => true,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$premium = Package::firstOrCreate(
|
||||
['slug' => 'premium'],
|
||||
[
|
||||
'type' => 'endcustomer',
|
||||
'name' => 'Premium',
|
||||
'name_translations' => ['de' => 'Premium', 'en' => 'Premium'],
|
||||
'price' => 149,
|
||||
'max_photos' => 5000,
|
||||
'max_guests' => 1000,
|
||||
'gallery_days' => 180,
|
||||
'features' => [
|
||||
'basic_uploads' => true,
|
||||
'unlimited_sharing' => true,
|
||||
'no_watermark' => true,
|
||||
'custom_branding' => true,
|
||||
'custom_tasks' => true,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$reseller = Package::firstOrCreate(
|
||||
['slug' => 'studio-annual'],
|
||||
[
|
||||
'type' => 'reseller',
|
||||
'name' => 'Studio Jahrespaket',
|
||||
'name_translations' => ['de' => 'Studio Jahrespaket', 'en' => 'Studio Annual'],
|
||||
'price' => 1299,
|
||||
'max_events_per_year' => 24,
|
||||
'features' => [
|
||||
'custom_branding' => true,
|
||||
'unlimited_sharing' => true,
|
||||
'basic_uploads' => true,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
return [$standard, $premium, $reseller];
|
||||
}
|
||||
|
||||
private function ensureEventTypes(): array
|
||||
{
|
||||
$weddingType = EventType::firstOrCreate(
|
||||
['slug' => 'wedding'],
|
||||
[
|
||||
'name' => 'Wedding',
|
||||
'name_translations' => ['de' => 'Hochzeit', 'en' => 'Wedding'],
|
||||
'icon' => 'heart',
|
||||
]
|
||||
);
|
||||
|
||||
$corporateType = EventType::firstOrCreate(
|
||||
['slug' => 'corporate'],
|
||||
[
|
||||
'name' => 'Corporate Event',
|
||||
'name_translations' => ['de' => 'Firmen-Event', 'en' => 'Corporate'],
|
||||
'icon' => 'presentation-chart',
|
||||
]
|
||||
);
|
||||
|
||||
return [$weddingType, $corporateType];
|
||||
}
|
||||
|
||||
private function seedOnboardingTenant(): void
|
||||
{
|
||||
$tenant = $this->createTenant('storycraft-weddings', [
|
||||
'name' => 'Storycraft Weddings',
|
||||
'contact_email' => 'hello@storycraft-weddings.demo',
|
||||
'event_credits_balance' => 0,
|
||||
'subscription_tier' => 'free',
|
||||
'subscription_status' => 'free',
|
||||
'subscription_expires_at' => null,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$this->createTenantAdmin($tenant, 'storycraft-owner@demo.fotospiel');
|
||||
$this->ensureOAuthClientForTenant($tenant, 'demo-tenant-admin-storycraft');
|
||||
}
|
||||
|
||||
private function seedActiveTenant(Package $standard, Package $premium, EventType $weddingType, EventType $corporateType): void
|
||||
{
|
||||
$tenant = $this->createTenant('lumen-moments', [
|
||||
'name' => 'Lumen Moments',
|
||||
'contact_email' => 'hello@lumen-moments.demo',
|
||||
'event_credits_balance' => 2,
|
||||
'subscription_tier' => 'starter',
|
||||
'subscription_status' => 'active',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
OAuthClient::query()
|
||||
->where('client_id', config('services.oauth.tenant_admin.id', 'tenant-admin-app'))
|
||||
->update(['tenant_id' => $tenant->id]);
|
||||
|
||||
$this->createTenantAdmin($tenant, 'hello@lumen-moments.demo');
|
||||
$this->ensureOAuthClientForTenant($tenant, 'demo-tenant-admin-lumen');
|
||||
|
||||
$purchase = PackagePurchase::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'package_id' => $premium->id,
|
||||
'provider_id' => 'stripe_demo_pi',
|
||||
'price' => $premium->price,
|
||||
'type' => 'endcustomer_event',
|
||||
'purchased_at' => Carbon::now()->subDays(3),
|
||||
'metadata' => ['demo' => true],
|
||||
]);
|
||||
|
||||
$publishedEvent = $this->createEventWithPackage(
|
||||
tenant: $tenant,
|
||||
package: $premium,
|
||||
eventType: $weddingType,
|
||||
attributes: [
|
||||
'name' => ['de' => 'Sommerhochzeit Lea & Tim', 'en' => 'Summer Wedding Lea & Tim'],
|
||||
'slug' => 'summer-wedding-lea-tim',
|
||||
'status' => 'published',
|
||||
'is_active' => true,
|
||||
'date' => Carbon::now()->addWeeks(4),
|
||||
]
|
||||
);
|
||||
|
||||
$draftEvent = $this->createEventWithPackage(
|
||||
tenant: $tenant,
|
||||
package: $standard,
|
||||
eventType: $corporateType,
|
||||
attributes: [
|
||||
'name' => ['de' => 'Startup Social 2025', 'en' => 'Startup Social 2025'],
|
||||
'slug' => 'startup-social-2025',
|
||||
'status' => 'draft',
|
||||
'is_active' => false,
|
||||
'date' => Carbon::now()->addWeeks(12),
|
||||
]
|
||||
);
|
||||
|
||||
$purchase->update(['event_id' => $publishedEvent->id]);
|
||||
|
||||
PackagePurchase::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'event_id' => $draftEvent->id,
|
||||
'package_id' => $standard->id,
|
||||
'provider_id' => 'paypal_demo_capture',
|
||||
'price' => $standard->price,
|
||||
'type' => 'endcustomer_event',
|
||||
'purchased_at' => Carbon::now()->subDays(1),
|
||||
'metadata' => ['demo' => true],
|
||||
]);
|
||||
}
|
||||
|
||||
private function seedResellerTenant(Package $reseller, Package $standard, EventType $weddingType): void
|
||||
{
|
||||
$tenant = $this->createTenant('viewfinder-studios', [
|
||||
'name' => 'Viewfinder Studios',
|
||||
'contact_email' => 'team@viewfinder.demo',
|
||||
'event_credits_balance' => 0,
|
||||
'subscription_tier' => 'reseller',
|
||||
'subscription_status' => 'active',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$this->createTenantAdmin($tenant, 'team@viewfinder.demo');
|
||||
$this->ensureOAuthClientForTenant($tenant, 'demo-tenant-admin-viewfinder');
|
||||
|
||||
$tenantPackage = TenantPackage::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'package_id' => $reseller->id,
|
||||
'price' => $reseller->price,
|
||||
'purchased_at' => Carbon::now()->subMonths(2),
|
||||
'expires_at' => Carbon::now()->addMonths(10),
|
||||
'used_events' => 6,
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
PackagePurchase::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'package_id' => $reseller->id,
|
||||
'provider_id' => 'stripe_demo_subscription',
|
||||
'price' => $reseller->price,
|
||||
'type' => 'reseller_subscription',
|
||||
'purchased_at' => $tenantPackage->purchased_at,
|
||||
'metadata' => ['demo' => true, 'plan' => 'studio-annual'],
|
||||
]);
|
||||
|
||||
// Create a mix of events representing allowance consumption.
|
||||
$statuses = ['published', 'published', 'draft', 'archived'];
|
||||
|
||||
foreach ($statuses as $index => $status) {
|
||||
$event = $this->createEventWithPackage(
|
||||
tenant: $tenant,
|
||||
package: $standard,
|
||||
eventType: $weddingType,
|
||||
attributes: [
|
||||
'name' => ['de' => 'Studio Event #'.($index + 1), 'en' => 'Studio Event #'.($index + 1)],
|
||||
'slug' => 'studio-event-'.($index + 1),
|
||||
'status' => $status,
|
||||
'is_active' => $status === 'published',
|
||||
'date' => $status === 'archived'
|
||||
? Carbon::now()->subMonths(3)
|
||||
: Carbon::now()->addWeeks($index * 3 + 1),
|
||||
]
|
||||
);
|
||||
|
||||
PackagePurchase::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'event_id' => $event->id,
|
||||
'package_id' => $standard->id,
|
||||
'provider_id' => 'reseller_allowance',
|
||||
'price' => 0,
|
||||
'type' => 'endcustomer_event',
|
||||
'purchased_at' => Carbon::now()->subDays(10 - $index),
|
||||
'metadata' => ['allowance' => true],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function seedDormantTenant(): void
|
||||
{
|
||||
$tenant = $this->createTenant('pixel-and-co', [
|
||||
'name' => 'Pixel & Co',
|
||||
'contact_email' => 'support@pixelco.demo',
|
||||
'subscription_status' => 'expired',
|
||||
'subscription_tier' => 'free',
|
||||
'subscription_expires_at' => Carbon::now()->subMonths(2),
|
||||
'is_active' => false,
|
||||
'is_suspended' => false,
|
||||
'event_credits_balance' => 0,
|
||||
]);
|
||||
|
||||
$this->createTenantAdmin($tenant, 'support@pixelco.demo', role: 'member');
|
||||
$this->ensureOAuthClientForTenant($tenant, 'demo-tenant-admin-pixel');
|
||||
}
|
||||
|
||||
private function createTenantAdmin(Tenant $tenant, string $email, string $role = 'tenant_admin'): User
|
||||
{
|
||||
return User::updateOrCreate(
|
||||
['email' => $email],
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'role' => $role,
|
||||
'password' => Hash::make('Demo1234!'),
|
||||
'first_name' => Str::headline(Str::before($tenant->slug, '-')),
|
||||
'last_name' => 'Team',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function createEventWithPackage(
|
||||
Tenant $tenant,
|
||||
Package $package,
|
||||
EventType $eventType,
|
||||
array $attributes
|
||||
): Event {
|
||||
$payload = array_merge([
|
||||
'tenant_id' => $tenant->id,
|
||||
'event_type_id' => $eventType->id,
|
||||
'settings' => [
|
||||
'features' => [
|
||||
'photo_likes_enabled' => true,
|
||||
'event_checklist' => true,
|
||||
],
|
||||
],
|
||||
], $attributes);
|
||||
|
||||
$event = Event::updateOrCreate(['slug' => $attributes['slug']], $payload);
|
||||
|
||||
EventPackage::updateOrCreate(
|
||||
[
|
||||
'event_id' => $event->id,
|
||||
'package_id' => $package->id,
|
||||
],
|
||||
[
|
||||
'purchased_price' => $package->price,
|
||||
'purchased_at' => Carbon::now()->subDays(2),
|
||||
'used_photos' => 0,
|
||||
'used_guests' => 0,
|
||||
'gallery_expires_at' => Carbon::now()->addDays($package->gallery_days ?? 30),
|
||||
]
|
||||
);
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
private function createTenant(string $slug, array $overrides = []): Tenant
|
||||
{
|
||||
$email = $overrides['contact_email'] ?? $slug.'@demo.fotospiel';
|
||||
|
||||
$defaults = [
|
||||
'name' => Str::headline(str_replace('-', ' ', $slug)),
|
||||
'slug' => $slug,
|
||||
'contact_email' => $email,
|
||||
'event_credits_balance' => 0,
|
||||
'subscription_tier' => 'free',
|
||||
'subscription_status' => 'free',
|
||||
'subscription_expires_at' => Carbon::now()->addMonths(6),
|
||||
'is_active' => true,
|
||||
'is_suspended' => false,
|
||||
'settings_updated_at' => Carbon::now(),
|
||||
'settings' => $this->defaultSettings($email),
|
||||
];
|
||||
|
||||
$attributes = array_merge($defaults, $overrides);
|
||||
|
||||
$tenant = Tenant::updateOrCreate(['slug' => $slug], $attributes);
|
||||
|
||||
return $tenant;
|
||||
}
|
||||
|
||||
private function defaultSettings(string $contactEmail): array
|
||||
{
|
||||
return [
|
||||
'branding' => [
|
||||
'logo_url' => null,
|
||||
'primary_color' => '#3B82F6',
|
||||
'secondary_color' => '#1F2937',
|
||||
'font_family' => 'Inter, sans-serif',
|
||||
],
|
||||
'features' => [
|
||||
'photo_likes_enabled' => true,
|
||||
'event_checklist' => true,
|
||||
'custom_domain' => false,
|
||||
'advanced_analytics' => false,
|
||||
],
|
||||
'custom_domain' => null,
|
||||
'contact_email' => $contactEmail,
|
||||
'event_default_type' => 'general',
|
||||
];
|
||||
}
|
||||
|
||||
private function ensureOAuthClientForTenant(Tenant $tenant, string $clientId): void
|
||||
{
|
||||
$redirectUris = config('services.oauth.tenant_admin.redirects', []);
|
||||
if (empty($redirectUris)) {
|
||||
$redirectUris = [
|
||||
'http://localhost:5173/event-admin/auth/callback',
|
||||
url('/event-admin/auth/callback'),
|
||||
];
|
||||
}
|
||||
|
||||
$client = OAuthClient::firstOrNew(['client_id' => $clientId]);
|
||||
|
||||
if (! $client->exists) {
|
||||
$client->id = (string) Str::uuid();
|
||||
}
|
||||
|
||||
$client->fill([
|
||||
'client_secret' => null,
|
||||
'tenant_id' => $tenant->id,
|
||||
'redirect_uris' => $redirectUris,
|
||||
'scopes' => ['tenant:read', 'tenant:write'],
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$client->save();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\EventPurchase;
|
||||
use App\Models\Package;
|
||||
use App\Models\PackagePurchase;
|
||||
use App\Models\Tenant;
|
||||
@@ -18,10 +19,19 @@ class DemoTenantSeeder extends Seeder
|
||||
$email = 'tenant-demo@fotospiel.app';
|
||||
$password = config('seeding.demo_tenant_password', 'Demo1234!');
|
||||
$package = Package::query()
|
||||
->where('type', 'reseller')
|
||||
->orderBy('price')
|
||||
->first()
|
||||
?? Package::query()->orderBy('price')->first();
|
||||
->where('slug', 'standard')
|
||||
->first();
|
||||
|
||||
if (! $package) {
|
||||
$package = Package::query()
|
||||
->where('type', 'endcustomer')
|
||||
->orderBy('price')
|
||||
->first();
|
||||
}
|
||||
|
||||
if (! $package) {
|
||||
$package = Package::query()->orderBy('price')->first();
|
||||
}
|
||||
|
||||
if (! $package) {
|
||||
$this->command?->warn('Skipped DemoTenantSeeder: no packages available.');
|
||||
@@ -75,13 +85,15 @@ class DemoTenantSeeder extends Seeder
|
||||
);
|
||||
|
||||
if ($tenant->wasRecentlyCreated && ! $tenant->slug) {
|
||||
$tenant->forceFill(['slug' => Str::slug('demo-tenant-'. $tenant->getKey())])->save();
|
||||
$tenant->forceFill(['slug' => Str::slug('demo-tenant-'.$tenant->getKey())])->save();
|
||||
}
|
||||
|
||||
if ($user->tenant_id !== $tenant->id) {
|
||||
$user->forceFill(['tenant_id' => $tenant->id])->save();
|
||||
}
|
||||
|
||||
$purchasedAt = now()->subDays(7);
|
||||
|
||||
TenantPackage::query()->updateOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
@@ -90,8 +102,8 @@ class DemoTenantSeeder extends Seeder
|
||||
[
|
||||
'price' => $package->price,
|
||||
'active' => true,
|
||||
'purchased_at' => now()->subDays(7),
|
||||
'expires_at' => now()->addYear(),
|
||||
'purchased_at' => $purchasedAt,
|
||||
'expires_at' => now()->addMonths(6),
|
||||
'used_events' => 0,
|
||||
]
|
||||
);
|
||||
@@ -103,13 +115,30 @@ class DemoTenantSeeder extends Seeder
|
||||
'provider_id' => 'demo-seed',
|
||||
],
|
||||
[
|
||||
'event_id' => null,
|
||||
'price' => $package->price,
|
||||
'type' => $package->type === 'reseller' ? 'reseller_subscription' : 'endcustomer_event',
|
||||
'purchased_at' => now()->subDays(7),
|
||||
'purchased_at' => $purchasedAt,
|
||||
'metadata' => [
|
||||
'seeded' => true,
|
||||
'note' => 'Demo tenant seed purchase',
|
||||
],
|
||||
'ip_address' => null,
|
||||
'user_agent' => null,
|
||||
]
|
||||
);
|
||||
|
||||
EventPurchase::query()->updateOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'provider' => 'demo-seed',
|
||||
],
|
||||
[
|
||||
'events_purchased' => 1,
|
||||
'amount' => $package->price,
|
||||
'currency' => 'EUR',
|
||||
'status' => 'completed',
|
||||
'purchased_at' => $purchasedAt,
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user