diff --git a/app/Filament/Resources/HostingAccountResource.php b/app/Filament/Resources/HostingAccountResource.php new file mode 100644 index 00000000..5faac56f --- /dev/null +++ b/app/Filament/Resources/HostingAccountResource.php @@ -0,0 +1,86 @@ +schema([ + Forms\Components\Select::make('customer_id') + ->relationship('customer', 'name') + ->required(), + Forms\Components\Select::make('subscription_id') + ->relationship('subscription', 'id') + ->required(), + Forms\Components\Select::make('control_panel') + ->options([ + 'cpanel' => 'cPanel', + 'plesk' => 'Plesk', + 'directadmin' => 'DirectAdmin', + ]) + ->required(), + Forms\Components\TextInput::make('username') + ->required() + ->maxLength(255), + Forms\Components\TextInput::make('domain') + ->required() + ->maxLength(255), + Forms\Components\TextInput::make('package') + ->required() + ->maxLength(255), + Forms\Components\Select::make('status') + ->options([ + 'active' => 'Active', + 'suspended' => 'Suspended', + ]) + ->required(), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('customer.name'), + Tables\Columns\TextColumn::make('subscription.id'), + Tables\Columns\TextColumn::make('control_panel'), + Tables\Columns\TextColumn::make('username'), + Tables\Columns\TextColumn::make('domain'), + Tables\Columns\TextColumn::make('package'), + Tables\Columns\TextColumn::make('status'), + ]) + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\DeleteBulkAction::make(), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ \ No newline at end of file diff --git a/app/Models/HostingAccount.php b/app/Models/HostingAccount.php new file mode 100644 index 00000000..9fed5be6 --- /dev/null +++ b/app/Models/HostingAccount.php @@ -0,0 +1,36 @@ +belongsTo(Customer::class); + } + + public function subscription() + { + return $this->belongsTo(Subscription::class); + } + + public function isActive() + { + return $this->status === 'active'; + } +} \ No newline at end of file diff --git a/app/Services/BillingService.php b/app/Services/BillingService.php index c933070d..a410f279 100644 --- a/app/Services/BillingService.php +++ b/app/Services/BillingService.php @@ -5,10 +5,18 @@ use App\Models\Invoice; use App\Models\Subscription; use App\Models\Customer; +use App\Models\HostingAccount; use Carbon\Carbon; class BillingService { + protected $hostingService; + + public function __construct(HostingService $hostingService) + { + $this->hostingService = $hostingService; + } + public function generateInvoice(Subscription $subscription) { $customer = $subscription->customer; @@ -34,10 +42,16 @@ public function processRecurringBilling() ->get(); foreach ($dueSubscriptions as $subscription) { - $this->generateInvoice($subscription); + $invoice = $this->generateInvoice($subscription); $subscription->update([ 'end_date' => Carbon::parse($subscription->end_date)->add($subscription->renewal_period), ]); + + if ($invoice->status === 'paid') { + $this->ensureHostingAccountActive($subscription); + } else { + $this->suspendHostingAccount($subscription); + } } } @@ -49,6 +63,7 @@ public function sendOverdueReminders() foreach ($overdueInvoices as $invoice) { // TODO: Send overdue reminder email + $this->suspendHostingAccount($invoice->subscription); } } @@ -56,4 +71,20 @@ private function generateInvoiceNumber() { return 'INV-' . strtoupper(uniqid()); } + + private function ensureHostingAccountActive(Subscription $subscription) + { + $hostingAccount = HostingAccount::where('subscription_id', $subscription->id)->first(); + if ($hostingAccount && !$hostingAccount->isActive()) { + $this->hostingService->unsuspendAccount($hostingAccount); + } + } + + private function suspendHostingAccount(Subscription $subscription) + { + $hostingAccount = HostingAccount::where('subscription_id', $subscription->id)->first(); + if ($hostingAccount && $hostingAccount->isActive()) { + $this->hostingService->suspendAccount($hostingAccount); + } + } } \ No newline at end of file diff --git a/app/Services/ControlPanels/CpanelClient.php b/app/Services/ControlPanels/CpanelClient.php new file mode 100644 index 00000000..79cfe9b4 --- /dev/null +++ b/app/Services/ControlPanels/CpanelClient.php @@ -0,0 +1,39 @@ +client = new Client(); + $this->apiUrl = config('services.cpanel.api_url'); + $this->apiToken = config('services.cpanel.api_token'); + } + + public function createAccount($username, $domain, $package) + { + // Implement cPanel API call to create account + } + + public function suspendAccount($username) + { + // Implement cPanel API call to suspend account + } + + public function unsuspendAccount($username) + { + // Implement cPanel API call to unsuspend account + } + + public function changePackage($username, $newPackage) + { + // Implement cPanel API call to change package + } +} \ No newline at end of file diff --git a/app/Services/HostingService.php b/app/Services/HostingService.php new file mode 100644 index 00000000..6166a9db --- /dev/null +++ b/app/Services/HostingService.php @@ -0,0 +1,91 @@ +cpanelClient = $cpanelClient; + $this->pleskClient = $pleskClient; + $this->directAdminClient = $directAdminClient; + } + + public function provisionAccount(HostingAccount $account) + { + $client = $this->getClientForControlPanel($account->control_panel); + $result = $client->createAccount($account->username, $account->domain, $account->package); + + if ($result) { + $account->status = 'active'; + $account->save(); + } + + return $result; + } + + public function suspendAccount(HostingAccount $account) + { + $client = $this->getClientForControlPanel($account->control_panel); + $result = $client->suspendAccount($account->username); + + if ($result) { + $account->status = 'suspended'; + $account->save(); + } + + return $result; + } + + public function unsuspendAccount(HostingAccount $account) + { + $client = $this->getClientForControlPanel($account->control_panel); + $result = $client->unsuspendAccount($account->username); + + if ($result) { + $account->status = 'active'; + $account->save(); + } + + return $result; + } + + public function upgradeAccount(HostingAccount $account, $newPackage) + { + $client = $this->getClientForControlPanel($account->control_panel); + $result = $client->changePackage($account->username, $newPackage); + + if ($result) { + $account->package = $newPackage; + $account->save(); + } + + return $result; + } + + protected function getClientForControlPanel($controlPanel) + { + switch ($controlPanel) { + case 'cpanel': + return $this->cpanelClient; + case 'plesk': + return $this->pleskClient; + case 'directadmin': + return $this->directAdminClient; + default: + throw new \Exception("Unsupported control panel: $controlPanel"); + } + } +} \ No newline at end of file diff --git a/database/migrations/2023_05_25_000000_create_hosting_accounts_table.php b/database/migrations/2023_05_25_000000_create_hosting_accounts_table.php new file mode 100644 index 00000000..0588f863 --- /dev/null +++ b/database/migrations/2023_05_25_000000_create_hosting_accounts_table.php @@ -0,0 +1,28 @@ +id(); + $table->foreignId('customer_id')->constrained()->onDelete('cascade'); + $table->foreignId('subscription_id')->constrained()->onDelete('cascade'); + $table->string('control_panel'); + $table->string('username'); + $table->string('domain'); + $table->string('package'); + $table->string('status'); + $table->timestamps(); + }); + } + + public function down() + { + Schema::dropIfExists('hosting_accounts'); + } +}; \ No newline at end of file