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 / admin / includes / Actions / Sync / ZohoCRMSync.php
commercebird / admin / includes / Actions / Sync Last commit date
ExactOnlineSync.php 5 months ago ZohoCRMSync.php 5 months ago ZohoInventorySync.php 4 months ago index.php 1 year ago
ZohoCRMSync.php
289 lines
1 <?php
2
3 namespace CommerceBird\Admin\Actions\Sync;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 use CMBIRD_API_Handler_Zoho;
10 use CMBIRD_Auth_Zoho;
11 use WP_Error;
12
13 /**
14 * Handles synchronization actions with Zoho CRM, including fetching fields and refreshing tokens.
15 */
16 class ZohoCRMSync {
17
18 /*
19 create API Get call to Zoho CRM to get all custom fields
20 *
21 * @param module $module - module name
22 * @return array | \WP_Error
23 */
24 public static function get_custom_fields( $module ) {
25 $zoho_crm_url = get_option( 'cmbird_zoho_crm_url' );
26 $url = $zoho_crm_url . 'crm/v7/settings/fields?module=' . $module;
27 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
28 $json = $execute_curl_call_handle->execute_curl_call_get( $url );
29 if ( is_wp_error( $json ) ) {
30 return $json;
31 } else {
32 // Parse the response.
33 $parsed_fields = array();
34
35 foreach ( $json->fields as $field ) {
36 if (
37 ! empty( $field->custom_field ) &&
38 ( empty( $field->read_only ) || $field->read_only === false )
39 ) {
40 $parsed_fields[] = array(
41 'id' => $field->id,
42 'apiName' => $field->api_name,
43 'visible' => (bool) $field->visible,
44 'fieldLabel' => $field->field_label,
45 'displayLabel' => $field->display_label,
46 'customField' => true,
47 'dataType' => $field->data_type,
48 );
49 }
50 }
51
52 return $parsed_fields;
53 }
54 }
55
56 /*
57 create API Get call to Zoho CRM to get ALL fields (standard + custom)
58 *
59 * @param module $module - module name
60 * @return array | \WP_Error
61 */
62 public static function get_all_fields( $module ) {
63 $zoho_crm_url = get_option( 'cmbird_zoho_crm_url' );
64 $url = $zoho_crm_url . 'crm/v7/settings/fields?module=' . $module;
65 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
66 $json = $execute_curl_call_handle->execute_curl_call_get( $url );
67 if ( is_wp_error( $json ) ) {
68 return $json;
69 } else {
70 // Parse the response to get ALL fields (both standard and custom).
71 $parsed_fields = array();
72
73 // Fields to exclude for Deals module.
74 $excluded_fields = array( 'Account_Name', 'Contact_Name', 'Stage' );
75
76 foreach ( $json->fields as $field ) {
77 // Include all fields that are not read-only and are visible.
78 // For Deals module, also exclude Account Name, Contact Name and Stage.
79 if ( ( empty( $field->read_only ) || $field->read_only === false ) && $field->visible ) {
80 // Skip excluded fields for Deals module.
81 if ( 'Deals' === $module && in_array( $field->api_name, $excluded_fields ) ) {
82 continue;
83 }
84 $parsed_fields[ $field->api_name ] = $field->field_label;
85 }
86 }
87
88 return $parsed_fields;
89 }
90 }
91
92 /**
93 * Refresh the access token of Zoho CRM.
94 *
95 * @since 1.0.0
96 * @return void | \WP_Error
97 */
98 public static function refresh_token() {
99 // return if there is no access token.
100 if ( empty( get_option( 'cmbird_zoho_crm_access_token' ) ) ) {
101 return;
102 }
103 $zoho_refresh_token = get_option( 'cmbird_zoho_crm_refresh_token' );
104 $zoho_timestamp = get_option( 'cmbird_zoho_crm_timestamp' );
105 $current_time = strtotime( gmdate( 'Y-m-d H:i:s' ) );
106 if ( $zoho_timestamp < $current_time ) {
107 $handlefunction = new CMBIRD_Auth_Zoho();
108 $respo_at_js = $handlefunction->get_zoho_refresh_token( $zoho_refresh_token, 'zoho_crm' );
109
110 if ( is_wp_error( $respo_at_js ) ) {
111 return $respo_at_js;
112 }
113
114 $access_token = '';
115 $expires_in = 0;
116
117 if ( is_array( $respo_at_js ) ) {
118 $access_token = isset( $respo_at_js['access_token'] ) ? (string) $respo_at_js['access_token'] : '';
119 $expires_in = isset( $respo_at_js['expires_in'] ) ? (int) $respo_at_js['expires_in'] : 0;
120 } elseif ( is_object( $respo_at_js ) ) {
121 $access_token = isset( $respo_at_js->access_token ) ? (string) $respo_at_js->access_token : '';
122 $expires_in = isset( $respo_at_js->expires_in ) ? (int) $respo_at_js->expires_in : 0;
123 }
124
125 if ( '' === $access_token ) {
126 return new WP_Error( 403, 'Access denied!' );
127 }
128
129 update_option( 'cmbird_zoho_crm_access_token', $access_token );
130 update_option( 'cmbird_zoho_crm_timestamp', strtotime( gmdate( 'Y-m-d H:i:s' ) ) + $expires_in );
131 }
132 }
133
134 /**
135 * Sync a WooCommerce customer to Zoho CRM as a contact.
136 *
137 * Called via Action Scheduler.
138 *
139 * @param int $customer_id The customer ID to sync.
140 *
141 * @return void
142 */
143 public static function cmbird_zcrm_contact_sync( $customer_id ) {
144 // Refresh token before making API calls.
145 $token_refresh_result = self::refresh_token();
146 if ( is_wp_error( $token_refresh_result ) ) {
147 ( 'Zoho CRM Contact Sync: Token refresh failed for customer ID ' . $customer_id . ': ' . $token_refresh_result->get_error_message() );
148 return;
149 }
150
151 $customer = get_user_by( 'id', $customer_id );
152 if ( ! $customer ) {
153 ( 'Zoho CRM Contact Sync: Customer not found with ID ' . $customer_id );
154 return;
155 }
156
157 // Get billing address data.
158 $billing_address_1 = get_user_meta( $customer_id, 'billing_address_1', true );
159 $billing_address_2 = get_user_meta( $customer_id, 'billing_address_2', true );
160 $mailing_street = trim( $billing_address_1 . ' ' . $billing_address_2 );
161
162 // Format customer data to match Zoho CRM contact fields.
163 $contact_data = array(
164 'First_Name' => ! empty( $customer->first_name ) ? $customer->first_name : '',
165 'Last_Name' => ! empty( $customer->last_name ) ? $customer->last_name : ( ! empty( $customer->display_name ) ? $customer->display_name : 'Customer' ),
166 'Email' => $customer->user_email,
167 );
168
169 // Add optional fields only if they have values.
170 $optional_fields = array(
171 'Phone' => get_user_meta( $customer_id, 'billing_phone', true ),
172 'Mobile' => get_user_meta( $customer_id, 'billing_phone', true ),
173 'Mailing_Street' => $mailing_street,
174 'Mailing_City' => get_user_meta( $customer_id, 'billing_city', true ),
175 'Mailing_State' => get_user_meta( $customer_id, 'billing_state', true ),
176 'Mailing_Zip' => get_user_meta( $customer_id, 'billing_postcode', true ),
177 'Mailing_Country' => get_user_meta( $customer_id, 'billing_country', true ),
178 );
179
180 foreach ( $optional_fields as $field => $value ) {
181 if ( ! empty( $value ) ) {
182 $contact_data[ $field ] = $value;
183 }
184 }
185
186 // Add description with customer ID.
187 $contact_data['Description'] = 'WooCommerce Customer ID: ' . $customer_id;
188
189 // Apply selected contact layout if available.
190 $selected_layout = get_option( 'zcrm_contact_layout', '' );
191 if ( ! empty( $selected_layout ) ) {
192 $contact_data['Layout'] = array( 'id' => $selected_layout );
193 }
194
195 $zoho_crm_url = get_option( 'cmbird_zoho_crm_url' );
196 if ( empty( $zoho_crm_url ) ) {
197 ( 'Zoho CRM Contact Sync: Zoho CRM URL not configured for customer ID ' . $customer_id );
198 return;
199 }
200
201 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
202
203 // Check if customer already has a Zoho CRM contact ID stored.
204 $existing_contact_id = get_user_meta( $customer_id, 'zcrm_contact_id', true );
205
206 // Prepare data in Zoho CRM format.
207 $zoho_data = array( 'data' => array( $contact_data ) );
208
209 if ( ! empty( $existing_contact_id ) ) {
210 // Customer already has a Zoho CRM contact ID, update the existing contact.
211 $contact_id = $existing_contact_id;
212
213 $update_url = $zoho_crm_url . 'crm/v7/Contacts/' . $contact_id;
214 $update_result = $execute_curl_call_handle->execute_curl_call_put( $update_url, $zoho_data );
215
216 if ( is_wp_error( $update_result ) ) {
217 ( 'Zoho CRM Contact Sync: Failed to update existing contact - Customer ID ' . $customer_id . ', Contact ID ' . $contact_id . ': ' . $update_result->get_error_message() );
218 return;
219 }
220
221 ( 'Zoho CRM Contact Sync: Successfully updated contact - Customer ID ' . $customer_id . ', Contact ID ' . $contact_id );
222 } else {
223 // No existing contact ID, need to check if contact exists by email.
224 $email = $customer->user_email;
225 if ( empty( $email ) ) {
226 ( 'Zoho CRM Contact Sync: Customer has no email address - ID ' . $customer_id );
227 return;
228 }
229
230 $search_url = $zoho_crm_url . 'crm/v7/Contacts/search?criteria=(Email:equals:' . urlencode( $email ) . ')';
231 $search_result = $execute_curl_call_handle->execute_curl_call_get( $search_url );
232
233 if ( is_wp_error( $search_result ) ) {
234 ( 'Zoho CRM Contact Sync: Failed to search for existing contact - Customer ID ' . $customer_id . ': ' . $search_result->get_error_message() );
235 return;
236 }
237
238 // Check if contact exists by email - at this point $search_result is not a WP_Error.
239 /** @var object $search_result */
240 if ( isset( $search_result->data ) && ! empty( $search_result->data ) ) {
241 // Contact found by email, update it and store the contact ID.
242 $existing_contact = $search_result->data[0];
243 $contact_id = $existing_contact->id;
244
245 // Store the contact ID for future syncs.
246 update_user_meta( $customer_id, 'zcrm_contact_id', $contact_id );
247
248 $update_url = $zoho_crm_url . 'crm/v7/Contacts/' . $contact_id;
249 $update_result = $execute_curl_call_handle->execute_curl_call_put( $update_url, $zoho_data );
250
251 if ( is_wp_error( $update_result ) ) {
252 ( 'Zoho CRM Contact Sync: Failed to update existing contact found by email - Customer ID ' . $customer_id . ', Contact ID ' . $contact_id . ': ' . $update_result->get_error_message() );
253 return;
254 }
255
256 ( 'Zoho CRM Contact Sync: Successfully updated contact found by email - Customer ID ' . $customer_id . ', Contact ID ' . $contact_id );
257 } else {
258 // Contact does not exist, create a new one.
259 $create_url = $zoho_crm_url . 'crm/v7/Contacts';
260 $create_result = $execute_curl_call_handle->execute_curl_call_post( $create_url, $zoho_data );
261
262 if ( is_wp_error( $create_result ) ) {
263 ( 'Zoho CRM Contact Sync: Failed to create new contact - Customer ID ' . $customer_id . ': ' . $create_result->get_error_message() );
264 return;
265 }
266
267 if (
268 isset( $create_result->data )
269 && is_array( $create_result->data )
270 && isset( $create_result->data[0] )
271 && is_object( $create_result->data[0] )
272 && isset( $create_result->data[0]->details )
273 && is_object( $create_result->data[0]->details )
274 && ! empty( $create_result->data[0]->details->id )
275 ) {
276 $new_contact_id = $create_result->data[0]->details->id;
277
278 // Store the new contact ID for future syncs.
279 update_user_meta( $customer_id, 'zcrm_contact_id', $new_contact_id );
280
281 ( 'Zoho CRM Contact Sync: Successfully created new contact - Customer ID ' . $customer_id . ', Contact ID ' . $new_contact_id );
282 } else {
283 ( 'Zoho CRM Contact Sync: Contact created but ID not returned - Customer ID ' . $customer_id );
284 }
285 }
286 }
287 }
288 }
289