PluginProbe
Lasso Lite – Affiliate Link Manager & Product Displays / 108
Lasso Lite – Affiliate Link Manager & Product Displays v108
157 155 156 154 153 152 151 150 149 148 trunk 0.9.9 104 105 106 107 108 109 110 111 112 113 114 115 116 All 56 releases
simple-urls / classes / class-amazon-api.php

class-amazon-api.php in Lasso Lite – Affiliate Link Manager & Product Displays 108, at classes/class-amazon-api.php

1,134 lines 34.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Declare class Config
4 *
5 * @package Config
6 */
7
8 namespace LassoLite\Classes;
9
10 use LassoLite\Classes\Affiliate_Link;
11 use LassoLite\Classes\Cache_Per_Process;
12 use LassoLite\Classes\Helper;
13 use LassoLite\Classes\Setting;
14
15 use LassoLite\Libs\Amazon_Api_V5\AwsV5;
16
17 /**
18 * Config
19 */
20 class Amazon_Api {
21
22 const OBJECT_KEY = 'lasso_amazon_api';
23 const FUNCTION_NAME_GET_LASSO_ID_BY_PRODUCT_ID_AND_TYPE = 'get_lasso_id_by_product_id_and_type';
24 const PRODUCT_TYPE = 'amazon';
25 const SHORT_LINK_DOMAINS = array( 'amzn.com', 'amzn.to' );
26 const FILTER_AMAZON_PRODUCT = 'filter_amazon_product';
27 const TRACKING_ID_REGEX = '^[a-zA-Z0-9-]+-\d{2,3}$';
28
29
30 /**
31 * Get amazon API countries
32 */
33 public static function get_amazon_api_countries() {
34 return array(
35 'us' => array(
36 'name' => 'United States',
37 'amazon_domain' => 'www.amazon.com',
38 'pa_endpoint' => 'webservices.amazon.com',
39 'region' => 'us-east-1',
40 ),
41 'usa' => array(
42 'name' => 'United States',
43 'amazon_domain' => 'www.amazon.com',
44 'pa_endpoint' => 'webservices.amazon.com',
45 'region' => 'us-east-1',
46 ),
47 'au' => array(
48 'name' => 'Australia',
49 'amazon_domain' => 'www.amazon.com.au',
50 'pa_endpoint' => 'webservices.amazon.com.au',
51 'region' => 'us-west-2',
52 ),
53 'aus' => array(
54 'name' => 'Australia',
55 'amazon_domain' => 'www.amazon.com.au',
56 'pa_endpoint' => 'webservices.amazon.com.au',
57 'region' => 'us-west-2',
58 ),
59 'br' => array(
60 'name' => 'Brazil',
61 'amazon_domain' => 'www.amazon.com.br',
62 'pa_endpoint' => 'webservices.amazon.com.br',
63 'region' => 'us-east-1',
64 ),
65 'bra' => array(
66 'name' => 'Brazil',
67 'amazon_domain' => 'www.amazon.com.br',
68 'pa_endpoint' => 'webservices.amazon.com.br',
69 'region' => 'us-east-1',
70 ),
71 'ca' => array(
72 'name' => 'Canada',
73 'amazon_domain' => 'www.amazon.ca',
74 'pa_endpoint' => 'webservices.amazon.ca',
75 'region' => 'us-east-1',
76 ),
77 'can' => array(
78 'name' => 'Canada',
79 'amazon_domain' => 'www.amazon.ca',
80 'pa_endpoint' => 'webservices.amazon.ca',
81 'region' => 'us-east-1',
82 ),
83 'cn' => array(
84 'name' => 'China',
85 'amazon_domain' => 'www.amazon.cn',
86 'pa_endpoint' => 'webservices.amazon.cn',
87 'region' => 'us-east-1',
88 ),
89 'chn' => array(
90 'name' => 'China',
91 'amazon_domain' => 'www.amazon.cn',
92 'pa_endpoint' => 'webservices.amazon.cn',
93 'region' => 'us-east-1',
94 ),
95 'fr' => array(
96 'name' => 'France',
97 'amazon_domain' => 'www.amazon.fr',
98 'pa_endpoint' => 'webservices.amazon.fr',
99 'region' => 'eu-west-1',
100 ),
101 'fra' => array(
102 'name' => 'France',
103 'amazon_domain' => 'www.amazon.fr',
104 'pa_endpoint' => 'webservices.amazon.fr',
105 'region' => 'eu-west-1',
106 ),
107 'de' => array(
108 'name' => 'Germany',
109 'amazon_domain' => 'www.amazon.de',
110 'pa_endpoint' => 'webservices.amazon.de',
111 'region' => 'eu-west-1',
112 ),
113 'deu' => array(
114 'name' => 'Germany',
115 'amazon_domain' => 'www.amazon.de',
116 'pa_endpoint' => 'webservices.amazon.de',
117 'region' => 'eu-west-1',
118 ),
119 'in' => array(
120 'name' => 'India',
121 'amazon_domain' => 'www.amazon.in',
122 'pa_endpoint' => 'webservices.amazon.in',
123 'region' => 'eu-west-1',
124 ),
125 'ind' => array(
126 'name' => 'India',
127 'amazon_domain' => 'www.amazon.in',
128 'pa_endpoint' => 'webservices.amazon.in',
129 'region' => 'eu-west-1',
130 ),
131 'it' => array(
132 'name' => 'Italy',
133 'amazon_domain' => 'www.amazon.it',
134 'pa_endpoint' => 'webservices.amazon.it',
135 'region' => 'eu-west-1',
136 ),
137 'ita' => array(
138 'name' => 'Italy',
139 'amazon_domain' => 'www.amazon.it',
140 'pa_endpoint' => 'webservices.amazon.it',
141 'region' => 'eu-west-1',
142 ),
143 'jp' => array(
144 'name' => 'Japan',
145 'amazon_domain' => 'www.amazon.co.jp',
146 'pa_endpoint' => 'webservices.amazon.co.jp',
147 'region' => 'us-west-2',
148 ),
149 'jpn' => array(
150 'name' => 'Japan',
151 'amazon_domain' => 'www.amazon.co.jp',
152 'pa_endpoint' => 'webservices.amazon.co.jp',
153 'region' => 'us-west-2',
154 ),
155 'mx' => array(
156 'name' => 'Mexico',
157 'amazon_domain' => 'www.amazon.com.mx',
158 'pa_endpoint' => 'webservices.amazon.com.mx',
159 'region' => 'us-east-1',
160 ),
161 'mex' => array(
162 'name' => 'Mexico',
163 'amazon_domain' => 'www.amazon.com.mx',
164 'pa_endpoint' => 'webservices.amazon.com.mx',
165 'region' => 'us-east-1',
166 ),
167 'nl' => array(
168 'name' => 'Netherlands',
169 'amazon_domain' => 'www.amazon.nl',
170 'pa_endpoint' => 'webservices.amazon.nl',
171 'region' => 'eu-west-1',
172 ),
173 'nld' => array(
174 'name' => 'Netherlands',
175 'amazon_domain' => 'www.amazon.nl',
176 'pa_endpoint' => 'webservices.amazon.nl',
177 'region' => 'eu-west-1',
178 ),
179 'se' => array(
180 'name' => 'Sweden',
181 'amazon_domain' => 'www.amazon.se',
182 'pa_endpoint' => 'webservices.amazon.se',
183 'region' => 'us-west-1',
184 ),
185 'sek' => array(
186 'name' => 'Sweden',
187 'amazon_domain' => 'www.amazon.se',
188 'pa_endpoint' => 'webservices.amazon.se',
189 'region' => 'us-west-1',
190 ),
191 'sg' => array(
192 'name' => 'Singapore',
193 'amazon_domain' => 'www.amazon.sg',
194 'pa_endpoint' => 'webservices.amazon.sg',
195 'region' => 'us-west-2',
196 ),
197 'sgp' => array(
198 'name' => 'Singapore',
199 'amazon_domain' => 'www.amazon.sg',
200 'pa_endpoint' => 'webservices.amazon.sg',
201 'region' => 'us-west-2',
202 ),
203 'es' => array(
204 'name' => 'Spain',
205 'amazon_domain' => 'www.amazon.es',
206 'pa_endpoint' => 'webservices.amazon.es',
207 'region' => 'eu-west-1',
208 ),
209 'esp' => array(
210 'name' => 'Spain',
211 'amazon_domain' => 'www.amazon.es',
212 'pa_endpoint' => 'webservices.amazon.es',
213 'region' => 'eu-west-1',
214 ),
215 'tr' => array(
216 'name' => 'Turkey',
217 'amazon_domain' => 'www.amazon.com.tr',
218 'pa_endpoint' => 'webservices.amazon.com.tr',
219 'region' => 'eu-west-1',
220 ),
221 'tur' => array(
222 'name' => 'Turkey',
223 'amazon_domain' => 'www.amazon.com.tr',
224 'pa_endpoint' => 'webservices.amazon.com.tr',
225 'region' => 'eu-west-1',
226 ),
227 'ae' => array(
228 'name' => 'United Arab Emirates',
229 'amazon_domain' => 'www.amazon.ae',
230 'pa_endpoint' => 'webservices.amazon.ae',
231 'region' => 'eu-west-1',
232 ),
233 'are' => array(
234 'name' => 'United Arab Emirates',
235 'amazon_domain' => 'www.amazon.ae',
236 'pa_endpoint' => 'webservices.amazon.ae',
237 'region' => 'eu-west-1',
238 ),
239 'gb' => array(
240 'name' => 'United Kingdom',
241 'amazon_domain' => 'www.amazon.co.uk',
242 'pa_endpoint' => 'webservices.amazon.co.uk',
243 'region' => 'eu-west-1',
244 ),
245 'gbr' => array(
246 'name' => 'United Kingdom',
247 'amazon_domain' => 'www.amazon.co.uk',
248 'pa_endpoint' => 'webservices.amazon.co.uk',
249 'region' => 'eu-west-1',
250 ),
251 );
252 }
253
254 /**
255 * Get ignore errors list
256 */
257 public static function get_ignore_error_codes() {
258 return array(
259 'ItemNotAccessible',
260 'InvalidParameterValue',
261 'AccessDeniedException',
262 'AccessDenied',
263 'TooManyRequestsException',
264 'TooManyRequests',
265 'ThrottlingException',
266 'RequestThrottled',
267 'AWS.ThrottlingException',
268 'AWS.RequestThrottled',
269 'AWS.AccessDeniedException',
270 'UnrecognizedClient',
271 );
272 }
273
274 /**
275 * Get amazon domains
276 */
277 public static function get_domains() {
278 return array(
279 'amazon.com', // ? US
280 'amazon.ca', // ? Canada
281 'amazon.co.uk', // ? UK
282 'amazon.com.au', // ? Australia
283 'amazon.com.br', // ? Brazil
284 'amazon.com.mx', // ? Mexico
285 'amazon.fr', // ? France
286 'amazon.de', // ? Germany
287 'amazon.it', // ? Italy
288 'amazon.in', // ? India
289 'amazon.es', // ? Spain
290 'amazon.cn', // ? China
291 'amazon.co.jp', // ? Japan
292 'amazon.nl', // ? Netherlands
293 'amazon.se', // ? Sweden
294 'amazon.sg', // ? Singapore
295 'amazon.com.tr', // ? Turkey
296 'amazon.ae', // ? United Arab Emirates
297 'amzn.com', // ? Short URL
298 'amzn.to', // ? Short URL
299 'amazon-adsystem.com', // ? Amazon Embed
300 'smile.amazon.com', // ? Amazon Smile
301 );
302 }
303
304 /**
305 * Get amazon link and flag
306 */
307 public static function get_aff_link_and_flag() {
308 return array(
309 'www.amazon.com' => array(
310 'flag' => '🇺🇸',
311 'aff_link' => 'https://affiliate-program.amazon.com/',
312 ),
313 'www.amazon.ca' => array(
314 'flag' => '🇨🇦',
315 'aff_link' => 'https://associates.amazon.ca/',
316 ),
317 'www.amazon.com.br' => array(
318 'flag' => '🇧🇷',
319 'aff_link' => 'https://associados.amazon.com.br/',
320 ),
321 'www.amazon.com.mx' => array(
322 'flag' => '🇲🇽',
323 'aff_link' => 'https://afiliados.amazon.com.mx/',
324 ),
325 'www.amazon.fr' => array(
326 'flag' => '🇫🇷',
327 'aff_link' => 'https://partenaires.amazon.fr/',
328 ),
329 'www.amazon.de' => array(
330 'flag' => '🇩🇪',
331 'aff_link' => 'https://partnernet.amazon.de/',
332 ),
333 'www.amazon.it' => array(
334 'flag' => '🇮🇹',
335 'aff_link' => 'https://programma-affiliazione.amazon.it/',
336 ),
337 'www.amazon.es' => array(
338 'flag' => '🇪🇸',
339 'aff_link' => 'https://afiliados.amazon.es/',
340 ),
341 'www.amazon.co.uk' => array(
342 'flag' => '🇬🇧',
343 'aff_link' => 'https://affiliate-program.amazon.co.uk/',
344 ),
345 'www.amazon.cn' => array(
346 'flag' => '🇨🇳',
347 'aff_link' => 'https://associates.amazon.cn/',
348 ),
349 'www.amazon.co.jp' => array(
350 'flag' => '🇯🇵',
351 'aff_link' => 'https://affiliate.amazon.co.jp/',
352 ),
353 'www.amazon.in' => array(
354 'flag' => '🇮🇳',
355 'aff_link' => 'https://affiliate-program.amazon.in/',
356 ),
357 'www.amazon.com.au' => array(
358 'flag' => '🇦🇺',
359 'aff_link' => 'https://affiliate-program.amazon.com.au/',
360 ),
361 );
362 }
363
364 /**
365 * Check whether a url is amazon link or not
366 *
367 * @param string $url Url.
368 */
369 public static function is_amazon_url( $url ) {
370 if ( empty( $url ) ) {
371 return false;
372 }
373
374 $domains = self::get_domains();
375 $url = Helper::add_https( $url );
376
377 if ( ! Helper::validate_url( $url ) ) {
378 return false;
379 }
380
381 $parse_url = wp_parse_url( $url );
382 if ( ! isset( $parse_url['host'] ) ) {
383 return false;
384 }
385
386 $domain = ltrim( $parse_url['host'], 'www.' );
387
388 if ( in_array( $domain, $domains, true ) ) {
389 return true;
390 }
391
392 return false;
393 }
394
395 /**
396 * Check whether a url is amazon link or not
397 *
398 * @param string $url Url.
399 */
400 public static function is_amazon_shortented_url( $url ) {
401 $is_amazon_url = self::is_amazon_url( $url );
402 $is_shortented_url = strpos( $url, 'amzn.to' ) || strpos( $url, 'amzn.com' );
403
404 return $is_amazon_url && $is_shortented_url;
405 }
406
407 /**
408 * Search amazon product
409 *
410 * @param string $keyword Keyword: product name,...
411 */
412 public function search_product( $keyword ) {
413 $result = $this->get_product_by_keyword_v5( $keyword, 'All' );
414 if ( isset( $result->SearchResult->Items ) ) { // phpcs:ignore
415 $items = $result->SearchResult->Items; // phpcs:ignore
416 $products = array();
417
418 foreach ( $items as $item ) {
419 $product = $this->extract_search_result_v5( $item );
420 array_push( $products, $product );
421 }
422
423 return $products;
424
425 } elseif ( isset( $result->Errors ) ) { // phpcs:ignore
426 return array(
427 'error' => $result->Errors, // phpcs:ignore
428 );
429 }
430 }
431
432 /**
433 * Check whether product url has the same domain with Amazon settings
434 *
435 * @param string $product_url Amazon link.
436 */
437 public function is_same_domain( $product_url ) {
438 if ( '' === $product_url ) {
439 return false;
440 }
441
442 $amazon_default_tracking_country = Setting::get_setting( 'amazon_default_tracking_country', 'usa' );
443 $all_countries = self::get_amazon_api_countries();
444 $domain = $all_countries[ $amazon_default_tracking_country ]['amazon_domain'] ?? 'www.amazon.com';
445 $domain = str_replace( 'www.', '', $domain );
446
447 return strpos( $product_url, $domain ) !== false;
448 }
449
450 /**
451 * Fetch amazon product from Amazon API v5
452 *
453 * @param string $product_id Amazon product id.
454 * @param bool $store_product Store product into DB or not. Default to false.
455 * @param bool|string $updated_at Set date time or not. Default to false.
456 * @param string $amz_link Amazon link. Default to empty.
457 * @return mixed
458 */
459 public function fetch_product_info( $product_id, $store_product = false, $updated_at = false, $amz_link = '' ) {
460 $lasso_db = new Lasso_DB();
461
462 $lasso_settings = Setting::get_settings();
463 $is_amazon_configured = $lasso_settings['amazon_access_key_id'] && $lasso_settings['amazon_secret_key'] && $lasso_settings['amazon_tracking_id'];
464 $result = $is_amazon_configured && $this->is_same_domain( $amz_link ) ? $this->get_product_by_id_v5( $product_id ) : false;
465 // phpcs:ignore
466 if ( isset( $result->Errors[0] ) && (
467 "The ItemId $product_id provided in the request is invalid." === $result->Errors[0]->Message // phpcs:ignore
468 || "The value [$product_id] provided in the request for ItemIds is invalid." === $result->Errors[0]->Message // phpcs:ignore
469 )
470 ) {
471 return array(
472 'product' => array(),
473 'api' => 'yes',
474 'full_item' => array(),
475 'status' => 'failed',
476 'error_code' => 'NotFound',
477 );
478 } elseif ( isset( $result->ItemsResult->Items[0] ) ) { // phpcs:ignore
479 $item = $result->ItemsResult->Items[0]; // phpcs:ignore
480
481 $product = $this->extract_search_result_v5( $item, true );
482 $product_url = $amz_link ? $amz_link : $product['url'];
483 $product['status_code'] = 200;
484 $product['url'] = self::get_amazon_product_url( $product_url );
485
486 // ? Get Lasso ID
487 $query = '
488 SELECT post_id
489 FROM ' . $lasso_db->postmeta . "
490 WHERE meta_key = 'amazon_product_id' AND meta_value = '" . $product['product_id'] . "'
491 ";
492 $lasso_id = $lasso_db->get_var( $query );
493 $product['lasso_id'] = ( isset( $lasso_id ) ) ? $lasso_id : 0;
494
495 if ( $store_product ) {
496 $amazon_tracking_id = Setting::get_setting( 'amazon_tracking_id', '' );
497 $product['default_url'] = '' === $amazon_tracking_id ? $amz_link : $product['default_url'];
498 $this->update_amazon_product_in_db( $product, $updated_at );
499 }
500
501 return array(
502 'product' => $product,
503 'api' => 'yes',
504 'full_item' => $item,
505 'status' => 'success',
506 'error_code' => '',
507 );
508 } else {
509 return array();
510 }
511 }
512
513 /**
514 * Insert or Update Amazon Product Data
515 *
516 * @param array $product Amazon product.
517 * @param bool|string $updated_at Set update date time. Default to false.
518 */
519 public function update_amazon_product_in_db( $product, $updated_at = false ) {
520 global $wpdb;
521
522 $lasso_db = new Lasso_DB();
523
524 $amazon_id = $product['product_id'] ?? '';
525 $default_product_name = $product['title'] ?? '';
526 $latest_price = $product['price'] ?? '';
527 $latest_price = '0' === $latest_price || ( is_int( $latest_price ) && 0 === $latest_price ) ? '' : $latest_price;
528 $base_url = self::get_amazon_product_url( $product['default_url'] ?? '', false, false );
529 $monetized_url = self::get_amazon_product_url( $product['url'] ?? '', true, false );
530 $default_image = trim( $product['image'] ?? '' );
531 $last_updated = gmdate( 'Y-m-d H:i:s', time() );
532 $last_updated = $updated_at ? $updated_at : $last_updated;
533 $is_prime = $product['is_prime'] ?? '';
534 $currency = $product['currency'] ?? '';
535 $features = wp_json_encode( $product['features'] ?? array() );
536 $savings_amount = $product['savings_amount'] ?? '';
537 $savings_percent = $product['savings_percent'] ?? '';
538 $savings_basis = $product['savings_basis'] ?? '';
539 $is_manual = $product['is_manual'] ?? 0;
540 $quantity = intval( $product['quantity'] ?? 200 );
541 $out_of_stock = 0 === $quantity ? 1 : 0;
542
543 if ( '' === $amazon_id || '' === $default_product_name || '' === $default_image
544 || ( '' !== $default_image && Helper::validate_url( $default_image ) === false && strpos( $default_image, 'data:image' ) !== 0 )
545 ) {
546 return false;
547 }
548
549 $base_url = trim( $base_url );
550 $monetized_url = trim( $monetized_url );
551
552 $query = '
553 INSERT INTO ' . $lasso_db->amazon_products . "
554 (
555 amazon_id, default_product_name, latest_price, base_url,
556 monetized_url, default_image, last_updated, is_prime,
557 currency, features, savings_amount, savings_percent,
558 savings_basis, is_manual, out_of_stock
559 )
560 VALUES
561 (
562 %s, %s, %s, %s,
563 %s, %s, %s, %d,
564 %s, %s, %s, %d,
565 %s, %d, %d
566 )
567 ON DUPLICATE KEY UPDATE
568 amazon_id = %s,
569 default_product_name = %s,
570 latest_price = %s,
571 base_url = %s,
572 monetized_url = %s,
573 default_image = (CASE WHEN %s='' or %s IS NULL THEN `default_image` ELSE %s END),
574 last_updated = %s,
575 is_prime = %d,
576 currency = %s,
577 features = %s,
578 savings_amount = %s,
579 savings_percent = %d,
580 savings_basis = %s,
581 is_manual = %d,
582 out_of_stock = %d
583 ;
584 ";
585 $prepare = $wpdb->prepare(
586 // phpcs:ignore
587 $query,
588 // ? First for insert
589 $amazon_id,
590 $default_product_name,
591 $latest_price,
592 $base_url,
593 $monetized_url,
594 $default_image,
595 $last_updated,
596 $is_prime,
597 $currency,
598 $features,
599 $savings_amount,
600 $savings_percent,
601 $savings_basis,
602 $is_manual,
603 $out_of_stock,
604 // ? Second for update
605 $amazon_id,
606 $default_product_name,
607 $latest_price,
608 $base_url,
609 $monetized_url,
610 $default_image,
611 $default_image,
612 $default_image,
613 $last_updated,
614 $is_prime,
615 $currency,
616 $features,
617 $savings_amount,
618 $savings_percent,
619 $savings_basis,
620 $is_manual,
621 $out_of_stock
622 );
623
624 $lasso_db->query( $prepare );
625
626 return true;
627 }
628
629 /**
630 * Get amazon product from DB
631 *
632 * @param string $product_id Amazon Product id.
633 */
634 public function get_amazon_product_from_db( $product_id ) {
635 if ( empty( $product_id ) ) {
636 return false;
637 }
638
639 global $wpdb;
640
641 $lasso_db = new Lasso_DB();
642
643 $sql = '
644 SELECT *
645 FROM ' . $lasso_db->amazon_products . '
646 WHERE amazon_id = %s
647 ';
648
649 $prepare = $wpdb->prepare( $sql, $product_id ); // phpcs:ignore
650 $result = $lasso_db->get_row( $prepare, ARRAY_A );
651
652 if ( $result ) {
653 $result = apply_filters( self::FILTER_AMAZON_PRODUCT, $result );
654 $result['monetized_url'] = self::get_amazon_product_url( $result['monetized_url'] );
655 $result['base_url'] = self::get_amazon_product_url( $result['base_url'], false );
656 $result['features'] = json_decode( $result['features'] );
657 }
658
659 return $result;
660 }
661
662 /**
663 * Extract result to an array
664 *
665 * @param object $response Data from Amazon.
666 * @param bool $large_image Get large image size. Default to false.
667 *
668 * @return array
669 */
670 private function extract_search_result_v5( $response, $large_image = false ) {
671
672 $image = '';
673 if ( isset( $response->Images->Primary ) ) { // phpcs:ignore
674 $image = $large_image ? $response->Images->Primary->Large->URL : $response->Images->Primary->Small->URL; // phpcs:ignore
675 }
676
677 // @codingStandardsIgnoreStart
678 $result = array(
679 'product_id' => $response->ASIN ?? 0,
680 'title' => $response->ItemInfo->Title->DisplayValue ?? '',
681 'url' => $response->DetailPageURL ?? '',
682 'default_url' => $response->DetailPageURL ?? '',
683 'image' => $image,
684 'quantity' => $response->Offers->Summaries[0]->OfferCount ?? 0,
685 'is_prime' => $response->Offers->Listings[0]->DeliveryInfo->IsPrimeEligible ?? false,
686 'price' => $response->Offers->Listings[0]->Price->DisplayAmount ?? 0,
687 'amount' => $response->Offers->Listings[0]->Price->Amount ?? 0,
688 'currency' => $response->Offers->Listings[0]->Price->Currency ?? '',
689 'features' => $response->ItemInfo->Features->DisplayValues ?? array(),
690 'savings_amount' => $response->Offers->Listings[0]->Price->Savings->Amount ?? 0.0,
691 'savings_percent' => $response->Offers->Listings[0]->Price->Savings->Percentage ?? 0,
692 'savings_basis' => $response->Offers->Listings[0]->SavingBasis->Amount ?? 0.0,
693 );
694 // @codingStandardsIgnoreEnd
695
696 return $result;
697 }
698
699 /**
700 * Query amazon v5
701 *
702 * @param array $parameters Amazon API params.
703 * @param boolean $lasso_settings Lasso settings. Default to false.
704 *
705 * @return array
706 */
707 public function query_amazon_v5( $parameters, $lasso_settings = false ) {
708 try {
709 if ( ! $lasso_settings ) {
710 $lasso_settings = Setting::get_settings();
711 }
712
713 $this->amazon_access_key_id = $lasso_settings['amazon_access_key_id'] ?? '';
714 $this->amazon_secret_key = $lasso_settings['amazon_secret_key'] ?? '';
715 $this->amazon_tracking_id = $lasso_settings['amazon_tracking_id'] ?? '';
716
717 $result = $this->aws_signed_request_v5( $parameters, $this->amazon_access_key_id, $this->amazon_secret_key, $this->amazon_tracking_id );
718
719 if ( isset( $result->Errors ) ) { // phpcs:ignore
720 $error = $result->Errors[0]; // phpcs:ignore
721 }
722
723 return $result;
724 } catch ( \Exception $e ) {
725 return array();
726 }
727 }
728
729 /**
730 * Get Amazon product by product id
731 *
732 * @param string $product_id Amazon product id.
733 *
734 * @return object
735 */
736 public function get_product_by_id_v5( $product_id ) {
737 $parameters = array(
738 'Operation' => 'GetItems',
739 'ItemIds' => array( $product_id ),
740 'Resources' => array(
741 'Images.Primary.Small',
742 'Images.Primary.Large',
743 'ItemInfo.Title',
744 'ItemInfo.ContentRating',
745 'ItemInfo.Features',
746 'ItemInfo.ProductInfo',
747 'ItemInfo.TechnicalInfo',
748 'Offers.Listings.Price',
749 'Offers.Listings.SavingBasis',
750 'Offers.Summaries.OfferCount',
751 'Offers.Listings.DeliveryInfo.IsPrimeEligible',
752 ),
753 );
754
755 $json_response = $this->query_amazon_v5( $parameters );
756
757 return $json_response;
758 }
759
760 /**
761 * Get product from Amazon by product name
762 *
763 * @param string $keyword Keyword.
764 * @param string $product_type Product type.
765 *
766 * @return object
767 */
768 public function get_product_by_keyword_v5( $keyword, $product_type ) {
769 $parameters = array(
770 'Operation' => 'SearchItems',
771 'Keywords' => $keyword,
772 'SearchIndex' => $product_type,
773 'Resources' => array(
774 'Images.Primary.Small',
775 'Images.Primary.Large',
776 'ItemInfo.Title',
777 'ItemInfo.ContentRating',
778 'ItemInfo.Features',
779 'ItemInfo.ProductInfo',
780 'ItemInfo.TechnicalInfo',
781 'Offers.Listings.Price',
782 'Offers.Listings.SavingBasis',
783 'Offers.Summaries.OfferCount',
784 'Offers.Listings.DeliveryInfo.IsPrimeEligible',
785 ),
786 );
787
788 $json_response = $this->query_amazon_v5( $parameters );
789
790 return $json_response;
791 }
792
793 /**
794 * Sign request v5
795 *
796 * @param array $params Amazon params.
797 * @param string $amazon_access_key_id Amazon access key.
798 * @param string $amazon_secret_key Amazon secret key.
799 * @param string $amazon_tracking_id Amazon tracking id.
800 *
801 * @return object|bool
802 */
803 private function aws_signed_request_v5( $params, $amazon_access_key_id, $amazon_secret_key, $amazon_tracking_id ) {
804 // phpcs:ignore
805 // $amazon_domain = 'www.amazon.com';
806 // $pa_endpoint = 'webservices.amazon.com';
807
808 $country = Setting::get_setting( 'amazon_default_tracking_country', 'usa' );
809 $countries = self::get_amazon_api_countries();
810 $amazon_domain = $countries[ $country ]['amazon_domain'];
811 $pa_endpoint = $countries[ $country ]['pa_endpoint'];
812 $amazon_region = $countries[ $country ]['region'];
813
814 $params['Marketplace'] = $amazon_domain;
815 $params['PartnerType'] = 'Associates';
816 $params['PartnerTag'] = $amazon_tracking_id;
817 $post_fields = wp_json_encode( $params );
818
819 $aws_v5 = new AwsV5( $amazon_access_key_id, $amazon_secret_key );
820 $aws_v5->setHost( $pa_endpoint );
821 $aws_v5->setRegionName( $amazon_region );
822 $aws_v5->setPayload( $post_fields );
823 $aws_v5->addHeader( 'x-amz-target', 'com.amazon.paapi5.v1.ProductAdvertisingAPIv1.' . $params['Operation'] );
824 $headers = $aws_v5->getHeaders( true );
825 $url = "https://$pa_endpoint/paapi5/searchitems";
826
827 // @codingStandardsIgnoreStart
828 $ch = curl_init();
829 curl_setopt( $ch, CURLOPT_URL, $url );
830 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
831 curl_setopt( $ch, CURLOPT_POSTFIELDS, $post_fields );
832 curl_setopt( $ch, CURLOPT_POST, 1 );
833
834 curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
835
836 $result = curl_exec( $ch );
837 if ( curl_errno( $ch ) ) {
838 // phpcs:ignore
839 // $error = curl_error( $ch );
840 return false;
841 }
842 curl_close( $ch );
843 // @codingStandardsIgnoreEnd
844
845 return json_decode( $result );
846 }
847
848 /**
849 * Get Amazon product id by url
850 *
851 * @param string $url Amazon link.
852 * @return string|bool
853 */
854 public static function get_product_id_by_url( $url ) {
855 $url = Helper::add_https( $url );
856
857 if ( ! self::is_amazon_url( $url ) || strpos( $url, '.' ) === false ) {
858 return false;
859 }
860
861 $parse_url = wp_parse_url( $url );
862 $amazon_domain = trim( $parse_url['host'] ?? '', 'www.' );
863 $amazon_domain_regex = str_replace( '.', '\.', str_replace( 'www.', '', $amazon_domain ) );
864
865 $reg = '#(?:https?://(?:www\.){0,1}' . $amazon_domain_regex . '(?:/.*){0,1}(?:/dp/|/gp/product/|/ASIN/|/gp/video/detail/))([a-zA-Z0-9]*)(.*?)(?:/.*|$)#';
866 $matches = array();
867 preg_match( $reg, $url, $matches );
868
869 return isset( $matches[1] ) && ! empty( $matches[1] ) ? $matches[1] : false;
870 }
871
872 /**
873 * Get amazon tracking id by url
874 * This function will be deprecated in the future. Please use Helper::get_argument_from_url() instead
875 *
876 * @param string $link Amazon link.
877 * @return string
878 */
879 public static function get_amazon_tracking_id_by_url( $link ) {
880 $search = '/([?|&|&amp;|\/]{1})tag=([a-zA-Z0-9\-\_]*)/i';
881 $matches = array();
882 preg_match( $search, $link, $matches );
883
884 return $matches[2] ?? '';
885 }
886
887 /**
888 * Make product url to shorten url if this setting is enabled
889 *
890 * @param string $product_url Amazon product url.
891 * @param bool $monetize Monetize link or not. Default to true.
892 * @param bool $check_lasso_post Check Lasso post or ignore. Default to true.
893 * @param string $custom_tracking_id Allow to use custom tracking id.
894 */
895 public static function get_amazon_product_url( $product_url, $monetize = true, $check_lasso_post = true, $custom_tracking_id = '' ) {
896 // ? amazon link but it is not product url
897 if ( ! self::is_amazon_url( $product_url ) || self::is_amazon_shortented_url( $product_url ) ) {
898 return $product_url;
899 }
900
901 $product_id = self::get_product_id_by_url( $product_url );
902
903 $tag = Helper::get_argument_from_url( $product_url, 'tag' );
904 $maas = Helper::get_argument_from_url( $product_url, 'maas' );
905 $ref_ = Helper::get_argument_from_url( $product_url, 'ref_' );
906 $s_args = Helper::get_argument_from_url( $product_url, 's' );
907
908 $maas_args = $maas ? 'maas=' . $maas : '';
909 $ref__args = $ref_ ? 'ref_=' . $ref_ : '';
910 $s_args = $s_args ? 's=' . $s_args : '';
911
912 // ? remove all url queries, just keep needed args
913 $url_without_params = explode( '?', $product_url )[0];
914 if ( $product_id && ! $monetize ) {
915 $args = Helper::build_url_parameter_string( array( $maas_args, $ref__args, $s_args ) );
916
917 return $args ? $url_without_params . '?' . $args : $url_without_params;
918 }
919
920 $lasso_settings = Setting::get_settings();
921 $amazon_multiple_tracking_id = $lasso_settings['amazon_multiple_tracking_id'] ?? true;
922 $amazon_tracking_id_whitelist = $lasso_settings['amazon_tracking_id_whitelist'] ?? array();
923 $amazon_tracking_id = trim( $lasso_settings['amazon_tracking_id'] ?? '' );
924
925 if ( ! $amazon_multiple_tracking_id ) {
926 $amazon_tracking_id_whitelist = array();
927 }
928
929 $lasso_db = new Lasso_DB();
930 $amz_cache_key = self::OBJECT_KEY . '_' . self::FUNCTION_NAME_GET_LASSO_ID_BY_PRODUCT_ID_AND_TYPE . '_' . $product_id . '_' . self::PRODUCT_TYPE;
931 $lasso_id = Cache_Per_Process::get_instance()->get_cache( $amz_cache_key, null );
932 if ( null === $lasso_id ) {
933 $lasso_id = $lasso_db->get_lasso_id_by_product_id_and_type( $product_id );
934 Cache_Per_Process::get_instance()->set_cache( $amz_cache_key, $lasso_id );
935 }
936
937 if ( $check_lasso_post && ! $lasso_id ) {
938 return $product_url;
939 }
940
941 $tag = $custom_tracking_id ? $custom_tracking_id : $tag;
942 $tag = $tag ? $tag : '';
943 $tag = ! empty( $amazon_tracking_id ) && ! in_array( $tag, $amazon_tracking_id_whitelist, true )
944 ? $amazon_tracking_id : $tag;
945
946 // ? Return the remove all url queries for product url
947 if ( $product_id ) {
948 $tag_args = $tag ? 'tag=' . $tag : '';
949 $args = Helper::build_url_parameter_string( array( $tag_args, $maas_args, $ref__args, $s_args ) );
950
951 $product_url = $args ? $url_without_params . '?' . $args : $url_without_params;
952 } else {
953 $product_url = str_replace( '&amp;', '&', $product_url );
954 $parse = wp_parse_url( $product_url );
955 parse_str( $parse['query'] ?? '', $query );
956
957 // ? set tag id (tracking id) at the end of the url
958 if ( $tag ) {
959 $query['tag'] = $tag;
960 } elseif ( ! empty( $amazon_tracking_id ) ) {
961 $query['tag'] = $amazon_tracking_id;
962 }
963
964 if ( ! $monetize ) {
965 unset( $query['tag'] );
966 }
967
968 $parse['query'] = Helper::get_query_from_array( $query );
969 $product_url = Helper::get_url_from_parse( $parse );
970 $product_url = trim( $product_url );
971 $product_url = trim( $product_url, '?' );
972 }
973
974 return $product_url;
975 }
976
977 /**
978 * Get Amazon product info from url accept url or post_id
979 *
980 * @param string|int $url_or_post_id URL or post id.
981 */
982 public function get_amazon_product( $url_or_post_id ) {
983
984 if ( is_numeric( $url_or_post_id ) ) {
985 // ? get amazon product using post_id
986 $post_id = $url_or_post_id;
987 $amazon_id = Affiliate_Link::get_amazon_id( $post_id );
988 $product = $this->get_amazon_product_from_db( $amazon_id );
989 return $product;
990 } else {
991 $url = $url_or_post_id;
992 }
993
994 // ? get amazon prodcut using url
995 if ( empty( $url ) ) {
996 return '';
997 }
998
999 $url = trim( $url, '/' );
1000 $product_id = self::get_product_id_by_url( $url );
1001 $product = $this->fetch_product_info( $product_id, true ); // ? Let's save all Amazon details as well
1002 $amazon_product = '';
1003
1004 if ( 'success' === $product['status'] ) {
1005 $product = $product['product'];
1006 $shorten_url = self::get_amazon_product_url( $product['url'] );
1007 $amazon_product = array(
1008 'id' => $product['product_id'],
1009 'name' => $product['title'],
1010 'price' => $product['price'],
1011 'url' => $shorten_url,
1012 'image' => $product['image'],
1013 'description' => '',
1014 );
1015 }
1016
1017 return $amazon_product;
1018 }
1019
1020 /**
1021 * Check whether a URL is amazon search page
1022 *
1023 * @param string $url URL.
1024 *
1025 * @return bool|string
1026 */
1027 public static function is_amazon_search_page( $url ) {
1028 $amazon_id = self::get_product_id_by_url( $url );
1029 $parse = wp_parse_url( $url );
1030 $path = $parse['path'] ?? '';
1031 $path = rtrim( $path, '/' );
1032 $keywords = Helper::get_argument_from_url( $url, 'keywords' );
1033 $field_keywords = Helper::get_argument_from_url( $url, 'field-keywords' );
1034 $k = Helper::get_argument_from_url( $url, 'k' );
1035 $k = $k ? $k : $field_keywords;
1036 $k = $k ? $k : $keywords;
1037
1038 if ( ! $amazon_id && ( '/s' === substr( $path, -2 ) || strpos( $path, '/s/' ) !== false ) && $k ) {
1039 return $k;
1040 }
1041
1042 return false;
1043 }
1044
1045 /**
1046 * Check whether a URL is amazon search page
1047 *
1048 * @param string $url URL.
1049 *
1050 * @return bool|string
1051 */
1052 public static function get_search_page_title( $url ) {
1053 $new_title = 'Amazon';
1054 $k = self::is_amazon_search_page( $url );
1055 if ( $k ) {
1056 $base_domain = Helper::get_base_domain( $url );
1057 $title_prefix = ucfirst( $base_domain );
1058 $new_title = $title_prefix . ' : ' . $k;
1059 }
1060
1061 return $new_title;
1062 }
1063
1064 /**
1065 * Validate Amazon tracking id
1066 *
1067 * @param string $tracking_id Amazon tracking id.
1068 * @return boolean
1069 */
1070 public static function validate_tracking_id( $tracking_id ) {
1071 return (bool) preg_match( '/' . self::TRACKING_ID_REGEX . '/i', $tracking_id );
1072 }
1073
1074 /**
1075 * Get Amazon link by product id
1076 *
1077 * @param string $product_id Amazon product id.
1078 * @param string $amz_link Amazon link. Default to empty.
1079 */
1080 public function get_amazon_link_by_product_id( $product_id, $amz_link = '' ) {
1081 if ( ! $product_id ) {
1082 return $amz_link;
1083 }
1084
1085 if ( '' !== $amz_link ) {
1086 $parse = wp_parse_url( $amz_link );
1087 $host = $parse['host'] ?? '';
1088 if ( '' !== $host ) {
1089 return 'https://' . $host . '/dp/' . $product_id;
1090 }
1091 }
1092
1093 $country = Setting::get_setting( 'amazon_default_tracking_country', 'usa' );
1094 $countries = self::get_amazon_api_countries();
1095 $amazon_domain = $countries[ $country ]['amazon_domain'];
1096
1097 return 'https://' . $amazon_domain . '/dp/' . $product_id;
1098 }
1099
1100 /**
1101 * Check whether a URL is Amazon redirect page
1102 *
1103 * @param string $url Amazon URL.
1104 */
1105 public static function is_amazon_redirect_page( $url ) {
1106 if ( ! self::is_amazon_url( $url ) || strpos( $url, '/gp/slredirect/' ) === false ) {
1107 return false;
1108 }
1109
1110 return true;
1111 }
1112
1113 /**
1114 * Get redirect url
1115 *
1116 * @param string $url Amazon URL.
1117 */
1118 public static function get_redirect_url( $url ) {
1119 if ( self::is_amazon_redirect_page( $url ) ) {
1120 $url_param = Helper::get_argument_from_url( $url, 'url' );
1121 $amazon_domain = Helper::get_base_domain( $url );
1122 $amazon_domain = Helper::add_https( $amazon_domain );
1123 $new_url = $amazon_domain . $url_param;
1124 $product_id = self::get_product_id_by_url( $new_url );
1125
1126 if ( $product_id ) {
1127 $url = $new_url;
1128 }
1129 }
1130
1131 return $url;
1132 }
1133 }
1134