t|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function updateMemberTags( $list_id, $email, $fail_silently = false, $order = null ) { $hash = md5( strtolower( trim( $email ) ) ); $tags = mailchimp_get_user_tags_to_update( $email, $order ); if ( empty( $tags ) ) { return false; } $data = array( 'tags' => $tags, ); mailchimp_debug( 'api.update_member_tags', "Updating {$email}", $data ); try { return $this->post( "lists/$list_id/members/$hash/tags", $data ); } catch ( Exception $e ) { if ( ! $fail_silently ) { throw $e; } } return false; } /** * @param $list_id * @param $email * @param bool $subscribed * @param array $merge_fields * @param array $list_interests * @param null $language * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function updateOrCreate( $list_id, $email, $subscribed = true, $merge_fields = array(), $list_interests = array(), $language = null ) { $hash = md5( strtolower( trim( $email ) ) ); if ( $subscribed === true ) { $status = 'subscribed'; $status_if_new = 'subscribed'; } elseif ( $subscribed === false ) { $status = 'transactional'; $status_if_new = 'pending'; } elseif ( $subscribed === null ) { $status = 'cleaned'; $status_if_new = 'subscribed'; } else { $status = $subscribed; $status_if_new = 'pending'; } $list_interests = apply_filters('mailchimp_sync_user_list_interests', $list_interests, $email); $data = array( 'email_address' => $email, 'status' => $status, 'status_if_new' => $status_if_new, 'merge_fields' => $merge_fields, 'interests' => is_array($list_interests) ? $list_interests : array(), 'language' => $language, ); if ( empty( $data['merge_fields'] ) ) { unset( $data['merge_fields'] ); } if ( empty( $data['interests'] ) ) { unset( $data['interests'] ); } if ( empty( $data['language'] ) ) { unset( $data['language'] ); } $this->validateNaughtyListEmail( $email ); mailchimp_debug( 'api.update_or_create', "Update Or Create {$email}", $data ); $member = $this->put( "lists/$list_id/members/$hash", $data ); /// update this for use with the admin view too. if (!empty($member) && !empty($member['status'])) { $transient = "mailchimp-woocommerce-subscribed.{$list_id}.{$hash}"; \Mailchimp_Woocommerce_DB_Helpers::set_transient( $transient, $member['status'], 60 * 5 ); } return $member; } /** * Subscribe a contact to SMS marketing * * @param string $list_id The audience/list ID * @param string|null $email The contact's email address (used to identify the contact, can be null for email-less checkout) * @param string $sms_phone The SMS phone number (E.164 format preferred) * @param bool $subscribed Whether to subscribe (true) or mark as pending (false) * @param bool $preserve_existing If true, don't change status of already subscribed contacts * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function subscribeSms( $list_id, $email, $sms_phone, $subscribed = true, $preserve_existing = true, $email_status = '0' ) { // Validate and format phone number $sms_phone = $this->formatSmsPhone( $sms_phone ); if ( empty( $sms_phone ) ) { throw new MailChimp_WooCommerce_Error( 'Invalid SMS phone number format' ); } // Build the SMS channel data according to Mailchimp API $sms_channel = array( 'sms_phone' => $sms_phone, 'marketing_consent' => array( 'status' => $subscribed ? 'confirmed' : 'pending', 'source' => 'Mailchimp for Woocommerce', ), ); // Handle email-less checkout (AC10 requirement) if ( empty( $email ) ) { // Use phone number as identifier for email-less subscribers $data = array( 'sms_channel' => $sms_channel, 'update_existing' => true, ); mailchimp_debug( 'api.sms_subscribe', "SMS Subscribe (no email) :: {$sms_phone}", $data ); // For email-less, we use the SMS phone endpoint return $this->post( "audiences/{$list_id}/contacts", $data ); } // Check if we should preserve existing status (AC8 requirement) if ( $preserve_existing && ! $subscribed ) { // Check current status first $current_status = $this->getSmsStatus( $list_id, $email ); if ( $current_status && isset( $current_status['marketing_consent']['status'] ) ) { if ( $current_status['marketing_consent']['status'] === 'confirmed' ) { mailchimp_debug( 'api.sms_subscribe', "Preserving existing SMS subscription for {$email}" ); return $current_status; } } } $email_subscribed = $email_status === 'subscribed' || $email_status === '1'; $data = array( 'email_channel' => [ 'email' => $email, 'marketing_consent' => [ 'status' => $email_subscribed ? 'confirmed' : 'unknown', 'source' => 'Mailchimp for Woocommerce', ] ], 'sms_channel' => $sms_channel, 'update_existing' => true, ); mailchimp_debug( 'api.sms_subscribe', "SMS Subscribe {$email} :: {$sms_phone}", $data ); try { // Use the audiences contacts endpoint for SMS return $this->post( "audiences/{$list_id}/contacts", $data ); } catch ( MailChimp_WooCommerce_Error $e ) { // If contact already exists, try to update instead if ( mailchimp_string_contains( $e->getMessage(), 'already a list member' ) || mailchimp_string_contains( $e->getMessage(), 'is already in the audience' ) ) { return $this->updateSmsConsent( $list_id, $email, $sms_phone, $subscribed, $preserve_existing ); } throw $e; } } /** * Update SMS consent for an existing contact * * @param string $list_id The audience/list ID * @param string $email The contact's email address * @param string $sms_phone The SMS phone number * @param bool $subscribed Whether subscribed or not * @param bool $preserve_existing If true and not subscribing, don't change existing subscribed status * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function updateSmsConsent( $list_id, $email, $sms_phone, $subscribed = true, $preserve_existing = true ) { $sms_phone = $this->formatSmsPhone( $sms_phone ); if ( empty( $sms_phone ) ) { throw new MailChimp_WooCommerce_Error( 'Invalid SMS phone number format' ); } $hash = md5( strtolower( trim( $email ) ) ); // Check if we should preserve existing status (AC8 requirement) if ( $preserve_existing && ! $subscribed ) { $current_status = $this->getSmsStatus( $list_id, $email ); if ( $current_status && isset( $current_status['marketing_consent']['status'] ) ) { if ( $current_status['marketing_consent']['status'] === 'confirmed' ) { mailchimp_debug( 'api.sms_update', "Preserving existing SMS subscription for {$email}" ); return $current_status; } } } $sms_channel = array( 'sms_phone' => $sms_phone, 'marketing_consent' => array( 'status' => $subscribed ? 'confirmed' : 'unsubscribed', 'source' => 'Mailchimp for Woocommerce', ), ); $data = array( 'sms_channel' => $sms_channel, ); mailchimp_debug( 'api.sms_update', "SMS Update {$email} :: {$sms_phone}", $data ); return $this->patch( "lists/{$list_id}/members/{$hash}", $data ); } /** * Unsubscribe a contact from SMS marketing * * @param string $list_id The audience/list ID * @param string $email The contact's email address * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function unsubscribeSms( $list_id, $email ) { $hash = md5( strtolower( trim( $email ) ) ); $data = array( 'sms_channel' => array( 'marketing_consent' => array( 'status' => 'unsubscribed', 'source' => 'Mailchimp for Woocommerce', ), ), ); mailchimp_debug( 'api.sms_unsubscribe', "SMS Unsubscribe {$email}", $data ); return $this->patch( "lists/{$list_id}/members/{$hash}", $data ); } /** * Get SMS subscription status for a contact * * @param string $list_id The audience/list ID * @param string $email The contact's email address * @return array|false SMS data or false if not subscribed * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getSmsStatus( $list_id, $email ) { try { $member = $this->member( $list_id, $email ); if ( isset( $member['sms_channel'] ) ) { return $member['sms_channel']; } return false; } catch ( Exception $e ) { return false; } } /** * Format phone number to E.164 format * * @param string $phone Raw phone number * @return string Formatted phone number or empty string if invalid */ protected function formatSmsPhone( $phone ) { // Remove all non-digit characters except + $phone = preg_replace( '/[^\+\d]/', '', $phone ); // If it doesn't start with +, assume US and add +1 if ( strpos( $phone, '+' ) !== 0 ) { // Remove leading 1 if present, then add +1 $phone = ltrim( $phone, '1' ); if ( strlen( $phone ) === 10 ) { $phone = '+1' . $phone; } else { $phone = '+' . $phone; } } // Validate minimum length (E.164 requires at least 8 digits) $digits = preg_replace( '/[^\d]/', '', $phone ); if ( strlen( $digits ) < 8 || strlen( $digits ) > 15 ) { return ''; } return $phone; } /** * Transient key used for the SMS-program cache for a given list. * Centralised so the cache-read and cache-write paths can't drift apart. */ protected function smsProgramTransientKey($list_id) { return "mailchimp_sms_program_{$list_id}"; } /** * Hydrate an SMS program object from a cached array payload. */ protected function hydrateSmsProgram(array $result) { $program = new MailChimp_WooCommerce_SmsProgram(); $program->program_id = $result['program_id'] ?? null; $program->registration_status = $result['registration_status'] ?? null; $program->program_name = $result['program_name'] ?? null; $program->program_sms_phone_number = $result['program_sms_phone_number'] ?? null; $program->can_send = $result['can_send'] ?? null; return $program; } /** * Fetch the SMS program from the API, cache the outcome, and return it. * * Uses native WP transients so with an object cache drop-in (Redis Object * Cache etc.) the read and write are single operations against Redis * with no wp_options I/O. Without an object cache, this is still two DB * writes (transient + timeout) vs. the ~5 the previous wrapper was * generating per call. * * Single-exit: the cache is written exactly once per invocation, not on * every return branch. * * @param $list_id * @return MailChimp_WooCommerce_SmsProgram */ public function getSmsProgram($list_id) { if (empty($list_id) || !mailchimp_is_configured()) { return new MailChimp_WooCommerce_SmsProgram(); } $transient_key = $this->smsProgramTransientKey($list_id); $payload = array(); // empty = "no program available" $ttl = 60 * 10; // 10 min for a valid program $negative_ttl = 60 * 2; // 2 min for a missing/empty program so // newly-enabled programs are discovered // faster than the positive TTL window. try { $result = $this->get("lists/{$list_id}/sms-program"); } catch (\Throwable $e) { $result = null; } if ( ! empty( $result['sms_program'] ) ) { $first = reset( $result['sms_program'] ); if ( is_array( $first ) && ! empty( $first ) ) { $payload = array( 'program_id' => $first['program_id'] ?? null, 'registration_status' => $first['registration_status'] ?? null, 'program_name' => $first['program_name'] ?? null, 'program_sms_phone_number' => $first['program_sms_phone_number'] ?? null, 'can_send' => $first['can_send'] ?? null, ); } } set_transient( $transient_key, $payload, empty( $payload ) ? $negative_ttl : $ttl ); return $this->hydrateSmsProgram( $payload ); } /** * Cache-first read of the SMS program. Single native get_transient call * — one Redis op on cache hit, no DB. On miss, delegates to * getSmsProgram() which handles both the API fetch and the cache write. * * @param $list_id * @return MailChimp_WooCommerce_SmsProgram */ public function getCachedSmsProgram($list_id) { if ( empty( $list_id ) ) { return new MailChimp_WooCommerce_SmsProgram(); } $cached = get_transient( $this->smsProgramTransientKey( $list_id ) ); // get_transient returns false on miss. An explicitly-cached empty // payload (negative cache) is an empty array — distinguishable from // a miss, so we don't hammer the API when a list legitimately has // no SMS program. if ( $cached === false ) { return $this->getSmsProgram( $list_id ); } return $this->hydrateSmsProgram( is_array( $cached ) ? $cached : array() ); } /** * Check if the merchant has an approved SMS application for the given audience * * @param string $list_id The audience/list ID * @return array|false SMS application status or false if not approved * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getSmsApplicationStatus( $list_id ) { try { // Try to get SMS settings for the audience $result = $this->get( "lists/{$list_id}/sms-program" ); if ( isset( $result['sms_enabled'] ) && $result['sms_enabled'] ) { return array( 'enabled' => true, 'sending_countries' => isset( $result['sending_countries'] ) ? $result['sending_countries'] : array(), 'phone_country' => isset( $result['phone_country'] ) ? $result['phone_country'] : null, ); } return false; } catch ( Exception $e ) { // If 404 or other error, SMS is not enabled mailchimp_debug( 'api.sms_status', "SMS not available for list {$list_id}: " . $e->getMessage() ); return false; } } /** * Get cached SMS application status (cached for 1 hour) * * @param string $list_id The audience/list ID * @return array|false SMS application status or false if not approved */ public function getCachedSmsApplicationStatus( $list_id ) { $transient_key = "mailchimp_sms_status_{$list_id}"; $cached = mailchimp_get_transient( $transient_key ); if ( $cached !== false ) { return $cached; } try { $status = $this->getSmsApplicationStatus( $list_id ); // Cache for 1 hour mailchimp_set_transient( $transient_key, $status !== false ? $status : 'disabled', 3600 ); return $status; } catch ( Exception $e ) { return false; } } /** * Check if a country is in the merchant's SMS sending countries * * @param string $list_id The audience/list ID * @param string $country_code The 2-letter country code to check * @return bool */ public function isSmsSendingCountry( $list_id, $country_code ) { $sms_status = $this->getCachedSmsApplicationStatus( $list_id ); if ( ! $sms_status || empty( $sms_status['sending_countries'] ) ) { return false; } $country_code = strtoupper( $country_code ); return in_array( $country_code, $sms_status['sending_countries'], true ); } /** * @param MailChimp_WooCommerce_CreateListSubmission $submission * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function createList( MailChimp_WooCommerce_CreateListSubmission $submission ) { return $this->post( 'lists', $submission->getSubmission() ); } /** * @param $list_id * @param MailChimp_WooCommerce_CreateListSubmission $submission * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError */ public function updateList( $list_id, MailChimp_WooCommerce_CreateListSubmission $submission ) { return $this->patch( "lists/{$list_id}", $submission->getSubmission() ); } /** * @param false $as_list * @param int $count * * @return array|bool|object * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getLists( $as_list = false, $count = 100 ) { $result = $this->get( 'lists', array( 'count' => $count ) ); if ( ! is_array( $result ) ) { throw new MailChimp_WooCommerce_RateLimitError( 'getting lists api failure, retry again.' ); } if ( $as_list ) { $lists = array(); if ( $result ) { $result = (object) $result; if ( isset( $result->lists ) && is_array( $result->lists ) ) { foreach ( $result->lists as $list ) { $list = (object) $list; $lists[ $list->id ] = $list->name; } } } return $lists; } return $result; } /** * @param $id * * @return bool */ public function hasList( $id ) { try { return (bool) $this->getList( $id ); } catch ( Exception $e ) { return false; } } /** * @param $id * * @return array|bool|object * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getList( $id ) { $result = $this->get( 'lists/' . $id ); if ( ! is_array( $result ) ) { throw new MailChimp_WooCommerce_RateLimitError( 'getting list api failure, retry again.' ); } return $result; } /** * @param $id * * @return bool * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_ServerError */ public function deleteList( $id ) { return (bool) $this->delete( 'lists/' . $id ); } /** * @return array|bool|mixed|object * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getListsWithMergeFields() { $lists = $this->getLists( true ); foreach ( $lists as $id => $name ) { $lists[ $id ] = $this->mergeFields( $id, 100 ); } return $lists; } /** * @param $list_id * @param int $count * * @return array|bool|object * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function mergeFields( $list_id, $count = 10 ) { $result = $this->get( "lists/$list_id/merge-fields", array( 'count' => $count ) ); if ( ! is_array( $result ) ) { throw new MailChimp_WooCommerce_RateLimitError( 'getting merge field api failure, retry again.' ); } return $result; } /** * @param $list_id * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getInterestGroups( $list_id ) { if ( empty( $list_id ) ) { return array(); } return $this->get( "lists/$list_id/interest-categories" ); } /** * @param $list_id * @param $group_id * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getInterestGroupOptions( $list_id, $group_id ) { if ( empty( $list_id ) || empty( $group_id ) ) { return array(); } return $this->get( "lists/$list_id/interest-categories/$group_id/interests" ); } /** * @param $store_id * @param int $page * @param int $count * @param DateTime|null $since * @param null $campaign_id * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function orders( $store_id, $page = 1, $count = 10, DateTime $since = null, $campaign_id = null ) { return $this->get( 'ecommerce/stores/' . $store_id . '/orders', array( 'start' => $page, 'count' => $count, 'offset' => ( $page * $count ), 'since' => ( $since ? $since->format( 'Y-m-d H:i:s' ) : null ), 'cid' => $campaign_id, ) ); } /** * @param $store_id * * @return int|mixed * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getOrderCount( $store_id ) { $data = $this->get( "ecommerce/stores/{$store_id}/orders?count=1" ); if ( ! is_array( $data ) ) { return 0; } return $data['total_items']; } /** * @param $store_id * * @return int|mixed * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getProductCount( $store_id ) { $data = $this->get( "ecommerce/stores/{$store_id}/products?count=1" ); if ( ! is_array( $data ) ) { return 0; } return $data['total_items']; } /** * @param $store_id * * @return int|mixed * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getCustomerCount( $store_id ) { $data = $this->get( "ecommerce/stores/{$store_id}/customers?count=1" ); if ( ! is_array( $data ) ) { return 0; } return $data['total_items']; } /** * @param $store_id * * @return false|MailChimp_WooCommerce_Store */ public function getStoreIfAvailable( $store_id ) { try { return $this->getStore( $store_id, true ); } catch ( Exception $e ) { return false; } } /** * @param $store_id * @param false $throw * * @return false|MailChimp_WooCommerce_Store * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getStore( $store_id, $throw = false ) { try { $data = $this->get( "ecommerce/stores/$store_id" ); if ( ! is_array( $data ) ) { throw new MailChimp_WooCommerce_RateLimitError( 'getting store api failure, retry again.' ); } if ( ! isset( $data['id'] ) || ! isset( $data['name'] ) ) { return false; } $store = new MailChimp_WooCommerce_Store(); return $store->fromArray( $data ); } catch ( MailChimp_WooCommerce_Error $e ) { if ( $throw ) { throw $e; } return false; } catch ( Exception $e ) { if ( $throw ) { throw $e; } return false; } } /** * @param $campaign_id * @param bool $throw_if_invalid * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getCampaign( $campaign_id, $throw_if_invalid = true ) { // don't let an empty campaign ID do anything if ( empty( $campaign_id ) ) { return false; } // if we found the campaign ID already and it's been stored in the cache, return it from the cache instead. if ( ( $data = \Mailchimp_Woocommerce_DB_Helpers::get_transient( 'mailchimp-woocommerce-has-campaign-id-' . $campaign_id ) ) && ! empty( $data ) ) { return $data; } if ( \Mailchimp_Woocommerce_DB_Helpers::get_transient( 'mailchimp-woocommerce-no-campaign-id-' . $campaign_id ) ) { return false; } try { $data = $this->get( "campaigns/$campaign_id" ); \Mailchimp_Woocommerce_DB_Helpers::delete_transient( 'mailchimp-woocommerce-no-campaign-id-' . $campaign_id ); \Mailchimp_Woocommerce_DB_Helpers::set_transient( 'mailchimp-woocommerce-has-campaign-id-' . $campaign_id, $data, 60 * 30 ); return $data; } catch ( Exception $e ) { mailchimp_debug( 'campaign_get.error', 'No campaign with provided ID: ' . $campaign_id . ' :: ' . $e->getMessage() . ' :: in ' . $e->getFile() . ' :: on ' . $e->getLine() ); \Mailchimp_Woocommerce_DB_Helpers::set_transient( 'mailchimp-woocommerce-no-campaign-id-' . $campaign_id, true, 60 * 30 ); if ( ! $throw_if_invalid ) { return false; } throw $e; } } /** * @param $store_id * * @return array|bool|mixed|object|null */ public function checkConnectedSite( $store_id ) { try { return $this->get( "connected-sites/{$store_id}" ); } catch ( MailChimp_WooCommerce_Error $e ) { return false; } catch ( Exception $e ) { return false; } } /** * @param $store_id * * @return array|bool|mixed|object|null */ public function connectSite( $store_id ) { try { return $this->post( "connected-sites/{$store_id}/actions/verify-script-installation", array() ); } catch ( MailChimp_WooCommerce_Error $e ) { return false; } catch ( Exception $e ) { return false; } } /** * @return array|false */ public function stores() { try { $data = $this->get( 'ecommerce/stores', array( 'count' => 1000 ) ); if ( ! isset( $data['stores'] ) || empty( $data['stores'] ) || ! is_array( $data['stores'] ) ) { return array(); } $response = array(); foreach ( $data['stores'] as $store_data ) { $store = new MailChimp_WooCommerce_Store(); $response[] = $store->fromArray( $store_data ); } return $response; } catch ( MailChimp_WooCommerce_Error $e ) { return false; } catch ( Exception $e ) { return false; } } /** * @param $store_id * @param $is_syncing * * @return array|bool|mixed|object|null */ public function flagStoreSync( $store_id, $is_syncing ) { try { // pull the store to make sure we have one. if ( ! ( $store = $this->getStore( $store_id ) ) ) { return false; } // flag it as ^^^ is_syncing ^^^ $store->flagSyncing( $is_syncing ); // patch the store data return $this->patch( "ecommerce/stores/{$store_id}", $store->toArray() ); } catch ( Exception $e ) { mailchimp_log( 'flag.store_sync', $e->getMessage() . ' :: in ' . $e->getFile() . ' :: on ' . $e->getLine() ); } return false; } /** * @param MailChimp_WooCommerce_Store $store * @param bool $silent * * @return false|MailChimp_WooCommerce_Store * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function addStore( MailChimp_WooCommerce_Store $store, $silent = true ) { try { $this->validateStoreSubmission( $store ); $data = $this->post( 'ecommerce/stores', $store->toArray() ); $store = new MailChimp_WooCommerce_Store(); return $store->fromArray( $data ); } catch ( Exception $e ) { if ( ! $silent ) { throw $e; } return false; } } /** * @param MailChimp_WooCommerce_Store $store * @param bool $silent * * @return false|MailChimp_WooCommerce_Store * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError */ public function updateStore( MailChimp_WooCommerce_Store $store, $silent = true ) { try { $this->validateStoreSubmission( $store ); $data = $this->patch( "ecommerce/stores/{$store->getId()}", $store->toArray() ); $store = new MailChimp_WooCommerce_Store(); return $store->fromArray( $data ); } catch ( Exception $e ) { if ( ! $silent ) { throw $e; } return false; } } /** * @param $store_id * * @return bool */ public function deleteStore( $store_id ) { try { return (bool) $this->delete( "ecommerce/stores/$store_id" ); } catch ( MailChimp_WooCommerce_Error $e ) { mailchimp_error( "delete_store {$store_id}", $e->getMessage() ); return false; } catch ( Exception $e ) { mailchimp_error( "delete_store {$store_id}", $e->getMessage() ); return false; } } /** * @param $store_id * @param $customer_id * @param false $throw * * @return false|MailChimp_WooCommerce_Customer * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getCustomer( $store_id, $customer_id, $throw = false ) { try { $data = $this->get( "ecommerce/stores/$store_id/customers/$customer_id" ); if ( ! is_array( $data ) ) { throw new MailChimp_WooCommerce_RateLimitError( 'getting customer api failure, retry again.' ); } $customer = new MailChimp_WooCommerce_Customer(); return $customer->fromArray( $data ); } catch ( MailChimp_WooCommerce_Error $e ) { if ( $throw ) { throw $e; } return false; } } /** * @param MailChimp_WooCommerce_Customer $customer * * @return false|MailChimp_WooCommerce_Customer * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function addCustomer( MailChimp_WooCommerce_Customer $customer ) { if ( ! ( $this->validateStoreSubmission( $customer ) ) ) { return false; } $data = $this->post( 'ecommerce/stores', $customer->toArray() ); if ( ! is_array( $data ) ) { throw new MailChimp_WooCommerce_RateLimitError( 'adding customer api failure, retry again.' ); } $customer = new MailChimp_WooCommerce_Customer(); return $customer->fromArray( $data ); } /** * @param $store_id * @param int $page * @param int $count * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function carts( $store_id, $page = 1, $count = 10 ) { return $this->get( 'ecommerce/stores/' . $store_id . '/carts', array( 'start' => $page, 'count' => $count, 'offset' => ( $page * $count ), ) ); } /** * @param $store_id * @param MailChimp_WooCommerce_Cart $cart * @param bool $silent * * @return false|MailChimp_WooCommerce_Cart * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function addCart( $store_id, MailChimp_WooCommerce_Cart $cart, $silent = true ) { try { $email = $cart->getCustomer()->getEmailAddress(); if ( mailchimp_email_is_privacy_protected( $email ) || mailchimp_email_is_amazon( $email ) ) { return false; } $this->validateNaughtyList( $cart->getCustomer() ); mailchimp_debug( 'api.addCart', "Adding Cart :: {$email}", $data = $cart->toArray() ); $data = $this->post( "ecommerce/stores/$store_id/carts", $data ); $cart = new MailChimp_WooCommerce_Cart(); return $cart->setStoreID( $store_id )->fromArray( $data ); } catch ( MailChimp_WooCommerce_Error $e ) { if ( ! $silent ) { throw $e; } mailchimp_log( 'api.addCart', $e->getMessage() ); return false; } catch ( Exception $e ) { if ( ! $silent ) { throw $e; } return false; } } /** * @param $store_id * @param MailChimp_WooCommerce_Cart $cart * @param bool $silent * * @return false|MailChimp_WooCommerce_Cart * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError */ public function updateCart( $store_id, MailChimp_WooCommerce_Cart $cart, $silent = true ) { try { $email = $cart->getCustomer()->getEmailAddress(); if ( mailchimp_email_is_privacy_protected( $email ) || mailchimp_email_is_amazon( $email ) ) { return false; } $this->validateNaughtyList( $cart->getCustomer() ); mailchimp_debug( 'api.updateCart', "Updating Cart :: {$email}", $data = $cart->toArrayForUpdate() ); $data = $this->patch( "ecommerce/stores/$store_id/carts/{$cart->getId()}", $data ); $cart = new MailChimp_WooCommerce_Cart(); return $cart->setStoreID( $store_id )->fromArray( $data ); } catch ( MailChimp_WooCommerce_Error $e ) { if ( ! $silent ) { throw $e; } mailchimp_log( 'api.updateCart', $e->getMessage() ); return false; } catch ( Exception $e ) { if ( ! $silent ) { throw $e; } return false; } } /** * @param $store_id * @param $id * * @return false|MailChimp_WooCommerce_Cart */ public function getCart( $store_id, $id ) { try { $data = $this->get( "ecommerce/stores/$store_id/carts/$id" ); $cart = new MailChimp_WooCommerce_Cart(); return $cart->setStoreID( $store_id )->fromArray( $data ); } catch ( MailChimp_WooCommerce_Error $e ) { return false; } catch ( Exception $e ) { return false; } } /** * @param $store_id * @param $id * * @return bool */ public function deleteCartByID( $store_id, $id ) { try { return (bool) $this->delete( "ecommerce/stores/$store_id/carts/$id" ); } catch ( MailChimp_WooCommerce_Error $e ) { return false; } catch ( Exception $e ) { return false; } } /** * @param $store_id * @param MailChimp_WooCommerce_Customer $customer * @param bool $silent * * @return false|MailChimp_WooCommerce_Customer * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError */ public function updateCustomer( $store_id, MailChimp_WooCommerce_Customer $customer, $silent = true ) { try { if ( ! $this->validateStoreSubmission( $customer ) ) { return false; } $data = $this->put( "ecommerce/stores/$store_id/customers/{$customer->getId()}", $customer->toArray() ); $customer = new MailChimp_WooCommerce_Customer(); return $customer->fromArray( $data ); } catch ( Exception $e ) { mailchimp_log('api', "error updating customer {$customer->getId()} - {$e->getMessage()}"); if ( ! $silent ) { throw $e; } return false; } } /** * @param $store_id * @param $customer_id * @return bool */ public function deleteCustomer( $store_id, $customer_id ) { try { return (bool) $this->delete( "ecommerce/stores/$store_id/customers/$customer_id" ); } catch ( Exception $e ) { return false; } } /** * @param $store_id * @param MailChimp_WooCommerce_Order $order * @param bool $silent * * @return false|MailChimp_WooCommerce_Order * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function addStoreOrder( $store_id, MailChimp_WooCommerce_Order $order, $silent = true ) { try { if ( ! $this->validateStoreSubmission( $order ) ) { return false; } // submit the first one $data = $this->post( "ecommerce/stores/$store_id/orders", $order->toArray() ); $email_address = $order->getCustomer()->getEmailAddress(); // if the order is in pending status, we need to submit the order again with a paid status. if ( $order->shouldConfirmAndPay() && $order->getFinancialStatus() !== 'paid' ) { $order->setFinancialStatus( 'paid' ); $data = $this->patch( "ecommerce/stores/{$store_id}/orders/{$order->getId()}", $order->toArray() ); } // update the member tags but fail silently just in case. $this->updateMemberTags( mailchimp_get_list_id(), $email_address, true, $order ); \Mailchimp_Woocommerce_DB_Helpers::update_option( 'mailchimp-woocommerce-resource-last-updated', time() ); $order = new MailChimp_WooCommerce_Order(); return $order->fromArray( $data ); } catch ( Exception $e ) { if ( ! $silent ) { throw $e; } mailchimp_log( 'api.add_order.error', $e->getMessage(), array( 'submission' => $order->toArray() ) ); return false; } } /** * @param $store_id * @param MailChimp_WooCommerce_Order $order * @param bool $silent * * @return false|MailChimp_WooCommerce_Order * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError */ public function updateStoreOrder( $store_id, MailChimp_WooCommerce_Order $order, $silent = true ) { try { if ( ! $this->validateStoreSubmission( $order ) ) { return false; } $order_id = $order->getId(); $data = $this->patch( "ecommerce/stores/{$store_id}/orders/{$order_id}", $order->toArray() ); // update user tags $email_address = $order->getCustomer()->getEmailAddress(); // if products list differs, we should remove the old products and add new ones $data_lines = $data['lines']; $order_lines = $order->getLinesIds(); foreach ( $data_lines as $line ) { if ( ! in_array( $line['id'], $order_lines ) ) { $this->deleteStoreOrderLine( $store_id, $order_id, $line['id'] ); } } // if the order is in pending status, we need to submit the order again with a paid status. if ( $order->shouldConfirmAndPay() && $order->getFinancialStatus() !== 'paid' ) { $order->setFinancialStatus( 'paid' ); $data = $this->patch( "ecommerce/stores/{$store_id}/orders/{$order_id}", $order->toArray() ); } // update the member tags but fail silently just in case. $this->updateMemberTags( mailchimp_get_list_id(), $email_address, true, $order ); $order = new MailChimp_WooCommerce_Order(); return $order->fromArray( $data ); } catch ( Exception $e ) { if ( ! $silent ) { throw $e; } mailchimp_log( 'api.update_order.error', $e->getMessage(), array( 'submission' => $order->toArray() ) ); return false; } } /** * @param $store_id * @param $order_id * @param false $throw * * @return false|MailChimp_WooCommerce_Order * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getStoreOrder( $store_id, $order_id, $throw = false ) { try { $data = $this->get( "ecommerce/stores/$store_id/orders/$order_id" ); if ( ! is_array( $data ) ) { throw new MailChimp_WooCommerce_RateLimitError( 'getting order api failure, retry again.' ); } $order = new MailChimp_WooCommerce_Order(); return $order->fromArray( $data ); } catch ( MailChimp_WooCommerce_Error $e ) { if ( $throw ) { throw $e; } return false; } } /** * @param $store_id * @param $order_id * * @return bool */ public function deleteStoreOrder( $store_id, $order_id ) { try { return (bool) $this->delete( "ecommerce/stores/$store_id/orders/$order_id" ); } catch ( Exception $e ) { return false; } } /** * @param $store_id * @param $order_id * @param $line_id * * @return bool */ public function deleteStoreOrderLine( $store_id, $order_id, $line_id ) { try { return (bool) $this->delete( "ecommerce/stores/{$store_id}/orders/{$order_id}/lines/{$line_id}" ); } catch ( Exception $e ) { return false; } } /** * @param $store_id * @param $product_id * @param false $throw * * @return false|MailChimp_WooCommerce_Product * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getStoreProduct( $store_id, $product_id, $throw = false ) { try { $data = $this->get( "ecommerce/stores/$store_id/products/$product_id" ); if ( ! is_array( $data ) ) { throw new MailChimp_WooCommerce_RateLimitError( 'getting product api failure, retry again.' ); } $product = new MailChimp_WooCommerce_Product(); return $product->fromArray( $data ); } catch ( MailChimp_WooCommerce_Error $e ) { if ( $throw ) { throw $e; } return false; } } /** * @param $store_id * @param $product_id * @param false $throw * * @return false|MailChimp_WooCommerce_ProductVariation * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getStoreProductVariant( $store_id, $product_id, $variation_id, $throw = false ) { try { $data = $this->get( "ecommerce/stores/$store_id/products/$product_id/variants/$variation_id" ); if ( ! is_array( $data ) ) { throw new MailChimp_WooCommerce_RateLimitError( 'getting product variant api failure, retry again.' ); } $product_variation = new MailChimp_WooCommerce_ProductVariation(); return $product_variation->fromArray( $data ); } catch ( MailChimp_WooCommerce_Error $e ) { if ( $throw ) { throw $e; } return false; } } /** * @param $store_id * @param int $page * @param int $count * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function products( $store_id, $page = 1, $count = 10 ) { return $this->get( 'ecommerce/stores/' . $store_id . '/products', array( 'start' => $page, 'count' => $count, 'offset' => ( $page * $count ), ) ); } /** * @param $store_id * @param MailChimp_WooCommerce_Product $product * @param bool $silent * * @return false|MailChimp_WooCommerce_Product * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function addStoreProduct( $store_id, MailChimp_WooCommerce_Product $product, $silent = true ) { try { if ( ! $this->validateStoreSubmission( $product ) ) { return false; } $data = $this->post( "ecommerce/stores/$store_id/products", $product->toArray() ); \Mailchimp_Woocommerce_DB_Helpers::update_option( 'mailchimp-woocommerce-resource-last-updated', time() ); $product = new MailChimp_WooCommerce_Product(); return $product->fromArray( $data ); } catch ( Exception $e ) { if ( ! $silent ) { throw $e; } mailchimp_log( 'api.add_product.error', $e->getMessage(), array( 'submission' => $product->toArray() ) ); return false; } } /** * @param $store_id * @param MailChimp_WooCommerce_Product $product * @param bool $silent * * @return false|MailChimp_WooCommerce_Product * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError */ public function updateStoreProduct( $store_id, MailChimp_WooCommerce_Product $product, $silent = true ) { try { if ( ! $this->validateStoreSubmission( $product ) ) { return false; } $data = $this->put( "ecommerce/stores/$store_id/products/{$product->getId()}", $product->toArray() ); \Mailchimp_Woocommerce_DB_Helpers::update_option( 'mailchimp-woocommerce-resource-last-updated', time() ); $product = new MailChimp_WooCommerce_Product(); return $product->fromArray( $data ); } catch ( Exception $e ) { if ( ! $silent ) { throw $e; } mailchimp_log( 'api.update_product.error', $e->getMessage(), array( 'submission' => $product->toArray() ) ); return false; } } /** * @param $store_id * @param MailChimp_WooCommerce_ProductVariation $product_variation * @param bool $silent * * @return false|MailChimp_WooCommerce_ProductVariation * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function addStoreProductVariation( $store_id, MailChimp_WooCommerce_ProductVariation $product_variation, $silent = true ) { try { if ( ! $this->validateStoreSubmission( $product_variation ) ) { return false; } if ($product_id = $product_variation->getProductId()) { $data = $this->post( "ecommerce/stores/$store_id/products/$product_id/variants", $product_variation->toArray() ); \Mailchimp_Woocommerce_DB_Helpers::update_option( 'mailchimp-woocommerce-resource-last-updated', time() ); $product_variation = new MailChimp_WooCommerce_ProductVariation(); return $product_variation->fromArray( $data ); } else { mailchimp_log( 'api.add_product_variation.error', "Unable to find product for variation", array( 'submission' => $product_variation->toArray() ) ); return false; } } catch ( Exception $e ) { if ( ! $silent ) { throw $e; } mailchimp_log( 'api.add_product_variation.error', $e->getMessage(), array( 'submission' => $product_variation->toArray() ) ); return false; } } /** * @param $store_id * @param MailChimp_WooCommerce_ProductVariation $product_variation * @param bool $silent * * @return false|MailChimp_WooCommerce_ProductVariation * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError */ public function updateStoreProductVariation( $store_id, MailChimp_WooCommerce_ProductVariation $product_variation, $silent = true ) { try { if ( ! $this->validateStoreSubmission( $product_variation ) ) { return false; } if ($product_id = $product_variation->getProductId()) { $data = $this->patch("ecommerce/stores/$store_id/products/$product_id/variants/{$product_variation->getId()}", $product_variation->toArray()); \Mailchimp_Woocommerce_DB_Helpers::update_option('mailchimp-woocommerce-resource-last-updated', time()); $product_variation = new MailChimp_WooCommerce_ProductVariation(); return $product_variation->fromArray($data); } else { mailchimp_log( 'api.update_product_variation.error', "Unable to find product for variation", array( 'submission' => $product_variation->toArray() ) ); return false; } } catch ( Exception $e ) { if ( ! $silent ) { throw $e; } mailchimp_log( 'api.update_product_variation.error', $e->getMessage(), array( 'submission' => $product_variation->toArray() ) ); return false; } } /** * @param $store_id * @param $product_id * @param $variation_id * * @return bool */ public function deleteStoreProductVariation($store_id, $product_id, $variation_id) { try { $data = $this->delete("ecommerce/stores/$store_id/products/$product_id/variants/$variation_id"); mailchimp_debug('product_variation', 'DELETE PRODUCT VARIATION', $data); return (bool) $data; } catch ( Exception $e ) { mailchimp_log( 'api.update_product_variation.error', $e->getMessage(), array( 'submission' => $variation_id ) ); return false; } } /** * @param MailChimp_WooCommerce_Order $order * * @return array * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function handleProductsMissingFromAPI( MailChimp_WooCommerce_Order $order ) { $missing_products = array(); foreach ( $order->items() as $order_item ) { /** @var MailChimp_WooCommerce_LineItem $order_item */ // get the line item name from the order detail just in case we need that title for the product. $job = new MailChimp_WooCommerce_Single_Product( $order_item->getProductId(), $order_item->getFallbackTitle() ); if ( $missing_products[ $order_item->getId() ] = $job->createModeOnly()->fromOrderItem( $order_item )->handle() ) { mailchimp_debug( 'missing_products.fallback', "Product {$order_item->getId()} had to be re-pushed into Mailchimp" ); } } return $missing_products; } /** * @return bool|MailChimp_WooCommerce_Product */ public function createEmptyLineItemProductPlaceholder() { $product = new MailChimp_WooCommerce_Product(); $product->setId( 'empty_line_item_placeholder' ); $product->setTitle( 'Empty Line Item Placeholder' ); $product->setVendor( 'deleted' ); $variation = new MailChimp_WooCommerce_ProductVariation(); $variation->setId( $product->getId() ); $variation->setTitle( $product->getTitle() ); $variation->setInventoryQuantity( 0 ); $variation->setVisibility( 'hidden' ); $variation->setPrice( 1 ); $product->addVariant( $variation ); if ( (bool) mailchimp_get_data( 'empty_line_item_placeholder', false ) ) { return $product; } $store_id = mailchimp_get_store_id(); $api = mailchimp_get_api(); try { $response = $api->addStoreProduct( $store_id, $product, false ); mailchimp_set_data( 'empty_line_item_placeholder', true ); return $response; } catch ( Exception $e ) { return $product; } } /** * @param $store_id * @param $category_id * @param $category * * @return bool */ public function updateProductCategory( $store_id, $category_id, MailChimp_WooCommerce_Product_Category $category ) { try { return (bool) $this->put( "ecommerce/stores/{$store_id}/collections/$category_id", $category->toArray() ); } catch ( MailChimp_WooCommerce_Error $e ) { mailchimp_log('mc_update_cat', 'failed', [ 'error' => $e->getMessage(), ]); return false; } } /** * @param $store_id * @param $category_id * @param $product_ids * @return bool */ public function syncProductsToCollection($store_id, $category_id, $product_ids) { try { $product_ids = array_map(function($id) { return "{$id}"; }, $product_ids); $data = array( 'product_ids' => $product_ids ); $updating_products = $this->put( "ecommerce/stores/{$store_id}/collections/{$category_id}/products", $data ); mailchimp_debug('mailchimp_api.syncProductsToCollection', 'updating', [ 'category_id' => $category_id, 'products' => $data, 'response' => $updating_products ]); return $updating_products; } catch ( MailChimp_WooCommerce_Error $e ) { return false; } } /** * @param $store_id * @param $category_id * @param $product_id * @return bool */ public function addProductToCategory($store_id, $category_id, $product_id) { try { $data = array( 'id' => "{$product_id}" ); return $this->post( "ecommerce/stores/{$store_id}/collections/{$category_id}/products", $data ); } catch ( MailChimp_WooCommerce_Error $e ) { mailchimp_error('mailchimp_api.addProductToCategory', 'failed', ['error' => $e->getMessage()]); return false; } } /** * @param $store_id * @param $category_id * @param $product_id * @return bool */ public function removeProductFromCategory($store_id, $category_id, $product_id) { try { $data = array( 'id' => "{$product_id}" ); return $this->delete( "ecommerce/stores/{$store_id}/collections/{$category_id}/products", $data ); } catch ( MailChimp_WooCommerce_Error $e ) { mailchimp_error('mailchimp_api.removeProductFromCategory', 'failed', ['error' => $e->getMessage()]); return false; } } /** * @param $store_id * @param $product_id * * @return bool */ public function deleteStoreProduct( $store_id, $product_id ) { try { return (bool) $this->delete( "ecommerce/stores/$store_id/products/$product_id" ); } catch ( MailChimp_WooCommerce_Error $e ) { return false; } } /** * @param $store_id * @param MailChimp_WooCommerce_PromoRule $rule * @param bool $throw * * @return false|MailChimp_WooCommerce_PromoRule * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function addPromoRule( $store_id, MailChimp_WooCommerce_PromoRule $rule, $throw = true ) { try { if ( ( $response = $this->updatePromoRule( $store_id, $rule, false ) ) ) { return $response; } $data = $this->post( "ecommerce/stores/{$store_id}/promo-rules", $rule->toArray() ); return ( new MailChimp_WooCommerce_PromoRule() )->fromArray( $data ); } catch ( MailChimp_WooCommerce_Error $e ) { if ( $throw ) { throw $e; } return false; } } /** * @param $store_id * @param MailChimp_WooCommerce_PromoRule $rule * @param bool $throw * * @return false|MailChimp_WooCommerce_PromoRule * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function updatePromoRule( $store_id, MailChimp_WooCommerce_PromoRule $rule, $throw = true ) { try { $data = $this->patch( "ecommerce/stores/{$store_id}/promo-rules/{$rule->getId()}", $rule->toArray() ); return ( new MailChimp_WooCommerce_PromoRule() )->fromArray( $data ); } catch ( Exception $e ) { if ( $throw ) { throw $e; } return false; } } /** * @param $store_id * @param $rule * * @return bool */ public function deletePromoRule( $store_id, $rule ) { try { $id = $rule instanceof MailChimp_WooCommerce_PromoRule ? $rule->getId() : $rule; // print_r(array('id' => $id, 'store' => $store_id));die(); return (bool) $this->delete( "ecommerce/stores/{$store_id}/promo-rules/{$id}" ); } catch ( MailChimp_WooCommerce_Error $e ) { // \Log::error("MC::deletePromoRule :: {$rule->getId()} :: {$e->getMessage()} on {$e->getLine()} in {$e->getFile()}"); return false; } } /** * @param $store_id * @param int $page * @param int $count * * @return array * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getPromoRuleIds( $store_id, $page = 1, $count = 10 ) { $result = $this->get( "ecommerce/stores/{$store_id}/promo-rules", array( 'start' => $page, 'count' => $count, 'offset' => $page > 1 ? ( ( $page - 1 ) * $count ) : 0, 'include' => 'id', ) ); $ids = array(); foreach ( $result['promo_rules'] as $rule ) { $id = (string) $rule['id']; $ids[ $id ] = $id; } return $ids; } /** * @param $store_id * @param int $page * @param int $count * @param false $return_original * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getPromoRules( $store_id, $page = 1, $count = 10, $return_original = false ) { $result = $this->get( "ecommerce/stores/{$store_id}/promo-rules", array( 'start' => $page, 'count' => $count, 'offset' => $page > 1 ? ( ( $page - 1 ) * $count ) : 0, ) ); if ( $return_original ) { return $result; } $rules = array(); foreach ( $result['promo_rules'] as $rule_data ) { $rule = new MailChimp_WooCommerce_PromoRule(); $rule->fromArray( $rule_data ); $rules[] = $rule; } return $rules; } /** * @param $store_id * @param $rule_id * @param int $page * @param int $count * @param false $return_original * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getPromoCodesForRule( $store_id, $rule_id, $page = 1, $count = 10, $return_original = false ) { $result = $this->get( "ecommerce/stores/{$store_id}/promo-rules/{$rule_id}/promo-codes", array( 'start' => $page, 'count' => $count, 'offset' => $page > 1 ? ( ( $page - 1 ) * $count ) : 0, ) ); if ( $return_original ) { return $result; } $rules = array(); foreach ( $result['promo_codes'] as $rule_data ) { $rule = new MailChimp_WooCommerce_PromoCode(); $rule->fromArray( $rule_data ); $rules[] = $rule; } return $rules; } /** * @param $store_id * @param $rule_id * @param $code_id * * @return false|MailChimp_WooCommerce_PromoCode */ public function getPromoCodeForRule( $store_id, $rule_id, $code_id ) { try { $data = $this->get( "ecommerce/stores/{$store_id}/promo-rules/{$rule_id}/promo-codes/{$code_id}" ); return ( new MailChimp_WooCommerce_PromoCode() )->fromArray( $data ); } catch ( MailChimp_WooCommerce_Error $e ) { return false; } } /** * @param $store_id * @param $rule_id * * @return object * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getPromoRuleWithCodes( $store_id, $rule_id ) { $rule = new MailChimp_WooCommerce_PromoCode(); $rule = $rule->fromArray( $this->get( "ecommerce/stores/{$store_id}/promo-rules/{$rule_id}" ) ); try { $promo_codes = $this->getPromoCodesForRule( $store_id, $rule_id, 1, 100 ); $codes = array(); foreach ( $promo_codes as $item ) { $codes[] = $item->toArray(); } return (object) array( 'rule' => $rule->toArray(), 'codes' => $codes, ); } catch ( Exception $e ) { return (object) array( 'rule' => $rule, 'error' => $e->getMessage(), ); } } /** * @param $store_id * @param MailChimp_WooCommerce_PromoRule $rule * @param MailChimp_WooCommerce_PromoCode $code * @param bool $throw * * @return false|MailChimp_WooCommerce_PromoCode * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function addPromoCodeForRule( $store_id, MailChimp_WooCommerce_PromoRule $rule, MailChimp_WooCommerce_PromoCode $code, $throw = true ) { try { if ( ( $result = $this->updatePromoCodeForRule( $store_id, $rule, $code, false ) ) ) { return $result; } $data = $this->post( "ecommerce/stores/{$store_id}/promo-rules/{$rule->getId()}/promo-codes", $code->toArray() ); return ( new MailChimp_WooCommerce_PromoCode() )->fromArray( $data ); } catch ( MailChimp_WooCommerce_Error $e ) { if ( $throw ) { throw $e; } return false; } } /** * @param $store_id * @param MailChimp_WooCommerce_PromoRule $rule * @param MailChimp_WooCommerce_PromoCode $code * @param bool $throw * * @return false|MailChimp_WooCommerce_PromoCode * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function updatePromoCodeForRule( $store_id, MailChimp_WooCommerce_PromoRule $rule, MailChimp_WooCommerce_PromoCode $code, $throw = true ) { try { $data = $this->patch( "ecommerce/stores/{$store_id}/promo-rules/{$rule->getId()}/promo-codes/{$code->getId()}", $code->toArray() ); return ( new MailChimp_WooCommerce_PromoCode() )->fromArray( $data ); } catch ( Exception $e ) { if ( $throw ) { throw $e; } return false; } } /** * @param $store_id * @param $rule_id * @param $code_id * * @return bool */ public function deletePromoCodeForRule( $store_id, $rule_id, $code_id ) { try { return (bool) $this->delete( "ecommerce/stores/{$store_id}/promo-rules/{$rule_id}/promo-codes/{$code_id}" ); } catch ( MailChimp_WooCommerce_Error $e ) { return false; } catch ( Exception $e ) { return false; } } /** * @param $target * * @return bool * @throws MailChimp_WooCommerce_Error */ protected function validateStoreSubmission( $target ) { if ( $target instanceof MailChimp_WooCommerce_Order ) { $this->validateNaughtyList( $target->getCustomer() ); return $this->validateStoreOrder( $target ); } elseif ( $target instanceof MailChimp_WooCommerce_Customer ) { $this->validateNaughtyList( $target ); return $this->validateStoreCustomer( $target ); } return true; } /** * @param MailChimp_WooCommerce_Order $order * * @return bool */ protected function validateStoreOrder( MailChimp_WooCommerce_Order $order ) { if ( ! $this->validateStoreCustomer( $order->getCustomer() ) ) { return false; } return true; } /** * @param MailChimp_WooCommerce_Customer $customer * * @return bool */ protected function validateStoreCustomer( MailChimp_WooCommerce_Customer $customer ) { $email = $customer->getEmailAddress(); if ( ! is_email( $email ) || mailchimp_email_is_amazon( $email ) || mailchimp_email_is_privacy_protected( $email ) ) { return false; } if (!$customer->getId()) { return false; } return true; } /** * @param $list_id * @param int $minutes * * @return array|mixed */ public function getCachedGDPRFields( $list_id, $minutes = 5 ) { $transient = "mailchimp-woocommerce-gdpr-fields.{$list_id}"; $GDPRfields = get_transient( $transient ); // only return the values if it's a false - or an array if ( is_array( $GDPRfields ) ) { return $GDPRfields; } try { $GDPRfields = $this->getGDPRFields( $list_id ); set_transient( $transient, $GDPRfields, 60 * $minutes ); } catch ( Exception $e ) { $GDPRfields = array(); } return $GDPRfields; } /** * @param $list_id * * @return array|mixed * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getGDPRFields( $list_id ) { $one_member = $this->get( "lists/$list_id/members?fields=members.marketing_permissions&count=1" ); $fields = array(); if ( is_array( $one_member ) && isset( $one_member['members'] ) && isset( $one_member['members'][0] ) && isset( $one_member['members'][0]['marketing_permissions'] ) ) { $fields = $one_member['members'][0]['marketing_permissions']; } return $fields; } /** * @param $list_id * @param $email * @param int $minutes * * @return mixed|string|null */ public function getCachedSubscriberStatusForAdminProfileView( $list_id, $email, $minutes = 5 ) { if ( ! is_email( $email ) || ! mailchimp_is_configured() ) { return null; } $email_hash = md5( strtolower( trim( $email ) ) ); $transient = "mailchimp-woocommerce-subscribed.{$list_id}.{$email_hash}"; $status = \Mailchimp_Woocommerce_DB_Helpers::get_transient( $transient ); if ( ! empty( $status ) ) { return $status; } try { $member = mailchimp_get_api()->member( $list_id, $email ); $status = $member['status']; } catch ( Exception $e ) { $status = 'not_found'; } \Mailchimp_Woocommerce_DB_Helpers::set_transient( $transient, $status, 60 * $minutes ); return $status; } /** * @param false $list_id * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getWebHooks( $list_id = false ) { if ( empty( $list_id ) ) { $list_id = mailchimp_get_list_id(); } return $this->get( "lists/{$list_id}/webhooks", array( 'count' => 1000 ) ); } /** * @param $list_id * @param $url * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function webHookSubscribe( $list_id, $url ) { return $this->post( "lists/{$list_id}/webhooks", array( 'url' => $url, 'events' => array( 'sms_subscribe' => true, 'sms_unsubscribe' => true, 'subscribe' => true, 'unsubscribe' => true, 'cleaned' => true, 'profile' => false, 'upemail' => false, 'campaign' => false, ), 'sources' => array( 'user' => true, 'admin' => true, 'api' => true, ), ) ); } /** * @param $list_id * @param $url * * @return int * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError * @throws Throwable */ public function webHookDelete( $list_id, $url ) { $deleted = 0; $hooks = $this->getWebHooks( $list_id ); foreach ( $hooks['webhooks'] as $hook ) { $href = isset( $hook['url'] ) ? $hook['url'] : ( isset( $hook['href'] ) ? $hook['href'] : null ); if ( $href && $href === $url || ( $href && ! empty( $url ) && mailchimp_string_contains( $href, $url ) ) ) { mailchimp_log( 'admin', "deleting webhook id {$hook['id']} - {$href}" ); $this->delete( "lists/{$list_id}/webhooks/{$hook['id']}" ); $deleted++; } } return $deleted; } /** * @param $list_id * @param $id * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function deleteWebhookByID($list_id, $id) { mailchimp_log('admin', "deleting webhook id {$id}"); return $this->delete("lists/{$list_id}/webhooks/{$id}"); } /** * @param $list_id * @param $url * * @return bool * @throws Throwable */ public function hasWebhook( $list_id, $url ) { $hooks = $this->getWebHooks( $list_id ); foreach ( $hooks['webhooks'] as $hook ) { $href = isset( $hook['url'] ) ? $hook['url'] : ( isset( $hook['href'] ) ? $hook['href'] : null ); if ( $href && $href === $url ) { return true; } } return false; } /** * @param MailChimp_WooCommerce_Customer $customer * * @return false * @throws MailChimp_WooCommerce_Error */ public function validateNaughtyList( MailChimp_WooCommerce_Customer $customer ) { $this->validateNaughtyListEmail( $customer->getEmailAddress() ); $this->validateNaughtyListNames( $customer->getFirstName(), $customer->getLastName() ); return false; } /** * @param $email * * @return false * @throws MailChimp_WooCommerce_Error */ public function validateNaughtyListEmail( $email ) { if (defined('DISABLE_MAILCHIMP_NAUGHTY_LIST') && DISABLE_MAILCHIMP_NAUGHTY_LIST) { return false; } if ( ! empty( $email ) && mailchimp_string_contains( $email, $this->getNaughtyList() ) ) { $this->reportSpamToTower( $email ); throw new MailChimp_WooCommerce_Error( "Email [{$email}] has been blocked due to spam reports." ); } return false; } /** * @return array|mixed|object */ public function getNaughtyList() { try { $domains = mailchimp_get_transient( 'naughty_list_domains' ); if ( is_array( $domains ) && isset( $domains['value'] ) ) { return $domains['value']; } $domains_response = wp_remote_get( 'https://tower.vextras.com/naughty-domains' ); $domains = json_decode( $domains_response['body'], true ); mailchimp_set_transient( 'naughty_list_domains', $domains, 1440 ); return $domains; } catch ( Throwable $e ) { $domains = array(); mailchimp_set_transient( 'naughty_list_domains', $domains, 300 ); return $domains; } } /** * @return bool */ private function allowedToSubmitSpam() { // check to see if we've already set the transient. $status = mailchimp_get_transient( 'tower' ); // if we've got it - just return it now. if ( ! empty( $status ) ) { return $status === 'green'; } // call the API to see if we need to block traffic or not // this only impacts reporting spam metrics, does not impact local site blocking $response = wp_remote_get( 'https://tower.vextras.com/api/traffic' ); $body = json_decode( $response['body'] ); $status = $body ? $body->status : 'red'; // set this for 5 minutes. mailchimp_set_transient( 'tower', $status, 120 ); return $status === 'green'; } /** * @param $email * * @return array|mixed|object|null */ private function reportSpamToTower( $email ) { try { if ( ! $this->allowedToSubmitSpam() ) { return null; } $payload = array( 'headers' => array( 'Content-type' => 'application/json', 'Accept' => 'application/json', 'X-Store-Platform' => 'woocommerce', 'X-List-Id' => mailchimp_get_list_id(), 'X-Store-Key' => base64_encode( mailchimp_get_store_id() . ':' . mailchimp_get_api_key() ), ), 'body' => json_encode( array( 'store_id' => mailchimp_get_store_id(), 'list_id' => mailchimp_get_list_id(), 'domain' => site_url(), 'email' => $email, ) ), 'timeout' => 30, ); $response = wp_remote_post( 'https://tower.vextras.com/admin-api/woocommerce/report-spam', $payload ); return json_decode( $response['body'] ); } catch ( Throwable $e ) { return null; } } /** * @param $first_name * @param $last_name * * @return false * @throws MailChimp_WooCommerce_Error */ public function validateNaughtyListNames( $first_name, $last_name ) { // add a list of naughty list customer names. Seems a little destructive though. $naughty_list_names = array( 'mark mustermann', ); $name = "{$first_name} {$last_name}"; if ( mailchimp_string_contains( strtolower( $name ), $naughty_list_names ) ) { throw new MailChimp_WooCommerce_Error( "Name [{$name}] has been blocked due to spam reports." ); } return false; } /** * @param $list_id * * @return array * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getAutomations( $list_id ) { $automations = $this->get( 'automations', array( 'limit' => 1000 ) ); if ( ! is_array( $automations ) || ! array_key_exists( 'automations', $automations ) ) { return array(); } $response = array(); foreach ( $automations['automations'] as $automation ) { if ( $list_id === $automation['recipients']['list_id'] ) { $response[] = $automation; } } return $response; } /** * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ public function getJourneys() { $journeys = $this->get( 'customer-journeys/journeys', array( 'limit' => 1000 )); return is_array($journeys) && array_key_exists( 'journeys', $journeys) ? $journeys['journeys'] : array(); } /** * @param $id */ public function getJourney($id) { return $this->get("customer-journeys/journeys/{$id}"); } /** * @param $url * @param null $params * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ protected function delete( $url, $params = null ) { $curl = curl_init(); $options = $this->applyCurlOptions( 'DELETE', $url, $params ); curl_setopt_array( $curl, $options ); return $this->processCurlResponse( $curl ); } /** * @param $url * @param null $params * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ protected function get( $url, $params = null ) { $curl = curl_init(); $options = $this->applyCurlOptions( 'GET', $url, $params ); curl_setopt_array( $curl, $options ); return $this->processCurlResponse( $curl ); } /** * @param $url * @param $body * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ protected function patch( $url, $body ) { // process the patch request the normal way $curl = curl_init(); $json = json_encode( $body ); $options = $this->applyCurlOptions( 'PATCH', $url, array(), $this->getHeadersForPostOrPut($json) ); $options[ CURLOPT_POSTFIELDS ] = $json; curl_setopt_array( $curl, $options ); return $this->processCurlResponse( $curl, $body ); } /** * @param $url * @param $body * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ protected function post( $url, $body ) { $curl = curl_init(); $json = json_encode( $body ); $options = $this->applyCurlOptions( 'POST', $url, array(), $this->getHeadersForPostOrPut($json) ); $options[ CURLOPT_POSTFIELDS ] = $json; curl_setopt_array( $curl, $options ); return $this->processCurlResponse( $curl, $body ); } /** * @param $json * @return string[] */ protected function getHeadersForPostOrPut($json) { $headers = array( 'Expect:', 'Content-Length: ' . strlen( $json ), ); if ($this->auto_doi) { $headers[] = 'X-Status-Resolution-Method: auto-doi'; } return $headers; } /** * @param $url * @param $body * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ protected function put( $url, $body ) { $curl = curl_init(); $json = json_encode( $body ); $options = $this->applyCurlOptions( 'PUT', $url, array(), $this->getHeadersForPostOrPut($json) ); $options[ CURLOPT_POSTFIELDS ] = $json; curl_setopt_array( $curl, $options ); return $this->processCurlResponse( $curl, $body ); } /** * @param string $extra * @param null $params * * @return string */ protected function url( $extra = '', $params = null ) { $url = "https://{$this->data_center}.api.mailchimp.com/{$this->version}/"; if ( ! empty( $extra ) ) { $url .= $extra; } if ( ! empty( $params ) ) { $url .= '?' . ( is_array( $params ) ? http_build_query( $params ) : $params ); } return $url; } /** * @param $method * @param $url * @param array $params * @param array $headers * * @return array */ protected function applyCurlOptions( $method, $url, $params = array(), $headers = array() ) { $env = mailchimp_environment_variables(); $headers = array_merge( array( 'content-type: application/json', 'accept: application/json', "user-agent: MailChimp for WooCommerce/{$env->version}; PHP/{$env->php_version}; WordPress/{$env->wp_version}; Woo/{$env->wc_version};", ), $headers ); if ($env->initial_sync) { $headers[] = 'X-Data-Mode: historical'; } if ($this->auto_doi) { mailchimp_debug('api', "applied doi headers", $headers); } $curl_options = array( CURLOPT_USERPWD => "mailchimp:{$this->api_key}", CURLOPT_CUSTOMREQUEST => strtoupper( $method ), CURLOPT_URL => $this->url( $url, $params ), CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 30, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLINFO_HEADER_OUT => true, CURLOPT_HTTPHEADER => $headers, ); // automatically set the proper outbound IP address if ( ( $outbound_ip = mailchimp_get_outbound_ip() ) && ! in_array( $outbound_ip, mailchimp_common_loopback_ips() ) ) { $curl_options[ CURLOPT_INTERFACE ] = $outbound_ip; } // if we need to define a specific http version being used for curl requests, we can override this here. if ( defined( 'MAILCHIMP_USE_HTTP_VERSION' ) ) { $curl_options[ CURLOPT_HTTP_VERSION ] = MAILCHIMP_USE_HTTP_VERSION; } return $curl_options; } /** * @param $curl * * @return array|bool|mixed|object|null * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError * @throws MailChimp_WooCommerce_ServerError */ protected function processCurlResponse( $curl, $request_data = null ) { $response = curl_exec( $curl ); $err = curl_error( $curl ); $info = curl_getinfo( $curl ); // Capture request details before closing curl $url = isset($info['url']) ? $info['url'] : 'UNKNOWN'; $request_headers = isset($info['request_header']) ? explode("\r\n", $info['request_header']) : array(); // Extract method from curl options $method = $info['effective_method'] ?? 'UNKNOWN'; if ($this->auto_doi) { mailchimp_debug('api.debug', 'message headers', ['headers' => $info['request_header']]); } curl_close( $curl ); // Initialize error info array if ( $err ) { $error_info = array( 'type' => 'curl_error', 'message' => $err, 'code' => 500 ); // Log with enhanced logger if (class_exists('MailChimp_WooCommerce_Enhanced_Logger')) { MailChimp_WooCommerce_Enhanced_Logger::log_connection_attempt( $method, $url, $info, $response, $error_info, $request_headers, $request_data ); } throw new MailChimp_WooCommerce_Error( 'CURL error :: ' . $err, 500 ); } $data = json_decode( $response, true ); $http_code = ! empty( $info ) && isset( $info['http_code'] ) ? $info['http_code'] : -1; $called_url = ! empty( $info ) && isset( $info['url'] ) ? $info['url'] : 'none'; // let's block these from doing anything below because the API seems to be having trouble. if ( $http_code <= 99 ) { $error_info = array( 'type' => 'api_failure', 'message' => 'API is failing - try again.', 'code' => $http_code ); // Log with enhanced logger if (class_exists('MailChimp_WooCommerce_Enhanced_Logger')) { MailChimp_WooCommerce_Enhanced_Logger::log_connection_attempt( $method, $url, $info, $response, $error_info, $request_headers, $request_data ); } throw new MailChimp_WooCommerce_RateLimitError( 'API is failing - try again.' ); } // possibily a successful DELETE operation if ( $http_code == 204 ) { return true; } if ( $http_code >= 200 && $http_code <= 400 ) { if ( is_array( $data ) ) { try { $this->checkForErrors( $data ); } catch ( Exception $e ) { // Log error from checkForErrors $error_info = array( 'type' => 'validation_error', 'message' => $e->getMessage(), 'code' => $e->getCode(), 'response_data' => $data ); if (class_exists('MailChimp_WooCommerce_Enhanced_Logger')) { MailChimp_WooCommerce_Enhanced_Logger::log_connection_attempt( $method, $url, $info, $response, $error_info, $request_headers, $request_data ); } throw $e; } } // Log successful request if debug logging is enabled if (mailchimp_environment_variables()->logging === 'debug' && class_exists('MailChimp_WooCommerce_Enhanced_Logger')) { MailChimp_WooCommerce_Enhanced_Logger::log_connection_attempt( $method, $url, $info, $response, array(), // No error $request_headers, $request_data ); } return $data; } $error_status = isset( $data['status'] ) && is_numeric( $data['status'] ) ? (int) $data['status'] : (int) $http_code; if ( $http_code >= 400 && $http_code <= 500 ) { if ( $http_code == 403 ) { $error_info = array( 'type' => 'rate_limit', 'message' => 'Rate limit exceeded', 'code' => 403, 'response_data' => $data ); // Log with enhanced logger if (class_exists('MailChimp_WooCommerce_Enhanced_Logger')) { MailChimp_WooCommerce_Enhanced_Logger::log_connection_attempt( $method, $url, $info, $response, $error_info, $request_headers, $request_data ); } throw new MailChimp_WooCommerce_RateLimitError(); } $error_message = isset( $data['title'] ) ? $data['title'] : ''; $error_message .= isset( $data['detail'] ) ? $data['detail'] : ''; $error_info = array( 'type' => 'client_error', 'message' => $error_message, 'code' => $error_status, 'response_data' => $data ); // Log with enhanced logger if (class_exists('MailChimp_WooCommerce_Enhanced_Logger')) { MailChimp_WooCommerce_Enhanced_Logger::log_connection_attempt( $method, $url, $info, $response, $error_info, $request_headers, $request_data ); } throw new MailChimp_WooCommerce_Error( $error_message, $error_status ); } if ( $http_code >= 500 ) { $error_message = isset( $data['detail'] ) ? $data['detail'] : ''; $error_info = array( 'type' => 'server_error', 'message' => $error_message, 'code' => $error_status, 'response_data' => $data ); // Log with enhanced logger if (class_exists('MailChimp_WooCommerce_Enhanced_Logger')) { MailChimp_WooCommerce_Enhanced_Logger::log_connection_attempt( $method, $url, $info, $response, $error_info, $request_headers, $request_data ); } throw new MailChimp_WooCommerce_ServerError( $error_message, $error_status ); } if ( ! is_array( $data ) ) { $error_info = array( 'type' => 'decode_error', 'message' => 'API response could not be decoded.', 'code' => $http_code, 'raw_response' => $response ); // Log with enhanced logger if (class_exists('MailChimp_WooCommerce_Enhanced_Logger')) { MailChimp_WooCommerce_Enhanced_Logger::log_connection_attempt( $method, $url, $info, $response, $error_info, $request_headers, $request_data ); } mailchimp_error( 'api.debug', 'fallback when data is empty from API', array( 'url' => $called_url, 'response' => $response, ) ); throw new MailChimp_WooCommerce_ServerError( 'API response could not be decoded.' ); } return null; } /** * @param array $data * * @return false * @throws MailChimp_WooCommerce_Error * @throws MailChimp_WooCommerce_RateLimitError */ protected function checkForErrors( array $data ) { // if we have an array of error data push it into a message if ( isset( $data['errors'] ) ) { $message = ''; foreach ( $data['errors'] as $error ) { $message .= '
' . $error['field'] . ': ' . $error['message'] . '
'; } throw new MailChimp_WooCommerce_Error( $message, (int) $data['status'] ); } // make sure the response is correct from the data in the response array if ( isset( $data['status'] ) && is_numeric( $data['status'] ) && $data['status'] >= 400 ) { if ( isset( $data['http_code'] ) && $data['http_code'] == 403 ) { throw new MailChimp_WooCommerce_RateLimitError(); } $error = isset( $data['detail'] ) ? $data['detail'] : ( 'Error code ' . $data['status'] ); throw new MailChimp_WooCommerce_Error( $error, (int) $data['status'] ); } return false; } } 1 - Uncaught Error: Class 'MailChimp_WooCommerce_MailChimpApi' not found in /home/st20space2tech/public_html/site/wp-content/plugins/mailchimp-for-woocommerce/bootstrap.php:913 Stack trace: #0 /home/st20space2tech/public_html/site/wp-content/plugins/mailchimp-for-woocommerce/includes/class-mailchimp-woocommerce-sms-consent.php(301): mailchimp_get_api() #1 /home/st20space2tech/public_html/site/wp-content/plugins/mailchimp-for-woocommerce/includes/class-mailchimp-woocommerce-sms-consent.php(312): MailChimp_Sms_Consent->getSmsProgram() #2 /home/st20space2tech/public_html/site/wp-content/plugins/mailchimp-for-woocommerce/bootstrap.php(2058): MailChimp_Sms_Consent::isSmsProgramActive() #3 /home/st20space2tech/public_html/site/wp-content/plugins/mailchimp-for-woocommerce/blocks/newsletter.php(18): mailchimp_sms_consent_enabled() #4 /home/st20space2tech/public_html/site/wp-includes/class-wp-hook.php(341): {closure}('') #5 /home/st20space2tech/public_html/site/wp-includes/class-wp-hook.php(365): WP_Hook->apply_filters('' - /home/st20space2tech/public_html/site/wp-content/plugins/mailchimp-for-woocommerce/bootstrap.php - 913