Fix Google product matching - GTIN is array in attributes
Based on actual API response analysis: - offerId is at top level (e.g., "220216" = WooCommerce product ID) - gtin is nested in attributes.gtin as an ARRAY (e.g., ["850018802833"]) - price is nested in attributes.price.amountMicros Changes: - Index Google products by each GTIN in the attributes.gtin array - Also check attributes.gtins (alternative field name) - Index by MPN if available - Update find_product_by_identifier to check array GTINs with in_array() - Fix price extraction to check attributes.price first, then top-level Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -840,47 +840,29 @@ class Informatiq_SP_Admin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ( $google_products_raw as $gp ) {
|
foreach ( $google_products_raw as $gp ) {
|
||||||
// Index by offerId (direct field).
|
// Index by offerId (top-level field).
|
||||||
if ( ! empty( $gp['offerId'] ) ) {
|
if ( ! empty( $gp['offerId'] ) ) {
|
||||||
$google_products['offer_' . $gp['offerId']] = $gp;
|
$google_products['offer_' . $gp['offerId']] = $gp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index by gtin (direct field).
|
// Index by GTIN - check attributes.gtin which is an ARRAY.
|
||||||
if ( ! empty( $gp['gtin'] ) ) {
|
if ( ! empty( $gp['attributes']['gtin'] ) && is_array( $gp['attributes']['gtin'] ) ) {
|
||||||
$google_products['gtin_' . $gp['gtin']] = $gp;
|
foreach ( $gp['attributes']['gtin'] as $gtin_value ) {
|
||||||
}
|
$google_products['gtin_' . $gtin_value] = $gp;
|
||||||
|
|
||||||
// Also check nested attributes structure (new Merchant API format).
|
|
||||||
if ( ! empty( $gp['attributes']['offerId'] ) ) {
|
|
||||||
$google_products['offer_' . $gp['attributes']['offerId']] = $gp;
|
|
||||||
}
|
|
||||||
if ( ! empty( $gp['attributes']['gtin'] ) ) {
|
|
||||||
$google_products['gtin_' . $gp['attributes']['gtin']] = $gp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check productId which might contain the identifier.
|
|
||||||
if ( ! empty( $gp['productId'] ) ) {
|
|
||||||
// productId format is usually: online:en:US:SKU or channel:language:country:offerId
|
|
||||||
$parts = explode( ':', $gp['productId'] );
|
|
||||||
if ( count( $parts ) >= 4 ) {
|
|
||||||
$extracted_offer = $parts[ count( $parts ) - 1 ];
|
|
||||||
$google_products['offer_' . $extracted_offer] = $gp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check name field which contains full resource path.
|
// Also check attributes.gtins (alternative field name).
|
||||||
if ( ! empty( $gp['name'] ) ) {
|
if ( ! empty( $gp['attributes']['gtins'] ) && is_array( $gp['attributes']['gtins'] ) ) {
|
||||||
// name format: accounts/{account}/products/{product_id}
|
foreach ( $gp['attributes']['gtins'] as $gtin_value ) {
|
||||||
// product_id format: online~en~US~SKU
|
$google_products['gtin_' . $gtin_value] = $gp;
|
||||||
if ( preg_match( '/products\/(.+)$/', $gp['name'], $matches ) ) {
|
|
||||||
$product_id = $matches[1];
|
|
||||||
$parts = explode( '~', $product_id );
|
|
||||||
if ( count( $parts ) >= 4 ) {
|
|
||||||
$extracted_offer = $parts[ count( $parts ) - 1 ];
|
|
||||||
$google_products['offer_' . $extracted_offer] = $gp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Index by MPN if available.
|
||||||
|
if ( ! empty( $gp['attributes']['mpn'] ) ) {
|
||||||
|
$google_products['mpn_' . $gp['attributes']['mpn']] = $gp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get WooCommerce in-stock products (limit to 50 for performance).
|
// Get WooCommerce in-stock products (limit to 50 for performance).
|
||||||
@@ -921,10 +903,12 @@ class Informatiq_SP_Admin {
|
|||||||
$match_type = 'gtin';
|
$match_type = 'gtin';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Google price.
|
// Get Google price (nested in attributes in new Merchant API).
|
||||||
$google_price = null;
|
$google_price = null;
|
||||||
if ( $google_product ) {
|
if ( $google_product ) {
|
||||||
if ( isset( $google_product['price']['amountMicros'] ) ) {
|
if ( isset( $google_product['attributes']['price']['amountMicros'] ) ) {
|
||||||
|
$google_price = (float) $google_product['attributes']['price']['amountMicros'] / 1000000;
|
||||||
|
} elseif ( isset( $google_product['price']['amountMicros'] ) ) {
|
||||||
$google_price = (float) $google_product['price']['amountMicros'] / 1000000;
|
$google_price = (float) $google_product['price']['amountMicros'] / 1000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,9 +300,16 @@ class Informatiq_SP_Google_API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: Use product's own price as reference.
|
// Fallback: Use product's own price as reference.
|
||||||
|
// Price is nested inside attributes in the new Merchant API.
|
||||||
|
if ( isset( $product['attributes']['price']['amountMicros'] ) ) {
|
||||||
|
$price = (float) $product['attributes']['price']['amountMicros'] / 1000000;
|
||||||
|
$this->logger->warning( "No competitive data available for SKU={$sku}, using own price as reference" );
|
||||||
|
return $price;
|
||||||
|
}
|
||||||
|
// Also check top-level price (in case API changes).
|
||||||
if ( isset( $product['price']['amountMicros'] ) ) {
|
if ( isset( $product['price']['amountMicros'] ) ) {
|
||||||
$price = (float) $product['price']['amountMicros'] / 1000000;
|
$price = (float) $product['price']['amountMicros'] / 1000000;
|
||||||
$this->logger->warning( "No competitive data available for SKU={$sku}, using own price as reference" );
|
$this->logger->warning( "No competitive data available for SKU={$sku}, using own price as reference (top-level)" );
|
||||||
return $price;
|
return $price;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,15 +354,31 @@ class Informatiq_SP_Google_API {
|
|||||||
return $product;
|
return $product;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if SKU matches Google's GTIN (for stores where SKU is the barcode).
|
// Check if SKU matches Google's GTIN array (for stores where SKU is the barcode).
|
||||||
if ( isset( $product['gtin'] ) && $product['gtin'] === $sku ) {
|
// GTIN is stored in attributes.gtin as an array.
|
||||||
$this->logger->info( "Product matched by GTIN={$sku} (SKU used as barcode)" );
|
if ( ! empty( $product['attributes']['gtin'] ) && is_array( $product['attributes']['gtin'] ) ) {
|
||||||
return $product;
|
if ( in_array( $sku, $product['attributes']['gtin'], true ) ) {
|
||||||
|
$this->logger->info( "Product matched by attributes.gtin={$sku} (SKU used as barcode)" );
|
||||||
|
return $product;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if separate GTIN field matches.
|
// Also check attributes.gtins (alternative field).
|
||||||
if ( ! empty( $gtin ) && isset( $product['gtin'] ) && $product['gtin'] === $gtin ) {
|
if ( ! empty( $product['attributes']['gtins'] ) && is_array( $product['attributes']['gtins'] ) ) {
|
||||||
return $product;
|
if ( in_array( $sku, $product['attributes']['gtins'], true ) ) {
|
||||||
|
$this->logger->info( "Product matched by attributes.gtins={$sku} (SKU used as barcode)" );
|
||||||
|
return $product;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if separate GTIN parameter matches.
|
||||||
|
if ( ! empty( $gtin ) ) {
|
||||||
|
if ( ! empty( $product['attributes']['gtin'] ) && is_array( $product['attributes']['gtin'] ) ) {
|
||||||
|
if ( in_array( $gtin, $product['attributes']['gtin'], true ) ) {
|
||||||
|
$this->logger->info( "Product matched by GTIN parameter={$gtin}" );
|
||||||
|
return $product;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user