Add platform admin hosting API for Ladill account provisioning.
Deploy Ladill Hosting / deploy (push) Successful in 1m29s

Expose authenticated service endpoints for assigning, listing, renewing, and managing hosting accounts from the platform, with tests and deploy docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-03 22:54:00 +00:00
co-authored by Cursor
parent b0e1cfab4e
commit 2c9877a87c
14 changed files with 806 additions and 66 deletions
@@ -0,0 +1,154 @@
<?php
namespace Tests\Feature;
use App\Models\HostingAccount;
use App\Models\HostingProduct;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PlatformHostingAdminApiTest extends TestCase
{
use RefreshDatabase;
private const API_KEY = 'test-platform-hosting-key';
protected function setUp(): void
{
parent::setUp();
config(['platform.service_api_keys' => ['account' => self::API_KEY]]);
}
protected function tearDown(): void
{
Carbon::setTestNow();
parent::tearDown();
}
public function test_platform_can_assign_hosting_package_to_user(): void
{
Carbon::setTestNow('2026-04-05 10:00:00');
$owner = User::query()->create([
'public_id' => (string) \Illuminate\Support\Str::uuid(),
'name' => 'Hosting Owner',
'email' => 'owner@example.com',
]);
$product = $this->createHostingProduct();
$response = $this->withToken(self::API_KEY)
->postJson('/api/platform/users/'.$owner->public_id.'/hosting-accounts', [
'hosting_product_id' => $product->id,
'duration_months' => 6,
'primary_domain' => 'example.com',
'username' => 'exampleuser',
'owner_email' => $owner->email,
'owner_name' => $owner->name,
'assigned_by_admin' => 99,
]);
$response->assertCreated()
->assertJsonPath('data.account.username', 'exampleuser')
->assertJsonPath('data.account.primary_domain', 'example.com')
->assertJsonPath('data.account.status', 'active')
->assertJsonPath('data.account.metadata.assigned_duration_months', 6)
->assertJsonPath('data.account.metadata.assigned_by_admin', 99);
$account = HostingAccount::query()->where('user_id', $owner->id)->firstOrFail();
$this->assertTrue($account->expires_at->equalTo(Carbon::parse('2026-10-05 10:00:00')));
}
public function test_platform_can_list_user_hosting_accounts(): void
{
$owner = User::query()->create([
'public_id' => (string) \Illuminate\Support\Str::uuid(),
'name' => 'Hosting Owner',
'email' => 'owner@example.com',
]);
$product = $this->createHostingProduct();
HostingAccount::query()->create([
'user_id' => $owner->id,
'hosting_product_id' => $product->id,
'username' => 'listeduser',
'type' => 'shared',
'status' => 'active',
'metadata' => ['assigned_duration_months' => 3],
]);
$response = $this->withToken(self::API_KEY)
->getJson('/api/platform/users/'.$owner->public_id.'/hosting-accounts');
$response->assertOk()
->assertJsonCount(1, 'data')
->assertJsonPath('data.0.username', 'listeduser');
}
public function test_platform_can_update_duration_and_renew(): void
{
Carbon::setTestNow('2026-04-05 10:00:00');
$owner = User::query()->create([
'public_id' => (string) \Illuminate\Support\Str::uuid(),
'name' => 'Hosting Owner',
'email' => 'owner@example.com',
]);
$product = $this->createHostingProduct();
$account = HostingAccount::query()->create([
'user_id' => $owner->id,
'hosting_product_id' => $product->id,
'username' => 'durationuser',
'type' => 'shared',
'status' => 'active',
'expires_at' => Carbon::parse('2026-05-05 10:00:00'),
'metadata' => ['assigned_duration_months' => 1],
]);
$this->withToken(self::API_KEY)
->patchJson('/api/platform/hosting-accounts/'.$account->id.'/duration', [
'duration_months' => 12,
'admin_id' => 7,
])
->assertOk()
->assertJsonPath('data.metadata.assigned_duration_months', 12);
$account->refresh();
$this->assertTrue($account->expires_at->equalTo(Carbon::parse('2027-04-05 10:00:00')));
$this->withToken(self::API_KEY)
->postJson('/api/platform/hosting-accounts/'.$account->id.'/renew', ['admin_id' => 7])
->assertOk();
$account->refresh();
$this->assertTrue($account->expires_at->equalTo(Carbon::parse('2028-04-05 10:00:00')));
}
public function test_unauthenticated_platform_requests_are_rejected(): void
{
$this->getJson('/api/platform/hosting-products')->assertUnauthorized();
}
private function createHostingProduct(): HostingProduct
{
return HostingProduct::query()->create([
'name' => 'Admin Assigned Shared Hosting',
'slug' => 'admin-assigned-shared-hosting',
'category' => 'shared',
'type' => 'single_domain',
'price_monthly' => 10,
'price_quarterly' => 30,
'price_yearly' => 120,
'price_biennial' => 240,
'currency' => 'GHS',
'max_domains' => 1,
'is_active' => true,
'is_visible' => true,
'sort_order' => 1,
]);
}
}