tainer->get( Middleware::class ); // Maybe the existing account is a sub-account! $state['set_id']['data']['from_mca'] = false; foreach ( $middleware->get_merchant_accounts() as $existing_account ) { if ( $existing_account['id'] === $account_id ) { $state['set_id']['data']['from_mca'] = $existing_account['subaccount']; break; } } $middleware->link_merchant_account( $account_id ); $state['set_id']['status'] = MerchantAccountState::STEP_DONE; $this->state->update( $state ); } catch ( ExceptionWithResponseData $e ) { throw $e; } catch ( Exception $e ) { throw $this->prepare_exception( $e->getMessage(), [], $e->getCode() ); } } /** * Run the process for setting up a Merchant Center account (sub-account or standalone). * * @param int $account_id * * @return array The account ID if setup has completed. * @throws ExceptionWithResponseData When the account is already connected or a setup error occurs. */ public function setup_account( int $account_id ) { // Reset the process if the provided ID isn't the same as the one stored in options. $merchant_id = $this->options->get_merchant_id(); if ( $merchant_id && $merchant_id !== $account_id ) { $this->reset_account_setup(); } try { return $this->setup_account_steps(); } catch ( ExceptionWithResponseData | ApiNotReady $e ) { throw $e; } catch ( Exception $e ) { throw $this->prepare_exception( $e->getMessage(), [], $e->getCode() ); } } /** * Create or link an account, switching the URL during the set_id step. * * @param int $account_id * * @return array * @throws ExceptionWithResponseData When a setup error occurs. */ public function switch_url( int $account_id ): array { $state = $this->state->get(); $switch_necessary = ! empty( $state['set_id']['data']['old_url'] ); $set_id_status = $state['set_id']['status'] ?? MerchantAccountState::STEP_PENDING; if ( ! $account_id || MerchantAccountState::STEP_DONE === $set_id_status || ! $switch_necessary ) { throw $this->prepare_exception( __( 'Attempting invalid URL switch.', 'google-listings-and-ads' ) ); } $this->allow_switch_url = true; $this->use_existing_account_id( $account_id ); return $this->setup_account( $account_id ); } /** * Create or link an account, overwriting the website claim during the claim step. * * @param int $account_id * * @return array * @throws ExceptionWithResponseData When a setup error occurs. */ public function overwrite_claim( int $account_id ): array { $state = $this->state->get( false ); $overwrite_necessary = ! empty( $state['claim']['data']['overwrite_required'] ); $claim_status = $state['claim']['status'] ?? MerchantAccountState::STEP_PENDING; if ( MerchantAccountState::STEP_DONE === $claim_status || ! $overwrite_necessary ) { throw $this->prepare_exception( __( 'Attempting invalid claim overwrite.', 'google-listings-and-ads' ) ); } $this->overwrite_claim = true; return $this->setup_account( $account_id ); } /** * Get the connected merchant account. * * @return array */ public function get_connected_status(): array { $id = $this->options->get_merchant_id(); $status = [ 'id' => $id, 'status' => $id ? 'connected' : 'disconnected', ]; $incomplete = $this->state->last_incomplete_step(); if ( ! empty( $incomplete ) ) { $status['status'] = 'incomplete'; $status['step'] = $incomplete; } return $status; } /** * Return the setup status to determine what step to continue at. * * @return array */ public function get_setup_status(): array { return $this->container->get( MerchantCenterService::class )->get_setup_status(); } /** * Disconnect Merchant Center account */ public function disconnect() { $this->options->delete( OptionsInterface::CONTACT_INFO_SETUP ); $this->options->delete( OptionsInterface::MC_SETUP_COMPLETED_AT ); $this->options->delete( OptionsInterface::MERCHANT_ACCOUNT_STATE ); $this->options->delete( OptionsInterface::MERCHANT_CENTER ); $this->options->delete( OptionsInterface::SITE_VERIFICATION ); $this->options->delete( OptionsInterface::TARGET_AUDIENCE ); $this->options->delete( OptionsInterface::MERCHANT_ID ); $this->options->delete( OptionsInterface::CLAIMED_URL_HASH ); $this->container->get( MerchantStatuses::class )->delete(); $this->container->get( MerchantIssueTable::class )->truncate(); $this->container->get( ShippingRateTable::class )->truncate(); $this->container->get( ShippingTimeTable::class )->truncate(); $this->container->get( CleanupSyncedProducts::class )->schedule(); $this->container->get( TransientsInterface::class )->delete( TransientsInterface::MC_ACCOUNT_REVIEW ); $this->container->get( TransientsInterface::class )->delete( TransientsInterface::URL_MATCHES ); $this->container->get( TransientsInterface::class )->delete( TransientsInterface::MC_IS_SUBACCOUNT ); } /** * Performs the steps necessary to initialize a Merchant Center account. * Should always resume up at the last pending or unfinished step. If the Merchant Center account * has already been created, the ID is simply returned. * * @return array The newly created (or pre-existing) Merchant account data. * @throws ExceptionWithResponseData If an error occurs during any step. * @throws Exception If the step is unknown. * @throws ApiNotReady If we should wait to complete the next step. */ private function setup_account_steps() { $state = $this->state->get(); $merchant_id = $this->options->get_merchant_id(); $merchant = $this->container->get( Merchant::class ); $middleware = $this->container->get( Middleware::class ); foreach ( $state as $name => &$step ) { if ( MerchantAccountState::STEP_DONE === $step['status'] ) { continue; } if ( 'link' === $name ) { $time_to_wait = $this->state->get_seconds_to_wait_after_created(); if ( $time_to_wait ) { sleep( $time_to_wait ); } } try { switch ( $name ) { case 'set_id': // Just in case, don't create another merchant ID. if ( ! empty( $merchant_id ) ) { break; } $merchant_id = $middleware->create_merchant_account(); $step['data']['from_mca'] = true; $step['data']['created_timestamp'] = time(); break; case 'verify': // Skip if previously verified. if ( $this->state->is_site_verified() ) { break; } $site_url = esc_url_raw( $this->get_site_url() ); $this->container->get( SiteVerification::class )->verify_site( $site_url ); break; case 'link': $middleware->link_merchant_to_mca(); break; case 'claim': // At this step, the website URL is assumed to be correct. // If the URL is already claimed, no claim should be attempted. if ( $merchant->get_accountstatus( $merchant_id )->getWebsiteClaimed() ) { break; } if ( $this->overwrite_claim ) { $middleware->claim_merchant_website( true ); } else { $merchant->claimwebsite(); } break; case 'link_ads': // Continue to next step if Ads account is not connected yet. if ( ! $this->options->get_ads_id() ) { // Save step as pending and continue the foreach loop with `continue 2`. $state[ $name ]['status'] = MerchantAccountState::STEP_PENDING; $this->state->update( $state ); continue 2; } $this->link_ads_account(); break; default: throw new Exception( sprintf( /* translators: 1: is a string representing an unknown step name */ __( 'Unknown merchant account creation step %1$s', 'google-listings-and-ads' ), $name ) ); } $step['status'] = MerchantAccountState::STEP_DONE; $step['message'] = ''; $this->state->update( $state ); } catch ( Exception $e ) { $step['status'] = MerchantAccountState::STEP_ERROR; $step['message'] = $e->getMessage(); // URL already claimed. if ( 'claim' === $name && 403 === $e->getCode() ) { $data = [ 'id' => $merchant_id, 'website_url' => $this->strip_url_protocol( esc_url_raw( $this->get_site_url() ) ), ]; // Sub-account: request overwrite confirmation. if ( $state['set_id']['data']['from_mca'] ?? true ) { do_action( 'woocommerce_gla_site_claim_overwrite_required', [] ); $step['data']['overwrite_required'] = true; $e = $this->prepare_exception( $e->getMessage(), $data, $e->getCode() ); } else { do_action( 'woocommerce_gla_site_claim_failure', [ 'details' => 'independent_account' ] ); // Independent account: overwrite not possible. $e = $this->prepare_exception( __( 'Unable to claim website URL with this Merchant Center Account.', 'google-listings-and-ads' ), $data, 406 ); } } elseif ( 'link' === $name && 401 === $e->getCode() ) { // New sub-account not yet manipulable. $state['set_id']['data']['created_timestamp'] = time(); $e = ApiNotReady::retry_after( MerchantAccountState::MC_DELAY_AFTER_CREATE ); } $this->state->update( $state ); throw $e; } } return [ 'id' => $merchant_id ]; } /** * Restart the account setup when we are connecting with a different account ID. * Do not allow reset when the full setup process has completed. * * @throws ExceptionWithResponseData When the full setup process is completed. */ private function reset_account_setup() { // Can't reset if the MC connection process has been completed previously. if ( $this->container->get( MerchantCenterService::class )->is_setup_complete() ) { throw $this->prepare_exception( sprintf( /* translators: 1: is a numeric account ID */ __( 'Merchant Center account already connected: %d', 'google-listings-and-ads' ), $this->options->get_merchant_id() ) ); } $this->disconnect(); } /** * Ensure the Merchant Center account's Website URL matches the site URL. Update an empty value or * a different, unclaimed URL value. Throw a 409 exception if a different, claimed URL is found. * * @param int $merchant_id The Merchant Center account to update. * * @throws ExceptionWithResponseData If the account URL doesn't match the site URL or the URL is invalid. */ private function maybe_add_merchant_center_url( int $merchant_id ) { $site_url = esc_url_raw( $this->get_site_url() ); if ( ! wc_is_valid_url( $site_url ) ) { throw $this->prepare_exception( __( 'Invalid site URL.', 'google-listings-and-ads' ) ); } /** @var Merchant $merchant */ $merchant = $this->container->get( Merchant::class ); /** @var Account $account */ $account = $merchant->get_account( $merchant_id ); $account_url = $account->getWebsiteUrl() ?: ''; if ( untrailingslashit( $site_url ) !== untrailingslashit( $account_url ) ) { $is_website_claimed = $merchant->get_accountstatus( $merchant_id )->getWebsiteClaimed(); if ( ! empty( $account_url ) && $is_website_claimed && ! $this->allow_switch_url ) { $state = $this->state->get(); $state['set_id']['data']['old_url'] = $account_url; $state['set_id']['status'] = MerchantAccountState::STEP_ERROR; $this->state->update( $state ); $clean_account_url = $this->strip_url_protocol( $account_url ); $clean_site_url = $this->strip_url_protocol( $site_url ); do_action( 'woocommerce_gla_url_switch_required', [] ); throw $this->prepare_exception( sprintf( /* translators: 1: is a website URL (without the protocol) */ __( 'This Merchant Center account already has a verified and claimed URL, %1$s', 'google-listings-and-ads' ), $clean_account_url ), [ 'id' => $merchant_id, 'claimed_url' => $clean_account_url, 'new_url' => $clean_site_url, ], 409 ); } $account->setWebsiteUrl( $site_url ); $merchant->update_account( $account ); // Clear previous hashed URL. $this->options->delete( OptionsInterface::CLAIMED_URL_HASH ); do_action( 'woocommerce_gla_url_switch_success', [] ); } } /** * Get the callback function for linking an Ads account. * * @throws Exception When the merchant account hasn't been set yet. */ private function link_ads_account() { if ( ! $this->options->get_merchant_id() ) { throw new Exception( 'A Merchant Center account must be connected' ); } $ads_state = $this->container->get( AdsAccountState::class ); // Create link for Merchant and accept it in Ads. $this->container->get( Merchant::class )->link_ads_id( $this->options->get_ads_id() ); $this->container->get( Ads::class )->accept_merchant_link( $this->options->get_merchant_id() ); $ads_state->complete_step( 'link_merchant' ); } /** * Prepares an Exception to be thrown with Merchant data: * - Ensure it has the merchant_id value * - Default to a 400 error code * * @param string $message * @param array $data * @param int|null $code * * @return ExceptionWithResponseData */ private function prepare_exception( string $message, array $data = [], int $code = null ): ExceptionWithResponseData { $merchant_id = $this->options->get_merchant_id(); if ( $merchant_id && ! isset( $data['id'] ) ) { $data['id'] = $merchant_id; } return new ExceptionWithResponseData( $message, $code ?: 400, null, $data ); } }
Warning: class_implements(): Class Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\AccountService does not exist and could not be loaded in /htdocs/wp-content/plugins/google-listings-and-ads/src/Internal/DependencyManagement/AbstractServiceProvider.php on line 119

