Files
WP-WHMCS-Pricing/whmcs-pricing-plugin/includes/api-handler.php
T
2025-11-16 21:47:01 +01:00

317 lines
9.8 KiB
PHP

<?php
/**
* WHMCS API Handler
*
* Handles all communication with WHMCS API
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
class WHMCS_Pricing_API {
/**
* Get settings
*/
private static function get_settings() {
$defaults = array(
'api_url' => '',
'api_identifier' => '',
'api_secret' => '',
'cache_duration' => 3600,
'default_currency' => 'USD',
'show_decimals' => true,
'debug_mode' => false
);
$settings = get_option('whmcs_pricing_settings', $defaults);
return wp_parse_args($settings, $defaults);
}
/**
* Make API request to WHMCS
*/
private static function make_request($action, $params = array()) {
$settings = self::get_settings();
// Validate settings
if (empty($settings['api_url']) || empty($settings['api_identifier']) || empty($settings['api_secret'])) {
self::log_error('API credentials not configured');
return new WP_Error('missing_credentials', __('WHMCS API credentials not configured', 'whmcs-pricing'));
}
// Prepare API request
$api_url = rtrim($settings['api_url'], '/') . '/includes/api.php';
$post_data = array_merge(array(
'action' => $action,
'identifier' => $settings['api_identifier'],
'secret' => $settings['api_secret'],
'responsetype' => 'json'
), $params);
// Make request
$response = wp_remote_post($api_url, array(
'timeout' => 15,
'body' => $post_data,
'sslverify' => true
));
// Check for errors
if (is_wp_error($response)) {
self::log_error('API request failed: ' . $response->get_error_message());
return $response;
}
// Parse response
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (json_last_error() !== JSON_ERROR_NONE) {
self::log_error('Invalid JSON response from WHMCS API');
return new WP_Error('invalid_response', __('Invalid response from WHMCS API', 'whmcs-pricing'));
}
// Check API result
if (isset($data['result']) && $data['result'] === 'error') {
$error_message = isset($data['message']) ? $data['message'] : __('Unknown API error', 'whmcs-pricing');
self::log_error('WHMCS API error: ' . $error_message);
return new WP_Error('api_error', $error_message);
}
return $data;
}
/**
* Get product details
*/
public static function get_product($product_id) {
$cache_key = 'whmcs_product_' . $product_id;
$cached_data = get_transient($cache_key);
if (false !== $cached_data && !self::is_debug_mode()) {
return $cached_data;
}
// Make API request
$response = self::make_request('GetProducts', array(
'pid' => $product_id
));
if (is_wp_error($response)) {
return $response;
}
// Extract product data
if (!isset($response['products']) || !isset($response['products']['product'])) {
self::log_error('Product not found: ' . $product_id);
return new WP_Error('product_not_found', __('Product not found', 'whmcs-pricing'));
}
$product_data = is_array($response['products']['product']) && isset($response['products']['product'][0])
? $response['products']['product'][0]
: $response['products']['product'];
// Debug log the product data structure
if (self::is_debug_mode()) {
self::log_error('Product data for ID ' . $product_id . ': ' . print_r($product_data, true));
}
// Cache the result
$settings = self::get_settings();
set_transient($cache_key, $product_data, absint($settings['cache_duration']));
return $product_data;
}
/**
* Get product price
*/
public static function get_price($product_id, $args = array()) {
$defaults = array(
'billing_cycle' => 'monthly',
'currency' => null,
'format' => 'default'
);
$args = wp_parse_args($args, $defaults);
$settings = self::get_settings();
// Use default currency if not specified
if (empty($args['currency'])) {
$args['currency'] = $settings['default_currency'];
}
// Get product data
$product = self::get_product($product_id);
if (is_wp_error($product)) {
return $product;
}
// Find pricing for the requested currency and billing cycle
$price = self::extract_price($product, $args['billing_cycle'], $args['currency']);
if (is_wp_error($price)) {
return $price;
}
// Format the price
return self::format_price($price, $args['currency'], $args['format']);
}
/**
* Extract price from product data
*/
private static function extract_price($product, $billing_cycle, $currency) {
// Normalize billing cycle
$cycle_map = array(
'monthly' => 'monthly',
'quarterly' => 'quarterly',
'semiannually' => 'semiannually',
'annually' => 'annually',
'biennially' => 'biennially',
'triennially' => 'triennially',
'month' => 'monthly',
'quarter' => 'quarterly',
'year' => 'annually',
'annual' => 'annually'
);
$billing_cycle = strtolower($billing_cycle);
$billing_cycle = isset($cycle_map[$billing_cycle]) ? $cycle_map[$billing_cycle] : $billing_cycle;
// Debug pricing structure
if (self::is_debug_mode()) {
self::log_error("Looking for price: currency={$currency}, cycle={$billing_cycle}");
if (isset($product['pricing'])) {
self::log_error('Available pricing: ' . print_r($product['pricing'], true));
}
}
// Look for pricing in product data
if (isset($product['pricing'][$currency][$billing_cycle])) {
$price = floatval($product['pricing'][$currency][$billing_cycle]);
// WHMCS uses -1.00 to indicate billing cycle not available
if ($price < 0) {
if (self::is_debug_mode()) {
self::log_error("Price is -1 (not available) for cycle: {$billing_cycle}");
}
return new WP_Error('price_not_available', __('This billing cycle is not available for this product', 'whmcs-pricing'));
}
if (self::is_debug_mode()) {
self::log_error("Found price: {$price}");
}
return $price;
}
// Try to find any available pricing and log it
if (self::is_debug_mode() && isset($product['pricing'])) {
self::log_error("Available currencies: " . implode(', ', array_keys($product['pricing'])));
if (isset($product['pricing'][$currency])) {
self::log_error("Available cycles for {$currency}: " . implode(', ', array_keys($product['pricing'][$currency])));
}
}
self::log_error("Price not found for product {$product['pid']}, cycle: {$billing_cycle}, currency: {$currency}");
return new WP_Error('price_not_found', __('Price not available for selected options', 'whmcs-pricing'));
}
/**
* Format price
*/
private static function format_price($price, $currency, $format) {
$settings = self::get_settings();
// Apply decimal formatting
if ($format === 'no_decimals' || !$settings['show_decimals']) {
$price = round($price);
$formatted_price = number_format($price, 0);
} else {
$formatted_price = number_format($price, 2);
}
// Get currency symbol
$currency_symbol = self::get_currency_symbol($currency);
return array(
'raw' => $price,
'formatted' => $formatted_price,
'currency' => $currency,
'symbol' => $currency_symbol
);
}
/**
* Get currency symbol
*/
private static function get_currency_symbol($currency) {
$symbols = array(
'USD' => '$',
'EUR' => '€',
'GBP' => '£',
'AUD' => 'A$',
'CAD' => 'C$',
'JPY' => '¥',
'CNY' => '¥',
'INR' => '₹',
'BRL' => 'R$',
'RUB' => '₽',
'CHF' => 'Fr',
'SEK' => 'kr',
'NOK' => 'kr',
'DKK' => 'kr',
'PLN' => 'zł',
'THB' => '฿',
'MYR' => 'RM',
'SGD' => 'S$',
'NZD' => 'NZ$',
'MXN' => 'Mex$',
'ZAR' => 'R'
);
return isset($symbols[$currency]) ? $symbols[$currency] : $currency;
}
/**
* Clear cache for specific product
*/
public static function clear_cache($product_id) {
delete_transient('whmcs_product_' . $product_id);
}
/**
* Clear all cached products
*/
public static function clear_all_cache() {
global $wpdb;
$wpdb->query(
"DELETE FROM {$wpdb->options}
WHERE option_name LIKE '_transient_whmcs_product_%'
OR option_name LIKE '_transient_timeout_whmcs_product_%'"
);
}
/**
* Check if debug mode is enabled
*/
private static function is_debug_mode() {
$settings = self::get_settings();
return !empty($settings['debug_mode']);
}
/**
* Log error message
*/
private static function log_error($message) {
if (self::is_debug_mode() && function_exists('error_log')) {
error_log('[WHMCS Pricing] ' . $message);
}
}
}