id(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->integer('level'); $table->decimal('amount', 10, 2); $table->timestamp('claimed_at'); $table->timestamps(); $table->unique(['user_id', 'level']); // Prevent double-claiming }); // --------------------------------------------------------------- // BONUSES (admin-created bonus campaigns) // --------------------------------------------------------------- Schema::create('bonuses', function (Blueprint $table) { $table->id(); $table->string('title'); $table->string('type')->nullable(); // welcome | reload | spins $table->decimal('amount_value', 18, 8)->nullable(); $table->string('amount_unit', 32)->nullable(); // USD | BTC | PERCENT | SPINS $table->decimal('min_deposit', 18, 8)->nullable(); $table->decimal('max_amount', 18, 8)->nullable(); $table->string('currency', 16)->nullable(); $table->string('code')->nullable(); $table->string('status')->default('draft'); // draft | active | paused | expired $table->timestamp('starts_at')->nullable(); $table->timestamp('expires_at')->nullable(); $table->json('rules')->nullable(); $table->text('description')->nullable(); $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete(); $table->timestamps(); $table->softDeletes(); $table->index(['status', 'starts_at', 'expires_at']); }); // --------------------------------------------------------------- // PROMOS (promo code system) // --------------------------------------------------------------- Schema::create('promos', function (Blueprint $table) { $table->id(); $table->string('code')->unique(); // Uppercase normalized $table->string('description')->nullable(); $table->decimal('bonus_amount', 16, 4)->default(0); $table->unsignedInteger('wager_multiplier')->default(0); $table->unsignedInteger('per_user_limit')->default(1); $table->unsignedInteger('global_limit')->nullable(); $table->timestamp('starts_at')->nullable(); $table->timestamp('ends_at')->nullable(); $table->decimal('min_deposit', 16, 4)->nullable(); $table->unsignedInteger('bonus_expires_days')->nullable(); $table->boolean('is_active')->default(true); $table->timestamps(); $table->index(['is_active', 'starts_at', 'ends_at']); }); // --------------------------------------------------------------- // PROMO USAGES (per-use audit) // --------------------------------------------------------------- Schema::create('promo_usages', function (Blueprint $table) { $table->id(); $table->foreignId('promo_id')->constrained('promos')->cascadeOnDelete(); $table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); $table->timestamp('used_at'); $table->string('ip', 45)->nullable(); $table->text('user_agent')->nullable(); $table->timestamps(); $table->unique(['promo_id', 'user_id', 'used_at']); $table->index(['user_id', 'promo_id']); }); // --------------------------------------------------------------- // PROMO CLAIMS (anti-abuse: one entry per user per promo) // --------------------------------------------------------------- Schema::create('promo_claims', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->foreignId('promo_id')->constrained()->cascadeOnDelete(); $table->string('ip_address')->nullable(); $table->string('device_hash')->nullable(); $table->timestamps(); $table->index(['user_id', 'promo_id']); }); // --------------------------------------------------------------- // USER BONUSES (active bonus progress per user) // --------------------------------------------------------------- Schema::create('user_bonuses', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); $table->foreignId('promo_id')->nullable()->constrained('promos')->nullOnDelete(); $table->decimal('amount', 16, 4); $table->decimal('wager_required', 16, 4)->default(0); $table->decimal('wager_progress', 16, 4)->default(0); $table->timestamp('expires_at')->nullable(); $table->boolean('is_active')->default(true); $table->timestamp('completed_at')->nullable(); $table->timestamps(); $table->index(['user_id', 'is_active']); }); } public function down(): void { Schema::dropIfExists('user_bonuses'); Schema::dropIfExists('promo_claims'); Schema::dropIfExists('promo_usages'); Schema::dropIfExists('promos'); Schema::dropIfExists('bonuses'); Schema::dropIfExists('vip_rewards'); } };