PluginProbe ʕ •ᴥ•ʔ
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). / 2.7.7
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). v2.7.7
3.0.3 3.0.2 3.0.1 trunk 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.3.0 2.3.1 2.3.10 2.3.11 2.3.12 2.3.13 2.3.14 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 2.3.8 2.3.9 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.7.91 2.7.92 2.7.93 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0
commercebird / includes / classes / class-auth-zoho.php
commercebird / includes / classes Last commit date
apis 4 months ago purchase-orders 5 months ago zoho-crm 7 months ago zoho-inventory 4 months ago class-api-handler-zoho.php 7 months ago class-auth-zoho.php 4 months ago class-common.php 7 months ago class-plugin.php 4 months ago class-wc-api.php 5 months ago index.php 1 year ago
class-auth-zoho.php
253 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6 /**
7 * Handles Zoho authentication for CommerceBird.
8 *
9 * Provides methods for obtaining and refreshing Zoho access tokens.
10 *
11 * @since 1.0.0
12 */
13 class CMBIRD_Auth_Zoho {
14
15 /**
16 * Configuration options for Zoho authentication.
17 *
18 * @var array|array[]
19 */
20 private array $config;
21
22 /**
23 * Given a raw domain portion from settings, return the normalized value.
24 *
25 * This ensures the rest of the code only needs to deal with lowercase,
26 * trimmed domain strings. It also protects against unexpected data from
27 * wp_options (users typing spaces or upper‑case characters).
28 *
29 * @param string $domain Raw option value.
30 * @return string Normalized domain (lowercase, trimmed).
31 */
32 public static function normalize_domain( string $domain ): string {
33 return strtolower( trim( $domain ) );
34 }
35
36 /**
37 * Accounts base URL for a given Zoho domain.
38 *
39 * Handles the special "ca" (Canada) case which must use the
40 * zohocloud.ca host instead of the normal zoho.{domain} pattern.
41 *
42 * @param string $domain Domain portion (any case / whitespace allowed).
43 * @return string Base accounts URL without trailing slash.
44 */
45 public static function get_accounts_base_url( string $domain ): string {
46 $norm = self::normalize_domain( $domain );
47 if ( 'ca' === $norm ) {
48 return 'https://accounts.zohocloud.ca';
49 }
50 return 'https://accounts.zoho.' . $norm;
51 }
52
53 /**
54 * Full token endpoint for a given domain.
55 *
56 * @param string $domain Domain portion (any case / whitespace allowed).
57 * @return string OAuth2 token URL.
58 */
59 public static function get_auth_url_for_domain( string $domain ): string {
60 return self::get_accounts_base_url( $domain ) . '/oauth/v2/token';
61 }
62
63 /**
64 * Full authorization redirect endpoint for a given domain.
65 *
66 * @param string $domain Domain portion (any case / whitespace allowed).
67 * @return string OAuth2 auth URL.
68 */
69 public static function get_auth_redirect_url_for_domain( string $domain ): string {
70 return self::get_accounts_base_url( $domain ) . '/oauth/v2/auth';
71 }
72
73 /**
74 * Constructor for the CMBIRD_Auth_Zoho class.
75 *
76 * Sets up the object properties from the WordPress options table.
77 *
78 * @since 1.0.0
79 */
80 public function __construct() {
81 // domain options are normalised via helper so the logic lives in one.
82 // place and tests can cover it directly.
83 $zi_domain = self::normalize_domain( (string) get_option( 'cmbird_zoho_inventory_domain', '' ) );
84 $zcrm_domain = self::normalize_domain( (string) get_option( 'cmbird_zoho_crm_domain', '' ) );
85
86 $config = array(
87
88 'ServiceZI' => array(
89 'OID' => get_option( 'cmbird_zoho_inventory_oid' ),
90 'CLIENTSECRET' => get_option( 'cmbird_zoho_inventory_cs' ),
91 'CLIENTID' => get_option( 'cmbird_zoho_inventory_cid' ),
92 'REDIRECTURL' => get_option( 'cmbird_authorization_redirect_uri' ),
93 'APIURL' => get_option( 'cmbird_zoho_inventory_url' ),
94 'DOMAINNAME' => $zi_domain,
95 'SCOPE' => 'ZohoInventory.FullAccess.all',
96 // 'STATE' => wp_create_nonce('redirect_url'),.
97 'AUTHURL' => self::get_auth_url_for_domain( $zi_domain ),
98
99 ),
100 'ServiceZCRM' => array(
101 'CLIENTSECRET' => get_option( 'cmbird_zoho_crm_cs' ),
102 'CLIENTID' => get_option( 'cmbird_zoho_crm_cid' ),
103 'REDIRECTURL' => get_option( 'cmbird_authorization_redirect_uri' ),
104 'APIURL' => get_option( 'cmbird_zoho_crm_url' ),
105 'DOMAINNAME' => $zcrm_domain,
106 'SCOPE' => 'ZohoCRM.users.ALL,ZohoCRM.bulk.ALL,ZohoCRM.modules.ALL,ZohoCRM.settings.ALL,ZohoCRM.org.ALL,profile.userphoto.READ,ZohoFiles.files.CREATE',
107 // 'STATE' => wp_create_nonce('redirect_url'),.
108 'AUTHURL' => self::get_auth_url_for_domain( $zcrm_domain ),
109 ),
110
111 );
112
113 $this->config = $config;
114 }
115
116 /**
117 * Expose configuration for testing or debugging.
118 *
119 * @return array The internal Zoho auth configuration.
120 */
121 public function get_config(): array {
122 return $this->config;
123 }
124
125 /**
126 * Retrieves the access token for the given Zoho application.
127 *
128 * @param string $code The authorization code from Zoho.
129 * @param string $app_name The name of the Zoho application (e.g. 'zoho_inventory', 'zoho_crm').
130 *
131 * @return array|WP_Error The decoded JSON response from the Zoho API, or a WP_Error object on failure.
132 */
133 public function get_zoho_access_token( $code, $app_name ) {
134
135 $headers = array( 'Content-Type: application/x-www-form-urlencoded' );
136 switch ( $app_name ) {
137 case 'zoho_inventory':
138 $params = array(
139 'code' => $code,
140 'client_id' => $this->config['ServiceZI']['CLIENTID'],
141 'client_secret' => $this->config['ServiceZI']['CLIENTSECRET'],
142 'redirect_uri' => $this->config['ServiceZI']['REDIRECTURL'],
143 'scope' => $this->config['ServiceZI']['SCOPE'],
144 'grant_type' => 'authorization_code',
145 );
146 $url = $this->config['ServiceZI']['AUTHURL'];
147 break;
148 default:
149 $params = array(
150 'code' => $code,
151 'client_id' => $this->config['ServiceZCRM']['CLIENTID'],
152 'client_secret' => $this->config['ServiceZCRM']['CLIENTSECRET'],
153 'redirect_uri' => $this->config['ServiceZCRM']['REDIRECTURL'],
154 'scope' => $this->config['ServiceZCRM']['SCOPE'],
155 'grant_type' => 'authorization_code',
156 );
157 $url = $this->config['ServiceZCRM']['AUTHURL'];
158 break;
159 }
160
161 // Set up the request arguments.
162 $args = array(
163 'headers' => $headers,
164 'body' => $params,
165 'method' => 'POST',
166 );
167
168 // Debug: Log the domain and auth URL being used.
169 if ( function_exists( 'wc_get_logger' ) ) {
170 $logger = wc_get_logger();
171 $domain_label = 'zoho_inventory' === $app_name ? $this->config['ServiceZI']['DOMAINNAME'] : $this->config['ServiceZCRM']['DOMAINNAME'];
172 $logger->debug( 'CommerceBird Auth Debug - App: ' . $app_name . ', Domain: ' . $domain_label . ', Auth URL: ' . $url, array( 'source' => 'commercebird' ) );
173 }
174
175 // Make the request using wp_remote_post().
176 $response = wp_remote_post( $url, $args );
177
178 // Check if the request was successful.
179 if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) {
180 // If successful, get the body of the response.
181 $body = wp_remote_retrieve_body( $response );
182
183 // Decode JSON response.
184 return json_decode( $body, true );
185 } else {
186 // If there was an error, handle it.
187 $error_message = is_wp_error( $response ) ? $response->get_error_message() : 'Unknown error.';
188 if ( function_exists( 'wc_get_logger' ) ) {
189 $logger = wc_get_logger();
190 $logger->error( 'CommerceBird Access Token Error - App: ' . $app_name . ', Error: ' . $error_message, array( 'source' => 'commercebird' ) );
191 }
192 return 'Error: ' . $error_message;
193 }
194 }
195
196
197 /**
198 * Retrieves a new access token for the given app using the refresh token.
199 *
200 * @param string $refresh_token The refresh token to use for the request.
201 * @param string $app_name The name of the app to retrieve the access token for (e.g. 'zoho_inventory', 'zoho_crm').
202 *
203 * @return object | WP_Error The JSON response from the request, or an error message if there was an error.
204 */
205 public function get_zoho_refresh_token( $refresh_token, $app_name ) {
206 $headers = array( 'Content-Type: application/x-www-form-urlencoded' );
207
208 $client_id = 'zoho_inventory' === $app_name ? $this->config['ServiceZI']['CLIENTID'] : $this->config['ServiceZCRM']['CLIENTID'];
209 $client_sec = 'zoho_inventory' === $app_name ? $this->config['ServiceZI']['CLIENTSECRET'] : $this->config['ServiceZCRM']['CLIENTSECRET'];
210 $params = array(
211 'refresh_token' => $refresh_token,
212 'grant_type' => 'refresh_token',
213 'client_id' => $client_id,
214 'client_secret' => $client_sec,
215 );
216 $url = 'zoho_inventory' === $app_name ? $this->config['ServiceZI']['AUTHURL'] : $this->config['ServiceZCRM']['AUTHURL'];
217 // Set up the request arguments.
218 $args = array(
219 'headers' => $headers,
220 'body' => $params,
221 'method' => 'POST',
222 );
223
224 // Debug: Log the domain and auth URL being used for refresh token.
225 if ( function_exists( 'wc_get_logger' ) ) {
226 $logger = wc_get_logger();
227 $domain_label = 'zoho_inventory' === $app_name ? $this->config['ServiceZI']['DOMAINNAME'] : $this->config['ServiceZCRM']['DOMAINNAME'];
228 $logger->debug( 'CommerceBird Refresh Token Debug - App: ' . $app_name . ', Domain: ' . $domain_label . ', Auth URL: ' . $url, array( 'source' => 'commercebird' ) );
229 }
230
231 // Make the request using wp_remote_post().
232 $response = wp_remote_post( $url, $args );
233 // Check if the request was successful.
234 if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) {
235 // If successful, get the body of the response.
236 $body = wp_remote_retrieve_body( $response );
237 // Decode JSON response.
238 return json_decode( $body, true );
239 } else {
240 // If there was an error, handle it.
241 $error_message = is_wp_error( $response ) ? $response->get_error_message() : 'Unknown error.';
242 if ( function_exists( 'wc_get_logger' ) ) {
243 $logger = wc_get_logger();
244 $logger->error( 'CommerceBird Refresh Token Error - App: ' . $app_name . ', Error: ' . $error_message, array( 'source' => 'commercebird' ) );
245 }
246 // return the WP Error object.
247 return new WP_Error( 'zoho_refresh_token_error', $error_message );
248 }
249 }
250 }
251
252 $cmbird_handlefunction = new CMBIRD_Auth_Zoho();
253