Enhance WHMCS integration with mTLS support and product mapping features. Added mTLS configuration options, updated API endpoints for mTLS status and fingerprint registration, and implemented product validation API. Updated database schema and documentation accordingly.
Some checks failed
CI / Node — lint, typecheck, test, build (push) Failing after 9s
CI / Go — node-agent tests (push) Failing after 8s
CI / Go — edge-gateway build (push) Successful in 18s

This commit is contained in:
smueller
2026-06-30 13:17:12 +02:00
parent 4b20efe4bc
commit 316679a913
34 changed files with 1174 additions and 30 deletions

View File

@@ -4,14 +4,19 @@ declare(strict_types=1);
final class HexaGameCloudAddonApiClient
{
private array $mtls = [];
public function __construct(
private readonly string $baseUrl,
private readonly string $integrationId,
private readonly string $apiSecret,
array $mtls = [],
) {
if ($this->baseUrl === '' || $this->integrationId === '' || $this->apiSecret === '') {
throw new RuntimeException('API URL, integration ID and secret are required');
}
$this->mtls = $mtls;
}
public function health(): array
@@ -58,6 +63,36 @@ final class HexaGameCloudAddonApiClient
);
}
public function listPlans(): array
{
return $this->request('GET', '/api/v1/integrations/whmcs/catalog/plans');
}
public function validatePlan(string $planSlug): array
{
return $this->request('POST', '/api/v1/integrations/whmcs/catalog/validate-plan', [
'planSlug' => $planSlug,
]);
}
public function registerMtlsFingerprint(string $fingerprint, ?string $subject = null): array
{
return $this->request(
'PUT',
'/api/v1/integrations/whmcs/mtls/fingerprint',
array_filter([
'fingerprint' => $fingerprint,
'subject' => $subject,
]),
'mtls:register:' . date('Y-m-d')
);
}
public function mtlsStatus(): array
{
return $this->request('GET', '/api/v1/integrations/whmcs/mtls/status');
}
private function request(string $method, string $pathWithQuery, array $body = [], ?string $idempotencySuffix = null): array
{
$pathParts = explode('?', $pathWithQuery, 2);
@@ -92,6 +127,20 @@ final class HexaGameCloudAddonApiClient
$signature = hash_hmac('sha256', $signatureBase, $this->apiSecret);
$requestUrl = rtrim($this->baseUrl, '/') . $path . ($canonicalQuery !== '' ? ('?' . $canonicalQuery) : '');
$headers = [
'Content-Type: application/json',
'Accept: application/json',
'X-HGC-Integration-ID: ' . $this->integrationId,
'X-HGC-Timestamp: ' . $timestamp,
'X-HGC-Nonce: ' . $nonce,
'X-HGC-Idempotency-Key: ' . $idempotencyKey,
'X-HGC-Signature: ' . $signature,
];
if ($this->mtls !== [] && class_exists('HexaGameCloudMtlsConfig')) {
$headers = array_merge($headers, HexaGameCloudMtlsConfig::fingerprintHeader($this->mtls));
}
$ch = curl_init($requestUrl);
if ($ch === false) {
throw new RuntimeException('Unable to initialize HTTP client');
@@ -101,18 +150,14 @@ final class HexaGameCloudAddonApiClient
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Accept: application/json',
'X-HGC-Integration-ID: ' . $this->integrationId,
'X-HGC-Timestamp: ' . $timestamp,
'X-HGC-Nonce: ' . $nonce,
'X-HGC-Idempotency-Key: ' . $idempotencyKey,
'X-HGC-Signature: ' . $signature,
],
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $bodyJson,
]);
if ($this->mtls !== [] && class_exists('HexaGameCloudMtlsConfig')) {
HexaGameCloudMtlsConfig::applyToCurl($ch, $this->mtls);
}
$responseBody = curl_exec($ch);
$statusCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
use WHMCS\Database\Capsule;
final class HexaGameCloudProductMappingStorage
{
public function listMappings(): array
{
return Capsule::table('mod_hexagamecloud_product_mappings')
->orderBy('whmcs_product_id', 'asc')
->get()
->map(static fn ($row) => (array) $row)
->all();
}
public function upsertMapping(array $input): void
{
Capsule::table('mod_hexagamecloud_product_mappings')->updateOrInsert(
['whmcs_product_id' => (int) $input['whmcs_product_id']],
[
'whmcs_product_name' => (string) ($input['whmcs_product_name'] ?? ''),
'plan_slug' => (string) $input['plan_slug'],
'default_edition' => (string) ($input['default_edition'] ?? 'JAVA'),
'default_software' => (string) ($input['default_software'] ?? 'VANILLA'),
'default_version' => (string) ($input['default_version'] ?? '1.21.1'),
'is_active' => !empty($input['is_active']) ? 1 : 0,
'updated_at' => gmdate('Y-m-d H:i:s'),
]
);
}
public function deleteMapping(int $productId): void
{
Capsule::table('mod_hexagamecloud_product_mappings')
->where('whmcs_product_id', $productId)
->delete();
}
public function listWhmcsProducts(): array
{
return Capsule::table('tblproducts')
->where('servertype', 'hexagamecloud')
->orderBy('name', 'asc')
->get(['id', 'name', 'gid'])
->map(static fn ($row) => (array) $row)
->all();
}
}