mirror of
https://gitlab.com/ArkHost/WP-WHMCS-Pricing.git
synced 2026-07-24 07:55:54 +02:00
first commit
This commit is contained in:
@@ -0,0 +1,160 @@
|
|||||||
|
# WHMCS Pricing
|
||||||
|
|
||||||
|
Display WHMCS product prices on WordPress using shortcodes. Lightweight, cached, works with Elementor.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Upload ZIP via `Plugins > Add New > Upload Plugin`
|
||||||
|
2. Activate
|
||||||
|
3. Go to `Settings > WHMCS Pricing`
|
||||||
|
4. Enter your WHMCS credentials
|
||||||
|
|
||||||
|
## WHMCS Setup
|
||||||
|
|
||||||
|
**Step 1: Allow API Access by IP**
|
||||||
|
|
||||||
|
1. Go to `General Settings > Security` tab in WHMCS
|
||||||
|
2. Find **API IP Access Restriction**
|
||||||
|
3. Add your WordPress server IP address
|
||||||
|
4. Save changes
|
||||||
|
|
||||||
|
**Step 2: Generate API Credentials**
|
||||||
|
|
||||||
|
1. Go to `System Settings > API Credentials`
|
||||||
|
2. Click `Generate New API Credential`
|
||||||
|
3. Set Access Type to `Allowed`
|
||||||
|
4. Copy Identifier and Secret
|
||||||
|
|
||||||
|
Enter these in WordPress plugin settings along with your WHMCS URL.
|
||||||
|
|
||||||
|
**Security Note:** Without adding your WordPress server IP to API IP Access Restriction, the API calls will be blocked by WHMCS.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Basic shortcode:
|
||||||
|
```
|
||||||
|
[whmcs_price product_id="123" billing_cycle="annually"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
- `product_id` - WHMCS product ID (required)
|
||||||
|
- `billing_cycle` - monthly, quarterly, semiannually, annually, biennially, triennially (default: monthly)
|
||||||
|
- `currency` - EUR, USD, GBP, etc. (uses plugin default if not specified)
|
||||||
|
- `format` - `default` or `no_decimals`
|
||||||
|
- `show_cycle` - `true` or `false` (default: true)
|
||||||
|
- `lang` - Language: `english`, `spanish`, `portuguese-pt`, `dutch`, `french`, `german`, `russian` (default: english)
|
||||||
|
- `class` - Custom CSS class
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
[whmcs_price product_id="5" billing_cycle="annually"]
|
||||||
|
Output: €24.00 per year
|
||||||
|
|
||||||
|
[whmcs_price product_id="5" billing_cycle="monthly" format="no_decimals"]
|
||||||
|
Output: €3 per month
|
||||||
|
|
||||||
|
[whmcs_price product_id="5" currency="USD" show_cycle="false"]
|
||||||
|
Output: $3.50
|
||||||
|
|
||||||
|
[whmcs_price product_id="5" billing_cycle="annually" lang="spanish"]
|
||||||
|
Output: €24.00 por año
|
||||||
|
|
||||||
|
[whmcs_price product_id="5" billing_cycle="monthly" lang="dutch"]
|
||||||
|
Output: €3.00 per maand
|
||||||
|
```
|
||||||
|
|
||||||
|
### Translations
|
||||||
|
|
||||||
|
Billing cycle text is translated via `languages.json`. Supported languages:
|
||||||
|
- `english`
|
||||||
|
- `spanish`
|
||||||
|
- `portuguese-pt`
|
||||||
|
- `dutch`
|
||||||
|
- `french`
|
||||||
|
- `german`
|
||||||
|
- `russian`
|
||||||
|
|
||||||
|
Add more languages by editing `languages.json` in the plugin folder (uses WHMCS language naming).
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"italian": {
|
||||||
|
"monthly": "al mese",
|
||||||
|
"annually": "all'anno"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Find Product IDs
|
||||||
|
|
||||||
|
In WHMCS admin:
|
||||||
|
1. `System Settings > Products/Services`
|
||||||
|
2. Click a product
|
||||||
|
3. ID is in URL: `?id=123`
|
||||||
|
|
||||||
|
## HTML Output
|
||||||
|
|
||||||
|
```html
|
||||||
|
<span class="whmcs-price" data-product-id="123" data-cycle="annually">
|
||||||
|
<span class="currency-symbol">€</span>
|
||||||
|
<span class="price-amount">24.00</span>
|
||||||
|
<span class="billing-cycle">per year</span>
|
||||||
|
</span>
|
||||||
|
```
|
||||||
|
|
||||||
|
Style it with CSS:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.whmcs-price { font-weight: bold; }
|
||||||
|
.price-amount { font-size: 24px; }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
**WHMCS uses `-1.00` for unavailable billing cycles.** If a product doesn't have monthly pricing in WHMCS, don't use `billing_cycle="monthly"` in the shortcode. Check your WHMCS product configuration first.
|
||||||
|
|
||||||
|
**Caching:** Prices are cached for 1 hour by default. Change this in plugin settings. Click "Clear All Cache" after updating prices in WHMCS.
|
||||||
|
|
||||||
|
**Errors:** Only administrators see error messages. Regular visitors see nothing if a price can't be loaded.
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
- **WHMCS URL** - Your WHMCS installation URL (https://billing.yourdomain.com)
|
||||||
|
- **API Identifier** - From WHMCS API credentials
|
||||||
|
- **API Secret** - From WHMCS API credentials
|
||||||
|
- **Default Currency** - Fallback currency (EUR, USD, etc.)
|
||||||
|
- **Show Decimals** - Toggle decimal places globally
|
||||||
|
- **Cache Duration** - How long to cache API responses (seconds)
|
||||||
|
- **Debug Mode** - Enable logging, disable cache
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Price shows as -1.00:**
|
||||||
|
- That billing cycle isn't available for this product in WHMCS
|
||||||
|
- Use a different `billing_cycle` parameter
|
||||||
|
|
||||||
|
**No price displays:**
|
||||||
|
- Check product ID exists in WHMCS
|
||||||
|
- Verify API credentials are correct
|
||||||
|
- Enable Debug Mode and check `/wp-content/debug.log`
|
||||||
|
- Clear plugin cache
|
||||||
|
|
||||||
|
**Wrong price:**
|
||||||
|
- Clear cache (prices are cached)
|
||||||
|
- Check currency matches WHMCS configuration
|
||||||
|
- Verify billing cycle is available
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- WordPress 5.0+
|
||||||
|
- PHP 7.4+
|
||||||
|
- WHMCS 8.x with API access
|
||||||
|
- SSL recommended
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
**By:** ArkHost
|
||||||
|
**Sponsored by:** Duplika.com
|
||||||
|
**License:** GPL v2+
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# WordPress
|
||||||
|
.htaccess
|
||||||
|
wp-config.php
|
||||||
|
wp-content/uploads/
|
||||||
|
wp-content/blogs.dir/
|
||||||
|
wp-content/upgrade/
|
||||||
|
wp-content/backup-db/
|
||||||
|
wp-content/advanced-cache.php
|
||||||
|
wp-content/wp-cache-config.php
|
||||||
|
wp-content/cache/
|
||||||
|
wp-content/cache/supercache/
|
||||||
|
|
||||||
|
# Plugin Development
|
||||||
|
node_modules/
|
||||||
|
vendor/
|
||||||
|
composer.lock
|
||||||
|
package-lock.json
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.sublime-project
|
||||||
|
*.sublime-workspace
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
tests/
|
||||||
|
phpunit.xml
|
||||||
|
.phpunit.result.cache
|
||||||
|
|
||||||
|
# Build
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.zip
|
||||||
|
!whmcs-pricing-plugin.zip
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Temporary
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
error_log
|
||||||
|
debug.log
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
/**
|
||||||
|
* WHMCS Pricing Admin Styles
|
||||||
|
*/
|
||||||
|
|
||||||
|
.whmcs-pricing-admin {
|
||||||
|
max-width: 1200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 30px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-main {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-sidebar {
|
||||||
|
width: 350px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-box {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ccd0d4;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-box h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-box h4 {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-box h4:first-of-type {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-box code {
|
||||||
|
display: block;
|
||||||
|
background: #f6f7f7;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 8px 0 15px 0;
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
word-wrap: break-word;
|
||||||
|
color: #c92c2c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-box ul {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-box ul li {
|
||||||
|
margin: 5px 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-box p {
|
||||||
|
margin: 10px 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-box p strong {
|
||||||
|
color: #1d2327;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form styles */
|
||||||
|
.whmcs-pricing-admin .form-table th {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-admin input[type="text"],
|
||||||
|
.whmcs-pricing-admin input[type="password"],
|
||||||
|
.whmcs-pricing-admin input[type="number"],
|
||||||
|
.whmcs-pricing-admin select {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-admin .description {
|
||||||
|
color: #646970;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Settings sections */
|
||||||
|
.whmcs-pricing-admin h2 {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 2px solid #2271b1;
|
||||||
|
color: #1d2327;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
.whmcs-pricing-admin .submit {
|
||||||
|
padding-top: 0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media screen and (max-width: 960px) {
|
||||||
|
.whmcs-pricing-container {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-sidebar {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-pricing-admin input[type="text"],
|
||||||
|
.whmcs-pricing-admin input[type="password"],
|
||||||
|
.whmcs-pricing-admin input[type="number"],
|
||||||
|
.whmcs-pricing-admin select {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success/Error messages */
|
||||||
|
.whmcs-pricing-admin .notice {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Help text */
|
||||||
|
.whmcs-pricing-admin .form-table td p:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,551 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Admin Settings
|
||||||
|
*
|
||||||
|
* Handles plugin settings page in WordPress admin
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Exit if accessed directly
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WHMCS_Pricing_Admin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single instance
|
||||||
|
*/
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get instance
|
||||||
|
*/
|
||||||
|
public static function get_instance() {
|
||||||
|
if (null === self::$instance) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
private function __construct() {
|
||||||
|
add_action('admin_menu', array($this, 'add_admin_menu'));
|
||||||
|
add_action('admin_init', array($this, 'register_settings'));
|
||||||
|
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_styles'));
|
||||||
|
add_action('admin_post_whmcs_clear_cache', array($this, 'handle_clear_cache'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add admin menu
|
||||||
|
*/
|
||||||
|
public function add_admin_menu() {
|
||||||
|
add_options_page(
|
||||||
|
__('WHMCS Pricing Settings', 'whmcs-pricing'),
|
||||||
|
__('WHMCS Pricing', 'whmcs-pricing'),
|
||||||
|
'manage_options',
|
||||||
|
'whmcs-pricing',
|
||||||
|
array($this, 'render_settings_page')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register settings
|
||||||
|
*/
|
||||||
|
public function register_settings() {
|
||||||
|
register_setting(
|
||||||
|
'whmcs_pricing_settings',
|
||||||
|
'whmcs_pricing_settings',
|
||||||
|
array($this, 'sanitize_settings')
|
||||||
|
);
|
||||||
|
|
||||||
|
// API Settings Section
|
||||||
|
add_settings_section(
|
||||||
|
'whmcs_pricing_api_section',
|
||||||
|
__('WHMCS API Configuration', 'whmcs-pricing'),
|
||||||
|
array($this, 'render_api_section'),
|
||||||
|
'whmcs-pricing'
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'api_url',
|
||||||
|
__('WHMCS URL', 'whmcs-pricing'),
|
||||||
|
array($this, 'render_text_field'),
|
||||||
|
'whmcs-pricing',
|
||||||
|
'whmcs_pricing_api_section',
|
||||||
|
array(
|
||||||
|
'name' => 'api_url',
|
||||||
|
'placeholder' => 'https://your-whmcs-domain.com',
|
||||||
|
'description' => __('Your WHMCS installation URL (without /includes/api.php)', 'whmcs-pricing')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'api_identifier',
|
||||||
|
__('API Identifier', 'whmcs-pricing'),
|
||||||
|
array($this, 'render_text_field'),
|
||||||
|
'whmcs-pricing',
|
||||||
|
'whmcs_pricing_api_section',
|
||||||
|
array(
|
||||||
|
'name' => 'api_identifier',
|
||||||
|
'description' => __('Your WHMCS API identifier', 'whmcs-pricing')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'api_secret',
|
||||||
|
__('API Secret', 'whmcs-pricing'),
|
||||||
|
array($this, 'render_password_field'),
|
||||||
|
'whmcs-pricing',
|
||||||
|
'whmcs_pricing_api_section',
|
||||||
|
array(
|
||||||
|
'name' => 'api_secret',
|
||||||
|
'description' => __('Your WHMCS API secret', 'whmcs-pricing')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Display Settings Section
|
||||||
|
add_settings_section(
|
||||||
|
'whmcs_pricing_display_section',
|
||||||
|
__('Display Settings', 'whmcs-pricing'),
|
||||||
|
array($this, 'render_display_section'),
|
||||||
|
'whmcs-pricing'
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'default_currency',
|
||||||
|
__('Default Currency', 'whmcs-pricing'),
|
||||||
|
array($this, 'render_currency_field'),
|
||||||
|
'whmcs-pricing',
|
||||||
|
'whmcs_pricing_display_section',
|
||||||
|
array(
|
||||||
|
'name' => 'default_currency',
|
||||||
|
'description' => __('Default currency for pricing display', 'whmcs-pricing')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'show_decimals',
|
||||||
|
__('Show Decimals', 'whmcs-pricing'),
|
||||||
|
array($this, 'render_checkbox_field'),
|
||||||
|
'whmcs-pricing',
|
||||||
|
'whmcs_pricing_display_section',
|
||||||
|
array(
|
||||||
|
'name' => 'show_decimals',
|
||||||
|
'label' => __('Display prices with decimal places (e.g., €67.00 instead of €67)', 'whmcs-pricing')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Performance Settings Section
|
||||||
|
add_settings_section(
|
||||||
|
'whmcs_pricing_performance_section',
|
||||||
|
__('Performance Settings', 'whmcs-pricing'),
|
||||||
|
array($this, 'render_performance_section'),
|
||||||
|
'whmcs-pricing'
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'cache_duration',
|
||||||
|
__('Cache Duration', 'whmcs-pricing'),
|
||||||
|
array($this, 'render_number_field'),
|
||||||
|
'whmcs-pricing',
|
||||||
|
'whmcs_pricing_performance_section',
|
||||||
|
array(
|
||||||
|
'name' => 'cache_duration',
|
||||||
|
'min' => 300,
|
||||||
|
'max' => 86400,
|
||||||
|
'step' => 300,
|
||||||
|
'description' => __('How long to cache API responses (in seconds). Default: 3600 (1 hour)', 'whmcs-pricing')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Advanced Settings Section
|
||||||
|
add_settings_section(
|
||||||
|
'whmcs_pricing_advanced_section',
|
||||||
|
__('Advanced Settings', 'whmcs-pricing'),
|
||||||
|
array($this, 'render_advanced_section'),
|
||||||
|
'whmcs-pricing'
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'debug_mode',
|
||||||
|
__('Debug Mode', 'whmcs-pricing'),
|
||||||
|
array($this, 'render_checkbox_field'),
|
||||||
|
'whmcs-pricing',
|
||||||
|
'whmcs_pricing_advanced_section',
|
||||||
|
array(
|
||||||
|
'name' => 'debug_mode',
|
||||||
|
'label' => __('Enable debug logging and disable cache', 'whmcs-pricing')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render settings page
|
||||||
|
*/
|
||||||
|
public function render_settings_page() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show success message
|
||||||
|
if (isset($_GET['settings-updated'])) {
|
||||||
|
add_settings_error(
|
||||||
|
'whmcs_pricing_messages',
|
||||||
|
'whmcs_pricing_message',
|
||||||
|
__('Settings saved successfully.', 'whmcs-pricing'),
|
||||||
|
'updated'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_GET['cache-cleared'])) {
|
||||||
|
add_settings_error(
|
||||||
|
'whmcs_pricing_messages',
|
||||||
|
'whmcs_pricing_message',
|
||||||
|
__('Cache cleared successfully.', 'whmcs-pricing'),
|
||||||
|
'updated'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
settings_errors('whmcs_pricing_messages');
|
||||||
|
?>
|
||||||
|
<div class="wrap whmcs-pricing-admin">
|
||||||
|
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||||
|
|
||||||
|
<div class="whmcs-pricing-container">
|
||||||
|
<div class="whmcs-pricing-main">
|
||||||
|
<form action="options.php" method="post">
|
||||||
|
<?php
|
||||||
|
settings_fields('whmcs_pricing_settings');
|
||||||
|
do_settings_sections('whmcs-pricing');
|
||||||
|
submit_button(__('Save Settings', 'whmcs-pricing'));
|
||||||
|
?>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
|
||||||
|
<?php wp_nonce_field('whmcs_clear_cache', 'whmcs_cache_nonce'); ?>
|
||||||
|
<input type="hidden" name="action" value="whmcs_clear_cache">
|
||||||
|
<?php submit_button(__('Clear All Cache', 'whmcs-pricing'), 'secondary', 'clear_cache', false); ?>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="whmcs-pricing-sidebar">
|
||||||
|
<div class="whmcs-pricing-box">
|
||||||
|
<h3 class="whmcs-toggle" style="cursor: pointer;">
|
||||||
|
<?php _e('Shortcode Usage', 'whmcs-pricing'); ?> <span style="float: right;">▲</span>
|
||||||
|
</h3>
|
||||||
|
<p class="whmcs-preview" style="margin: 10px 0; color: #666; font-size: 13px; display: none;">
|
||||||
|
<?php _e('Expand to view shortcode examples and parameters...', 'whmcs-pricing'); ?>
|
||||||
|
</p>
|
||||||
|
<div class="whmcs-toggle-content">
|
||||||
|
<p><?php _e('Use these shortcodes in your posts, pages, or Elementor:', 'whmcs-pricing'); ?></p>
|
||||||
|
|
||||||
|
<h4><?php _e('Basic Usage:', 'whmcs-pricing'); ?></h4>
|
||||||
|
<code>[whmcs_price product_id="123"]</code>
|
||||||
|
|
||||||
|
<h4><?php _e('With Billing Cycle:', 'whmcs-pricing'); ?></h4>
|
||||||
|
<code>[whmcs_price product_id="123" billing_cycle="monthly"]</code>
|
||||||
|
|
||||||
|
<h4><?php _e('With Currency:', 'whmcs-pricing'); ?></h4>
|
||||||
|
<code>[whmcs_price product_id="123" currency="EUR"]</code>
|
||||||
|
|
||||||
|
<h4><?php _e('Without Decimals:', 'whmcs-pricing'); ?></h4>
|
||||||
|
<code>[whmcs_price product_id="123" format="no_decimals"]</code>
|
||||||
|
|
||||||
|
<h4><?php _e('With Language:', 'whmcs-pricing'); ?></h4>
|
||||||
|
<code>[whmcs_price product_id="123" lang="dutch"]</code>
|
||||||
|
|
||||||
|
<h4><?php _e('Available Billing Cycles:', 'whmcs-pricing'); ?></h4>
|
||||||
|
<ul>
|
||||||
|
<li>monthly</li>
|
||||||
|
<li>quarterly</li>
|
||||||
|
<li>semiannually</li>
|
||||||
|
<li>annually</li>
|
||||||
|
<li>biennially</li>
|
||||||
|
<li>triennially</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="whmcs-pricing-box">
|
||||||
|
<h3 class="whmcs-toggle" style="cursor: pointer;">
|
||||||
|
<?php _e('CSS Styling Guide', 'whmcs-pricing'); ?> <span style="float: right;">▼</span>
|
||||||
|
</h3>
|
||||||
|
<p class="whmcs-preview" style="margin: 10px 0; color: #666; font-size: 13px;">
|
||||||
|
<?php _e('Expand to view CSS examples and styling options...', 'whmcs-pricing'); ?>
|
||||||
|
</p>
|
||||||
|
<div class="whmcs-toggle-content" style="display: none;">
|
||||||
|
<p><?php _e('Add custom CSS to style your prices. Go to Appearance > Customize > Additional CSS:', 'whmcs-pricing'); ?></p>
|
||||||
|
|
||||||
|
<h4><?php _e('HTML Structure:', 'whmcs-pricing'); ?></h4>
|
||||||
|
<code style="white-space: pre-wrap;"><span class="whmcs-price">
|
||||||
|
<span class="currency-symbol">€</span>
|
||||||
|
<span class="price-amount">67.00</span>
|
||||||
|
<span class="billing-cycle">per month</span>
|
||||||
|
</span></code>
|
||||||
|
|
||||||
|
<h4><?php _e('Example CSS:', 'whmcs-pricing'); ?></h4>
|
||||||
|
<code style="white-space: pre-wrap;">.whmcs-price {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2271b1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-price .price-amount {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whmcs-price .billing-cycle {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}</code>
|
||||||
|
|
||||||
|
<h4><?php _e('Using Custom Class:', 'whmcs-pricing'); ?></h4>
|
||||||
|
<code>[whmcs_price product_id="123" class="featured"]</code>
|
||||||
|
<p style="margin-top: 8px; font-size: 13px;"><?php _e('Then style with: .whmcs-price.featured { }', 'whmcs-pricing'); ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="whmcs-pricing-box">
|
||||||
|
<h3><?php _e('Plugin Information', 'whmcs-pricing'); ?></h3>
|
||||||
|
<p><strong><?php _e('Version:', 'whmcs-pricing'); ?></strong> <?php echo WHMCS_PRICING_VERSION; ?></p>
|
||||||
|
<p><strong><?php _e('Author:', 'whmcs-pricing'); ?></strong> <a href="https://arkhost.com" target="_blank" rel="noopener noreferrer">ArkHost</a></p>
|
||||||
|
<p><strong><?php _e('Sponsored by:', 'whmcs-pricing'); ?></strong> <a href="https://duplika.com" target="_blank" rel="noopener noreferrer">Duplika</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
$('.whmcs-toggle').on('click', function() {
|
||||||
|
var clickedBox = $(this).parent();
|
||||||
|
var clickedPreview = clickedBox.find('.whmcs-preview');
|
||||||
|
var clickedContent = clickedBox.find('.whmcs-toggle-content');
|
||||||
|
var clickedArrow = $(this).find('span');
|
||||||
|
|
||||||
|
// Close all other boxes first
|
||||||
|
$('.whmcs-pricing-box').not(clickedBox).each(function() {
|
||||||
|
var otherPreview = $(this).find('.whmcs-preview');
|
||||||
|
var otherContent = $(this).find('.whmcs-toggle-content');
|
||||||
|
var otherArrow = $(this).find('.whmcs-toggle span');
|
||||||
|
|
||||||
|
if (otherContent.is(':visible')) {
|
||||||
|
otherPreview.slideDown(200);
|
||||||
|
otherContent.slideUp(200);
|
||||||
|
otherArrow.html('▼');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle clicked box
|
||||||
|
clickedPreview.slideToggle(200);
|
||||||
|
clickedContent.slideToggle(200);
|
||||||
|
|
||||||
|
if (clickedContent.is(':visible')) {
|
||||||
|
clickedArrow.html('▲');
|
||||||
|
} else {
|
||||||
|
clickedArrow.html('▼');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Section callbacks
|
||||||
|
*/
|
||||||
|
public function render_api_section() {
|
||||||
|
echo '<p>' . esc_html__('Configure your WHMCS API credentials. You can generate API credentials in WHMCS Admin > System Settings > API Credentials.', 'whmcs-pricing') . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_display_section() {
|
||||||
|
echo '<p>' . esc_html__('Configure how prices are displayed on your website.', 'whmcs-pricing') . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_performance_section() {
|
||||||
|
echo '<p>' . esc_html__('Optimize plugin performance with caching.', 'whmcs-pricing') . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_advanced_section() {
|
||||||
|
echo '<p>' . esc_html__('Advanced options for troubleshooting and development.', 'whmcs-pricing') . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field rendering methods
|
||||||
|
*/
|
||||||
|
public function render_text_field($args) {
|
||||||
|
$settings = get_option('whmcs_pricing_settings', array());
|
||||||
|
$value = isset($settings[$args['name']]) ? $settings[$args['name']] : '';
|
||||||
|
$placeholder = isset($args['placeholder']) ? $args['placeholder'] : '';
|
||||||
|
?>
|
||||||
|
<input type="text"
|
||||||
|
name="whmcs_pricing_settings[<?php echo esc_attr($args['name']); ?>]"
|
||||||
|
value="<?php echo esc_attr($value); ?>"
|
||||||
|
placeholder="<?php echo esc_attr($placeholder); ?>"
|
||||||
|
class="regular-text">
|
||||||
|
<?php if (isset($args['description'])): ?>
|
||||||
|
<p class="description"><?php echo esc_html($args['description']); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_password_field($args) {
|
||||||
|
$settings = get_option('whmcs_pricing_settings', array());
|
||||||
|
$value = isset($settings[$args['name']]) ? $settings[$args['name']] : '';
|
||||||
|
?>
|
||||||
|
<input type="password"
|
||||||
|
name="whmcs_pricing_settings[<?php echo esc_attr($args['name']); ?>]"
|
||||||
|
value="<?php echo esc_attr($value); ?>"
|
||||||
|
class="regular-text">
|
||||||
|
<?php if (isset($args['description'])): ?>
|
||||||
|
<p class="description"><?php echo esc_html($args['description']); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_number_field($args) {
|
||||||
|
$settings = get_option('whmcs_pricing_settings', array());
|
||||||
|
$value = isset($settings[$args['name']]) ? $settings[$args['name']] : '';
|
||||||
|
$min = isset($args['min']) ? $args['min'] : 0;
|
||||||
|
$max = isset($args['max']) ? $args['max'] : 100000;
|
||||||
|
$step = isset($args['step']) ? $args['step'] : 1;
|
||||||
|
?>
|
||||||
|
<input type="number"
|
||||||
|
name="whmcs_pricing_settings[<?php echo esc_attr($args['name']); ?>]"
|
||||||
|
value="<?php echo esc_attr($value); ?>"
|
||||||
|
min="<?php echo esc_attr($min); ?>"
|
||||||
|
max="<?php echo esc_attr($max); ?>"
|
||||||
|
step="<?php echo esc_attr($step); ?>"
|
||||||
|
class="regular-text">
|
||||||
|
<?php if (isset($args['description'])): ?>
|
||||||
|
<p class="description"><?php echo esc_html($args['description']); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_checkbox_field($args) {
|
||||||
|
$settings = get_option('whmcs_pricing_settings', array());
|
||||||
|
$checked = isset($settings[$args['name']]) && $settings[$args['name']] ? 'checked' : '';
|
||||||
|
?>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox"
|
||||||
|
name="whmcs_pricing_settings[<?php echo esc_attr($args['name']); ?>]"
|
||||||
|
value="1"
|
||||||
|
<?php echo $checked; ?>>
|
||||||
|
<?php echo esc_html($args['label']); ?>
|
||||||
|
</label>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_currency_field($args) {
|
||||||
|
$settings = get_option('whmcs_pricing_settings', array());
|
||||||
|
$value = isset($settings[$args['name']]) ? $settings[$args['name']] : 'USD';
|
||||||
|
|
||||||
|
$currencies = array(
|
||||||
|
'USD' => 'USD - US Dollar ($)',
|
||||||
|
'EUR' => 'EUR - Euro (€)',
|
||||||
|
'GBP' => 'GBP - British Pound (£)',
|
||||||
|
'AUD' => 'AUD - Australian Dollar (A$)',
|
||||||
|
'CAD' => 'CAD - Canadian Dollar (C$)',
|
||||||
|
'JPY' => 'JPY - Japanese Yen (¥)',
|
||||||
|
'CNY' => 'CNY - Chinese Yuan (¥)',
|
||||||
|
'INR' => 'INR - Indian Rupee (₹)',
|
||||||
|
'BRL' => 'BRL - Brazilian Real (R$)',
|
||||||
|
'RUB' => 'RUB - Russian Ruble (₽)',
|
||||||
|
'CHF' => 'CHF - Swiss Franc (Fr)',
|
||||||
|
'SEK' => 'SEK - Swedish Krona (kr)',
|
||||||
|
'NOK' => 'NOK - Norwegian Krone (kr)',
|
||||||
|
'DKK' => 'DKK - Danish Krone (kr)',
|
||||||
|
'PLN' => 'PLN - Polish Zloty (zł)',
|
||||||
|
'THB' => 'THB - Thai Baht (฿)',
|
||||||
|
'MYR' => 'MYR - Malaysian Ringgit (RM)',
|
||||||
|
'SGD' => 'SGD - Singapore Dollar (S$)',
|
||||||
|
'NZD' => 'NZD - New Zealand Dollar (NZ$)',
|
||||||
|
'MXN' => 'MXN - Mexican Peso (Mex$)',
|
||||||
|
'ZAR' => 'ZAR - South African Rand (R)'
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
<select name="whmcs_pricing_settings[<?php echo esc_attr($args['name']); ?>]" class="regular-text">
|
||||||
|
<?php foreach ($currencies as $code => $label): ?>
|
||||||
|
<option value="<?php echo esc_attr($code); ?>" <?php selected($value, $code); ?>>
|
||||||
|
<?php echo esc_html($label); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<?php if (isset($args['description'])): ?>
|
||||||
|
<p class="description"><?php echo esc_html($args['description']); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitize settings
|
||||||
|
*/
|
||||||
|
public function sanitize_settings($input) {
|
||||||
|
$sanitized = array();
|
||||||
|
|
||||||
|
// Sanitize URL
|
||||||
|
if (isset($input['api_url'])) {
|
||||||
|
$sanitized['api_url'] = esc_url_raw($input['api_url']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize text fields
|
||||||
|
$text_fields = array('api_identifier', 'api_secret', 'default_currency');
|
||||||
|
foreach ($text_fields as $field) {
|
||||||
|
if (isset($input[$field])) {
|
||||||
|
$sanitized[$field] = sanitize_text_field($input[$field]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize number
|
||||||
|
if (isset($input['cache_duration'])) {
|
||||||
|
$sanitized['cache_duration'] = absint($input['cache_duration']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize checkboxes
|
||||||
|
$sanitized['show_decimals'] = isset($input['show_decimals']) ? true : false;
|
||||||
|
$sanitized['debug_mode'] = isset($input['debug_mode']) ? true : false;
|
||||||
|
|
||||||
|
return $sanitized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle clear cache action
|
||||||
|
*/
|
||||||
|
public function handle_clear_cache() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_die(__('Unauthorized', 'whmcs-pricing'));
|
||||||
|
}
|
||||||
|
|
||||||
|
check_admin_referer('whmcs_clear_cache', 'whmcs_cache_nonce');
|
||||||
|
|
||||||
|
WHMCS_Pricing_API::clear_all_cache();
|
||||||
|
|
||||||
|
wp_redirect(add_query_arg(
|
||||||
|
array(
|
||||||
|
'page' => 'whmcs-pricing',
|
||||||
|
'cache-cleared' => 'true'
|
||||||
|
),
|
||||||
|
admin_url('options-general.php')
|
||||||
|
));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueue admin styles
|
||||||
|
*/
|
||||||
|
public function enqueue_admin_styles($hook) {
|
||||||
|
if ('settings_page_whmcs-pricing' !== $hook) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_enqueue_style(
|
||||||
|
'whmcs-pricing-admin',
|
||||||
|
WHMCS_PRICING_PLUGIN_URL . 'assets/css/admin.css',
|
||||||
|
array(),
|
||||||
|
WHMCS_PRICING_VERSION
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,316 @@
|
|||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Shortcode Handler
|
||||||
|
*
|
||||||
|
* Registers and processes shortcodes
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Exit if accessed directly
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WHMCS_Pricing_Shortcodes {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single instance
|
||||||
|
*/
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translations cache
|
||||||
|
*/
|
||||||
|
private static $translations = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get instance
|
||||||
|
*/
|
||||||
|
public static function get_instance() {
|
||||||
|
if (null === self::$instance) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
private function __construct() {
|
||||||
|
add_shortcode('whmcs_price', array($this, 'render_price_shortcode'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load translations from JSON file
|
||||||
|
*/
|
||||||
|
private static function load_translations() {
|
||||||
|
if (null !== self::$translations) {
|
||||||
|
return self::$translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
$json_file = WHMCS_PRICING_PLUGIN_DIR . 'languages.json';
|
||||||
|
|
||||||
|
if (file_exists($json_file)) {
|
||||||
|
$json_content = file_get_contents($json_file);
|
||||||
|
self::$translations = json_decode($json_content, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array(self::$translations)) {
|
||||||
|
self::$translations = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render price shortcode
|
||||||
|
*
|
||||||
|
* @param array $atts Shortcode attributes
|
||||||
|
* @return string HTML output
|
||||||
|
*/
|
||||||
|
public function render_price_shortcode($atts) {
|
||||||
|
// Parse attributes
|
||||||
|
$atts = shortcode_atts(array(
|
||||||
|
'product_id' => '',
|
||||||
|
'billing_cycle' => 'monthly',
|
||||||
|
'currency' => '',
|
||||||
|
'format' => 'default',
|
||||||
|
'show_cycle' => 'true',
|
||||||
|
'class' => '',
|
||||||
|
'lang' => 'english',
|
||||||
|
'language' => '' // Support both lang and language
|
||||||
|
), $atts, 'whmcs_price');
|
||||||
|
|
||||||
|
// Validate product ID
|
||||||
|
if (empty($atts['product_id'])) {
|
||||||
|
return $this->render_error(__('Product ID is required', 'whmcs-pricing'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize attributes
|
||||||
|
$product_id = absint($atts['product_id']);
|
||||||
|
$billing_cycle = sanitize_text_field($atts['billing_cycle']);
|
||||||
|
$currency = !empty($atts['currency']) ? sanitize_text_field($atts['currency']) : '';
|
||||||
|
$format = sanitize_text_field($atts['format']);
|
||||||
|
$show_cycle = filter_var($atts['show_cycle'], FILTER_VALIDATE_BOOLEAN);
|
||||||
|
$custom_class = sanitize_html_class($atts['class']);
|
||||||
|
|
||||||
|
// Support both 'lang' and 'language' parameters
|
||||||
|
$lang = !empty($atts['language']) ? sanitize_text_field($atts['language']) : sanitize_text_field($atts['lang']);
|
||||||
|
|
||||||
|
// Get price from API
|
||||||
|
$price_data = WHMCS_Pricing_API::get_price($product_id, array(
|
||||||
|
'billing_cycle' => $billing_cycle,
|
||||||
|
'currency' => $currency,
|
||||||
|
'format' => $format
|
||||||
|
));
|
||||||
|
|
||||||
|
// Check for errors
|
||||||
|
if (is_wp_error($price_data)) {
|
||||||
|
return $this->render_error($price_data->get_error_message());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the price
|
||||||
|
return $this->render_price_html($price_data, $billing_cycle, $product_id, $show_cycle, $custom_class, $lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render price HTML
|
||||||
|
*
|
||||||
|
* @param array $price_data Price data from API
|
||||||
|
* @param string $billing_cycle Billing cycle
|
||||||
|
* @param int $product_id Product ID
|
||||||
|
* @param bool $show_cycle Whether to show billing cycle
|
||||||
|
* @param string $custom_class Custom CSS class
|
||||||
|
* @param string $lang Language code
|
||||||
|
* @return string HTML output
|
||||||
|
*/
|
||||||
|
private function render_price_html($price_data, $billing_cycle, $product_id, $show_cycle, $custom_class, $lang = 'english') {
|
||||||
|
$classes = array('whmcs-price');
|
||||||
|
|
||||||
|
if (!empty($custom_class)) {
|
||||||
|
$classes[] = $custom_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cycle_text = $show_cycle ? $this->get_cycle_text($billing_cycle, $lang) : '';
|
||||||
|
|
||||||
|
$html = sprintf(
|
||||||
|
'<span class="%s" data-product-id="%d" data-cycle="%s">',
|
||||||
|
esc_attr(implode(' ', $classes)),
|
||||||
|
esc_attr($product_id),
|
||||||
|
esc_attr($billing_cycle)
|
||||||
|
);
|
||||||
|
|
||||||
|
$html .= sprintf(
|
||||||
|
'<span class="currency-symbol">%s</span>',
|
||||||
|
esc_html($price_data['symbol'])
|
||||||
|
);
|
||||||
|
|
||||||
|
$html .= sprintf(
|
||||||
|
'<span class="price-amount">%s</span>',
|
||||||
|
esc_html($price_data['formatted'])
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!empty($cycle_text)) {
|
||||||
|
$html .= sprintf(
|
||||||
|
'<span class="billing-cycle">%s</span>',
|
||||||
|
esc_html($cycle_text)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '</span>';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get billing cycle text
|
||||||
|
*
|
||||||
|
* @param string $cycle Billing cycle
|
||||||
|
* @param string $lang Language code
|
||||||
|
* @return string Human-readable cycle text
|
||||||
|
*/
|
||||||
|
private function get_cycle_text($cycle, $lang = 'english') {
|
||||||
|
$cycle = strtolower($cycle);
|
||||||
|
|
||||||
|
// Load translations
|
||||||
|
$translations = self::load_translations();
|
||||||
|
|
||||||
|
// Check if language exists, fallback to English
|
||||||
|
if (!isset($translations[$lang])) {
|
||||||
|
$lang = 'english';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get translated text
|
||||||
|
if (isset($translations[$lang][$cycle])) {
|
||||||
|
return ' ' . $translations[$lang][$cycle];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final fallback to English defaults
|
||||||
|
$defaults = array(
|
||||||
|
'monthly' => 'per month',
|
||||||
|
'quarterly' => 'per quarter',
|
||||||
|
'semiannually' => 'per 6 months',
|
||||||
|
'annually' => 'per year',
|
||||||
|
'biennially' => 'per 2 years',
|
||||||
|
'triennially' => 'per 3 years'
|
||||||
|
);
|
||||||
|
|
||||||
|
return isset($defaults[$cycle]) ? ' ' . $defaults[$cycle] : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render error message
|
||||||
|
*
|
||||||
|
* @param string $message Error message
|
||||||
|
* @return string HTML output
|
||||||
|
*/
|
||||||
|
private function render_error($message) {
|
||||||
|
// Only show errors to administrators
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
return '<span class="whmcs-price whmcs-price-error" style="display:none;"></span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
'<span class="whmcs-price whmcs-price-error" style="color: #dc3232; font-style: italic;">[WHMCS Pricing Error: %s]</span>',
|
||||||
|
esc_html($message)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"english": {
|
||||||
|
"monthly": "per month",
|
||||||
|
"quarterly": "per quarter",
|
||||||
|
"semiannually": "per 6 months",
|
||||||
|
"annually": "per year",
|
||||||
|
"biennially": "per 2 years",
|
||||||
|
"triennially": "per 3 years"
|
||||||
|
},
|
||||||
|
"spanish": {
|
||||||
|
"monthly": "por mes",
|
||||||
|
"quarterly": "por trimestre",
|
||||||
|
"semiannually": "por 6 meses",
|
||||||
|
"annually": "por año",
|
||||||
|
"biennially": "por 2 años",
|
||||||
|
"triennially": "por 3 años"
|
||||||
|
},
|
||||||
|
"portuguese-pt": {
|
||||||
|
"monthly": "por mês",
|
||||||
|
"quarterly": "por trimestre",
|
||||||
|
"semiannually": "por 6 meses",
|
||||||
|
"annually": "por ano",
|
||||||
|
"biennially": "por 2 anos",
|
||||||
|
"triennially": "por 3 anos"
|
||||||
|
},
|
||||||
|
"dutch": {
|
||||||
|
"monthly": "per maand",
|
||||||
|
"quarterly": "per kwartaal",
|
||||||
|
"semiannually": "per 6 maanden",
|
||||||
|
"annually": "per jaar",
|
||||||
|
"biennially": "per 2 jaar",
|
||||||
|
"triennially": "per 3 jaar"
|
||||||
|
},
|
||||||
|
"french": {
|
||||||
|
"monthly": "par mois",
|
||||||
|
"quarterly": "par trimestre",
|
||||||
|
"semiannually": "par 6 mois",
|
||||||
|
"annually": "par an",
|
||||||
|
"biennially": "par 2 ans",
|
||||||
|
"triennially": "par 3 ans"
|
||||||
|
},
|
||||||
|
"german": {
|
||||||
|
"monthly": "pro Monat",
|
||||||
|
"quarterly": "pro Quartal",
|
||||||
|
"semiannually": "pro 6 Monate",
|
||||||
|
"annually": "pro Jahr",
|
||||||
|
"biennially": "pro 2 Jahre",
|
||||||
|
"triennially": "pro 3 Jahre"
|
||||||
|
},
|
||||||
|
"russian": {
|
||||||
|
"monthly": "в месяц",
|
||||||
|
"quarterly": "в квартал",
|
||||||
|
"semiannually": "за 6 месяцев",
|
||||||
|
"annually": "в год",
|
||||||
|
"biennially": "за 2 года",
|
||||||
|
"triennially": "за 3 года"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Plugin Name: WHMCS Pricing
|
||||||
|
* Description: Display WHMCS product pricing via shortcodes. Lightweight, Elementor-compatible.
|
||||||
|
* Version: 1.0
|
||||||
|
* Author: ArkHost
|
||||||
|
* Author URI: https://arkhost.com
|
||||||
|
* Sponsored by: Duplika.com
|
||||||
|
* Text Domain: whmcs-pricing
|
||||||
|
* Domain Path: /languages
|
||||||
|
* Requires at least: 5.0
|
||||||
|
* Requires PHP: 7.4
|
||||||
|
* License: GPL v2 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Exit if accessed directly
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define plugin constants
|
||||||
|
define('WHMCS_PRICING_VERSION', '1.0');
|
||||||
|
define('WHMCS_PRICING_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||||
|
define('WHMCS_PRICING_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||||
|
define('WHMCS_PRICING_PLUGIN_BASENAME', plugin_basename(__FILE__));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main plugin class
|
||||||
|
*/
|
||||||
|
class WHMCS_Pricing_Plugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single instance of the class
|
||||||
|
*/
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get instance
|
||||||
|
*/
|
||||||
|
public static function get_instance() {
|
||||||
|
if (null === self::$instance) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
private function __construct() {
|
||||||
|
$this->load_dependencies();
|
||||||
|
$this->init_hooks();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load required files
|
||||||
|
*/
|
||||||
|
private function load_dependencies() {
|
||||||
|
require_once WHMCS_PRICING_PLUGIN_DIR . 'includes/api-handler.php';
|
||||||
|
require_once WHMCS_PRICING_PLUGIN_DIR . 'includes/shortcodes.php';
|
||||||
|
|
||||||
|
if (is_admin()) {
|
||||||
|
require_once WHMCS_PRICING_PLUGIN_DIR . 'includes/admin-settings.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize hooks
|
||||||
|
*/
|
||||||
|
private function init_hooks() {
|
||||||
|
add_action('plugins_loaded', array($this, 'load_textdomain'));
|
||||||
|
|
||||||
|
// Add settings link on plugins page
|
||||||
|
add_filter('plugin_action_links_' . WHMCS_PRICING_PLUGIN_BASENAME, array($this, 'add_settings_link'));
|
||||||
|
|
||||||
|
// Make author link open in new tab
|
||||||
|
add_filter('plugin_row_meta', array($this, 'modify_plugin_row_meta'), 10, 2);
|
||||||
|
|
||||||
|
// Initialize components
|
||||||
|
if (is_admin()) {
|
||||||
|
WHMCS_Pricing_Admin::get_instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
WHMCS_Pricing_Shortcodes::get_instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add settings link on plugins page
|
||||||
|
*/
|
||||||
|
public function add_settings_link($links) {
|
||||||
|
$settings_link = '<a href="' . esc_url(admin_url('options-general.php?page=whmcs-pricing')) . '">' . __('Settings', 'whmcs-pricing') . '</a>';
|
||||||
|
array_unshift($links, $settings_link);
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify plugin row meta to open author link in new tab
|
||||||
|
*/
|
||||||
|
public function modify_plugin_row_meta($links, $file) {
|
||||||
|
if ($file === WHMCS_PRICING_PLUGIN_BASENAME) {
|
||||||
|
// Add target="_blank" to author link
|
||||||
|
$links = array_map(function($link) {
|
||||||
|
if (strpos($link, 'arkhost.com') !== false) {
|
||||||
|
return str_replace('<a href=', '<a target="_blank" rel="noopener noreferrer" href=', $link);
|
||||||
|
}
|
||||||
|
return $link;
|
||||||
|
}, $links);
|
||||||
|
}
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load plugin textdomain
|
||||||
|
*/
|
||||||
|
public function load_textdomain() {
|
||||||
|
load_plugin_textdomain('whmcs-pricing', false, dirname(WHMCS_PRICING_PLUGIN_BASENAME) . '/languages');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate plugin
|
||||||
|
*/
|
||||||
|
public static function activate() {
|
||||||
|
// Set default options
|
||||||
|
$defaults = array(
|
||||||
|
'api_url' => '',
|
||||||
|
'api_identifier' => '',
|
||||||
|
'api_secret' => '',
|
||||||
|
'cache_duration' => 3600, // 1 hour
|
||||||
|
'default_currency' => 'USD',
|
||||||
|
'show_decimals' => true,
|
||||||
|
'debug_mode' => false
|
||||||
|
);
|
||||||
|
|
||||||
|
add_option('whmcs_pricing_settings', $defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivate plugin
|
||||||
|
*/
|
||||||
|
public static function deactivate() {
|
||||||
|
// Clear all cached data
|
||||||
|
WHMCS_Pricing_API::clear_all_cache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activation hook
|
||||||
|
*/
|
||||||
|
register_activation_hook(__FILE__, array('WHMCS_Pricing_Plugin', 'activate'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivation hook
|
||||||
|
*/
|
||||||
|
register_deactivation_hook(__FILE__, array('WHMCS_Pricing_Plugin', 'deactivate'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize plugin
|
||||||
|
*/
|
||||||
|
function whmcs_pricing_init() {
|
||||||
|
return WHMCS_Pricing_Plugin::get_instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the plugin
|
||||||
|
whmcs_pricing_init();
|
||||||
Reference in New Issue
Block a user