287 lines
7.8 KiB
PHP
287 lines
7.8 KiB
PHP
<?php
|
|
/**
|
|
* Google Merchant Center API integration.
|
|
*
|
|
* @package InformatiqSmartPricing
|
|
*/
|
|
|
|
// Exit if accessed directly.
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Google API class.
|
|
*/
|
|
class Informatiq_SP_Google_API {
|
|
/**
|
|
* Google Client instance.
|
|
*
|
|
* @var Google_Client
|
|
*/
|
|
private $client;
|
|
|
|
/**
|
|
* Google Shopping Content service.
|
|
*
|
|
* @var Google_Service_ShoppingContent
|
|
*/
|
|
private $service;
|
|
|
|
/**
|
|
* Merchant ID.
|
|
*
|
|
* @var string
|
|
*/
|
|
private $merchant_id;
|
|
|
|
/**
|
|
* Logger instance.
|
|
*
|
|
* @var Informatiq_SP_Logger
|
|
*/
|
|
private $logger;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param string $merchant_id Google Merchant ID.
|
|
* @param string $service_account Service account JSON.
|
|
* @param Informatiq_SP_Logger $logger Logger instance.
|
|
* @throws Exception If authentication fails.
|
|
*/
|
|
public function __construct( $merchant_id, $service_account, $logger ) {
|
|
$this->merchant_id = $merchant_id;
|
|
$this->logger = $logger;
|
|
|
|
try {
|
|
$this->authenticate( $service_account );
|
|
} catch ( Exception $e ) {
|
|
$this->logger->error( 'Google API authentication failed: ' . $e->getMessage() );
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Authenticate with Google API.
|
|
*
|
|
* @param string $service_account Service account JSON.
|
|
* @throws Exception If authentication fails.
|
|
*/
|
|
private function authenticate( $service_account ) {
|
|
// Decode service account JSON.
|
|
$credentials = json_decode( $service_account, true );
|
|
|
|
if ( json_last_error() !== JSON_ERROR_NONE ) {
|
|
throw new Exception( 'Invalid service account JSON: ' . json_last_error_msg() );
|
|
}
|
|
|
|
// Initialize Google Client.
|
|
$this->client = new Google_Client();
|
|
$this->client->setApplicationName( 'Informatiq Smart Pricing' );
|
|
$this->client->setScopes( array( 'https://www.googleapis.com/auth/content' ) );
|
|
$this->client->setAuthConfig( $credentials );
|
|
|
|
// Initialize Shopping Content service.
|
|
$this->service = new Google_Service_ShoppingContent( $this->client );
|
|
}
|
|
|
|
/**
|
|
* Get competitive pricing for a product.
|
|
*
|
|
* @param string $sku Product SKU.
|
|
* @param string $gtin Product GTIN (optional).
|
|
* @return float|null Lowest competitor price or null if not found.
|
|
*/
|
|
public function get_competitive_price( $sku, $gtin = '' ) {
|
|
try {
|
|
// Search for the product using SKU or GTIN.
|
|
$product_id = $this->find_product_by_identifier( $sku, $gtin );
|
|
|
|
if ( ! $product_id ) {
|
|
$this->logger->warning( "Product not found in Google Merchant Center: SKU={$sku}, GTIN={$gtin}" );
|
|
return null;
|
|
}
|
|
|
|
// Get competitive pricing data.
|
|
$competitive_price = $this->fetch_competitive_price( $product_id );
|
|
|
|
return $competitive_price;
|
|
|
|
} catch ( Exception $e ) {
|
|
$this->logger->error( 'Error fetching competitive price: ' . $e->getMessage() );
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find product by SKU or GTIN.
|
|
*
|
|
* @param string $sku Product SKU.
|
|
* @param string $gtin Product GTIN.
|
|
* @return string|null Product ID or null if not found.
|
|
*/
|
|
private function find_product_by_identifier( $sku, $gtin ) {
|
|
try {
|
|
// List products from merchant center.
|
|
$parameters = array();
|
|
|
|
// Add pagination support.
|
|
$max_results = 250;
|
|
$parameters['maxResults'] = $max_results;
|
|
|
|
$products = $this->service->products->listProducts( $this->merchant_id, $parameters );
|
|
|
|
foreach ( $products->getResources() as $product ) {
|
|
// Check if SKU or GTIN matches.
|
|
$product_data = $product->toSimpleObject();
|
|
|
|
if ( isset( $product_data->offerId ) && $product_data->offerId === $sku ) {
|
|
return $product->getId();
|
|
}
|
|
|
|
if ( ! empty( $gtin ) && isset( $product_data->gtin ) && $product_data->gtin === $gtin ) {
|
|
return $product->getId();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
|
|
} catch ( Exception $e ) {
|
|
$this->logger->error( 'Error finding product: ' . $e->getMessage() );
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch competitive price for a product.
|
|
*
|
|
* @param string $product_id Product ID.
|
|
* @return float|null Lowest competitor price or null if not available.
|
|
*/
|
|
private function fetch_competitive_price( $product_id ) {
|
|
try {
|
|
// Get product status which includes competitive pricing data.
|
|
$product_status = $this->service->productstatuses->get( $this->merchant_id, $product_id );
|
|
|
|
// Extract competitive pricing data.
|
|
$price_insights = $product_status->getItemLevelIssues();
|
|
|
|
// Try to get the product to access price competitiveness.
|
|
$product = $this->service->products->get( $this->merchant_id, $product_id );
|
|
|
|
// Check if we have access to competitive visibility data via Reports API.
|
|
// Note: This requires the Merchant Center account to have competitor data enabled.
|
|
$lowest_price = $this->get_lowest_price_from_competitivevisibility( $product_id );
|
|
|
|
if ( $lowest_price ) {
|
|
return $lowest_price;
|
|
}
|
|
|
|
// Fallback: If competitive visibility is not available, we'll need to use
|
|
// the product's own price as reference (this happens when no competitor data exists).
|
|
$product_data = $product->toSimpleObject();
|
|
|
|
if ( isset( $product_data->price->value ) ) {
|
|
$this->logger->warning( "No competitive data available for product {$product_id}, using own price as reference" );
|
|
return (float) $product_data->price->value;
|
|
}
|
|
|
|
return null;
|
|
|
|
} catch ( Exception $e ) {
|
|
$this->logger->error( 'Error fetching competitive price: ' . $e->getMessage() );
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get lowest price from competitive visibility data.
|
|
*
|
|
* @param string $product_id Product ID.
|
|
* @return float|null Lowest competitor price or null.
|
|
*/
|
|
private function get_lowest_price_from_competitivevisibility( $product_id ) {
|
|
try {
|
|
// Use the Reports API to get competitive pricing data.
|
|
// This requires the account to have access to competitive pricing insights.
|
|
$service = new Google_Service_ShoppingContent_Reports( $this->client );
|
|
|
|
// Note: The actual implementation depends on the specific API endpoints available.
|
|
// For this implementation, we'll use a simplified approach.
|
|
|
|
// The competitive visibility API may not be directly accessible via standard API.
|
|
// In production, you might need to:
|
|
// 1. Enable Merchant Center competitive visibility
|
|
// 2. Use the Shopping Ads API
|
|
// 3. Or scrape data from Google Merchant Center dashboard (not recommended)
|
|
|
|
// For now, return null to indicate no competitive data found.
|
|
// The calling code will handle this gracefully.
|
|
return null;
|
|
|
|
} catch ( Exception $e ) {
|
|
$this->logger->error( 'Error accessing competitive visibility data: ' . $e->getMessage() );
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test API connection.
|
|
*
|
|
* @return bool True if connection successful.
|
|
*/
|
|
public function test_connection() {
|
|
try {
|
|
// Try to list products to test connection.
|
|
$parameters = array( 'maxResults' => 1 );
|
|
$this->service->products->listProducts( $this->merchant_id, $parameters );
|
|
|
|
$this->logger->info( 'Google API connection test successful' );
|
|
return true;
|
|
|
|
} catch ( Exception $e ) {
|
|
$this->logger->error( 'Google API connection test failed: ' . $e->getMessage() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all products from Merchant Center.
|
|
*
|
|
* @return array Array of products.
|
|
*/
|
|
public function get_all_products() {
|
|
try {
|
|
$all_products = array();
|
|
$page_token = null;
|
|
|
|
do {
|
|
$parameters = array(
|
|
'maxResults' => 250,
|
|
);
|
|
|
|
if ( $page_token ) {
|
|
$parameters['pageToken'] = $page_token;
|
|
}
|
|
|
|
$response = $this->service->products->listProducts( $this->merchant_id, $parameters );
|
|
$products = $response->getResources();
|
|
|
|
if ( $products ) {
|
|
$all_products = array_merge( $all_products, $products );
|
|
}
|
|
|
|
$page_token = $response->getNextPageToken();
|
|
|
|
} while ( $page_token );
|
|
|
|
return $all_products;
|
|
|
|
} catch ( Exception $e ) {
|
|
$this->logger->error( 'Error fetching products: ' . $e->getMessage() );
|
|
return array();
|
|
}
|
|
}
|
|
}
|