Current Situation: The WHMCS module is incorrectly calculating storage prices, showing significantly higher prices than the STACKIT portal.
Root Cause: STACKIT uses two different pricing models for storage:
Why This Happened: The module assumes all storage classes use per-GB pricing, but storage_premium_perf0 actually uses disk-based pricing with separate GB charges that aren't exposed in the pricing API.
These storage classes charge per GB/hour and match the expected pricing model:
No GB-based storage classes found in the current pricing data.
These storage classes charge a flat rate per disk plus hidden per-GB charges:
Modify your WHMCS module to handle both pricing models correctly:
function update_stackit_pricing(){
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://pim.api.eu01.stackit.cloud/v1/skus?region=eu01',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'GET',
));
$response = curl_exec($curl);
$response = json_decode($response, true);
curl_close($curl);
Capsule::table("stackit_pricing")->truncate();
foreach($response['services'] as $service){
if($service['priceListVisibility'] == "Yes"){
$insert = false;
if($service['attributes']['metro'] == 1){
$metro = "Yes";
}else{
$metro = "No";
}
// IMPORTANT: Differentiate between pricing models
if($service['category'] == "Storage" &&
$service['attributes']['storage'] == "volume" &&
$service['attributes']['type'] == "performance"){
$type = "volume";
$pclass = $service['attributes']['class'];
// Check billing unit to determine pricing model
if($service['unitBilling'] == "per GB/hour"){
// GB-based pricing - use directly
$insert = true;
$flavor = "none";
$pricemonthly = $service['monthlyPrice']; // This is per GB
} elseif($service['unitBilling'] == "per disk/hour"){
// Disk-based pricing - needs special handling
// Store with a flag or skip entirely
continue; // For now, skip disk-based pricing
}
}
// ... rest of flavor handling ...
if($insert){
Capsule::table("stackit_pricing")->insert([
'type' => $type,
'flavor' => $flavor,
'pclass' => $pclass,
'price' => $pricemonthly,
'currency' => $currency,
'metro' => $metro,
'billing_unit' => $service['unitBilling'] // Add this field
]);
}
}
}
}
function getvolumeprice($volume, $availabilityzone, $activeCurrency, $flavor, $pid){
if(!empty($pid)){
$marginpercent = Capsule::table("tblproducts")
->where("id",$pid)
->value("configoption3");
}
$zonename = Capsule::table("tblproductconfigoptionssub")
->where('id',$availabilityzone)
->select('optionname')
->first()->optionname;
$performClass = "storage_premium_perf0";
if($zonename == "eu01-m"){
$metro = "Yes";
}else{
$metro = "No";
}
// First, try to find GB-based pricing
$valldata = Capsule::table("stackit_pricing")
->where('type',"volume")
->where('metro',$metro)
->whereNotNull('pclass')
->where('billing_unit', 'per GB/hour') // Only GB-based
->orderBy('price', 'asc') // Get cheapest
->first();
// If no GB-based pricing found, use hardcoded rates
if(!$valldata){
// Hardcoded rates based on portal observation
$hardcodedRates = [
'storage_premium_perf0' => [
'No' => 0.0653125, // Single AZ
'Yes' => 0.1640625 // Metro (estimated)
],
// Add other classes as needed
];
if(isset($hardcodedRates[$performClass][$metro])){
$pricePerGB = $hardcodedRates[$performClass][$metro];
$monthlyprice = $pricePerGB * $volume;
$monthlyprice = $monthlyprice + ($monthlyprice * $marginpercent / 100);
// Continue with flavor pricing...
// ... rest of the function
}
}
// ... rest of the original function
}
Configure your WHMCS module to only offer storage classes that use per-GB pricing. This ensures consistent and predictable pricing.
// Create a custom pricing table in your database
CREATE TABLE `stackit_custom_pricing` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`class` varchar(255) NOT NULL,
`metro` enum('Yes','No') NOT NULL,
`price_per_gb` decimal(10,8) NOT NULL,
`base_disk_price` decimal(10,8) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `class_metro` (`class`,`metro`)
);
// Insert known pricing
INSERT INTO `stackit_custom_pricing` VALUES
(NULL, 'storage_premium_perf0', 'No', 0.0653125, 1.77),
(NULL, 'storage_premium_perf0', 'Yes', 0.1640625, 3.55);
If STACKIT provides a portal API that returns the actual customer pricing, use that instead of the SKU API.