<?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\API\Google;
use Automattic\WooCommerce\GoogleListingsAndAds\Exception\ExceptionWithResponseData;
use Automattic\WooCommerce\GoogleListingsAndAds\Google\GoogleHelper;
use Automattic\WooCommerce\GoogleListingsAndAds\Exception\InvalidTerm;
use Automattic\WooCommerce\GoogleListingsAndAds\Exception\InvalidDomainName;
use Automattic\WooCommerce\GoogleListingsAndAds\Internal\ContainerAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\Internal\Interfaces\ContainerAwareInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\Options\TransientsInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;
use Automattic\WooCommerce\GoogleListingsAndAds\Proxies\WC;
use Automattic\WooCommerce\GoogleListingsAndAds\Proxies\WP;
use Automattic\WooCommerce\GoogleListingsAndAds\Utility\DateTimeUtility;
use Automattic\WooCommerce\GoogleListingsAndAds\Utility\ISOUtility;
use Automattic\WooCommerce\GoogleListingsAndAds\Value\TosAccepted;
use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\GuzzleHttp\Client;
use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\Psr\Container\ContainerExceptionInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\Psr\Container\NotFoundExceptionInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\Psr\Http\Client\ClientExceptionInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\GuzzleHttp\Exception\BadResponseException;
use Exception;
defined( 'ABSPATH' ) || exit;
/**
* Class Middleware
*
* Container used for:
* - Ads
* - Client
* - DateTimeUtility
* - GoogleHelper
* - ISOUtility
* - Merchant
* - WC
* - WP
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\API\Google
*/
class Middleware implements ContainerAwareInterface, OptionsAwareInterface {
use ContainerAwareTrait;
use ExceptionTrait;
use OptionsAwareTrait;
use PluginHelper;
/**
* Get all Merchant Accounts associated with the connected account.
*
* @return array
* @throws Exception When an Exception is caught.
* @since 1.7.0
*/
public function get_merchant_accounts(): array {
try {
/** @var Client $client */
$client = $this->container->get( Client::class );
$result = $client->get( $this->get_manager_url( 'merchant-accounts' ) );
$response = json_decode( $result->getBody()->getContents(), true );
$accounts = [];
if ( 200 === $result->getStatusCode() && is_array( $response ) ) {
foreach ( $response as $account ) {
$accounts[] = $account;
}
}
return $accounts;
} catch ( ClientExceptionInterface $e ) {
do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ );
throw new Exception(
$this->client_exception_message( $e, __( 'Error retrieving accounts', 'google-listings-and-ads' ) ),
$e->getCode()
);
}
}
/**
* Create a new Merchant Center account.
*
* @return int Created merchant account ID
*
* @throws Exception When an Exception is caught or we receive an invalid response.
*/
public function create_merchant_account(): int {
$user = wp_get_current_user();
$tos = $this->mark_tos_accepted( 'google-mc', $user->user_email );
if ( ! $tos->accepted() ) {
throw new Exception( __( 'Unable to log accepted TOS', 'google-listings-and-ads' ) );
}
$site_url = esc_url_raw( $this->get_site_url() );
if ( ! wc_is_valid_url( $site_url ) ) {
throw new Exception( __( 'Invalid site URL.', 'google-listings-and-ads' ) );
}
try {
return $this->create_merchant_account_request(
$this->new_account_name(),
$site_url
);
} catch ( InvalidTerm $e ) {
// Try again with a default account name.
return $this->create_merchant_account_request(
$this->default_account_name(),
$site_url
);
}
}
/**
* Send a request to create a merchant account.
*
* @param string $name Site name
* @param string $site_url Website URL
*
* @return int Created merchant account ID
*
* @throws Exception When an Exception is caught or we receive an invalid response.
* @throws InvalidTerm When the account name contains invalid terms.
* @throws InvalidDomainName When the site URL ends with an invalid top-level domain.
* @since 1.5.0
*/
protected function create_merchant_account_request( string $name, string $site_url ): int {
try {
/** @var Client $client */
$client = $this->container->get( Client::class );
/** @var ISOUtility $iso_utility */
$iso_utility = $this->container->get( ISOUtility::class );
/** @var WP $wp */
$wp = $this->container->get( WP::class );
$result = $client->post(
$this->get_manager_url( 'create-merchant' ),
[
'body' => wp_json_encode(
[
'name' => $name,
'websiteUrl' => $site_url,
'timeZone' => $this->get_site_timezone_string(),
'languageCode' => $iso_utility->wp_locale_to_bcp47( $wp->get_user_locale() ),
]
),
]
);
$response = json_decode( $result->getBody()->getContents(), true );
if ( 200 === $result->getStatusCode() && isset( $response['id'] ) ) {
$id = absint( $response['id'] );
$this->container->get( Merchant::class )->update_merchant_id( $id );
return $id;
}
do_action( 'woocommerce_gla_guzzle_invalid_response', $response, __METHOD__ );
$error = $response['message'] ?? __( 'Invalid response when creating account', 'google-listings-and-ads' );
throw new Exception( $error, $result->getStatusCode() );
} catch ( ClientExceptionInterface $e ) {
$message = $this->client_exception_message( $e, __( 'Error creating account', 'google-listings-and-ads' ) );
$status = $e->getCode() ?: 400;
// Content API for Shopping: Invalid account name terms.
// Merchant API: Account name validation failed.
if ( preg_match( '/terms?.* are|is not allowed/', $message ) ||
preg_match( '/the account did not pass validation/i', $message ) ) {
throw InvalidTerm::contains_invalid_terms( $name );
}
$site_url = $this->strip_url_protocol( esc_url_raw( $this->get_site_url() ) );
// Content API for Shopping: Invalid top-level domain name.
if ( strpos( $message, 'URL ends with an invalid top-level domain name' ) !== false ) {
throw InvalidDomainName::create_account_failed_invalid_top_level_domain_name( $site_url );
}
// Merchant API: Invalid homepage URL.
if ( strpos( $message, 'Unable to set homepage URL' ) !== false ) {
throw InvalidDomainName::create_account_failed_invalid_homepage_url( $site_url );
}
do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ );
throw new Exception( $message, $status, $e );
}
}
/**
* Link an existing Merchant Center account.
*
* @param int $id Existing account ID.
*
* @return int
*/
public function link_merchant_account( int $id ): int {
$this->container->get( Merchant::class )->update_merchant_id( $id );
return $id;
}
/**
* Link Merchant Center account to MCA.
*
* @return bool
* @throws Exception When a ClientException is caught or we receive an invalid response.
*/
public function link_merchant_to_mca(): bool {
try {
/** @var Client $client */
$client = $this->container->get( Client::class );
$result = $client->post(
$this->get_manager_url( 'link-merchant' ),
[
'body' => wp_json_encode(
[
'accountId' => $this->options->get_merchant_id(),
]
),
]
);
$response = json_decode( $result->getBody()->getContents(), true );
if ( 200 === $result->getStatusCode() && isset( $response['status'] ) && 'success' === $response['status'] ) {
return true;
}
do_action( 'woocommerce_gla_guzzle_invalid_response', $response, __METHOD__ );
$error = $response['message'] ?? __( 'Invalid response when linking merchant to MCA', 'google-listings-and-ads' );
throw new Exception( $error, $result->getStatusCode() );
} catch ( ClientExceptionInterface $e ) {
do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ );
$message = $this->client_exception_message( $e, __( 'Error linking merchant to MCA', 'google-listings-and-ads' ) );
$status = $e->getCode() ?: 400;
throw new Exception( $message, $status, $e );
}
}
/**
* Claim the website for a MCA.
*
* @param bool $overwrite To enable claim overwriting.
* @return bool
* @throws Exception When an Exception is caught or we receive an invalid response.
*/
public function claim_merchant_website( bool $overwrite = false ): bool {
try {
/** @var Client $client */
$client = $this->container->get( Client::class );
$result = $client->post(
$this->get_manager_url( 'claim-website' ),
[
'body' => wp_json_encode(
[
'accountId' => $this->options->get_merchant_id(),
'overwrite' => $overwrite,
]
),
]
);
$response = json_decode( $result->getBody()->getContents(), true );
if ( 200 === $result->getStatusCode() && isset( $response['status'] ) && 'success' === $response['status'] ) {
do_action( 'woocommerce_gla_site_claim_success', [ 'details' => 'google_manager' ] );
return true;
}
do_action( 'woocommerce_gla_guzzle_invalid_response', $response, __METHOD__ );
do_action( 'woocommerce_gla_site_claim_failure', [ 'details' => 'google_manager' ] );
$error = $response['message'] ?? __( 'Invalid response when claiming website', 'google-listings-and-ads' );
throw new Exception( $error, $result->getStatusCode() );
} catch ( ClientExceptionInterface $e ) {
do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ );
do_action( 'woocommerce_gla_site_claim_failure', [ 'details' => 'google_manager' ] );
if ( $e instanceof BadResponseException ) {
$decoded = json_decode( (string) $e->getResponse()->getBody(), true );
$error = is_array( $decoded ) ? ( $decoded['error'] ?? [] ) : [];
$message = is_array( $error ) && isset( $error['message'] )
? (string) $error['message']
: $this->client_exception_message( $e, __( 'Error claiming website', 'google-listings-and-ads' ) );
throw new ExceptionWithResponseData(
$message,
$e->getCode() ?: 400,
null,
[
'code' => 'API_ERROR',
'error' => $decoded,
]
);
}
$message = $this->client_exception_message( $e, __( 'Error claiming website', 'google-listings-and-ads' ) );
$status = $e->getCode() ?: 400;
throw new Exception( $message, $status, $e );
}
}
/**
* Create a new Google Ads account.
*
* @return array
* @throws Exception When a ClientException is caught, unsupported store country, or we receive an invalid response.
*/
public function create_ads_account(): array {
try {
$country = $this->container->get( WC::class )->get_base_country();
/** @var GoogleHelper $google_helper */
$google_helper = $this->container->get( GoogleHelper::class );
if ( ! $google_helper->is_country_supported( $country ) ) {
throw new Exception( __( 'Store country is not supported', 'google-listings-and-ads' ) );
}
$user = wp_get_current_user();
$tos = $this->mark_tos_accepted( 'google-ads', $user->user_email );
if ( ! $tos->accepted() ) {
throw new Exception( __( 'Unable to log accepted TOS', 'google-listings-and-ads' ) );
}
/** @var Client $client */
$client = $this->container->get( Client::class );
$result = $client->post(
$this->get_manager_url( $country . '/create-customer' ),
[
'body' => wp_json_encode(
[
'descriptive_name' => $this->new_account_name(),
'currency_code' => get_woocommerce_currency(),
'time_zone' => $this->get_site_timezone_string(),
]
),
]
);
$response = json_decode( $result->getBody()->getContents(), true );
if ( 200 === $result->getStatusCode() && isset( $response['resourceName'] ) ) {
/** @var Ads $ads */
$ads = $this->container->get( Ads::class );
$id = $ads->parse_ads_id( $response['resourceName'] );
$ads->update_ads_id( $id );
$ads->use_store_currency();
$billing_url = $response['invitationLink'] ?? '';
$ads->update_billing_url( $billing_url );
$ads->update_ocid_from_billing_url( $billing_url );
return [
'id' => $id,
'billing_url' => $billing_url,
];
}
do_action( 'woocommerce_gla_guzzle_invalid_response', $response, __METHOD__ );
$error = $response['message'] ?? __( 'Invalid response when creating account', 'google-listings-and-ads' );
throw new Exception( $error, $result->getStatusCode() );
} catch ( ClientExceptionInterface $e ) {
do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ );
throw new Exception(
$this->client_exception_message( $e, __( 'Error creating account', 'google-listings-and-ads' ) ),
$e->getCode()
);
}
}
/**
* Link an existing Google Ads account.
*
* @param int $id Existing account ID.
*
* @return array
* @throws Exception When a ClientException is caught or we receive an invalid response.
*/
public function link_ads_account( int $id ): array {
try {
/** @var Client $client */
$client = $this->container->get( Client::class );
$result = $client->post(
$this->get_manager_url( 'link-customer' ),
[
'body' => wp_json_encode(
[
'client_customer' => $id,
]
),
]
);
$response = json_decode( $result->getBody()->getContents(), true );
$name = "customers/{$id}";
if ( 200 === $result->getStatusCode() && isset( $response['resourceName'] ) && 0 === strpos( $response['resourceName'], $name ) ) {
/** @var Ads $ads */
$ads = $this->container->get( Ads::class );
$ads->update_ads_id( $id );
$ads->request_ads_currency();
return [ 'id' => $id ];
}
do_action( 'woocommerce_gla_guzzle_invalid_response', $response, __METHOD__ );
$error = $response['message'] ?? __( 'Invalid response when linking account', 'google-listings-and-ads' );
throw new Exception( $error, $result->getStatusCode() );
} catch ( ClientExceptionInterface $e ) {
do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ );
if ( $e instanceof BadResponseException ) {
$raw = json_decode( $e->getResponse()->getBody()->getContents(), true );
throw new ExceptionWithResponseData(
$raw['message'] ?? __( 'Error linking ads account', 'google-listings-and-ads' ),
$e->getCode() ?: 400,
null,
[
'code' => 'API_ERROR',
'data' => $raw,
]
);
}
throw new Exception(
$this->client_exception_message( $e, __( 'Error linking account', 'google-listings-and-ads' ) ),
$e->getCode()
);
}
}
/**
* Determine whether the TOS have been accepted.
*
* @param string $service Name of service.
*
* @return TosAccepted
*/
public function check_tos_accepted( string $service ): TosAccepted {
try {
/** @var Client $client */
$client = $this->container->get( Client::class );
$result = $client->get( $this->get_tos_url( $service ) );
return new TosAccepted( 200 === $result->getStatusCode(), $result->getBody()->getContents() );
} catch ( ClientExceptionInterface $e ) {
do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ );
return new TosAccepted( false, $e->getMessage() );
}
}
/**
* Record TOS acceptance for a particular email address.
*
* @param string $service Name of service.
* @param string $email
*
* @return TosAccepted
*/
public function mark_tos_accepted( string $service, string $email ): TosAccepted {
try {
/** @var Client $client */
$client = $this->container->get( Client::class );
$result = $client->post(
$this->get_tos_url( $service ),
[
'body' => wp_json_encode(
[
'email' => $email,
]
),
]
);
return new TosAccepted(
200 === $result->getStatusCode(),
$result->getBody()->getContents() ?? $result->getReasonPhrase()
);
} catch ( ClientExceptionInterface $e ) {
do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ );
return new TosAccepted( false, $e->getMessage() );
} catch ( Exception $e ) {
do_action( 'woocommerce_gla_exception', $e, __METHOD__ );
return new TosAccepted( false, $e->getMessage() );
}
}
/**
* Get the TOS endpoint URL
*
* @param string $service Name of service.
*
* @return string
*/
protected function get_tos_url( string $service ): string {
$url = $this->container->get( 'connect_server_root' ) . 'tos';
return $service ? trailingslashit( $url ) . $service : $url;
}
/**
* Get the manager endpoint URL
*
* @param string $name Resource name.
*
* @return string
*/
protected function get_manager_url( string $name = '' ): string {
$url = $this->container->get( 'connect_server_root' ) . 'google/manager';
return $name ? trailingslashit( $url ) . $name : $url;
}
/**
* Get the server endpoint URL
*
* @since 3.2.0
*
* @param string $name Resource name.
*
* @return string
*/
protected function get_server_url( string $name = '' ): string {
$url = $this->container->get( 'connect_server_root' );
return $name ? trailingslashit( $url ) . $name : $url;
}
/**
* Get the Google Shopping Data Integration auth endpoint URL
*
* @return string
*/
public function get_sdi_auth_endpoint(): string {
return $this->container->get( 'connect_server_root' )
. 'google/google-sdi/v1/credentials/partners/WOO_COMMERCE/merchants/'
. $this->strip_url_protocol( $this->get_site_url() )
. '/oauth/redirect:generate'
. '?merchant_id=' . $this->options->get_merchant_id();
}
/**
* Get the URL for the Google SDI merchant update endpoint.
*
* @return string
*/
public function get_sdi_merchant_update_endpoint(): string {
return $this->get_sdi_endpoint() . 'account:connect';
}
/**
* Get the base endpoint to the Google Shopping Data Integration (SDI).
*
* @return string
*/
protected function get_sdi_endpoint(): string {
/**
* Reason for using `rawurlencode` to encode the site URL:
*
* This plugin doesn't natively support linking a Google Merchant
* Center account and the Google Shopping Data Integration API using a
* merchant URL with paths. However, some merchants still use the
* plugin's WP filter `woocommerce_gla_site_url` to achieve this.
*
* However, a site URL containing paths will cause API routing errors
* if it's not encoded, due to the unexpected appearance of `/`.
* For example, when the URL is 'wp.test/ja/shop',
* `[...]/merchants/wp.test/ja/shop/account:connect` will cause an API error.
* The encoded `[...]/merchants/wp.test%2Fja%2Fshop/account:connect` will
* point to the correct route.
*/
return $this->container->get( 'connect_server_root' )
. 'google/google-sdi/v1/credentials/partners/WOO_COMMERCE/merchants/'
. rawurlencode( $this->strip_url_protocol( $this->get_site_url() ) )
. '/';
}
/**
* Performs a request to Google Shopping Data Integration (SDI) to update the merchant center account.
*
* @throws NotFoundExceptionInterface When the container was not found.
* @throws ContainerExceptionInterface When an error happens while retrieving the container.
* @throws Exception When the response status is not successful, or merchant ID is not set.
* @see google-sdi in google/services inside WCS
*/
public function update_sdi_merchant_account() {
try {
if ( ! $this->options->get_merchant_id() ) {
throw new Exception( __( 'Merchant ID must be set before updating in SDI.', 'google-listings-and-ads' ) );
}
/** @var Client $client */
$client = $this->container->get( Client::class );
$result = $client->post(
$this->get_sdi_merchant_update_endpoint(),
[
'body' => wp_json_encode(
[
'merchant_center_id' => $this->options->get_merchant_id(),
'blog_id' => \Jetpack_Options::get_option( 'id' ),
]
),
]
);
// Check the status, since an empty response is returned upon success.
if ( 200 !== $result->getStatusCode() ) {
$response = json_decode( $result->getBody()->getContents(), true );
do_action( 'woocommerce_gla_guzzle_invalid_response', $response, __METHOD__ );
throw new Exception(
__( 'Invalid response when updating merchant account in Google Partner APP.', 'google-listings-and-ads' ),
$result->getStatusCode()
);
}
} catch ( ClientExceptionInterface $e ) {
do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ );
throw new Exception(
$this->client_exception_message( $e, __( 'Error updating merchant account in Google Partner APP.', 'google-listings-and-ads' ) ),
$e->getCode()
);
}
}
/**
* Generate a descriptive name for a new account.
* Use site name if available.
*
* @return string
*/
protected function new_account_name(): string {
$site_name = get_bloginfo( 'name' );
return ! empty( $site_name ) ? $site_name : $this->default_account_name();
}
/**
* Generate a default account name based on the date.
*
* @return string
*/
protected function default_account_name(): string {
return 'Account ' . gmdate( 'dMy' );
}
/**
* Get a timezone string from WP Settings.
*
* @return string
*/
protected function get_site_timezone_string(): string {
/** @var WP $wp */
$wp = $this->container->get( WP::class );
$timezone = $wp->wp_timezone_string();
/** @var DateTimeUtility $datetime_util */
$datetime_util = $this->container->get( DateTimeUtility::class );
return $datetime_util->maybe_convert_tz_string( $timezone );
}
/**
* This function detects if the current account is a sub-account
* This function is cached in the MC_IS_SUBACCOUNT transient
*
* @return bool True if it's a standalone account.
*/
public function is_subaccount(): bool {
/** @var TransientsInterface $transients */
$transients = $this->container->get( TransientsInterface::class );
$is_subaccount = $transients->get( $transients::MC_IS_SUBACCOUNT );
if ( is_null( $is_subaccount ) ) {
$is_subaccount = 0;
$merchant_id = $this->options->get_merchant_id();
$accounts = $this->get_merchant_accounts();
foreach ( $accounts as $account ) {
if ( $account['id'] === $merchant_id && $account['subaccount'] ) {
$is_subaccount = 1;
}
}
$transients->set( $transients::MC_IS_SUBACCOUNT, $is_subaccount );
}
// since transients don't support booleans, we save them as 0/1 and do the conversion here
return boolval( $is_subaccount );
}
/**
* Performs a request to Google Shopping Data Integration (SDI) to get required information in order to form an auth URL.
*
* @return array An array with the JSON response from the WCS server.
* @throws NotFoundExceptionInterface When the container was not found.
* @throws ContainerExceptionInterface When an error happens while retrieving the container.
* @throws Exception When the response status is not successful.
* @see google-sdi in google/services inside WCS
*/
public function get_sdi_auth_params() {
try {
/** @var Client $client */
$client = $this->container->get( Client::class );
$result = $client->get( $this->get_sdi_auth_endpoint() );
$response = json_decode( $result->getBody()->getContents(), true );
if ( 200 !== $result->getStatusCode() ) {
do_action(
'woocommerce_gla_partner_app_auth_failure',
[
'error' => 'response',
'response' => $response,
]
);
do_action( 'woocommerce_gla_guzzle_invalid_response', $response, __METHOD__ );
$error = $response['message'] ?? __( 'Invalid response authenticating partner app.', 'google-listings-and-ads' );
throw new Exception( $error, $result->getStatusCode() );
}
return $response;
} catch ( ClientExceptionInterface $e ) {
do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ );
throw new Exception(
$this->client_exception_message( $e, __( 'Error authenticating Google Partner APP.', 'google-listings-and-ads' ) ),
$e->getCode()
);
}
}
/**
* Fetch incentive credits from the Google Ads API.
*
* @since 3.2.0
*
* @return array The incentive credits data.
* @throws Exception When an error occurs during the request.
*/
public function get_incentive_credits(): array {
$country = $this->container->get( WC::class )->get_base_country();
try {
/** @var Client $client */
$client = $this->container->get( Client::class );
// Send GET request to the incentive credits endpoint using the stores base country.
$result = $client->get(
$this->get_server_url( 'google/ads/incentive-credits' ),
[
'query' => [
'countries' => $country,
],
]
);
$response = json_decode( $result->getBody()->getContents(), true );
if ( 200 !== $result->getStatusCode() ) {
do_action( 'woocommerce_gla_guzzle_invalid_response', $response, __METHOD__ );
$error = $response['message'] ?? __( 'Invalid response when fetching incentive credits.', 'google-listings-and-ads' );
throw new Exception( $error, $result->getStatusCode() );
}
if ( ! empty( $response['offers'] ) && is_array( $response['offers'] ) ) {
$offers = $response['offers'];
$ads_currency = $this->container->get( Ads::class )->get_ads_currency();
$store_currency = $this->container->get( WC::class )->get_woocommerce_currency();
$ads_currency_index = -1;
$store_currency_index = -1;
$index_key = -1;
// Loop through all offers and locate Ads currency and store currency as a fallback.
foreach ( $offers as $index => $offer ) {
if ( $offer['currency'] === $ads_currency ) {
$ads_currency_index = $index;
}
if ( $offer['currency'] === $store_currency ) {
$store_currency_index = $index;
}
}
// Ads account currency is prioritized.
if ( $ads_currency_index !== -1 ) {
$index_key = $ads_currency_index;
} elseif ( $store_currency_index !== -1 ) {
$index_key = $store_currency_index;
} else {
$index_key = array_key_first( $offers );
}
// Include Ads account currency in the response.
$offers[ $index_key ]['ads_currency'] = $ads_currency;
return $offers[ $index_key ];
}
return [];
} catch ( ClientExceptionInterface $e ) {
do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ );
throw new Exception(
$this->client_exception_message( $e, __( 'Error fetching incentive credits.', 'google-listings-and-ads' ) ),
$e->getCode()
);
}
}
/**
* Return the MCA ID for the WooCommerce Connect Server.
*
* @return int Positive MCA ID.
* @throws Exception When the HTTP response is invalid, mcaId is missing, or mcaId is not a positive integer.
*/
public function get_wcs_mca_id(): int {
try {
/** @var Client $client */
$client = $this->container->get( Client::class );
$result = $client->get( $this->get_manager_url( 'mca' ) );
$response = json_decode( $result->getBody()->getContents(), true );
if ( 200 !== $result->getStatusCode() || ! is_array( $response ) || ! isset( $response['mcaId'] ) ) {
throw new Exception(
__( 'Invalid response when retrieving MCA ID from WooCommerce Connect Server.', 'google-listings-and-ads' ),
$result->getStatusCode()
);
}
$mca_id = filter_var(
$response['mcaId'],
FILTER_VALIDATE_INT,
[
'options' => [
'min_range' => 1,
],
]
);
if ( false === $mca_id ) {
throw new Exception(
__( 'Invalid response when retrieving MCA ID from WooCommerce Connect Server.', 'google-listings-and-ads' ),
$result->getStatusCode()
);
}
return $mca_id;
} catch ( ClientExceptionInterface $e ) {
do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ );
throw new Exception(
$this->client_exception_message( $e, __( 'Error retrieving MCA ID from WooCommerce Connect Server', 'google-listings-and-ads' ) ),
$e->getCode()
);
}
}
}