access_key_paapi5 per compatibilità */ add_action('plugins_loaded', function() { $settings = get_option('affiamsh_plugin_settings', []); // Verifica se è necessaria la migrazione // (vecchi campi esistono MA nuovi campi NON esistono) $needs_migration = ( !empty($settings['access_key']) && !empty($settings['secret_key']) && empty($settings['access_key_paapi5']) && empty($settings['access_key_creators']) ); if ($needs_migration) { // Migra i vecchi dati a PA-API 5.0 (erano quelli usati prima) $settings['access_key_paapi5'] = $settings['access_key']; $settings['secret_key_paapi5'] = $settings['secret_key']; // Mantieni i vecchi campi per sicurezza (ma non saranno più usati) // Non li cancelliamo per evitare perdita di dati // Se non c'è api_version impostato, usa paapi5 (era quello prima) if (empty($settings['api_version'])) { $settings['api_version'] = 'paapi5'; } update_option('affiamsh_plugin_settings', $settings); } }); /** * Ottiene un OAuth Access Token per Creators API usando AWS Cognito * Il token viene cachato per 55 minuti (scade dopo 60) */ function affiamsh_get_oauth_token($credential_id, $credential_secret, $version = '2.2') { // Verifica se abbiamo un token valido in cache $cache_key = 'affiamsh_oauth_token_' . md5($credential_id); $cached_token = get_transient($cache_key); if ($cached_token !== false) { return $cached_token; } // Determina il token endpoint basato sulla versione $token_endpoints = [ '2.1' => 'https://creatorsapi.auth.us-east-1.amazoncognito.com/oauth2/token', // NA '2.2' => 'https://creatorsapi.auth.eu-south-2.amazoncognito.com/oauth2/token', // EU '2.3' => 'https://creatorsapi.auth.us-west-2.amazoncognito.com/oauth2/token', // FE ]; $token_url = $token_endpoints[$version] ?? $token_endpoints['2.2']; // Default EU // Richiedi nuovo token $response = wp_remote_post($token_url, [ 'headers' => [ 'Content-Type' => 'application/x-www-form-urlencoded' ], 'body' => [ 'grant_type' => 'client_credentials', 'client_id' => $credential_id, 'client_secret' => $credential_secret, 'scope' => 'creatorsapi/default' // Scope corretto per Creators API ], 'timeout' => 15 ]); if (is_wp_error($response)) { return false; } $http_code = wp_remote_retrieve_response_code($response); if ($http_code !== 200) { return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (empty($data['access_token'])) { return false; } $access_token = $data['access_token']; // Cachea il token per 55 minuti (scade dopo 60) set_transient($cache_key, $access_token, 55 * MINUTE_IN_SECONDS); return $access_token; } /** * Funzione helper per ottenere le credenziali * Restituisce null se non ci sono credenziali valide */ function affiamsh_get_credentials() { $settings = get_option('affiamsh_plugin_settings', []); $api_version = $settings['api_version'] ?? 'creators'; // Determina quali credenziali usare in base all'API selezionata if ($api_version === 'creators') { $credential_id = $settings['credential_id_creators'] ?? ''; $credential_secret = $settings['credential_secret_creators'] ?? ''; $has_credentials = !empty($credential_id) && !empty($credential_secret); if (!$has_credentials || empty($settings['partner_tag'])) { return null; } // Determina la versione basata sul marketplace // EU marketplaces usano version 2.2 $marketplace = $settings['marketplace'] ?? 'www.amazon.com'; $eu_marketplaces = ['www.amazon.co.uk', 'www.amazon.de', 'www.amazon.fr', 'www.amazon.it', 'www.amazon.es', 'www.amazon.nl', 'www.amazon.ae', 'www.amazon.in', 'www.amazon.eg', 'www.amazon.sa', 'www.amazon.se', 'www.amazon.pl', 'www.amazon.be', 'www.amazon.ie', 'www.amazon.tr']; $na_marketplaces = ['www.amazon.com', 'www.amazon.ca', 'www.amazon.com.mx', 'www.amazon.com.br']; $fe_marketplaces = ['www.amazon.co.jp', 'www.amazon.com.au', 'www.amazon.sg']; if (in_array($marketplace, $eu_marketplaces)) { $version = '2.2'; } elseif (in_array($marketplace, $na_marketplaces)) { $version = '2.1'; } elseif (in_array($marketplace, $fe_marketplaces)) { $version = '2.3'; } else { $version = '2.2'; // Default EU } return [ 'credential_id' => $credential_id, 'credential_secret' => $credential_secret, 'partner_tag' => $settings['partner_tag'], 'marketplace' => $marketplace, 'version' => $version, 'api_version' => 'creators' ]; } else { // PA-API 5.0 $access_key = $settings['access_key_paapi5'] ?? ''; $secret_key = $settings['secret_key_paapi5'] ?? ''; $has_credentials = !empty($access_key) && !empty($secret_key); if (!$has_credentials || empty($settings['partner_tag'])) { return null; } $marketplaces = affiamsh_get_amazon_marketplaces(); return [ 'access_key' => $access_key, 'secret_key' => $secret_key, 'partner_tag' => $settings['partner_tag'], 'marketplace' => $settings['marketplace'] ?? 'www.amazon.com', 'region' => $settings['region'] ?? $marketplaces[$settings['marketplace'] ?? 'www.amazon.com'] ?? 'us-east-1', 'api_version' => 'paapi5' ]; } } // Enqueue plugin styles add_action('wp_enqueue_scripts', function () { wp_enqueue_style( 'affiamsh-amazon-products-style', plugins_url('amazon-products.css', __FILE__), [], AFFIAMSH_VER ); }); // Add the settings menu add_action('admin_menu', function () { add_menu_page( 'Affiliate Amazon Shortcode Settings', 'Affiliate Amazon Shortcode', 'manage_options', 'affiamsh-amazon-api-settings', 'affiamsh_amazon_api_settings_page', 'dashicons-cart' ); }); // Get Amazon marketplaces function affiamsh_get_amazon_marketplaces() { return [ 'www.amazon.it' => 'eu-west-1', 'www.amazon.com' => 'us-east-1', 'www.amazon.co.uk' => 'eu-west-1', 'www.amazon.de' => 'eu-central-1', 'www.amazon.fr' => 'eu-west-1', 'www.amazon.es' => 'eu-west-1', 'www.amazon.ca' => 'ca-central-1', 'www.amazon.co.jp' => 'ap-northeast-1', 'www.amazon.in' => 'ap-south-1', ]; } // Get image sizes function affiamsh_get_image_sizes() { return [ 'Medium' => 'Images.Primary.Medium', 'Large' => 'Images.Primary.Large', ]; } // Settings Page function affiamsh_amazon_api_settings_page() { if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['affiamsh_api_settings_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['affiamsh_api_settings_nonce'])), 'affiamsh_save_settings')) { $marketplace = isset($_POST['marketplace']) ? sanitize_text_field(wp_unslash($_POST['marketplace'])) : ''; $marketplaces = affiamsh_get_amazon_marketplaces(); $image_sizes = affiamsh_get_image_sizes(); // ✅ IMPORTANTE: Carica settings esistenti prima di sovrascrivere $existing_settings = get_option('affiamsh_plugin_settings', []); $settings = [ // Credenziali PA-API 5.0 (Legacy) - preserva se non nel POST 'access_key_paapi5' => isset($_POST['access_key_paapi5']) ? sanitize_text_field(wp_unslash($_POST['access_key_paapi5'])) : ($existing_settings['access_key_paapi5'] ?? ''), 'secret_key_paapi5' => isset($_POST['secret_key_paapi5']) ? sanitize_text_field(wp_unslash($_POST['secret_key_paapi5'])) : ($existing_settings['secret_key_paapi5'] ?? ''), // Credenziali Creators API (OAuth 2.0) - preserva se non nel POST 'credential_id_creators' => isset($_POST['credential_id_creators']) ? sanitize_text_field(wp_unslash($_POST['credential_id_creators'])) : ($existing_settings['credential_id_creators'] ?? ''), 'credential_secret_creators' => isset($_POST['credential_secret_creators']) ? sanitize_text_field(wp_unslash($_POST['credential_secret_creators'])) : ($existing_settings['credential_secret_creators'] ?? ''), // Impostazioni comuni 'partner_tag' => isset($_POST['partner_tag']) ? sanitize_text_field(wp_unslash($_POST['partner_tag'])) : ($existing_settings['partner_tag'] ?? ''), 'region' => $marketplaces[$marketplace] ?? 'us-east-1', 'marketplace' => $marketplace, // ✅ IMPORTANTE: Preserva api_version esistente se non nel POST 'api_version' => isset($_POST['api_version']) ? sanitize_text_field(wp_unslash($_POST['api_version'])) : ($existing_settings['api_version'] ?? 'paapi5'), 'num_products' => isset($_POST['num_products']) ? absint(wp_unslash($_POST['num_products'])) : 3, 'num_columns' => isset($_POST['num_columns']) ? absint(wp_unslash($_POST['num_columns'])) : 3, 'image_size' => isset($_POST['image_size']) ? sanitize_text_field(wp_unslash($_POST['image_size'])) : 'Medium', 'font_size' => isset($_POST['font_size']) ? affiamsh_validate_font_size(sanitize_text_field(wp_unslash($_POST['font_size']))) : '16px', ]; // ✅ ENFORCE FREE LIMITS (max 3 products, max 3 columns) $is_pro = affiamsh_is_pro(); if (!$is_pro) { if ($settings['num_products'] > 3) { $settings['num_products'] = 3; echo '

⚠️ FREE version limit: Number of products set to maximum 3. Upgrade to PRO for up to 9 products.

'; } if ($settings['num_columns'] > 3) { $settings['num_columns'] = 3; echo '

⚠️ FREE version limit: Number of columns set to maximum 3.

'; } } update_option('affiamsh_plugin_settings', $settings); echo '

Settings saved successfully.

'; } $settings = get_option('affiamsh_plugin_settings', []); $credentials = affiamsh_get_credentials(); $marketplaces = affiamsh_get_amazon_marketplaces(); $image_sizes = affiamsh_get_image_sizes(); ?>

Amazon API Settings 💎 PRO FREE ⚡ Upgrade to PRO

⚠️ API Credentials Required

To display Amazon products, you need to configure your API credentials below.

Get Creators API credentials → Configure your PA-API 5.0 credentials below.

⚠️ Legacy API Selected

You're currently using PA-API 5.0 which will be deprecated after January 31, 2026.

Please migrate to Creators API as soon as possible to avoid service interruptions.

API Version

Select which Amazon API to use. Creators API is the recommended option going forward.

🆕 Creators API Credentials (OAuth 2.0)

Select which Amazon API to use. Creators API is the recommended option going forward.

Credential ID (Client ID)

Example: 1n26dp0288qdpmtqomel7xxxxx

Credential Secret (Client Secret)

Example: 1nosi490l2rho03dhjnq9f7o3vmufb90drds71kqcfjc1jixxxxx

PA-API 5.0 Credentials (Legacy)

PA-API 5.0 credentials (legacy mode).

Access Key (PA-API 5.0)

Your legacy PA-API 5.0 Access Key

Secret Key (PA-API 5.0)

Your legacy PA-API 5.0 Secret Key

Partner Tag

Your Amazon Affiliate ID (same for both APIs)

Marketplace
Number of Products

🔒 FREE version limited to 3 products. Upgrade to PRO to display up to 9 products per shortcode.

Number of Columns

🔒 4-column layout requires PRO.

Image Size
Font Size

Enter font size, e.g., 14px, 16px, 18px

Shortcode Format

Add this shortcode in your post/page [affiamsh_amazon keyword="Your Keyword"]

Go Premium

Unlock additional features with the Premium version of this plugin. Learn more.


Test API Connection

Click the button below to test your API credentials with a sample request.

'; echo '

✅ API Connection Successful!

'; echo '

Your credentials are working correctly.

'; echo '

Found ' . esc_html($test_result['item_count']) . ' products for test keyword "laptop".

'; echo '
'; } else { echo '
'; echo '

❌ API Connection Failed

'; echo '

' . esc_html($test_result['message']) . '

'; if (!empty($test_result['details'])) { echo '
Error Details'; echo '
' . esc_html($test_result['details']) . '
'; echo '
'; } echo '
'; } } endif; ?> false, 'message' => 'No credentials configured', 'details' => '' ]; } $api_version = $credentials['api_version'] ?? 'creators'; // Test per Creators API (OAuth 2.0 Cognito) if ($api_version === 'creators') { // Step 1: Ottieni OAuth token $access_token = affiamsh_get_oauth_token( $credentials['credential_id'], $credentials['credential_secret'], $credentials['version'] ?? '2.2' ); if ($access_token === false) { return [ 'success' => false, 'message' => 'OAuth authentication failed. Could not obtain access token from Cognito.', 'details' => 'Check your Credential ID and Credential Secret. Version: ' . ($credentials['version'] ?? '2.2') ]; } // Step 2: Test con richiesta API Creators $payload = json_encode([ 'keywords' => 'laptop', 'resources' => ['itemInfo.title'], 'searchIndex' => 'All', 'partnerTag' => $credentials['partner_tag'], 'marketplace' => $credentials['marketplace'], 'itemCount' => 1 ]); // Endpoint Creators API corretto $url = 'https://creatorsapi.amazon/catalog/v1/searchItems'; $url = add_query_arg(['marketplace' => $credentials['marketplace']], $url); // Headers Creators API con Version $headers = [ 'Authorization' => 'Bearer ' . $access_token . ', Version ' . ($credentials['version'] ?? '2.2'), 'Content-Type' => 'application/json', 'x-marketplace' => $credentials['marketplace'] ]; $response = wp_remote_post($url, [ 'headers' => $headers, 'body' => $payload, 'timeout' => 15, 'method' => 'POST', ]); } else { // Test per PA-API 5.0 (AWS Signature) $payload = json_encode([ 'Keywords' => 'laptop', 'Resources' => ['ItemInfo.Title'], 'SearchIndex' => 'All', 'PartnerTag' => $credentials['partner_tag'], 'PartnerType' => 'Associates', 'Marketplace' => $credentials['marketplace'], 'ItemCount' => 1 ]); $host = str_replace('www.', 'webservices.', $credentials['marketplace']); $uriPath = "/paapi5/searchitems"; $url = 'https://' . $host . $uriPath; $awsv4 = new Affiamsh_AwsV4($credentials['access_key'], $credentials['secret_key']); $awsv4->setRegionName($credentials['region']); $awsv4->setServiceName("ProductAdvertisingAPI"); $awsv4->setPath($uriPath); $awsv4->setPayload($payload); $awsv4->setRequestMethod("POST"); $awsv4->addHeader('content-encoding', 'amz-1.0'); $awsv4->addHeader('content-type', 'application/json; charset=utf-8'); $awsv4->addHeader('host', $host); $awsv4->addHeader('x-amz-target', 'com.amazon.paapi5.v1.ProductAdvertisingAPIv1.SearchItems'); $headers = $awsv4->getHeaders(); $response = wp_remote_post($url, [ 'headers' => $headers, 'body' => $payload, 'timeout' => 15, 'method' => 'POST', ]); } // Verifica risposta (comune per entrambe le API) if (is_wp_error($response)) { return [ 'success' => false, 'message' => 'Connection error: ' . $response->get_error_message(), 'details' => '' ]; } $http_code = wp_remote_retrieve_response_code($response); $body = wp_remote_retrieve_body($response); if ($http_code !== 200) { $api_name = $api_version === 'creators' ? 'Creators API' : 'PA-API 5.0'; if ($http_code === 401) { $message = 'Authentication failed with ' . $api_name . '. '; if ($api_version === 'creators') { $message .= 'Make sure your Credential ID and Credential Secret are correct and from Creators API section in Associates Central.'; } else { $message .= 'Check that your Access Key and Secret Key are correct.'; } } else { $message = $api_name . ' returned HTTP ' . $http_code; } return [ 'success' => false, 'message' => $message, 'details' => $body ]; } // Successo - conta items $data = json_decode($body, true); $items = $data['searchResult']['items'] ?? $data['SearchResult']['Items'] ?? []; return [ 'success' => true, 'message' => 'Connection successful!', 'item_count' => count($items), 'details' => '' ]; } // Shortcode function function affiamsh_amazon_handler($atts) { $atts = shortcode_atts(['keyword' => 'smartphone'], $atts, 'affiamsh_amazon'); $settings = get_option('affiamsh_plugin_settings', []); // Ottieni le credenziali $credentials = affiamsh_get_credentials(); // Verifica se le credenziali sono state configurate if ($credentials === null) { $api_version = $settings['api_version'] ?? 'creators'; $api_name = $api_version === 'creators' ? 'Creators API' : 'PA-API 5.0'; return '
' . '

API not configured

' . '

Configure ' . esc_html($api_name) . ' in ' . 'Settings

' . '
'; } // Determina quale API usare $api_version = $settings['api_version'] ?? 'creators'; if ($api_version === 'creators') { return affiamsh_call_creators_api($atts, $settings, $credentials); } else { return affiamsh_call_paapi5($atts, $settings, $credentials); } } add_shortcode('affiamsh_amazon', 'affiamsh_amazon_handler'); /** * Funzione per chiamare la nuova Creators API con OAuth 2.0 Cognito */ function affiamsh_call_creators_api($atts, $settings, $credentials) { // Ottieni OAuth access token $access_token = affiamsh_get_oauth_token( $credentials['credential_id'], $credentials['credential_secret'], $credentials['version'] ?? '2.2' ); if ($access_token === false) { return '
' . '

Authentication failed

' . '

Check credentials in Settings

' . (current_user_can('manage_options') ? '

ID: ' . esc_html(substr($credentials['credential_id'], 0, 10)) . '... (len: ' . strlen($credentials['credential_id']) . ')

' : '') . '
'; } // Construct the payload con naming lowerCamelCase per Creators API $image_sizes = affiamsh_get_image_sizes(); $image_size_key = $settings['image_size'] ?? 'Medium'; $image_size_mapping = [ 'Small' => 'images.primary.small', 'Medium' => 'images.primary.medium', 'Large' => 'images.primary.large' ]; $image_resource = $image_size_mapping[$image_size_key] ?? 'images.primary.medium'; $payload = json_encode([ 'keywords' => $atts['keyword'], 'resources' => [ $image_resource, 'itemInfo.title', 'itemInfo.features', 'offersV2.listings.price' ], 'searchIndex' => 'All', 'partnerTag' => $credentials['partner_tag'], 'marketplace' => $credentials['marketplace'], 'itemCount' => absint($settings['num_products'] ?? 3) ]); // Nuovo endpoint Creators API $url = 'https://creatorsapi.amazon/catalog/v1/searchItems'; $url = add_query_arg(['marketplace' => $credentials['marketplace']], $url); // Headers con formato Creators API (include Version!) $headers = [ 'Authorization' => 'Bearer ' . $access_token . ', Version ' . ($credentials['version'] ?? '2.2'), 'Content-Type' => 'application/json', 'x-marketplace' => $credentials['marketplace'] ]; // Perform the API request $response = wp_remote_post($url, [ 'headers' => $headers, 'body' => $payload, 'timeout' => 20, 'method' => 'POST', ]); if (is_wp_error($response)) { return '

Error: ' . esc_html($response->get_error_message()) . '

'; } $http_code = wp_remote_retrieve_response_code($response); if ($http_code !== 200) { $error_body = wp_remote_retrieve_body($response); // Messaggio discreto per utenti pubblici $error_message = '
'; $error_message .= '

API Error (HTTP ' . esc_html($http_code) . ')

'; // Dettagli tecnici SOLO per admin, molto discreti if (current_user_can('manage_options')) { $error_message .= '
'; $error_message .= 'Technical details'; $error_message .= '
'; if ($http_code === 401 || $http_code === 403) { $error_message .= '

Authentication failed. Possible causes:

'; $error_message .= '
    '; $error_message .= '
  • Incorrect credentials
  • '; $error_message .= '
  • Token expired
  • '; $error_message .= '
  • Account not eligible (needs 10 sales)
  • '; $error_message .= '
'; } $error_message .= '
'; $error_message .= 'API: Creators API (OAuth 2.0)
'; $error_message .= 'Endpoint: ' . esc_html($url) . '
'; $error_message .= 'Version: ' . esc_html($credentials['version'] ?? '2.2') . '
'; $error_message .= 'Marketplace: ' . esc_html($credentials['marketplace']) . '
'; $error_message .= 'Token: ' . ($access_token ? 'Present' : 'Missing') . '

'; $error_message .= 'Response: ' . esc_html($error_body); $error_message .= '
'; $error_message .= '
'; $error_message .= '
'; } $error_message .= '
'; return $error_message; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (empty($data)) { return '

Error: Unable to parse the Amazon server response.

'; } // Gestione risposta Creators API if (empty($data['searchResult']['items'])) { return '

No products found for keyword: ' . esc_html($atts['keyword']) . '

'; } $items = $data['searchResult']['items']; return affiamsh_render_products($items, $settings, $image_size_key); } /** * Funzione per chiamare la vecchia PA-API 5.0 (Legacy) */ function affiamsh_call_paapi5($atts, $settings, $credentials) { // Construct the payload con naming PascalCase per PA-API 5.0 $image_sizes = affiamsh_get_image_sizes(); $image_size_key = $settings['image_size'] ?? 'Medium'; $image_size = $image_sizes[$image_size_key] ?? 'Images.Primary.Medium'; $payload = "{" . " \"Keywords\": \"" . esc_js($atts['keyword']) . "\"," . " \"Resources\": [" . " \"$image_size\"," . " \"ItemInfo.ContentInfo\"," . " \"ItemInfo.Title\"," . " \"Offers.Listings.Price\"" . " ]," . " \"SearchIndex\": \"All\"," . " \"PartnerTag\": \"" . esc_js($credentials['partner_tag']) . "\"," . " \"PartnerType\": \"Associates\"," . " \"Marketplace\": \"" . esc_js($credentials['marketplace']) . "\"," . " \"ItemCount\": " . absint($settings['num_products'] ?? 3) . "}"; $host = str_replace('www.', 'webservices.', $credentials['marketplace']); $uriPath = "/paapi5/searchitems"; $url = 'https://' . $host . $uriPath; // Sign the request using AwsV4 $awsv4 = new Affiamsh_AwsV4($credentials['access_key'], $credentials['secret_key']); $awsv4->setRegionName($credentials['region']); $awsv4->setServiceName("ProductAdvertisingAPI"); $awsv4->setPath($uriPath); $awsv4->setPayload($payload); $awsv4->setRequestMethod("POST"); $awsv4->addHeader('content-encoding', 'amz-1.0'); $awsv4->addHeader('content-type', 'application/json; charset=utf-8'); $awsv4->addHeader('host', $host); $awsv4->addHeader('x-amz-target', 'com.amazon.paapi5.v1.ProductAdvertisingAPIv1.SearchItems'); $headers = $awsv4->getHeaders(); // Perform the API request $response = wp_remote_post($url, [ 'headers' => $headers, 'body' => $payload, 'timeout' => 20, 'method' => 'POST', ]); if (is_wp_error($response)) { return '

Error: ' . esc_html($response->get_error_message()) . '

'; } $http_code = wp_remote_retrieve_response_code($response); if ($http_code !== 200) { $error_body = wp_remote_retrieve_body($response); $error_data = json_decode($error_body, true); // Messaggio discreto per utenti pubblici $error_message = '
'; $error_message .= '

API Error (HTTP ' . esc_html($http_code) . ')

'; // Dettagli tecnici SOLO per admin, molto discreti if (current_user_can('manage_options')) { $error_message .= '
'; $error_message .= 'Technical details'; $error_message .= '
'; if ($http_code === 401) { $error_message .= '

Authentication failed. Possible causes:

'; $error_message .= '
    '; $error_message .= '
  • Incorrect Access Key or Secret Key
  • '; $error_message .= '
  • Credentials expired or revoked
  • '; $error_message .= '
  • Need to regenerate in Amazon Associates
  • '; $error_message .= '
'; } $error_message .= '
'; $error_message .= 'API: PA-API 5.0 (Legacy)
'; $error_message .= 'Endpoint: ' . esc_html($url) . '

'; $error_message .= 'Response: ' . esc_html($error_body); $error_message .= '
'; $error_message .= '
'; $error_message .= '
'; } $error_message .= '
'; return $error_message; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (empty($data)) { return '

Error: Unable to parse the Amazon server response.

'; } if (empty($data['SearchResult']['Items'])) { return '

No products found.

'; } return affiamsh_render_products($data['SearchResult']['Items'], $settings, $image_size_key); } /** * Funzione per renderizzare i prodotti (comune per entrambe le API) */ function affiamsh_render_products($items, $settings, $image_size_key) { // Generate HTML $num_products = $settings['num_products'] ?? 3; $num_columns = $settings['num_columns'] ?? 3; $font_size = esc_attr($settings['font_size'] ?? '16px'); // ✅ ENFORCE FREE LIMITS at render time $is_pro = affiamsh_is_pro(); if (!$is_pro) { $num_products = min($num_products, 3); // Max 3 products for FREE $num_columns = min($num_columns, 3); // Max 3 columns for FREE } // Get PRO options $pro_opts = get_option('affiamsh_pro_display_options', [ 'template' => 'card', 'theme' => 'slate', 'text_color' => '', 'title_color' => '', 'price_color' => '' ]); $template = isset($pro_opts['template']) && !empty($pro_opts['template']) ? $pro_opts['template'] : 'card'; $theme = isset($pro_opts['theme']) && !empty($pro_opts['theme']) ? $pro_opts['theme'] : 'slate'; // Build classes $container_classes = 'affiamsh-amazon-products template-' . esc_attr($template); $container_classes .= ' affiamsh-theme-' . esc_attr($theme); // Build inline styles con CSS variables $inline_styles = "display: grid; grid-template-columns: repeat(" . esc_attr($num_columns) . ", 1fr); gap: 20px;margin-bottom:20px;"; // Add custom color overrides SOLO se theme è CUSTOM e PRO è attivo if ($is_pro && $theme === 'custom') { if (!empty($pro_opts['text_color'])) { $inline_styles .= " --affiamsh-text: " . esc_attr($pro_opts['text_color']) . " !important;"; } if (!empty($pro_opts['title_color'])) { $inline_styles .= " --affiamsh-title: " . esc_attr($pro_opts['title_color']) . " !important;"; } if (!empty($pro_opts['price_color'])) { $inline_styles .= " --affiamsh-price: " . esc_attr($pro_opts['price_color']) . " !important;"; } if (!empty($pro_opts['box_bg'])) { $inline_styles .= " --affiamsh-bg: " . esc_attr($pro_opts['box_bg']) . " !important;"; } if (!empty($pro_opts['ticket_bg'])) { $inline_styles .= " --affiamsh-discount-bg: " . esc_attr($pro_opts['ticket_bg']) . " !important;"; } if (!empty($pro_opts['discount_fg'])) { $inline_styles .= " --affiamsh-discount-fg: " . esc_attr($pro_opts['discount_fg']) . " !important;"; } if (!empty($pro_opts['box_bg_hover'])) { $inline_styles .= " --affiamsh-bg-hover: " . esc_attr($pro_opts['box_bg_hover']) . " !important;"; } } $html = '
'; foreach ($items as $index => $item) { if ($index >= $num_products) break; // Supporto per entrambi i formati di naming (camelCase e PascalCase) $title = esc_html( $item['itemInfo']['title']['displayValue'] ?? $item['ItemInfo']['Title']['DisplayValue'] ?? 'Title not available' ); $short_title = (strlen($title) > 50) ? substr($title, 0, 50) . '...' : $title; $_img_key = strtolower($image_size_key); $image = esc_url( $item['images']['primary'][$_img_key]['url'] ?? $item['Images']['Primary'][$image_size_key]['URL'] ?? '' ); $link = esc_url( $item['detailPageURL'] ?? $item['DetailPageURL'] ?? '#' ); $price = $item['offersV2']['listings'][0]['price']['money']['displayAmount'] ?? $item['offersV2']['listings'][0]['price']['displayAmount'] ?? $item['offers']['listings'][0]['price']['displayAmount'] ?? $item['Offers']['Listings'][0]['Price']['DisplayAmount'] ?? 'Price not available'; $percentage = $item['offersV2']['listings'][0]['price']['savings']['percentage'] ?? $item['offers']['listings'][0]['price']['savings']['percentage'] ?? $item['Offers']['Listings'][0]['Price']['Savings']['Percentage'] ?? null; $html .= '
'; $html .= ''; $html .= '' . $title . ''; $html .= '
' . $short_title . '
'; $html .= '
'; $price_class = 'affiamsh-amazon-product-price'; if ($percentage) { $html .= '

' . '-' . esc_html($percentage) . '% ' . '' . esc_html($price) . '' . '

'; } else { $html .= '

' . esc_html($price) . '

'; } $html .= '
'; } $html .= '
'; return $html; } /** * Classe AwsV4 */ class Affiamsh_AwsV4 { private $accessKey = null; private $secretKey = null; private $path = null; private $regionName = null; private $serviceName = null; private $httpMethodName = null; private $queryParametes = array(); private $awsHeaders = array(); private $payload = ""; private $HMACAlgorithm = "AWS4-HMAC-SHA256"; private $aws4Request = "aws4_request"; private $strSignedHeader = null; private $xAmzDate = null; private $currentDate = null; public function __construct($accessKey, $secretKey) { $this->accessKey = $accessKey; $this->secretKey = $secretKey; $this->xAmzDate = $this->getTimeStamp(); $this->currentDate = $this->getDate(); } function setPath($path) { $this->path = $path; } function setServiceName($serviceName) { $this->serviceName = $serviceName; } function setRegionName($regionName) { $this->regionName = $regionName; } function setPayload($payload) { $this->payload = $payload; } function setRequestMethod($method) { $this->httpMethodName = $method; } function addHeader($headerName, $headerValue) { $this->awsHeaders[$headerName] = $headerValue; } private function prepareCanonicalRequest() { $canonicalURL = ""; $canonicalURL .= $this->httpMethodName . "\n"; $canonicalURL .= $this->path . "\n" . "\n"; $signedHeaders = ''; foreach ($this->awsHeaders as $key => $value) { $signedHeaders .= $key . ";"; $canonicalURL .= $key . ":" . $value . "\n"; } $canonicalURL .= "\n"; $this->strSignedHeader = substr($signedHeaders, 0, -1); $canonicalURL .= $this->strSignedHeader . "\n"; $canonicalURL .= $this->generateHex($this->payload); return $canonicalURL; } private function prepareStringToSign($canonicalURL) { $stringToSign = ''; $stringToSign .= $this->HMACAlgorithm . "\n"; $stringToSign .= $this->xAmzDate . "\n"; $stringToSign .= $this->currentDate . "/" . $this->regionName . "/" . $this->serviceName . "/" . $this->aws4Request . "\n"; $stringToSign .= $this->generateHex($canonicalURL); return $stringToSign; } private function calculateSignature($stringToSign) { $signatureKey = $this->getSignatureKey($this->secretKey, $this->currentDate, $this->regionName, $this->serviceName); $signature = hash_hmac("sha256", $stringToSign, $signatureKey, true); $strHexSignature = strtolower(bin2hex($signature)); return $strHexSignature; } public function getHeaders() { $this->awsHeaders['x-amz-date'] = $this->xAmzDate; ksort($this->awsHeaders); $canonicalURL = $this->prepareCanonicalRequest(); $stringToSign = $this->prepareStringToSign($canonicalURL); $signature = $this->calculateSignature($stringToSign); if ($signature) { $this->awsHeaders['Authorization'] = $this->buildAuthorizationString($signature); return $this->awsHeaders; } } private function buildAuthorizationString($strSignature) { return $this->HMACAlgorithm . " " . "Credential=" . $this->accessKey . "/" . $this->getDate() . "/" . $this->regionName . "/" . $this->serviceName . "/" . $this->aws4Request . "," . "SignedHeaders=" . $this->strSignedHeader . "," . "Signature=" . $strSignature; } private function generateHex($data) { return strtolower(bin2hex(hash("sha256", $data, true))); } private function getSignatureKey($key, $date, $regionName, $serviceName) { $kSecret = "AWS4" . $key; $kDate = hash_hmac("sha256", $date, $kSecret, true); $kRegion = hash_hmac("sha256", $regionName, $kDate, true); $kService = hash_hmac("sha256", $serviceName, $kRegion, true); $kSigning = hash_hmac("sha256", $this->aws4Request, $kService, true); return $kSigning; } private function getTimeStamp() { return gmdate("Ymd\THis\Z"); } private function getDate() { return gmdate("Ymd"); } } // === PRO Licensing & Display Options === if (!defined('AFFIAMSH_PRO_KEY_PLAIN')) { define('AFFIAMSH_PRO_KEY_PLAIN', 'AMSH1899271'); } if (!function_exists('affiamsh_is_pro')) { function affiamsh_is_pro(): bool { return (bool) get_option('affiamsh_is_pro', false); } } add_action('admin_menu', function () { add_submenu_page( 'affiamsh-amazon-api-settings', __('Activate PRO', 'affiliate-amazon-shortcode'), __('Activate PRO', 'affiliate-amazon-shortcode'), 'manage_options', 'affiamsh-activate-pro', 'affiamsh_pro_activation_page' ); add_submenu_page( 'affiamsh-amazon-api-settings', __('Display (PRO)', 'affiliate-amazon-shortcode'), __('Display (PRO)', 'affiliate-amazon-shortcode'), 'manage_options', 'affiamsh-pro-display', 'affiamsh_pro_display_settings_page' ); }); add_action('admin_init', function () { if (!isset($_POST['affiamsh_activate_pro'])) { return; } if (!current_user_can('manage_options')) { return; } check_admin_referer('affiamsh_activate_pro'); $key = isset($_POST['affiamsh_license_key']) ? sanitize_text_field( wp_unslash($_POST['affiamsh_license_key']) ) : ''; if (hash_equals(AFFIAMSH_PRO_KEY_PLAIN, $key)) { update_option('affiamsh_is_pro', 1); update_option('affiamsh_license_key', $key); add_settings_error('affiamsh_pro', 'pro_ok', __('PRO version successful activated.', 'affiliate-amazon-shortcode'), 'updated'); } else { update_option('affiamsh_is_pro', 0); add_settings_error('affiamsh_pro', 'pro_ko', __('License Code Invalid.', 'affiliate-amazon-shortcode'), 'error'); } }); add_action('admin_init', function () { register_setting('affiamsh_pro_display_group', 'affiamsh_pro_display_options', function ($value) { $out = [ 'template' => 'card', 'theme' => 'slate', 'text_color' => '', 'title_color' => '', 'price_color' => '', 'box_bg' => '', 'ticket_bg' => '', 'discount_fg' => '', 'box_bg_hover' => '', ]; if (is_array($value)) { $tpl = isset($value['template']) ? sanitize_text_field($value['template']) : 'card'; $out['template'] = in_array($tpl, ['card','list'], true) ? $tpl : 'card'; $allowed_themes = ['slate','blue','emerald','amber','rose','violet','indigo','teal','zinc','custom']; $th = isset($value['theme']) ? sanitize_text_field($value['theme']) : 'slate'; if (!in_array($th, $allowed_themes, true)) { $th = 'slate'; } if (!affiamsh_is_pro()) { $th = 'slate'; } $out['theme'] = $th; if ($th === 'custom') { foreach (['text_color','title_color','price_color','box_bg','ticket_bg','discount_fg','box_bg_hover'] as $k) { $c = isset($value[$k]) ? sanitize_text_field($value[$k]) : ''; if ($c && preg_match('/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', $c)) { $out[$k] = $c; } } } else { $out['text_color'] = ''; $out['title_color'] = ''; $out['price_color'] = ''; $out['box_bg'] = ''; $out['ticket_bg'] = ''; $out['discount_fg'] = ''; $out['box_bg_hover'] = ''; } } return $out; }); }); function affiamsh_pro_activation_page() { ?>

✅

'card', 'theme' => 'slate', 'text_color' => '', 'title_color' => '', 'price_color' => '', 'box_bg' => '', 'ticket_bg' => '', 'discount_fg' => '', 'box_bg_hover' => '', ]); $theme = isset($opts['theme']) ? $opts['theme'] : 'slate'; $is_pro = affiamsh_is_pro(); $show_colors = ($theme === 'custom'); ?>

