diff --git a/app/Models/User.php b/app/Models/User.php index 54f2910..166745c 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -12,13 +12,14 @@ use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Laravel\Cashier\Billable; #[Fillable(['name', 'email', 'password', 'plan', 'settings'])] #[Hidden(['password', 'remember_token'])] class User extends Authenticatable { /** @use HasFactory */ - use HasFactory, Notifiable; + use Billable, HasFactory, Notifiable; protected $attributes = [ 'plan' => 'free', diff --git a/database/migrations/2026_06_02_143126_create_customer_columns.php b/database/migrations/2026_06_02_143126_create_customer_columns.php new file mode 100644 index 0000000..974b381 --- /dev/null +++ b/database/migrations/2026_06_02_143126_create_customer_columns.php @@ -0,0 +1,40 @@ +string('stripe_id')->nullable()->index(); + $table->string('pm_type')->nullable(); + $table->string('pm_last_four', 4)->nullable(); + $table->timestamp('trial_ends_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropIndex([ + 'stripe_id', + ]); + + $table->dropColumn([ + 'stripe_id', + 'pm_type', + 'pm_last_four', + 'trial_ends_at', + ]); + }); + } +}; diff --git a/database/migrations/2026_06_02_143127_create_subscriptions_table.php b/database/migrations/2026_06_02_143127_create_subscriptions_table.php new file mode 100644 index 0000000..ccbcc6d --- /dev/null +++ b/database/migrations/2026_06_02_143127_create_subscriptions_table.php @@ -0,0 +1,37 @@ +id(); + $table->foreignId('user_id'); + $table->string('type'); + $table->string('stripe_id')->unique(); + $table->string('stripe_status'); + $table->string('stripe_price')->nullable(); + $table->integer('quantity')->nullable(); + $table->timestamp('trial_ends_at')->nullable(); + $table->timestamp('ends_at')->nullable(); + $table->timestamps(); + + $table->index(['user_id', 'stripe_status']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('subscriptions'); + } +}; diff --git a/database/migrations/2026_06_02_143128_create_subscription_items_table.php b/database/migrations/2026_06_02_143128_create_subscription_items_table.php new file mode 100644 index 0000000..420e23f --- /dev/null +++ b/database/migrations/2026_06_02_143128_create_subscription_items_table.php @@ -0,0 +1,34 @@ +id(); + $table->foreignId('subscription_id'); + $table->string('stripe_id')->unique(); + $table->string('stripe_product'); + $table->string('stripe_price'); + $table->integer('quantity')->nullable(); + $table->timestamps(); + + $table->index(['subscription_id', 'stripe_price']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('subscription_items'); + } +}; diff --git a/database/migrations/2026_06_02_143129_add_meter_id_to_subscription_items_table.php b/database/migrations/2026_06_02_143129_add_meter_id_to_subscription_items_table.php new file mode 100644 index 0000000..033bb82 --- /dev/null +++ b/database/migrations/2026_06_02_143129_add_meter_id_to_subscription_items_table.php @@ -0,0 +1,28 @@ +string('meter_id')->nullable()->after('stripe_price'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('subscription_items', function (Blueprint $table) { + $table->dropColumn('meter_id'); + }); + } +}; diff --git a/database/migrations/2026_06_02_143130_add_meter_event_name_to_subscription_items_table.php b/database/migrations/2026_06_02_143130_add_meter_event_name_to_subscription_items_table.php new file mode 100644 index 0000000..b157b3a --- /dev/null +++ b/database/migrations/2026_06_02_143130_add_meter_event_name_to_subscription_items_table.php @@ -0,0 +1,28 @@ +string('meter_event_name')->nullable()->after('quantity'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('subscription_items', function (Blueprint $table) { + $table->dropColumn('meter_event_name'); + }); + } +}; diff --git a/tests/Unit/Billing/CashierBillableTest.php b/tests/Unit/Billing/CashierBillableTest.php new file mode 100644 index 0000000..c992d8b --- /dev/null +++ b/tests/Unit/Billing/CashierBillableTest.php @@ -0,0 +1,10 @@ +toHaveKey(Billable::class); +});