Fatal error: Uncaught TypeError: array_key_exists(): Argument #2 ($array) must be of type array, bool given in /htdocs/wp-content/plugins/google-listings-and-ads/src/Internal/DependencyManagement/AbstractServiceProvider.php:120 Stack trace: #0 /htdocs/wp-content/plugins/google-listings-and-ads/src/Internal/DependencyManagement/AbstractServiceProvider.php(120): array_key_exists('Automattic\\WooC...', false) #1 /htdocs/wp-content/plugins/google-listings-and-ads/src/Internal/DependencyManagement/CoreServiceProvider.php(309): Automattic\WooCommerce\GoogleListingsAndAds\Internal\DependencyManagement\AbstractServiceProvider->conditionally_share_with_tags('Automattic\\WooC...', 'Automattic\\WooC...') #2 /htdocs/wp-content/plugins/google-listings-and-ads/vendor/league/container/src/ServiceProvider/ServiceProviderAggregate.php(102): Automattic\WooCommerce\GoogleListingsAndAds\Internal\DependencyManagement\CoreServiceProvider->register() #3 /htdocs/wp-content/plugins/google-listings-and-ads/vendor/league/container/src/Container.php(172): Automattic\WooCommerce\GoogleListingsAndAds\Vendor\League\Container\ServiceProvider\ServiceProviderAggregate->register('Automattic\\WooC...') #4 /htdocs/wp-content/plugins/google-listings-and-ads/src/Container.php(90): Automattic\WooCommerce\GoogleListingsAndAds\Vendor\League\Container\Container->get('Automattic\\WooC...') #5 /htdocs/wp-content/plugins/google-listings-and-ads/src/Infrastructure/GoogleListingsAndAdsPlugin.php(130): Automattic\WooCommerce\GoogleListingsAndAds\Container->get('Automattic\\WooC...') #6 /htdocs/wp-content/plugins/google-listings-and-ads/src/Infrastructure/GoogleListingsAndAdsPlugin.php(91): Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\GoogleListingsAndAdsPlugin->maybe_register_services() #7 /htdocs/wp-includes/class-wp-hook.php(324): Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\GoogleListingsAndAdsPlugin->Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\{closure}('') #8 /htdocs/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters(NULL, Array) #9 /htdocs/wp-includes/plugin.php(517): WP_Hook->do_action(Array) #10 /htdocs/wp-settings.php(559): do_action('plugins_loaded') #11 /htdocs/wp-config.php(85): require_once('/htdocs/wp-sett...') #12 /htdocs/wp-load.php(50): require_once('/htdocs/wp-conf...') #13 /htdocs/wp-blog-header.php(13): require_once('/htdocs/wp-load...') #14 /htdocs/index.php(17): require('/htdocs/wp-blog...') #15 {main} thrown in /htdocs/wp-content/plugins/google-listings-and-ads/src/Internal/DependencyManagement/AbstractServiceProvider.php on line 120