2 Commits
Author SHA1 Message Date
Yuri Karamian 7a19e78d51 update v1.2.5 2026-07-15 00:03:43 +02:00
Yuri Karamian fc2d2d4df6 docs: update README 2026-06-22 01:18:13 +02:00
4 changed files with 145 additions and 69 deletions
+25 -27
View File
@@ -59,7 +59,7 @@ WHMCS server module for Hetzner Cloud VPS management.
- Server Type: `cx23`, `cx33`, `cx43`, `cx53` (Cost-Optimized), `cpx11`, `cpx21`, `cax11`, `ccx13`, etc.
- Operating System: `ubuntu-22.04`, `debian-11`, `rocky-9`, etc.
- Datacenter: `fsn1`, `nbg1`, `hel1`, `ash`, `hil`, `sin`
- Location: `fsn1`, `nbg1`, `hel1`, `ash`, `hil`, `sin`
- Backups: On/Off
- Create Floating IP: On/Off
- Cloud-Init YAML: Optional custom cloud-init configuration
@@ -84,17 +84,19 @@ Create in Setup → Products/Services → Configurable Options:
- `No|None`
- `Yes|1 Floating IP`
**2\. Datacenter Selection**
**2\. Location Selection**
- Option Name: `datacenter`
- Option Name: `Location`
- Option Type: `Dropdown`
- Options:
- `fsn1-dc14|Falkenstein, Germany`
- `nbg1-dc3|Nuremberg, Germany`
- `hel1-dc2|Helsinki, Finland`
- `ash-dc1|Ashburn, USA`
- `hil-dc1|Hillsboro, USA`
- `sin-dc1|Singapore, Singapore`
- `fsn1|Falkenstein, Germany`
- `nbg1|Nuremberg, Germany`
- `hel1|Helsinki, Finland`
- `ash|Ashburn, USA`
- `hil|Hillsboro, USA`
- `sin|Singapore, Singapore`
Existing configurable options named `datacenter` remain supported. Legacy values such as `fsn1-dc14` are automatically converted to their corresponding location (`fsn1`) when provisioning.
**3\. Server Type**
@@ -306,33 +308,22 @@ runcmd:
Enable module debugging: Configuration → System Logs → Module Log
## Support Policy
## Support
This is free, open-source software provided AS-IS.
This module is free and open source, and we maintain it in our spare time. We hope it makes running Hetzner Cloud on WHMCS a little easier for you.
**NO SUPPORT** is provided except through paid channels:
- Premium Support: €100/hour (2 hour minimum)
- Email: support@arkhost.com (paid support only)
If something isn't working as expected:
### Before Requesting Support
- English only
- Must provide complete error logs
- Must provide steps to reproduce
- Vague reports like "doesn't work" will be ignored
1. Check the module log first (Configuration → System Logs → Module Log). It usually points straight to the cause.
2. Open an issue with what you saw, the steps that led to it, and the relevant log output. The more detail you share, the quicker we can help.
### GitHub Issues
- For documented bugs only
- Requires complete technical information
- Issues lacking proper information will be closed immediately
- This is not a support forum
If you need help with basic installation or configuration, purchase premium support.
We can't promise a response time, but we read every report and genuinely appreciate bug reports, ideas, and pull requests. Contributions are always welcome.
## License
MIT License - see [LICENSE](LICENSE) file for details.
Free and open source. Commercial support available.
Free and open source. Use it, fork it, improve it.
## Other ArkHost Modules
@@ -340,6 +331,13 @@ Check out our other WHMCS modules at [arkhost.com/whmcs-modules.php](https://ar
## Changelog
### v1.2.5
**Fixed:**
- Restored server provisioning after Hetzner removed the deprecated `datacenter` request property on July 1, 2026
- Replaced datacenter discovery with the Hetzner Locations API
- Updated client-area location display for the current server response format
- Added automatic conversion of legacy datacenter values such as `fsn1-dc14` to `fsn1`
### v1.2.4
**Fixed:**
- IP address now stored on the service at provisioning (and backfilled for existing services) so it displays in WHMCS instead of showing blank
@@ -3,7 +3,7 @@
* WHMCS Server Module - Hetzner VPS
*
* @package WHMCS
* @version 1.2.4
* @version 1.2.5
* @copyright Copyright (c) ArkHost 2025
* @author ArkHost <support@arkhost.com>
* @link https://arkhost.com
@@ -104,6 +104,57 @@ function ArkHostHetznerVPS_GetConfigurableOption(array $params, $optionName) {
return null;
}
/**
* Convert a legacy Hetzner datacenter name (for example fsn1-dc14) to the
* corresponding location name (fsn1). Location names are normalized to
* lowercase, while numeric IDs remain unchanged.
*/
function ArkHostHetznerVPS_NormalizeLocation($location) {
if (!is_scalar($location)) {
return '';
}
$location = trim((string)$location);
if ($location === '') {
return '';
}
if (preg_match('/^([a-z0-9]+)-dc[0-9]+$/i', $location, $matches)) {
return strtolower($matches[1]);
}
return strtolower($location);
}
/**
* Resolve the server location while remaining compatible with configurable
* options named either "Location" or the former "Datacenter".
*/
function ArkHostHetznerVPS_GetLocationOption(array $params, $default = NULL) {
foreach (array('configoptions', 'customfields') as $collection) {
if (!isset($params[$collection]) || !is_array($params[$collection])) {
continue;
}
// Prefer the current option name if both current and legacy options
// happen to exist on the same product.
foreach (array('location', 'datacenter') as $optionName) {
foreach ($params[$collection] as $name => $value) {
$normalizedName = strtolower(trim((string)$name));
if ($normalizedName === $optionName && $value !== '') {
return ArkHostHetznerVPS_NormalizeLocation($value);
}
}
}
}
// Keep using the original module setting key so existing WHMCS products
// retain their configoption3 value after upgrading.
return ArkHostHetznerVPS_NormalizeLocation(
ArkHostHetznerVPS_GetOption($params, 'datacenter', $default)
);
}
function ArkHostHetznerVPS_API(array $params) {
$url = 'https://api.hetzner.cloud/v1/';
$data = [];
@@ -125,8 +176,8 @@ function ArkHostHetznerVPS_API(array $params) {
$method = 'GET';
break;
case 'Datacenters':
$url .= 'datacenters';
case 'Locations':
$url .= 'locations?per_page=50';
$method = 'GET';
break;
@@ -160,10 +211,11 @@ function ArkHostHetznerVPS_API(array $params) {
)
);
// Add datacenter if specified
$datacenter = ArkHostHetznerVPS_GetOption($params, 'datacenter');
if ($datacenter) {
$data['datacenter'] = $datacenter;
// Hetzner removed the datacenter request property on 1 July 2026.
// Normalize legacy values such as fsn1-dc14 before sending location.
$location = ArkHostHetznerVPS_GetLocationOption($params);
if ($location) {
$data['location'] = $location;
}
// Handle backups
@@ -975,9 +1027,11 @@ function ArkHostHetznerVPS_ConfigOptions() {
'Type' => 'dropdown',
'Options' => array(),
),
// Keep the original array key and position for existing WHMCS product
// settings. Only the Hetzner-facing value and admin label have changed.
'datacenter' => array(
'FriendlyName' => 'Datacenter',
'Description' => 'The datacenter location for the server.',
'FriendlyName' => 'Location',
'Description' => 'The Hetzner location for the server (Configurable option: Location or legacy datacenter).',
'Type' => 'dropdown',
'Options' => array(),
),
@@ -993,7 +1047,7 @@ function ArkHostHetznerVPS_ConfigOptions() {
),
'floating_ip_location' => array(
'FriendlyName' => 'Floating IP Location',
'Description' => 'Location for floating IPs (same as datacenter)',
'Description' => 'Location for floating IPs (normally the same as the server location).',
'Type' => 'dropdown',
'Options' => array(),
),
@@ -1082,19 +1136,31 @@ function ArkHostHetznerVPS_ConfigOptions() {
}
}
// Fetch datacenters
$params['action'] = 'Datacenters';
$datacenters = ArkHostHetznerVPS_API($params);
if (isset($datacenters['datacenters'])) {
foreach ($datacenters['datacenters'] as $datacenter) {
$array['datacenter']['Options'] += array(
$datacenter['name'] => $datacenter['description'] . ' (' . $datacenter['location']['city'] . ')'
);
// Also populate floating IP locations
$array['floating_ip_location']['Options'] += array(
$datacenter['location']['name'] => $datacenter['location']['city'] . ', ' . $datacenter['location']['country']
);
// Fetch locations. Hetzner's datacenter endpoints are deprecated
// and are scheduled for removal after 1 October 2026.
$params['action'] = 'Locations';
$locations = ArkHostHetznerVPS_API($params);
if (isset($locations['locations'])) {
foreach ($locations['locations'] as $location) {
if (empty($location['name'])) {
continue;
}
$place = array_filter(array(
$location['city'] ?? '',
$location['country'] ?? ''
));
$description = !empty($location['description'])
? $location['description']
: $location['name'];
$label = $description;
if (!empty($place)) {
$label .= ' (' . implode(', ', $place) . ')';
}
$array['datacenter']['Options'][$location['name']] = $label;
$array['floating_ip_location']['Options'][$location['name']] = $label;
}
}
}
@@ -1218,7 +1284,9 @@ function ArkHostHetznerVPS_CreateAccount(array $params) {
$floatingIpParams = $params;
$floatingIpParams['action'] = 'Create Floating IP';
$floatingIpParams['ip_type'] = $ipType;
$floatingIpParams['location'] = ArkHostHetznerVPS_GetOption($params, 'floating_ip_location');
$floatingIpParams['location'] = ArkHostHetznerVPS_NormalizeLocation(
ArkHostHetznerVPS_GetOption($params, 'floating_ip_location')
);
$floatingIpParams['description'] = 'Server ID: ' . $create['server']['id'];
$floatingIpParams['assign_to_server'] = true;
@@ -1691,8 +1759,9 @@ function ArkHostHetznerVPS_ClientAreaAPI(array $params) {
$createParams = $params;
$createParams['action'] = 'Create Floating IP';
$createParams['ip_type'] = 'ipv4';
$createParams['location'] = ArkHostHetznerVPS_GetOption($params, 'floating_ip_location') ?:
ArkHostHetznerVPS_GetOption($params, 'datacenter');
$createParams['location'] = ArkHostHetznerVPS_NormalizeLocation(
ArkHostHetznerVPS_GetOption($params, 'floating_ip_location')
) ?: ArkHostHetznerVPS_GetLocationOption($params);
$createParams['description'] = 'WHMCS Service ID: ' . $params['serviceid'];
$createParams['assign_to_server'] = true;
@@ -2485,21 +2554,31 @@ function ArkHostHetznerVPS_ClientArea(array $params) {
$serverInfo['bandwidth'] = 20480; // 20TB in GB (20 * 1024)
$serverInfo['bandwidth_used'] = 0; // Not available via API
// Add datacenter info - format as "City, Country"
$city = isset($serverInfo['datacenter']['location']['city']) ? $serverInfo['datacenter']['location']['city'] : '';
$country = isset($serverInfo['datacenter']['location']['country']) ? $serverInfo['datacenter']['location']['country'] : '';
if ($city && $country) {
$serverInfo['datacenter'] = $city . ', ' . $country;
} elseif ($city) {
$serverInfo['datacenter'] = $city;
} elseif ($country) {
$serverInfo['datacenter'] = $country;
} else {
$serverInfo['datacenter'] = 'N/A';
// Hetzner now returns location directly on the server. Retain a fallback
// for cached/older responses that still contain datacenter.location.
$locationInfo = array();
if (isset($serverInfo['location']) && is_array($serverInfo['location'])) {
$locationInfo = $serverInfo['location'];
} elseif (isset($serverInfo['datacenter']['location']) && is_array($serverInfo['datacenter']['location'])) {
$locationInfo = $serverInfo['datacenter']['location'];
}
$serverInfo['location'] = $city ?: 'N/A';
$city = $locationInfo['city'] ?? '';
$country = $locationInfo['country'] ?? '';
if ($city && $country) {
$locationDisplay = $city . ', ' . $country;
} elseif ($city) {
$locationDisplay = $city;
} elseif ($country) {
$locationDisplay = $country;
} else {
$locationDisplay = 'N/A';
}
$serverInfo['location_display'] = $locationDisplay;
// Preserve the former display key for third-party/custom templates.
$serverInfo['datacenter'] = $locationDisplay;
// Get root password with expiration check (72 hours)
$passwordSetTime = $params['model']->serviceProperties->get('ArkHostHetznerVPS|Password Set Time');
+1 -2
View File
@@ -3,7 +3,7 @@
* WHMCS Server Module - Hetzner VPS Hooks
*
* @package WHMCS
* @version 1.2.4
* @version 1.2.5
* @copyright Copyright (c) ArkHost 2025
* @author ArkHost <support@arkhost.com>
* @link https://arkhost.com
@@ -70,4 +70,3 @@ add_hook('ClientAreaHeadOutput', 1, function($vars) {
</style>
HTML;
});
@@ -207,7 +207,7 @@
</tr>
<tr>
<td class="text-muted">{$ADDONLANG.Overview.Location}:</td>
<td><i class="fa fa-map-marker-alt mr-1"></i>{$serverInfo['datacenter']}</td>
<td><i class="fa fa-map-marker-alt mr-1"></i>{$serverInfo['location_display']}</td>
</tr>
<tr>
<td class="text-muted">{$ADDONLANG.Overview.ServerType}:</td>