0) ? $v : 1;
}
function affiamsh_bump_cache_ver() {
$v = affiamsh_get_cache_ver();
update_option('affiamsh_cache_ver', $v + 1, false);
}
/**
* Invalida la cache quando salvi un post/pagina che contiene lo shortcode.
*/
add_action('save_post', function($post_id, $post, $update) {
if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) return;
if (!isset($post->post_content) || !is_string($post->post_content)) return;
if (strpos($post->post_content, '[affiamsh_amazon') !== false) {
affiamsh_bump_cache_ver();
}
}, 10, 3);
/**
* Migrazione automatica dei dati dalla versione precedente
* Converte access_key -> 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['credential_id_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';
}
// ✅ FIX: Aggiorna region basandosi sul marketplace
// Questo risolve problemi di InvalidSignature con PA-API 5.0
if (!empty($settings['marketplace'])) {
$marketplaces = affiamsh_get_amazon_marketplaces();
$settings['region'] = $marketplaces[$settings['marketplace']] ?? 'us-east-1';
}
update_option('affiamsh_plugin_settings', $settings);
affiamsh_bump_cache_ver();
}
// ✅ FIX AGGIUNTIVO: Verifica region anche per chi ha già migrato
// Questo risolve InvalidSignature per utenti che hanno già v1.5 installata
if (!empty($settings['marketplace'])) {
$marketplaces = affiamsh_get_amazon_marketplaces();
$expected_region = $marketplaces[$settings['marketplace']] ?? 'us-east-1';
// Se region è sbagliata o mancante, aggiornala
if (empty($settings['region']) || $settings['region'] !== $expected_region) {
$settings['region'] = $expected_region;
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;
}
$marketplace = $settings['marketplace'] ?? 'www.amazon.com';
$marketplaces = affiamsh_get_amazon_marketplaces();
// ✅ CRITICAL FIX: SEMPRE calcola region dal marketplace
// NON usare region dal database (potrebbe essere sbagliata)
$region = $marketplaces[$marketplace] ?? 'us-east-1';
return [
'access_key' => $access_key,
'secret_key' => $secret_key,
'partner_tag' => $settings['partner_tag'],
'marketplace' => $marketplace,
'region' => $region, // ← Sempre calcolata, MAI dal DB
'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() {
// ✅ MAPPING UFFICIALE Amazon PA-API 5.0
// Source: https://webservices.amazon.com/paapi5/documentation/common-request-parameters.html
return [
// Europe - ALL use eu-west-1 for PA-API 5.0
'www.amazon.de' => 'eu-west-1',
'www.amazon.co.uk' => 'eu-west-1',
'www.amazon.fr' => 'eu-west-1',
'www.amazon.it' => 'eu-west-1',
'www.amazon.es' => 'eu-west-1',
'www.amazon.nl' => 'eu-west-1',
'www.amazon.se' => 'eu-west-1',
'www.amazon.pl' => 'eu-west-1',
'www.amazon.be' => 'eu-west-1',
'www.amazon.ie' => 'eu-west-1',
// North America
'www.amazon.com' => 'us-east-1',
'www.amazon.ca' => 'us-east-1',
'www.amazon.com.mx' => 'us-east-1',
// Far East
'www.amazon.co.jp' => 'us-west-2',
'www.amazon.com.au' => 'us-west-2',
'www.amazon.sg' => 'us-west-2',
// India
'www.amazon.in' => 'eu-west-1',
// Brazil
'www.amazon.com.br' => 'us-east-1',
// Middle East & Africa
'www.amazon.ae' => 'eu-west-1',
'www.amazon.sa' => 'eu-west-1',
'www.amazon.eg' => 'eu-west-1',
'www.amazon.tr' => 'eu-west-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);
affiamsh_bump_cache_ver();
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();
?>
⚠️ 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.
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".
';
if (!empty($test_result['details'])) {
echo '
Show Debug Info
';
echo '' . esc_html($test_result['details']) . '
';
echo '';
}
echo '
';
} else {
echo '';
echo '
❌ API Connection Failed
';
echo '
' . esc_html($test_result['message']) . '
';
if (!empty($test_result['details'])) {
echo '
Debug Information (Important!)
';
echo '' . esc_html($test_result['details']) . '
';
echo '';
}
echo '
';
}
}
endif;
?>
false,
'message' => 'No credentials configured',
'details' => 'Please configure your API credentials in the settings above.'
];
}
$api_version = $credentials['api_version'] ?? 'creators';
// Array per debug info
$debug_info = [];
$debug_info[] = '=== TEST API CONNECTION DEBUG INFO ===';
$debug_info[] = 'API Version: ' . $api_version;
$debug_info[] = 'Marketplace: ' . ($credentials['marketplace'] ?? 'N/A');
// Test per Creators API (OAuth 2.0 Cognito)
if ($api_version === 'creators') {
$debug_info[] = 'Version: ' . ($credentials['version'] ?? '2.2');
$debug_info[] = 'Partner Tag: ' . ($credentials['partner_tag'] ?? 'N/A');
$debug_info[] = '';
$debug_info[] = '--- Step 1: OAuth Token Request ---';
// 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) {
$debug_info[] = 'Result: FAILED';
$debug_info[] = 'Cognito Endpoint: https://api.amazon.com/auth/o2/token';
$debug_info[] = 'Credential ID: ' . substr($credentials['credential_id'], 0, 8) . '...';
return [
'success' => false,
'message' => 'OAuth authentication failed. Could not obtain access token from Cognito.',
'details' => implode("\n", $debug_info) . "\n\nCheck your Credential ID and Credential Secret in Creators API section."
];
}
$debug_info[] = 'Result: SUCCESS';
$debug_info[] = 'Token obtained: ' . substr($access_token, 0, 20) . '...';
$debug_info[] = '';
$debug_info[] = '--- Step 2: Creators API Request ---';
// 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);
$debug_info[] = 'Endpoint: ' . $url;
$debug_info[] = 'Method: POST';
$debug_info[] = 'Headers:';
$debug_info[] = ' - Authorization: Bearer [token], Version ' . ($credentials['version'] ?? '2.2');
$debug_info[] = ' - Content-Type: application/json';
$debug_info[] = ' - x-marketplace: ' . $credentials['marketplace'];
// 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)
$debug_info[] = 'AWS Region: ' . ($credentials['region'] ?? 'N/A');
$debug_info[] = 'Partner Tag: ' . ($credentials['partner_tag'] ?? 'N/A');
$debug_info[] = 'Access Key: ' . substr($credentials['access_key'], 0, 8) . '...';
$debug_info[] = '';
$debug_info[] = '--- PA-API 5.0 Request ---';
$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;
$debug_info[] = 'Endpoint: ' . $url;
$debug_info[] = 'Host: ' . $host;
$debug_info[] = 'Method: POST';
$debug_info[] = 'Signing with AWS Signature Version 4';
$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',
]);
}
$debug_info[] = '';
$debug_info[] = '--- Response ---';
// Verifica risposta (comune per entrambe le API)
if (is_wp_error($response)) {
$debug_info[] = 'Result: CONNECTION ERROR';
$debug_info[] = 'Error: ' . $response->get_error_message();
return [
'success' => false,
'message' => 'Connection error: ' . $response->get_error_message(),
'details' => implode("\n", $debug_info)
];
}
$http_code = wp_remote_retrieve_response_code($response);
$body = wp_remote_retrieve_body($response);
$debug_info[] = 'HTTP Status: ' . $http_code;
if ($http_code !== 200) {
$api_name = $api_version === 'creators' ? 'Creators API' : 'PA-API 5.0';
// Parse JSON per determinare tipo di errore specifico
$error_data = json_decode($body, true);
$error_type = $error_data['__type'] ?? '';
$error_code = $error_data['Errors'][0]['Code'] ?? '';
$debug_info[] = 'Result: FAILED';
$debug_info[] = '';
$debug_info[] = '--- Error Details ---';
if ($http_code === 401) {
if ($api_version === 'creators') {
$message = 'Authentication failed with ' . $api_name . '. ';
$message .= 'Make sure your Credential ID and Credential Secret are correct and from Creators API section in Associates Central.';
$debug_info[] = 'Possible causes:';
$debug_info[] = ' - Incorrect Credential ID or Credential Secret';
$debug_info[] = ' - Credentials not from Creators API section';
$debug_info[] = ' - Account not eligible for Creators API';
} else {
// PA-API 5.0 - distingui tra UnrecognizedClient e InvalidSignature
if (strpos($error_type, 'UnrecognizedClient') !== false || $error_code === 'UnrecognizedClient') {
$message = 'UnrecognizedClient: The Access Key ID is invalid or not recognized.';
$debug_info[] = 'ERROR TYPE: UnrecognizedClient';
$debug_info[] = '';
$debug_info[] = 'This means:';
$debug_info[] = ' ❌ Access Key is INCORRECT or not recognized by Amazon';
$debug_info[] = '';
$debug_info[] = 'Possible causes:';
$debug_info[] = ' - Access Key contains typos';
$debug_info[] = ' - Access Key was revoked or deleted';
$debug_info[] = ' - Access Key is from wrong Amazon account';
$debug_info[] = ' - Access Key not yet active (if just created)';
$debug_info[] = '';
$debug_info[] = '✓ Good news: Your Secret Key and Region are not being checked yet.';
$debug_info[] = ' Fix the Access Key first, then retry.';
} elseif (strpos($error_type, 'InvalidSignature') !== false || $error_code === 'InvalidSignature') {
$message = 'InvalidSignature: The AWS signature is invalid.';
$debug_info[] = 'ERROR TYPE: InvalidSignature';
$debug_info[] = '';
$debug_info[] = 'This means EITHER:';
$debug_info[] = ' A) Secret Key is INCORRECT, OR';
$debug_info[] = ' B) AWS Region does not match marketplace';
$debug_info[] = '';
$debug_info[] = 'How to diagnose:';
$debug_info[] = ' 1. Check AWS Region below (most common cause!)';
$debug_info[] = ' 2. If region is correct → Secret Key is wrong';
$debug_info[] = ' 3. If not sure → Click "Save Changes" in Settings';
$debug_info[] = '';
// Validazione region
$marketplaces = affiamsh_get_amazon_marketplaces();
$expected_region = $marketplaces[$credentials['marketplace']] ?? 'us-east-1';
if ($credentials['region'] !== $expected_region) {
$debug_info[] = '⚠️ REGION MISMATCH DETECTED!';
$debug_info[] = ' Current: ' . $credentials['region'] . ' ❌';
$debug_info[] = ' Expected: ' . $expected_region . ' ✓';
$debug_info[] = ' For marketplace: ' . $credentials['marketplace'];
$debug_info[] = '';
$debug_info[] = '→ ACTION: Go to Settings and click "Save Changes" to fix region!';
} else {
$debug_info[] = '✓ Region verification: PASSED';
$debug_info[] = ' Region ' . $credentials['region'] . ' is CORRECT for ' . $credentials['marketplace'];
$debug_info[] = '';
$debug_info[] = '→ CONCLUSION: Your SECRET KEY is INCORRECT!';
$debug_info[] = ' Region is correct, so the signature failed due to wrong Secret Key.';
$debug_info[] = ' Check your Secret Key in Amazon Associates Central.';
}
} else {
// Fallback generico 401
$message = 'Authentication failed with ' . $api_name . '. ';
$message .= 'Check that your Access Key and Secret Key are correct.';
$debug_info[] = 'Possible causes:';
$debug_info[] = ' - Incorrect Access Key or Secret Key';
$debug_info[] = ' - WRONG AWS REGION for marketplace';
$debug_info[] = ' - Credentials expired or revoked';
}
}
} elseif ($http_code === 403) {
$message = $api_name . ' returned HTTP 403 (Forbidden)';
$debug_info[] = 'Possible causes:';
$debug_info[] = ' - Account not eligible (need 10 sales for Creators API)';
$debug_info[] = ' - Subscription expired';
$debug_info[] = ' - IP address blocked';
} elseif ($http_code === 429) {
$message = $api_name . ' returned HTTP 429 (Too Many Requests)';
$debug_info[] = 'You are being rate-limited. Wait a few minutes and try again.';
} else {
$message = $api_name . ' returned HTTP ' . $http_code;
}
$debug_info[] = '';
$debug_info[] = '--- Amazon Response ---';
$debug_info[] = $body;
return [
'success' => false,
'message' => $message,
'details' => implode("\n", $debug_info)
];
}
// Successo - conta items
$data = json_decode($body, true);
$items = $data['searchResult']['items'] ?? $data['SearchResult']['Items'] ?? [];
$debug_info[] = 'Result: SUCCESS';
$debug_info[] = 'Items found: ' . count($items);
return [
'success' => true,
'message' => 'Connection successful!',
'item_count' => count($items),
'details' => implode("\n", $debug_info)
];
}
// Shortcode function
function affiamsh_amazon_handler($atts) {
$atts = shortcode_atts(['keyword' => 'smartphone', 'number' => ''], $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';
// ✅ Only PRO: parametro number="x" (1..9) sovrascrive num_products impostato
$requested_number = isset($atts['number']) ? absint($atts['number']) : 0;
if ($requested_number < 1 || $requested_number > 9) {
$requested_number = 0;
}
if ($requested_number && function_exists('affiamsh_is_pro') && affiamsh_is_pro()) {
$settings['num_products'] = $requested_number;
}
// ✅ Cache output shortcode (riduce throttling e accelera le pagine)
$effective_num = absint($settings['num_products'] ?? 3);
if ($effective_num < 1) $effective_num = 1;
if ($effective_num > 9) $effective_num = 9;
$cache_ver = affiamsh_get_cache_ver();
$cache_ttl = 30 * MINUTE_IN_SECONDS; // 30 min
$cache_key = 'affiamsh_sc_v' . $cache_ver . '_' . md5(wp_json_encode([
'api' => (string)$api_version,
'kw' => (string)($atts['keyword'] ?? ''),
'n' => (int)$effective_num,
'cols' => (int)absint($settings['num_columns'] ?? 3),
'img' => (string)($settings['image_size'] ?? ''),
'mp' => (string)($credentials['marketplace'] ?? ''),
'ver' => (string)($credentials['version'] ?? ''),
// ✅ aggiunti per vedere subito cambi partner tag / credenziali
'tag' => (string)($credentials['partner_tag'] ?? ''),
'ak' => (string)($credentials['access_key'] ?? ''), // opzionale ma utile; NON include secret
]));
$cached = get_transient($cache_key);
if ($cached !== false && is_string($cached) && $cached !== '') {
return $cached;
}if ($api_version === 'creators') {
$html = affiamsh_call_creators_api($atts, $settings, $credentials);
} else {
$html = affiamsh_call_paapi5($atts, $settings, $credentials);
}
// Non cachare errori
if (is_string($html)
&& stripos($html, 'request throttling') === false
&& stripos($html, 'throttl') === false
&& stripos($html, 'Authentication failed') === false
&& stripos($html, 'InvalidSignature') === false
&& stripos($html, 'SignatureDoesNotMatch') === false
&& stripos($html, 'Unauthorized') === false
&& stripos($html, 'AccessDenied') === false
&& stripos($html, 'invalid_client') === false
&& stripos($html, 'invalid_grant') === false
&& stripos($html, 'eligible') === false
&& stripos($html, 'not eligible') === false
&& stripos($html, 'not authorized') === false
&& stripos($html, 'API Error') === false
&& stripos($html, 'Error:') === false
) {
set_transient($cache_key, $html, $cache_ttl);
}
return $html;
}
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);
// Determina il tipo di errore specifico
$error_type = $error_data['__type'] ?? '';
$error_code = $error_data['Errors'][0]['Code'] ?? '';
// 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) {
// Distingui tra UnrecognizedClient e InvalidSignature
if (strpos($error_type, 'UnrecognizedClient') !== false || $error_code === 'UnrecognizedClient') {
$error_message .= '
UnrecognizedClient Error
';
$error_message .= '
The Access Key ID is invalid or not recognized.
';
$error_message .= '
';
$error_message .= '- Access Key is incorrect
';
$error_message .= '- Access Key was revoked or deleted
';
$error_message .= '- Access Key is from wrong Amazon account
';
$error_message .= '- Access Key not yet active (if just created)
';
$error_message .= '
';
$error_message .= '
✓ Good news: Your Secret Key and Region are not being checked yet. Fix the Access Key first.
';
} elseif (strpos($error_type, 'InvalidSignature') !== false || $error_code === 'InvalidSignature') {
$error_message .= '
InvalidSignature Error
';
$error_message .= '
The AWS signature failed. This means:
';
$error_message .= '
';
$error_message .= '- Secret Key is incorrect, OR
';
$error_message .= '- AWS Region does not match marketplace
';
$error_message .= '
';
$error_message .= '
🔍 How to diagnose:
';
$error_message .= '
';
$error_message .= '- First, verify AWS Region below - this is the most common cause!
';
$error_message .= '- If region is correct for your marketplace → Secret Key is wrong
';
$error_message .= '- If not sure → Try "Save Changes" to recalculate region
';
$error_message .= '
';
} else {
// Fallback generico 401
$error_message .= '
Authentication Error
';
$error_message .= '
Authentication failed. Possible causes:
';
$error_message .= '
';
$error_message .= '- Incorrect Access Key or Secret Key
';
$error_message .= '- Wrong AWS Region for marketplace
';
$error_message .= '- Credentials expired or revoked
';
$error_message .= '
';
}
}
$error_message .= '
';
$error_message .= 'Configuration Used:
';
$error_message .= 'API: PA-API 5.0 (Legacy)
';
$error_message .= 'Marketplace: ' . esc_html($credentials['marketplace']) . '
';
$error_message .= 'AWS Region: ' . esc_html($credentials['region']) . '
';
$error_message .= 'Access Key: ' . esc_html(substr($credentials['access_key'], 0, 8)) . '...
';
$error_message .= 'Endpoint: ' . esc_html($url) . '
';
// Validazione region per marketplace
$marketplaces = affiamsh_get_amazon_marketplaces();
$expected_region = $marketplaces[$credentials['marketplace']] ?? 'us-east-1';
if ($credentials['region'] !== $expected_region) {
$error_message .= '
⚠️ REGION MISMATCH DETECTED!
';
$error_message .= 'Current region: ' . esc_html($credentials['region']) . '
';
$error_message .= 'Expected region: ' . esc_html($expected_region) . '
';
$error_message .= '→ Go to Settings and click "Save Changes" to fix!
';
} else {
$error_message .= '
✓ Region is correct for ' . esc_html($credentials['marketplace']) . '
';
if (strpos($error_type, 'InvalidSignature') !== false) {
$error_message .= '→ This means your Secret Key is incorrect!
';
}
}
$error_message .= '
Amazon Response:
';
$error_message .= '' . 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 .= '
';
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');
?>
:
[affiamsh_amazon keyword="Your Keyword"]
:
[affiamsh_amazon keyword="Your Keyword" number="x"]
'.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);
?>