Es. #333333

Es. #111111

Es. #b12704

Es. #ffffff

E.g. #f8fafc

Es. #fff1f2

Es. #be123c

'.esc_html__('Settings','affiliate-amazon-shortcode').''; $links[] = ''.esc_html__('Activate PRO','affiliate-amazon-shortcode').''; $links[] = ''.esc_html__('Purchase PRO','affiliate-amazon-shortcode').''; return $links; }); add_action('admin_post_affiamsh_reset_pro', function () { if (!current_user_can('manage_options')) { wp_die( esc_html__( 'Forbidden', 'affiliate-amazon-shortcode' ) ); } check_admin_referer('affiamsh_reset_pro'); delete_option('affiamsh_is_pro'); add_settings_error('affiamsh_pro', 'pro_reset', __('PRO disabled. You are in FREE version.', 'affiliate-amazon-shortcode'), 'updated'); wp_redirect( admin_url('admin.php?page=affiamsh-activate-pro') ); exit; }); add_action('wp_enqueue_scripts', function () { wp_register_script('affiamsh-frontend', '', [], AFFIAMSH_VER, true); wp_enqueue_script('affiamsh-frontend'); $opts = get_option('affiamsh_pro_display_options', []); $data = [ 'isPro' => affiamsh_is_pro() ? 1 : 0, 'template' => isset($opts['template']) ? $opts['template'] : 'default', 'theme' => isset($opts['theme']) ? $opts['theme'] : 'slate', ]; $js = '(function(d,w){try{var CFG=' . wp_json_encode($data) . ';' . 'function injectWatermark(){' . 'if(CFG.isPro){return;}' . 'var wrappers=d.querySelectorAll(".affiamsh-amazon-products");' . 'wrappers.forEach(function(w){' . 'if(w.querySelector(".affiamsh-watermark"))return;' . 'var wm=d.createElement("span");' . 'wm.className="affiamsh-watermark";' . 'wm.setAttribute("aria-hidden","true");' . 'wm.textContent="Made with Affiliate Amazon Shortcode Plugin";' . 'w.appendChild(wm);' . 'try{' . 'var cs=w.ownerDocument.defaultView.getComputedStyle(w);' . 'if(!cs||cs.position==="static"){w.style.position="relative";}' . '}catch(e){if(!w.style.position){w.style.position="relative";}}' . '});' . '}' . 'injectWatermark();' . 'w.addEventListener("load",injectWatermark);' . '}catch(e){}})(document,window);'; wp_add_inline_script('affiamsh-frontend', $js); }, 40); ?>