rrors. * * @since 1.5.0 */ public function verify_phone_number( string $verification_id, string $verification_code, string $verification_method ): string { $merchant_id = $this->options->get_merchant_id(); $request = new VerifyPhoneNumberRequest( [ 'verificationId' => $verification_id, 'verificationCode' => $verification_code, 'phoneVerificationMethod' => $verification_method, ] ); try { return $this->service->accounts->verifyphonenumber( $merchant_id, $merchant_id, $request )->getVerifiedPhoneNumber(); } catch ( GoogleServiceException $e ) { do_action( 'woocommerce_gla_mc_client_exception', $e, __METHOD__ ); throw $e; } } /** * Retrieve the user's Merchant Center account information. * * @param int $id Optional - the Merchant Center account to retrieve * * @return Account The user's Merchant Center account. * @throws ExceptionWithResponseData If the account can't be retrieved. */ public function get_account( int $id = 0 ): Account { $id = $id ?: $this->options->get_merchant_id(); try { $mc_account = $this->service->accounts->get( $id, $id ); } catch ( GoogleException $e ) { do_action( 'woocommerce_gla_mc_client_exception', $e, __METHOD__ ); $errors = $this->get_exception_errors( $e ); throw new ExceptionWithResponseData( /* translators: %s Error message */ sprintf( __( 'Unable to retrieve Merchant Center account: %s', 'google-listings-and-ads' ), reset( $errors ) ), $e->getCode(), null, [ 'errors' => $errors ] ); } return $mc_account; } /** * Get hash of the site URL we used during onboarding. * If not available in a local option, it's fetched from the Merchant Center account. * * @since 1.13.0 * @return string|null */ public function get_claimed_url_hash(): ?string { $claimed_url_hash = $this->options->get( OptionsInterface::CLAIMED_URL_HASH ); if ( empty( $claimed_url_hash ) && $this->options->get_merchant_id() ) { try { $homepage = $this->homepage_service->get_homepage(); $account_url = $homepage['uri'] ?? ''; if ( empty( $account_url ) || empty( $homepage['claimed'] ) ) { return null; } $claimed_url_hash = md5( untrailingslashit( $account_url ) ); $this->options->update( OptionsInterface::CLAIMED_URL_HASH, $claimed_url_hash ); } catch ( Exception $e ) { return null; } } return $claimed_url_hash; } /** * Retrieve the user's Merchant Center account information. * * @param int $id Optional - the Merchant Center account to retrieve * @return AccountStatus The user's Merchant Center account status. * @throws Exception If the account can't be retrieved. */ public function get_accountstatus( int $id = 0 ): AccountStatus { $id = $id ?: $this->options->get_merchant_id(); try { $mc_account_status = $this->service->accountstatuses->get( $id, $id ); } catch ( GoogleException $e ) { do_action( 'woocommerce_gla_mc_client_exception', $e, __METHOD__ ); throw new Exception( __( 'Unable to retrieve Merchant Center account status.', 'google-listings-and-ads' ), $e->getCode() ); } return $mc_account_status; } /** * Update the provided Merchant Center account information. * * @param Account $account The Account data to update. * * @return Account The user's Merchant Center account. * @throws ExceptionWithResponseData If the account can't be updated. */ public function update_account( Account $account ): Account { try { $account = $this->service->accounts->update( $account->getId(), $account->getId(), $account ); } catch ( GoogleException $e ) { do_action( 'woocommerce_gla_mc_client_exception', $e, __METHOD__ ); $errors = $this->get_exception_errors( $e ); throw new ExceptionWithResponseData( /* translators: %s Error message */ sprintf( __( 'Unable to update Merchant Center account: %s', 'google-listings-and-ads' ), reset( $errors ) ), $e->getCode(), null, [ 'errors' => $errors ] ); } return $account; } /** * Link a Google Ads ID to this Merchant account. * * @param int $ads_id Google Ads ID to link. * * @return bool True if the link invitation is waiting for acceptance. False if the link is already active. * @throws ExceptionWithResponseData When unable to retrieve or update account data. */ public function link_ads_id( int $ads_id ): bool { try { $link = $this->services_service->get_google_ads_link( $ads_id ); if ( null === $link ) { $link = $this->services_service->propose_google_ads_link( $ads_id ); } } catch ( MerchantApiException $e ) { throw new ExceptionWithResponseData( $e->getMessage(), $e->getCode(), $e, $e->get_response_body() ); } // The MAPI handshake is ESTABLISHED once both sides have approved; anything // else means the Ads-side acceptance is still pending. return 'ESTABLISHED' !== ( $link['handshake']['approvalState'] ?? '' ); } /** * Get the business information for the connected Merchant Center account. * * @return array The businessInfo resource decoded as an array. * @throws MerchantApiException If the business info can't be retrieved. */ public function get_business_info(): array { return $this->business_info_service->get_business_info(); } /** * Update the business information for the connected Merchant Center account. * * @param array $business_info BusinessInfo fields to write. * @param string $update_mask Comma-separated list of fields to update. * * @return array The updated businessInfo. * @throws MerchantApiException If the business info can't be updated. */ public function update_business_info( array $business_info, string $update_mask ): array { return $this->business_info_service->update_business_info( $business_info, $update_mask ); } /** * Check if we have access to the merchant account. * * A MerchantApiException from the users lookup is caught and reported as no * access (returns false) rather than propagated; it is logged through the * woocommerce_gla_mc_client_exception action fired by the exception. * * @param string $email Email address of the connected account. * * @return bool */ public function has_access( string $email ): bool { try { $user = $this->users_service->get_current_user(); } catch ( MerchantApiException $e ) { // The woocommerce_gla_mc_client_exception action is fired by MerchantApiException::__construct(). return false; } $name_parts = explode( '/', $user['name'] ?? '' ); $user_email = (string) end( $name_parts ); return $email === $user_email && in_array( 'ADMIN', $user['accessRights'] ?? [], true ); } /** * Update the Merchant Center ID to use for requests. * * @param int $id Merchant ID number. * * @return bool */ public function update_merchant_id( int $id ): bool { $previous_id = $this->options->get_merchant_id(); // Cached data source resource names embed the account id (accounts/{id}/dataSources/...), // so a relink to a different account must not reuse the previous account's names. if ( $previous_id && $previous_id !== $id ) { $this->options->delete( OptionsInterface::MAPI_DATA_SOURCES ); } return $this->options->update( OptionsInterface::MERCHANT_ID, $id ); } /** * Render the account-level issues and their resolution actions for the connected account. * * @since 2.7.1 * * @param string $user_input_action_option How user-input actions are rendered (in-app built-in vs redirect). * * @return array The RenderAccountIssuesResponse (renderedIssues with their actions). * @throws Exception When an exception happens in the Merchant API. */ public function get_account_review_status( string $user_input_action_option = MapiIssueResolutionService::USER_INPUT_BUILT_IN ) { try { return $this->issue_resolution_service->render_account_issues( $user_input_action_option ); } catch ( MerchantApiException $e ) { // The woocommerce_gla_mc_client_exception action is fired by MerchantApiException::__construct(). throw new Exception( $e->getMessage(), $e->getCode() ); } } /** * Trigger the in-app account-review action. * * Completes the review request without leaving the store, using the action context and * flow taken from the rendered review action. * * @since 2.7.1 * * @param string $action_context The action context taken from the rendered review action. * @param string $action_flow_id The review action flow id. * @param array $input_values Optional input values for the action flow. * * @return array The Merchant API response. * @throws Exception When the trigger produces an exception on the Merchant API side. */ public function trigger_review_action( string $action_context, string $action_flow_id, array $input_values = [] ): array { try { return $this->issue_resolution_service->trigger_action( $action_context, $action_flow_id, $input_values ); } catch ( MerchantApiException $e ) { // The woocommerce_gla_mc_client_exception action is fired by MerchantApiException::__construct(). throw new Exception( $e->getMessage(), $e->getCode() ); } } }