fix: Use HVAC_Secure_Storage consistently for Zoho credentials
- Fix Client ID regex to allow lowercase letters - Update HVAC_Zoho_CRM_Auth to use encrypted storage for all operations - Update class-zoho-admin.php to use HVAC_Secure_Storage for credential retrieval - Update OAuth callback to use secure storage for token storage - Update Status.md with blocking production issue (400 Bad Request) Note: Issue persists on production - needs further investigation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
08944d48ee
commit
5a55b78d03
3 changed files with 90 additions and 35 deletions
44
Status.md
44
Status.md
|
|
@ -8,9 +8,49 @@
|
||||||
|
|
||||||
## 🎯 CURRENT SESSION - ZOHO CRM INTEGRATION SETUP (Dec 16, 2025)
|
## 🎯 CURRENT SESSION - ZOHO CRM INTEGRATION SETUP (Dec 16, 2025)
|
||||||
|
|
||||||
### Zoho CRM Integration - Staging Environment
|
### Zoho CRM Integration - Production Issue (BLOCKING)
|
||||||
|
|
||||||
**Objective:** Configure and test Zoho CRM sync implementation for staging environment.
|
**Objective:** Configure and test Zoho CRM sync implementation for production environment.
|
||||||
|
|
||||||
|
**Status:** 🔴 BLOCKING - Credential save hangs on production (400 Bad Request)
|
||||||
|
|
||||||
|
### Active Issue - Credential Save Hanging on Production
|
||||||
|
|
||||||
|
**Problem:** When saving Zoho CRM credentials at `https://upskillhvac.com/wp-admin/admin.php?page=hvac-zoho-sync`, the AJAX request returns a 400 Bad Request error and the form hangs on "Saving...".
|
||||||
|
|
||||||
|
**Console Error:**
|
||||||
|
```
|
||||||
|
POST https://upskillhvac.com/wp-admin/admin-ajax.php 400 (Bad Request)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Investigation Completed:**
|
||||||
|
1. ✅ Fixed Client ID regex to allow lowercase letters (`[A-Z0-9]` → `[A-Za-z0-9]`)
|
||||||
|
2. ✅ Fixed credential storage mismatch - all methods now use `HVAC_Secure_Storage`
|
||||||
|
3. ✅ Updated `HVAC_Zoho_CRM_Auth` class to use encrypted storage consistently
|
||||||
|
4. ✅ Updated OAuth callback to use secure storage
|
||||||
|
5. ✅ Updated test_connection to use secure storage
|
||||||
|
6. ✅ Deployed fixes to production - **Issue persists**
|
||||||
|
|
||||||
|
**Files Modified:**
|
||||||
|
- `includes/admin/class-zoho-admin.php` - Secure storage for credentials, fixed regex
|
||||||
|
- `includes/zoho/class-zoho-crm-auth.php` - All credential operations use HVAC_Secure_Storage
|
||||||
|
|
||||||
|
**Next Steps for Investigation:**
|
||||||
|
1. Check PHP error logs on production server for detailed error
|
||||||
|
2. Test AJAX endpoint directly via curl to isolate frontend vs backend issue
|
||||||
|
3. Verify nonce generation and validation on production
|
||||||
|
4. Check if WAF/security plugin is blocking the request
|
||||||
|
5. Test with browser network tab to see exact request/response
|
||||||
|
|
||||||
|
**Possible Causes:**
|
||||||
|
- Server-side security rules (Cloudflare, ModSecurity) blocking POST to admin-ajax.php
|
||||||
|
- Nonce validation failing due to caching
|
||||||
|
- Plugin conflict on production
|
||||||
|
- HVAC_Secure_Storage encryption key difference between environments
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Zoho CRM Integration - Staging Environment (Working)
|
||||||
|
|
||||||
**Status:** ✅ OAuth Working, Sync Methods Implemented, Dry-Run Tested
|
**Status:** ✅ OAuth Working, Sync Methods Implemented, Dry-Run Tested
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -386,7 +386,7 @@ class HVAC_Zoho_Admin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate Client ID format (should start with "1000.")
|
// Validate Client ID format (should start with "1000.")
|
||||||
if (!preg_match('/^1000\.[A-Z0-9]+$/', $client_id)) {
|
if (!preg_match('/^1000\.[A-Za-z0-9]+$/', $client_id)) {
|
||||||
wp_send_json_error(array('message' => 'Invalid Client ID format. Should start with "1000."'));
|
wp_send_json_error(array('message' => 'Invalid Client ID format. Should start with "1000."'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -557,6 +557,11 @@ class HVAC_Zoho_Admin {
|
||||||
wp_die('OAuth callback missing state parameter. Possible CSRF attack.');
|
wp_die('OAuth callback missing state parameter. Possible CSRF attack.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load secure storage for credential handling
|
||||||
|
if (!class_exists('HVAC_Secure_Storage')) {
|
||||||
|
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-secure-storage.php';
|
||||||
|
}
|
||||||
|
|
||||||
require_once HVAC_PLUGIN_DIR . 'includes/zoho/class-zoho-crm-auth.php';
|
require_once HVAC_PLUGIN_DIR . 'includes/zoho/class-zoho-crm-auth.php';
|
||||||
$auth = new HVAC_Zoho_CRM_Auth();
|
$auth = new HVAC_Zoho_CRM_Auth();
|
||||||
|
|
||||||
|
|
@ -564,9 +569,12 @@ class HVAC_Zoho_Admin {
|
||||||
wp_die('OAuth state validation failed. Please try the authorization again.');
|
wp_die('OAuth state validation failed. Please try the authorization again.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get credentials from WordPress options
|
// Get credentials using secure storage (credentials are stored encrypted)
|
||||||
$client_id = get_option('hvac_zoho_client_id', '');
|
if (!class_exists('HVAC_Secure_Storage')) {
|
||||||
$client_secret = get_option('hvac_zoho_client_secret', '');
|
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-secure-storage.php';
|
||||||
|
}
|
||||||
|
$client_id = HVAC_Secure_Storage::get_credential('hvac_zoho_client_id', '');
|
||||||
|
$client_secret = HVAC_Secure_Storage::get_credential('hvac_zoho_client_secret', '');
|
||||||
|
|
||||||
if (empty($client_id) || empty($client_secret)) {
|
if (empty($client_id) || empty($client_secret)) {
|
||||||
wp_die('OAuth callback error: Missing client credentials. Please configure your Zoho CRM credentials first.');
|
wp_die('OAuth callback error: Missing client credentials. Please configure your Zoho CRM credentials first.');
|
||||||
|
|
@ -609,20 +617,19 @@ class HVAC_Zoho_Admin {
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save tokens
|
// Save tokens using secure storage
|
||||||
update_option('hvac_zoho_access_token', $token_data['access_token']);
|
HVAC_Secure_Storage::store_credential('hvac_zoho_access_token', $token_data['access_token']);
|
||||||
update_option('hvac_zoho_token_expires', time() + ($token_data['expires_in'] ?? 3600));
|
update_option('hvac_zoho_token_expires', time() + ($token_data['expires_in'] ?? 3600));
|
||||||
|
|
||||||
// Refresh token might not be returned on subsequent authorizations
|
// Refresh token might not be returned on subsequent authorizations
|
||||||
if (isset($token_data['refresh_token']) && !empty($token_data['refresh_token'])) {
|
if (isset($token_data['refresh_token']) && !empty($token_data['refresh_token'])) {
|
||||||
update_option('hvac_zoho_refresh_token', $token_data['refresh_token']);
|
HVAC_Secure_Storage::store_credential('hvac_zoho_refresh_token', $token_data['refresh_token']);
|
||||||
} else {
|
} else {
|
||||||
$existing_refresh = get_option('hvac_zoho_refresh_token');
|
$existing_refresh = HVAC_Secure_Storage::get_credential('hvac_zoho_refresh_token', '');
|
||||||
if (empty($existing_refresh)) {
|
if (empty($existing_refresh)) {
|
||||||
// This is critical - we need a refresh token for long-term access
|
// This is critical - we need a refresh token for long-term access
|
||||||
// Store a warning but still complete the flow
|
// Store a warning but still complete the flow
|
||||||
update_option('hvac_zoho_missing_refresh_token', true);
|
update_option('hvac_zoho_missing_refresh_token', true);
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -653,9 +660,12 @@ class HVAC_Zoho_Admin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get credentials from WordPress options
|
// Get credentials using secure storage (credentials are stored encrypted)
|
||||||
$client_id = get_option('hvac_zoho_client_id', '');
|
if (!class_exists('HVAC_Secure_Storage')) {
|
||||||
$client_secret = get_option('hvac_zoho_client_secret', '');
|
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-secure-storage.php';
|
||||||
|
}
|
||||||
|
$client_id = HVAC_Secure_Storage::get_credential('hvac_zoho_client_id', '');
|
||||||
|
$client_secret = HVAC_Secure_Storage::get_credential('hvac_zoho_client_secret', '');
|
||||||
|
|
||||||
// Check configuration before attempting connection
|
// Check configuration before attempting connection
|
||||||
if (empty($client_id)) {
|
if (empty($client_id)) {
|
||||||
|
|
@ -679,7 +689,7 @@ class HVAC_Zoho_Admin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have stored refresh token from previous OAuth
|
// Check if we have stored refresh token from previous OAuth
|
||||||
$stored_refresh_token = get_option('hvac_zoho_refresh_token');
|
$stored_refresh_token = HVAC_Secure_Storage::get_credential('hvac_zoho_refresh_token', '');
|
||||||
|
|
||||||
if (empty($stored_refresh_token)) {
|
if (empty($stored_refresh_token)) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ if (!defined('ABSPATH')) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class HVAC_Zoho_CRM_Auth {
|
class HVAC_Zoho_CRM_Auth {
|
||||||
|
|
||||||
private $client_id;
|
private $client_id;
|
||||||
private $client_secret;
|
private $client_secret;
|
||||||
private $refresh_token;
|
private $refresh_token;
|
||||||
|
|
@ -21,27 +21,32 @@ class HVAC_Zoho_CRM_Auth {
|
||||||
private $access_token;
|
private $access_token;
|
||||||
private $token_expiry;
|
private $token_expiry;
|
||||||
private $last_error = null;
|
private $last_error = null;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
// Load credentials from WordPress options (new approach)
|
// Load secure storage class
|
||||||
$this->client_id = get_option('hvac_zoho_client_id', '');
|
if (!class_exists('HVAC_Secure_Storage')) {
|
||||||
$this->client_secret = get_option('hvac_zoho_client_secret', '');
|
require_once plugin_dir_path(dirname(__FILE__)) . 'class-hvac-secure-storage.php';
|
||||||
$this->refresh_token = get_option('hvac_zoho_refresh_token', '');
|
}
|
||||||
|
|
||||||
|
// Load credentials from WordPress options using secure storage (encrypted)
|
||||||
|
$this->client_id = HVAC_Secure_Storage::get_credential('hvac_zoho_client_id', '');
|
||||||
|
$this->client_secret = HVAC_Secure_Storage::get_credential('hvac_zoho_client_secret', '');
|
||||||
|
$this->refresh_token = HVAC_Secure_Storage::get_credential('hvac_zoho_refresh_token', '');
|
||||||
$this->redirect_uri = get_site_url() . '/oauth/callback';
|
$this->redirect_uri = get_site_url() . '/oauth/callback';
|
||||||
|
|
||||||
// Fallback to config file if options are empty (backward compatibility)
|
// Fallback to config file if options are empty (backward compatibility)
|
||||||
if (empty($this->client_id) || empty($this->client_secret)) {
|
if (empty($this->client_id) || empty($this->client_secret)) {
|
||||||
$config_file = plugin_dir_path(__FILE__) . 'zoho-config.php';
|
$config_file = plugin_dir_path(__FILE__) . 'zoho-config.php';
|
||||||
if (file_exists($config_file)) {
|
if (file_exists($config_file)) {
|
||||||
require_once $config_file;
|
require_once $config_file;
|
||||||
|
|
||||||
$this->client_id = empty($this->client_id) && defined('ZOHO_CLIENT_ID') ? ZOHO_CLIENT_ID : $this->client_id;
|
$this->client_id = empty($this->client_id) && defined('ZOHO_CLIENT_ID') ? ZOHO_CLIENT_ID : $this->client_id;
|
||||||
$this->client_secret = empty($this->client_secret) && defined('ZOHO_CLIENT_SECRET') ? ZOHO_CLIENT_SECRET : $this->client_secret;
|
$this->client_secret = empty($this->client_secret) && defined('ZOHO_CLIENT_SECRET') ? ZOHO_CLIENT_SECRET : $this->client_secret;
|
||||||
$this->refresh_token = empty($this->refresh_token) && defined('ZOHO_REFRESH_TOKEN') ? ZOHO_REFRESH_TOKEN : $this->refresh_token;
|
$this->refresh_token = empty($this->refresh_token) && defined('ZOHO_REFRESH_TOKEN') ? ZOHO_REFRESH_TOKEN : $this->refresh_token;
|
||||||
$this->redirect_uri = defined('ZOHO_REDIRECT_URI') ? ZOHO_REDIRECT_URI : $this->redirect_uri;
|
$this->redirect_uri = defined('ZOHO_REDIRECT_URI') ? ZOHO_REDIRECT_URI : $this->redirect_uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load stored access token from WordPress options
|
// Load stored access token from WordPress options
|
||||||
$this->load_access_token();
|
$this->load_access_token();
|
||||||
}
|
}
|
||||||
|
|
@ -354,31 +359,31 @@ class HVAC_Zoho_CRM_Auth {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save tokens to WordPress options
|
* Save tokens to WordPress options using secure storage
|
||||||
*/
|
*/
|
||||||
private function save_tokens() {
|
private function save_tokens() {
|
||||||
update_option('hvac_zoho_refresh_token', $this->refresh_token);
|
HVAC_Secure_Storage::store_credential('hvac_zoho_refresh_token', $this->refresh_token);
|
||||||
$this->save_access_token();
|
$this->save_access_token();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save access token
|
* Save access token using secure storage
|
||||||
*/
|
*/
|
||||||
private function save_access_token() {
|
private function save_access_token() {
|
||||||
update_option('hvac_zoho_access_token', $this->access_token);
|
HVAC_Secure_Storage::store_credential('hvac_zoho_access_token', $this->access_token);
|
||||||
update_option('hvac_zoho_token_expiry', $this->token_expiry);
|
update_option('hvac_zoho_token_expiry', $this->token_expiry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load access token from WordPress options
|
* Load access token from WordPress options using secure storage
|
||||||
*/
|
*/
|
||||||
private function load_access_token() {
|
private function load_access_token() {
|
||||||
$this->access_token = get_option('hvac_zoho_access_token');
|
$this->access_token = HVAC_Secure_Storage::get_credential('hvac_zoho_access_token', '');
|
||||||
$this->token_expiry = get_option('hvac_zoho_token_expiry', 0);
|
$this->token_expiry = get_option('hvac_zoho_token_expiry', 0);
|
||||||
|
|
||||||
// Load refresh token if not set
|
// Load refresh token if not set
|
||||||
if (!$this->refresh_token) {
|
if (!$this->refresh_token) {
|
||||||
$this->refresh_token = get_option('hvac_zoho_refresh_token');
|
$this->refresh_token = HVAC_Secure_Storage::get_credential('hvac_zoho_refresh_token', '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue