Files
IQ-Dynamic-Google-Pricing/includes/class-informatiq-sp-google-api.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();
}
}
}