Changeset 3358723
- Timestamp:
- 09/09/2025 03:30:55 PM (6 months ago)
- Location:
- helloextend-protection
- Files:
-
- 2 added
- 19 edited
- 4 copied
-
tags/1.2.0 (copied) (copied from helloextend-protection/trunk)
-
tags/1.2.0/helloextend-protection.php (copied) (copied from helloextend-protection/trunk/helloextend-protection.php) (1 diff)
-
tags/1.2.0/includes/class-helloextend-global.php (modified) (8 diffs)
-
tags/1.2.0/includes/class-helloextend-protection-cart-offer.php (modified) (5 diffs)
-
tags/1.2.0/includes/class-helloextend-protection-orders.php (copied) (copied from helloextend-protection/trunk/includes/class-helloextend-protection-orders.php) (12 diffs)
-
tags/1.2.0/includes/class-helloextend-protection-shipping.php (modified) (1 diff)
-
tags/1.2.0/includes/class-helloextend-protection.php (modified) (1 diff)
-
tags/1.2.0/js/helloextend-cart-offers.js (modified) (4 diffs)
-
tags/1.2.0/js/helloextend-global.js (modified) (1 diff)
-
tags/1.2.0/js/helloextend-pdp-offers.js (modified) (8 diffs)
-
tags/1.2.0/js/helloextend-post-purchase.js (added)
-
tags/1.2.0/js/helloextend-shipping-offers.js (modified) (1 diff)
-
tags/1.2.0/readme.txt (copied) (copied from helloextend-protection/trunk/readme.txt) (2 diffs)
-
trunk/helloextend-protection.php (modified) (1 diff)
-
trunk/includes/class-helloextend-global.php (modified) (8 diffs)
-
trunk/includes/class-helloextend-protection-cart-offer.php (modified) (5 diffs)
-
trunk/includes/class-helloextend-protection-orders.php (modified) (12 diffs)
-
trunk/includes/class-helloextend-protection-shipping.php (modified) (1 diff)
-
trunk/includes/class-helloextend-protection.php (modified) (1 diff)
-
trunk/js/helloextend-cart-offers.js (modified) (4 diffs)
-
trunk/js/helloextend-global.js (modified) (1 diff)
-
trunk/js/helloextend-pdp-offers.js (modified) (8 diffs)
-
trunk/js/helloextend-post-purchase.js (added)
-
trunk/js/helloextend-shipping-offers.js (modified) (1 diff)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
helloextend-protection/tags/1.2.0/helloextend-protection.php
r3352239 r3358723 17 17 * Plugin URI: https://docs.extend.com/docs/extend-protection-plugin-for-woocommerce 18 18 * Description: Extend Protection for Woocommerce. Allows WooCommerce merchants to offer product and shipping protection to their customers. 19 * Version: 1. 1.319 * Version: 1.2.0 20 20 * Author: Extend, Inc. 21 21 * Author URI: https://extend.com/ -
helloextend-protection/tags/1.2.0/includes/class-helloextend-global.php
r3348358 r3358723 54 54 { 55 55 56 $this->helloextend_protection = $helloextend_protection;57 $this->version = $version;56 $this->helloextend_protection = $helloextend_protection; 57 $this->version = $version; 58 58 $this->hooks(); 59 59 } … … 121 121 // add sku to cart item and label it referenceId 122 122 $cart[$cart_item_key]['referenceId'] = $referenceId; 123 $cart[$cart_item_key]['product_name'] = $_woo_product->get_ title();123 $cart[$cart_item_key]['product_name'] = $_woo_product->get_name(); 124 124 } 125 125 … … 270 270 } 271 271 272 if ($helloextend_data['leadToken']) { 273 $helloextend_data['leadQuantity'] = $quantity; 274 } 275 272 276 WC()->cart->add_to_cart($warranty_product_id, $quantity, 0, 0, ['extendData' => $helloextend_data]); 273 277 } … … 346 350 $covered = wc_get_product($covered_id); 347 351 $sku = $cart_item['extendData']['planId']; 348 $covered_title = $covered->get_ title();352 $covered_title = $covered->get_name(); 349 353 350 354 $item->add_meta_data('Warranty', $title); … … 352 356 $item->add_meta_data('Plan Id', $sku); 353 357 $item->add_meta_data('Covered Product', $covered_title); 358 if (isset($cart_item['extendData']['leadToken'])) { 359 $item->add_meta_data('Lead Token', $cart_item['extendData']['leadToken']); 360 } 354 361 } 355 362 } … … 372 379 $covered = wc_get_product($covered_id); 373 380 $sku = $cart_item['extendData']['planId']; 374 $covered_title = $covered->get_ title();381 $covered_title = $covered->get_name(); 375 382 $data[] = [ 376 383 'key' => 'Product', … … 403 410 wp_enqueue_script('helloextend_global_script'); 404 411 wp_localize_script('helloextend_global_script', 'ExtendWooCommerce', compact('store_id', 'ajaxurl', 'environment')); 412 413 // Get the leadToken from URL parameters 414 $lead_token = $this->get_lead_token_from_url(); 415 if ($lead_token) { 416 // Sanitize the token for safe JavaScript output 417 $safe_lead_token = esc_js($lead_token); 418 419 // Output JavaScript to console 420 echo "<script type='text/javascript'>\n"; 421 echo "console.log('found leadToken: ', '" . $safe_lead_token . "');\n"; 422 echo "</script>\n"; 423 424 // next step: Run Post Purchase logic to handle lead Token 425 $this->helloextend_post_purchase($lead_token, $store_id, $environment, $ajaxurl); 426 } 405 427 } else { 406 428 HelloExtend_Protection_Logger::helloextend_log_error('Store Id missing or Extend Product Protection is disabled'); … … 509 531 return $categories[0]->name ?? 'Uncategorized'; 510 532 } 533 534 /** 535 * Get leadToken from URL parameters 536 * 537 * @return string|null The leadToken value or null if not found 538 */ 539 private function get_lead_token_from_url() { 540 // Check if leadToken exists in GET parameters 541 if (isset($_GET['leadToken']) && !empty($_GET['leadToken'])) { 542 // Sanitize the input 543 return sanitize_text_field($_GET['leadToken']); 544 } 545 546 return null; 547 } 548 549 /** 550 * Get the Post Purchase Logic if the lead token is passed 551 * 552 * @since 1.0.0 553 */ 554 private function helloextend_post_purchase($leadToken, $store_id, $environment, $ajaxurl) 555 { 556 $cart_url = wc_get_cart_url(); 557 wp_enqueue_script('helloextend_global_post_purchase_script'); 558 wp_localize_script('helloextend_global_post_purchase_script', 'ExtendWooCommerce', compact('store_id', 'leadToken', 'ajaxurl', 'environment', 'cart_url')); 559 } 511 560 } -
helloextend-protection/tags/1.2.0/includes/class-helloextend-protection-cart-offer.php
r3348358 r3358723 48 48 49 49 protected string $warranty_product_id; 50 protected array $products = [];51 protected array $updates = [];52 50 private array $settings; 53 51 … … 77 75 } 78 76 77 private function is_item_helloextend($item) 78 { 79 $warranty_product_id = $this->settings['warranty_product_id']; 80 return $item['product_id'] == $warranty_product_id && isset($item['extendData']) && !empty($item['extendData']); 81 } 82 83 private function is_lead($item) 84 { 85 return $this->is_item_helloextend($item) && isset($item['extendData']['leadToken']) && isset($item['extendData']['leadQuantity']); 86 } 87 88 private function is_warranty($item) 89 { 90 return $this->is_item_helloextend($item) && !isset($item['extendData']['leadToken']) && !isset($item['extendData']['leadQuantity']); 91 } 92 93 private function get_product_id($line) 94 { 95 if ($this->is_item_helloextend($line)) { 96 return $line['extendData']['covered_product_id']; 97 } else { 98 return $line['variation_id'] > 0 ? $line['variation_id'] : $line['product_id']; 99 } 100 } 101 102 private function map_cart_items_with_warranties() 103 { 104 $cart_contents = WC()->cart->get_cart_contents(); 105 106 $products = array(); 107 108 foreach ( $cart_contents as $line ) { 109 110 $product_id = $this->get_product_id($line); 111 $id = $line['extendData']['leadToken'] ?? $product_id; 112 113 $product = $products[ $id ] ?? array( 114 'quantity' => 0, 115 'warranty_quantity' => 0, 116 'warranties' => array(), 117 ); 118 119 if ($this->is_warranty($line)) { 120 $product['warranty_quantity'] += $line['quantity']; 121 $product['warranties'][] = $line; 122 } else { 123 $product['quantity'] += $line['quantity']; 124 125 if (isset($line['extendData']) && isset($line['extendData']['leadQuantity'])) { 126 $product['leadQuantity'] = $line['extendData']['leadQuantity']; 127 $product['leadProductKey'] = $line['key']; 128 } 129 } 130 131 $products[ $id ] = $product; 132 } 133 134 return $products; 135 } 136 79 137 // get_cart_updates() 80 138 // goes through the cart and gets updates to products/plans for normalization 81 public function get_cart_updates() 82 { 83 84 $cart_contents = WC()->cart->get_cart_contents(); 85 86 foreach ( $cart_contents as $line ) { 87 88 // if we're on a warranty item 89 if (intval($line['product_id']) === intval($this->settings['warranty_product_id']) && isset($line['extendData']) && !empty($line['extendData']) ) { 90 // Grab reference id 91 $product_reference_id = $line['extendData']['covered_product_id']; 92 93 // If this product doesn't exist, create it with the warranty quantity and warranty added, else add to warranty quantity, and add warranty to warranty list 94 if (! isset($products[ $product_reference_id ]) ) { 95 $products[ $product_reference_id ] = [ 96 'quantity' => 0, 97 'warranty_quantity' => $line['quantity'], 98 'warranties' => [ $line ], 99 ]; 100 } else { 101 $products[ $product_reference_id ]['warranty_quantity'] += $line['quantity']; 102 array_push($products[ $product_reference_id ]['warranties'], $line); 103 } 104 // if we're on a non-warranty check if the product exists in list, if so add quantity, if not add to product list 105 } else { 106 $id = $line['variation_id'] > 0 ? $line['variation_id'] : $line['product_id']; 107 if (! isset($products[ $id ]) ) { 108 $products[ $id ] = [ 109 'quantity' => $line['quantity'], 110 'warranty_quantity' => 0, 111 'warranties' => [], 112 ]; 113 } else { 114 $products[ $id ]['quantity'] += $line['quantity']; 115 } 116 } 117 } 118 139 public function get_cart_updates($products) 140 { 119 141 $cart_balancing = $this->settings['helloextend_enable_cart_balancing'] == 1 ? true : false; 120 142 121 // if we have products, go through each and check for updates 122 if (isset($products) ) { 123 foreach ( $products as $product ) { 124 125 // if warranty quantity is greater than 0 and product quantity is 0 set warranty quantity to 0 126 if (intval($product['warranty_quantity']) > 0 && intval($product['quantity']) == 0 ) { 127 foreach ( $product['warranties'] as $warranty ) { 128 $updates[ $warranty['key'] ] = [ 'quantity' => 0 ]; 143 $updates = array(); 144 145 foreach ( $products as $product ) { 146 147 // If warranty item is coming from lead and the quantity in the cart does not match the lead quantity 148 if (isset($product['leadQuantity']) && isset($product['leadProductKey'])) { 149 if ($product['leadQuantity'] != $product['quantity']) { 150 $updates[$product['leadProductKey']] = $product['leadQuantity']; 151 } 152 153 continue; 154 } 155 156 // Remove warranties without products 157 if ($product['warranty_quantity'] > 0 && $product['quantity'] == 0 ) { 158 foreach ( $product['warranties'] as $warranty ) { 159 $updates[ $warranty['key'] ] = 0; 160 } 161 continue; 162 } 163 164 // grab difference of warranty quantity and product quantity 165 $quantity_diff = $product['warranty_quantity'] - $product['quantity']; 166 167 // No difference or warranties, no updates 168 if ($quantity_diff == 0 || $product['warranty_quantity'] == 0) { 169 continue; 170 } 171 172 // Too many warranties 173 if ($quantity_diff > 0 ) { 174 foreach ( $product['warranties'] as $warranty ) { 175 if ($quantity_diff == 0) { 176 break; 129 177 } 130 } else { 131 // grab difference of warranty_quantity and product quantity 132 $diff = $product['warranty_quantity'] - $product['quantity']; 133 134 // if there's a difference & that difference is greater than 0, we remove warranties till we reach the product quantity 135 if ($diff !== 0 ) { 136 if ($diff > 0 ) { 137 foreach ( $product['warranties'] as $warranty ) { 138 $new_quantity_diff = max([ 0, $diff - $warranty['quantity'] ]); 139 $removed_quantity = $diff - $new_quantity_diff; 140 $updates[ $warranty['key'] ] = [ 'quantity' => $warranty['quantity'] - $removed_quantity ]; 141 $diff = $new_quantity_diff; 142 } 143 } elseif ($cart_balancing && $diff < 0 ) { 144 foreach ( $product['warranties'] as $warranty ) { 145 $new_quantity_diff = max([ 0, $diff - $warranty['quantity'] ]); 146 $new_quantity = $warranty['quantity'] - $diff; 147 $updates[ $warranty['key'] ] = [ 'quantity' => $new_quantity ]; 148 $diff = $new_quantity_diff; 149 } 150 } 151 } 152 } 153 } 154 } 155 156 // if there's updates return updates 157 if (isset($updates) ) { 158 return $updates; 159 } 178 179 $new_quantity_diff = max([ 0, $quantity_diff - $warranty['quantity'] ]); 180 $removed_quantity = $quantity_diff - $new_quantity_diff; 181 $updates[ $warranty['key'] ] = $warranty['quantity'] - $removed_quantity; 182 $quantity_diff = $new_quantity_diff; 183 } 184 continue; 185 } 186 187 // Else, not enough warranties 188 if ($cart_balancing && $quantity_diff < 0 ) { 189 $warranty = $product['warranties'][0]; 190 $updates[$warranty['key']] = $warranty['quantity'] - $quantity_diff; 191 } 192 } 193 194 return $updates; 160 195 } 161 196 … … 165 200 { 166 201 167 $newUpdates = $this->get_cart_updates(); 168 169 if (isset($newUpdates) ) { 170 $cart = WC()->cart->get_cart_contents(); 171 foreach ( $cart as $line ) { 172 173 foreach ( $newUpdates as $key => $value ) { 174 if ($key == $line['key'] ) { 175 WC()->cart->set_quantity($key, $value['quantity'], true); 176 } 177 } 178 } 202 $products = $this->map_cart_items_with_warranties(); 203 204 $updates = $this->get_cart_updates($products); 205 206 foreach ( $updates as $key => $quantity_update ) { 207 WC()->cart->set_quantity($key, $quantity_update, true); 179 208 } 180 209 … … 192 221 if (! isset($cart_item['extendData']) ) { 193 222 $item_id = $cart_item['variation_id'] ? $cart_item['variation_id'] : $cart_item['product_id']; 194 $item_sku = $cart_item['data']->get_sku() ? $cart_item['data']->get_sku() : $item_id; 195 $referenceId = $item_id; 196 $categories = get_the_terms($item_id, 'product_cat'); 223 $parent_id = $cart_item['product_id']; 224 $categories = get_the_terms($parent_id, 'product_cat'); 197 225 $category = HelloExtend_Protection_Global::helloextend_get_first_valid_category($categories); 198 199 echo "<div id='offer_".esc_attr($item_id)."' class='cart-extend-offer' data-covered='".esc_attr($referenceId)."' data-category='".esc_attr($category)."'></div>"; 226 $price = (int) floatval($cart_item['data']->get_price() * 100); 227 228 echo "<div id='offer_".esc_attr($item_id)."' class='cart-extend-offer' data-covered='".esc_attr($item_id)."' data-category='".esc_attr($category)."' data-price='" . $price . "'></div>"; 200 229 } 201 230 } … … 208 237 $enable_helloextend = trim($this->settings['enable_helloextend']); 209 238 $helloextend_enable_cart_offers = $this->settings['helloextend_enable_cart_offers']; 210 $cart = WC()->cart;239 $cart = WC()->cart; 211 240 212 241 if ($helloextend_enable_cart_offers === '1' && $enable_helloextend === '1' ) { -
helloextend-protection/tags/1.2.0/includes/class-helloextend-protection-orders.php
r3352239 r3358723 54 54 private array $settings; 55 55 56 private const TRANSACTION_STORE_PREFIX = 'STORE::'; 57 private const TRANSACTION_ORDER_PREFIX = '::ORDER::'; 58 private const TRANSACTION_PRODUCT_PREFIX = '::PRODUCT::'; 59 private const TRANSACTION_OLI_PREFIX = '::OLI::'; 60 56 61 /** 57 62 * Initialize the class and set its properties. … … 67 72 /* retrieve environment variables */ 68 73 $this->settings = HelloExtend_Protection_Global::helloextend_get_settings(); 69 74 $this->helloextend_product_protection_id = $this->settings['warranty_product_id']; 75 70 76 // Hook the callback function to the 'woocommerce_new_order' action 71 77 add_action('woocommerce_checkout_order_processed', [$this, 'create_update_order'], 10, 1); … … 76 82 // Hook the callback function to the order cancelled action 77 83 add_action('woocommerce_order_status_cancelled', [$this, 'cancel_order'], 10, 1); 78 } 79 80 public function get_product_image_url($product) 84 add_action('woocommerce_order_status_refunded', [$this, 'cancel_order'], 10, 1); 85 86 // Handle refunded orders 87 add_action('woocommerce_order_refunded', [$this, 'handle_contract_refund'], 10, 2); 88 } 89 90 private function get_product_image_url($product) 81 91 { 82 92 // Only accept valid WooCommerce product objects … … 127 137 } 128 138 139 private function is_item_helloextend($item) 140 { 141 $helloextend_meta = $item->get_meta('_helloextend_data'); 142 return $item->get_product_id() == $this->helloextend_product_protection_id && isset($helloextend_meta) && isset($helloextend_meta['planId']); 143 } 144 145 private function is_item_helloextend_no_lead($item) 146 { 147 $helloextend_meta = $item->get_meta('_helloextend_data'); 148 return $this->is_item_helloextend($item) && !isset($helloextend_meta['leadToken']); 149 } 150 151 private function is_item_helloextend_lead($item) 152 { 153 $helloextend_meta = $item->get_meta('_helloextend_data'); 154 return $this->is_item_helloextend($item) && isset($helloextend_meta['leadToken']); 155 } 156 157 private function get_price_in_cents($item_price) 158 { 159 return (int) floatval($item_price * 100); 160 } 161 162 private function get_purchased_leads($order) 163 { 164 $lead_line_items = array(); 165 foreach ($order->get_items() as $item) { 166 if ($this->is_item_helloextend_lead($item)) { 167 $helloextend_meta = $item->get_meta('_helloextend_data'); 168 169 $lead_line_items[] = array( 170 'leadToken' => $helloextend_meta['leadToken'], 171 'lineItemTransactionId' => self::TRANSACTION_STORE_PREFIX . $this->settings['store_id'] . self::TRANSACTION_ORDER_PREFIX . $order->get_id() . self::TRANSACTION_PRODUCT_PREFIX . $helloextend_meta['covered_product_id'] . self::TRANSACTION_OLI_PREFIX . $item->get_id(), 172 'plan' => array( 173 'id' => $helloextend_meta['planId'], 174 'purchasePrice' => $helloextend_meta['price'], 175 ), 176 'quantity' => $item->get_quantity() 177 ); 178 } 179 } 180 181 return empty($lead_line_items) ? null : $lead_line_items; 182 } 183 129 184 /** 130 185 * helloextend_get_plans_and_products($order, $fulfill_now = false) … … 143 198 $helloextend_meta_data = (array) $item->get_meta('_helloextend_data'); 144 199 145 // if item id is for extend-product-protection gra m$helloextend_meta_data and push it to the plans array146 if ($ helloextend_meta_data['planId']) {200 // if item id is for extend-product-protection grab $helloextend_meta_data and push it to the plans array 201 if ($this->is_item_helloextend_no_lead($item) && $helloextend_meta_data['planId']) { 147 202 $helloextend_plans[] = array( 148 203 'id' => $helloextend_meta_data['planId'], … … 153 208 } 154 209 210 $leads = $this->get_purchased_leads($order); 211 155 212 // Loop through the order items and add them to the line_items array 156 213 $helloextend_line_items = array(); … … 162 219 163 220 // Get the first product category 164 $product_category_ids = $product->get_category_ids(); 165 $cat_term = get_term_by('id', $product_category_ids[0], 'product_cat'); 166 $first_category = $cat_term->name; 221 if ($product->get_type() == 'variation') { 222 $first_category = HelloExtend_Protection_Global::helloextend_get_first_valid_category(get_the_terms($product->get_parent_id(), 'product_cat')); 223 } else { 224 $first_category = HelloExtend_Protection_Global::helloextend_get_first_valid_category(get_the_terms($product_id, 'product_cat')); 225 } 167 226 168 227 // if line_id matches any id in $helloextend_plans[], push the plan data into the covered product … … 175 234 } 176 235 177 // Get extend product id from settings178 $helloextend_product_protection_id = $this->settings['warranty_product_id'];179 180 236 // Add relevant data to the line_items array 181 237 // if product id for extend-product-protection, do not add it to helloextend_line_items array 182 if ($product_id != $ helloextend_product_protection_id) {238 if ($product_id != $this->helloextend_product_protection_id) { 183 239 184 240 $image_url = $this->get_product_image_url($product); 185 241 186 $helloextend_line_item s[]= array(242 $helloextend_line_item = array( 187 243 'lineItemTransactionId' => $product->get_id(), 188 244 'product' => array( … … 190 246 'title' => $product->get_name(), 191 247 'category' => $first_category, 192 'listPrice' => (int) floatval($product->get_regular_price() * 100),193 'purchasePrice' => (int) floatval($product->get_price() * 100),248 'listPrice' => $this->get_price_in_cents($product->get_regular_price() * 100), 249 'purchasePrice' => $this->get_price_in_cents($product->get_price() * 100), 194 250 'purchaseDate' => $order->get_data()['date_created']->getTimestamp() * 1000, 195 251 'imageUrl' => $image_url … … 201 257 // if $plan is not empty, add the plan to the current line item 202 258 if (!empty($plan)) { 203 $helloextend_line_items[count($helloextend_line_items) - 1]['plan'] = $plan; 204 } 205 } 206 } 259 $helloextend_line_item['plan'] = $plan; 260 } 261 262 $helloextend_line_items[] = $helloextend_line_item; 263 } 264 } 265 266 if ($leads) { 267 $helloextend_line_items = array_merge($helloextend_line_items, $leads); 268 } 269 207 270 return $helloextend_line_items; 208 271 } … … 441 504 442 505 if ( ! $order instanceof WC_Order ) { 443 HelloExtend_Protection_Logger::helloextend_log_error(444 'Cannot cancel Extend order – WooCommerce order ' . $order_id . ' not found.'445 );446 return;447 }506 HelloExtend_Protection_Logger::helloextend_log_error( 507 'Cannot cancel Extend order – WooCommerce order ' . $order_id . ' not found.' 508 ); 509 return; 510 } 448 511 449 512 // Get Token from Global function … … 526 589 } 527 590 } 591 592 public function handle_contract_refund(string $order_id, string $refund_id) 593 { 594 $order = wc_get_order($order_id); 595 596 if ( ! $order instanceof WC_Order ) { 597 HelloExtend_Protection_Logger::helloextend_log_error( 598 'Cannot refund Extend order - WooCommerce order ' . $order_id . ' not found.' 599 ); 600 return; 601 } 602 603 $refund = wc_get_order($refund_id); 604 605 if (!$refund instanceof WC_Order_Refund) { 606 HelloExtend_Protection_Logger::helloextend_log_error( 607 'Cannot refund Extend order - WooCommerce refund ' . $refund_id . ' not found.' 608 ); 609 return; 610 } 611 612 $refund_items = $refund->get_items(); 613 $refunded_contracts = []; 614 615 // Get contract IDs on this order and add them to contracts array 616 $contracts = get_post_meta($order->get_id(), '_product_protection_contracts', true); 617 foreach($refund_items as $refund_item) { 618 $refunded_item_id = $refund_item->get_meta('_refunded_item_id'); 619 $order_item = $order->get_item($refunded_item_id); 620 $helloextend_data = $order_item->get_meta('_helloextend_data'); 621 622 if ($refund_item->get_product_id() == $this->helloextend_product_protection_id && $helloextend_data) { 623 foreach($contracts as $covered_product => $contract_id) { 624 if ($helloextend_data['covered_product_id'] == $covered_product) { 625 626 $refunded_contracts[] = $contract_id; 627 break; 628 } 629 } 630 } 631 } 632 633 634 // Get Token from Global function 635 $token = HelloExtend_Protection_Global::helloextend_get_token(); 636 637 // If token exists, log successful token 638 if ($this->settings['enable_helloextend_debug'] == 1 && $token) { 639 HelloExtend_Protection_Logger::helloextend_log_debug('Access token created successfully'); 640 } 641 // If token does not exist, log error 642 if ($this->settings['enable_helloextend_debug'] == 1 && !$token) { 643 HelloExtend_Protection_Logger::helloextend_log_error('Error:Access token was not created, exiting order refund'); 644 return; 645 } 646 647 648 $cancellation_errors = []; 649 // Cancel the contract 650 // {{API_HOST}}/contracts/{{contractId}}/cancel 651 foreach ($refunded_contracts as $contract_id) { 652 $contract_cancel_endpoint = $this->settings['api_host'] . '/contracts/' . $contract_id . '/cancel'; 653 $contract_cancel_args = array( 654 'method' => 'POST', 655 'headers' => array( 656 'Content-Type' => 'application/json', 657 'Accept' => 'application/json; version=latest', 658 'X-Extend-Access-Token' => $token, 659 ), 660 ); 661 662 $contract_cancel_response = wp_remote_request( $contract_cancel_endpoint, $contract_cancel_args ); 663 664 if (is_wp_error($contract_cancel_response)) { 665 $error_message = $contract_cancel_response->get_error_message(); 666 $cancellation_errors[] = 'Cancel Contract Failed for ID ' . $contract_id . ' : POST request failed: ' . $error_message.', cannot cancel contract'; 667 } 668 669 $contract_cancel_response_code = wp_remote_retrieve_response_code( $contract_cancel_response ); 670 $data = json_decode(wp_remote_retrieve_body( $contract_cancel_response )); 671 if ($contract_cancel_response_code < 200 || $contract_cancel_response_code >= 300) { 672 $cancellation_errors[] = 'Contract cancel for ID ' . $contract_id . ' : POST request returned status ' . $contract_cancel_response_code . ' with body ' . $data; 673 } 674 675 if ($this->settings['enable_helloextend_debug']) { 676 HelloExtend_Protection_Logger::helloextend_log_debug( 677 'Contract ID ' . $contract_id . ' canceled successfully.' 678 ); 679 } 680 } 681 682 if (!empty($cancellation_errors)) { 683 HelloExtend_Protection_Logger::helloextend_log_error( 684 'Some contracts failed to cancel: ' . implode('; ', $cancellation_errors) 685 ); 686 } 687 688 if ($this->settings['enable_helloextend_debug'] == 1 && !empty($refunded_contracts)) { 689 HelloExtend_Protection_Logger::helloextend_log_debug( 690 'Contract IDs ' . join(", ", $refunded_contracts) . ' canceled succesfully.' 691 ); 692 } 693 } 528 694 } -
helloextend-protection/tags/1.2.0/includes/class-helloextend-protection-shipping.php
r3348358 r3358723 119 119 if (! $product->is_virtual() ) { 120 120 $referenceId = $product->get_id(); 121 $categories = get_the_terms($cart_item['product_id'], 'product_cat'); 122 $category = HelloExtend_Protection_Global::helloextend_get_first_valid_category(is_wp_error($categories) ? [] : (array) $categories); 121 123 $items[] = array( 122 124 'referenceId' => $referenceId, 123 125 'quantity' => $cart_item['quantity'], 124 'category' => get_the_terms($product->get_id(), 'product_cat')[0]->name,126 'category' => $category, 125 127 'purchasePrice' => (int) floatval($product->get_price() * 100), 126 128 'productName' => $product->get_name(), -
helloextend-protection/tags/1.2.0/includes/class-helloextend-protection.php
r3263092 r3358723 247 247 wp_register_script('helloextend_script', $this->sdk_url, array(), '1.0.0', true); 248 248 wp_register_script('helloextend_global_script', $this->url . '../js/helloextend-global.js', [ 'jquery', 'helloextend_script' ], '1.0.0', true); 249 wp_register_script('helloextend_global_post_purchase_script', $this->url . '../js/helloextend-post-purchase.js', [ 'jquery', 'helloextend_script' ], '1.0.0', true); 249 250 wp_register_script('helloextend_product_integration_script', $this->url . '../js/helloextend-pdp-offers.js', [ 'jquery', 'helloextend_global_script' ], '1.0.0', true); 250 251 wp_register_script('helloextend_cart_integration_script', $this->url . '../js/helloextend-cart-offers.js', [ 'jquery', 'helloextend_script' ], '1.0.0', true); -
helloextend-protection/tags/1.2.0/js/helloextend-cart-offers.js
r3348358 r3358723 1 1 // This script is used to handle the rendering and functionality of Extend offers in a WooCommerce cart. 2 2 (($) => { 3 4 // If necessary objects (ExtendWooCommerce and ExtendCartIntegration) do not exist, stop the execution of the script.5 if (!ExtendWooCommerce || !ExtendCartIntegration) {6 return;7 }8 3 9 4 const SELECTORS = { … … 11 6 TITLE: '.product-name', 12 7 IMAGE: '.product-thumbnail', 13 PRICE: '.product-price',14 8 QUANTITY: 'input.qty', 15 9 EXTEND_OFFER: '.cart-extend-offer', … … 79 73 const quantity = $lineItemElement.find(SELECTORS.QUANTITY).val(); 80 74 81 const [ dollars, cents = '00' ] = $lineItemElement.find(SELECTORS.PRICE).text().trim().replace(/[$,]/g, '').split('.'); 82 const normalizedCents = cents.padEnd(2, '0'); 83 const price = `${dollars}${normalizedCents}`; 75 const price = $offer.data('price'); 84 76 85 77 renderExtendOffer($offer[0], { referenceId, category, price }, quantity); … … 89 81 90 82 // Wait until the document is fully loaded before running the script. 91 $(document).ready(() => { 83 $(document).off('integration.extend.cart').on('integration.extend.cart', () => { 84 // If necessary objects (ExtendWooCommerce and ExtendCartIntegration) do not exist, stop the execution of the script. 85 if (typeof Extend === 'undefined' 86 || typeof ExtendWooCommerce === 'undefined' 87 || typeof ExtendCartIntegration === 'undefined') { 88 if (ExtendWooCommerce.debugLogEnabled) 89 ExtendWooCommerce.extendAjaxLog('debug', 'One of Extend, ExtendWooCommerce, ExtendCartIntegration is not defined'); 90 return; 91 } 92 92 93 initCartOffers(); 93 94 }); -
helloextend-protection/tags/1.2.0/js/helloextend-global.js
r3348358 r3358723 1 1 (function ( $ ) { 2 2 'use strict'; 3 $(document).ready( 4 function ($) { 5 if(!ExtendWooCommerce) { return; 6 } 3 $(document).ready(function ($) { 4 if(!ExtendWooCommerce) { return; 5 } 7 6 8 const { store_id: storeId, ajaxurl, environment, debug_log_enabled: debugLogEnabled } = ExtendWooCommerce;7 const { store_id: storeId, ajaxurl, environment, debug_log_enabled: debugLogEnabled } = ExtendWooCommerce; 9 8 10 Extend.config( 11 { 12 storeId, 13 environment 9 Extend.config({ 10 storeId, 11 environment 12 }); 13 14 async function addPlanToCart(opts) 15 { 16 return await jQuery.post( 17 ajaxurl, { 18 action: "add_to_cart_helloextend", 19 quantity: opts.quantity, 20 extendData: { 21 ...opts.plan, 22 leadToken: opts.leadToken 23 } 14 24 } 15 ); 25 ).promise() 26 } 16 27 17 window.ExtendWooCommerce = { 18 ...ExtendWooCommerce, 19 addPlanToCart, 20 getCart, 21 warrantyAlreadyInCart, 22 extendAjaxLog, 23 debugLogEnabled 24 } 25 26 async function addPlanToCart(opts) 27 { 28 return await jQuery.post( 28 async function getCart() 29 { 30 return JSON.parse( 31 await jQuery.post( 29 32 ajaxurl, { 30 action: "add_to_cart_helloextend", 31 quantity: opts.quantity, 32 extendData: opts.plan 33 action: "get_cart_helloextend" 33 34 } 34 35 ).promise() 36 ); 37 } 38 39 function warrantyAlreadyInCart(variantId, cart) 40 { 41 let cartContents = cart['cart_contents']; 42 if (!cartContents) { 43 cartContents = cart; 35 44 } 36 37 async function getCart() 38 { 39 return JSON.parse( 40 await jQuery.post( 41 ajaxurl, { 42 action: "get_cart_helloextend" 43 } 44 ).promise() 45 ); 46 } 47 48 function warrantyAlreadyInCart(variantId, cart) 49 { 50 let cartContents = cart['cart_contents']; 51 if (!cartContents) { 52 cartContents = cart; 53 } 54 const cartItems = Object.values(cartContents); 55 const extendWarranties = cartItems.filter( 56 function (lineItem) { 57 //filter through the customAttributes and grab the referenceId 58 let extendData = lineItem.extendData; 59 if (extendData && extendData['covered_product_id']) { 60 let referenceId = extendData['covered_product_id']; 61 return ( 45 const cartItems = Object.values(cartContents); 46 const extendWarranties = cartItems.filter( 47 function (lineItem) { 48 //filter through the customAttributes and grab the referenceId 49 let extendData = lineItem.extendData; 50 if (extendData && extendData['covered_product_id']) { 51 let referenceId = extendData['covered_product_id']; 52 return ( 62 53 extendData && 63 54 !extendData.leadToken && 64 55 referenceId && 65 56 referenceId.toString() === variantId.toString() 66 ); 67 } 57 ); 68 58 } 69 ); 70 return extendWarranties.length > 0; 71 } 59 } 60 ); 61 return extendWarranties.length > 0; 62 } 72 63 73 function extendAjaxLog(method, ...message) 74 { 64 function extendAjaxLog(method, ...message) { 75 65 76 message = message.join(' ');77 /* Now use an ajax call to write logs from js files... */78 $.ajax(79 {80 type: 'POST',81 url: ajaxurl,66 message = message.join(' '); 67 /* Now use an ajax call to write logs from js files... */ 68 $.ajax( 69 { 70 type: 'POST', 71 url: ajaxurl, 82 72 83 data: { 84 action: 'helloextend_logger_ajax_call', 85 message: message, 86 method: method, 87 }, 88 success: function (xhr, x, checkStatus) { 89 return null; 90 }, 91 error: function (e) { 92 console.error("helloextendAjaxLog error: ", e.statusText) 93 } 73 data: { 74 action: 'helloextend_logger_ajax_call', 75 message: message, 76 method: method, 77 }, 78 success: function (xhr, x, checkStatus) { 79 return null; 80 }, 81 error: function (e) { 82 console.error("helloextendAjaxLog error: ", e.statusText) 94 83 } 95 ); 96 } 84 } 85 ); 86 } 97 87 88 window.ExtendWooCommerce = { 89 ...ExtendWooCommerce, 90 addPlanToCart, 91 getCart, 92 warrantyAlreadyInCart, 93 extendAjaxLog, 94 debugLogEnabled 98 95 } 99 ) 96 97 $(document).trigger('integration.extend'); 98 }); 100 99 })(jQuery); 101 100 -
helloextend-protection/tags/1.2.0/js/helloextend-pdp-offers.js
r3348358 r3358723 1 1 (function( $ ) { 2 2 'use strict'; 3 $(document). ready(function($) {3 $(document).off('integration.extend.pdp').on('integration.extend.pdp', function() { 4 4 5 5 if (!ExtendWooCommerce || !ExtendProductIntegration) return; … … 8 8 const { type: product_type, id: product_id, sku, first_category, price, helloextend_pdp_offers_enabled, helloextend_modal_offers_enabled, atc_button_selector } = ExtendProductIntegration; 9 9 10 const $atcButton = jQuery(atc_button_selector)10 const $atcButton = $(atc_button_selector); 11 11 12 const quantity = parseInt(document.querySelector('input[name="quantity"]').value || 1)12 const getQuantity = () => parseInt(($('input[name="quantity"]').val() || 1), 10); 13 13 14 let supportedProductType = true;15 14 let reference_id = product_id; 16 15 17 16 // If PDP offers are not enabled, hide Extend offer div 18 17 if (helloextend_pdp_offers_enabled === '0') { 19 const extendOffer = document.querySelector('.helloextend-offer') 20 extendOffer.style.display = 'none'; 18 $('.helloextend-offer').hide(); 21 19 } 22 20 23 function handleAddToCartLogic( variation_id) {21 function handleAddToCartLogic() { 24 22 25 $atcButton.o n('click', function extendHandler(e) {23 $atcButton.off('click.extend').on('click.extend', function extendHandler(e) { 26 24 e.preventDefault(); 27 25 e.stopImmediatePropagation(); … … 32 30 33 31 function triggerAddToCart() { 34 $atcButton.off('click ', extendHandler);32 $atcButton.off('click.extend', extendHandler); 35 33 $atcButton.trigger('click'); 36 $atcButton.on('click ', extendHandler);34 $atcButton.on('click.extend', extendHandler); 37 35 } 38 36 39 37 const component = Extend.buttons.instance('.helloextend-offer'); 40 38 41 39 /** get the users plan selection */ 42 40 const plan = component.getPlanSelection(); 43 const product = component.getActiveProduct();41 const referenceId = component.getActiveProduct().id; 44 42 45 43 if (plan) { 46 var planCopy = { ...plan, covered_product_id: variation_id }47 vardata = {48 quantity: quantity,44 let planCopy = { ...plan, covered_product_id: referenceId }; 45 let data = { 46 quantity: getQuantity(), 49 47 plan: planCopy, 50 48 price: (plan.price / 100).toFixed(2) 51 } 49 }; 50 52 51 ExtendWooCommerce.addPlanToCart(data) 53 52 .then(() => { 54 53 triggerAddToCart(); 55 }) 54 }); 56 55 } else { 57 56 if(helloextend_modal_offers_enabled === '1') { 58 57 Extend.modal.open({ 59 referenceId : variation_id,58 referenceId, 60 59 price: price, 61 60 category: first_category, 62 61 onClose: function(plan, product) { 63 62 if (plan && product) { 64 var planCopy = { ...plan, covered_product_id: variation_id }65 vardata = {66 quantity: quantity,63 let planCopy = { ...plan, covered_product_id: referenceId }; 64 let data = { 65 quantity: getQuantity(), 67 66 plan: planCopy, 68 67 price: (plan.price / 100).toFixed(2) 69 } 68 }; 69 70 70 ExtendWooCommerce.addPlanToCart(data) 71 71 .then(() => { 72 72 triggerAddToCart(); 73 }) 73 }); 74 74 } else { 75 triggerAddToCart() 75 triggerAddToCart(); 76 76 } 77 77 }, 78 78 }); 79 79 } else { 80 triggerAddToCart() 80 triggerAddToCart(); 81 81 } 82 82 } … … 97 97 98 98 // TODO: initalize cart offers 99 handleAddToCartLogic( reference_id);99 handleAddToCartLogic(); 100 100 101 101 } else if (product_type === 'variable') { 102 102 103 jQuery( ".single_variation_wrap" ).on( "show_variation", function ( event, variation ) {103 $( ".single_variation_wrap" ).on( "show_variation", function ( event, variation ) { 104 104 105 setTimeout(function(){106 105 let component = Extend.buttons.instance('.helloextend-offer'); 107 106 let variation_id = variation.variation_id; … … 109 108 110 109 if (component) { 111 112 110 if(variation_id) { 113 114 111 Extend.setActiveProduct('.helloextend-offer', 115 112 { … … 119 116 } 120 117 ); 121 122 123 118 } 124 119 } else { … … 130 125 } 131 126 132 handleAddToCartLogic(variation_id);127 }); 133 128 134 }, 1000); 135 }); 129 handleAddToCartLogic(); 136 130 } else if (product_type === 'composite') { 137 131 … … 145 139 146 140 // These two variables need to be settings in the plugin 147 let compositeProductOptionsSelector = '.dd-option' 148 let priceSelector = '.summary > .price > .woocommerce-Price-amount' 141 let compositeProductOptionsSelector = '.dd-option'; 142 let priceSelector = '.summary > .price > .woocommerce-Price-amount'; 149 143 150 jQuery(compositeProductOptionsSelector).on("click", function() {151 const compositeProductPrice = parseFloat( document.querySelector(priceSelector).textContent.replace("$", "")) * 100;152 if (compositeProductOptionsSelector && priceSelector) {153 Extend.setActiveProduct('.helloextend-offer', {154 referenceId: reference_id,155 price: compositeProductPrice,156 category: first_category157 });158 } 144 $(compositeProductOptionsSelector).on("click", function() { 145 const compositeProductPrice = parseFloat($(priceSelector).text().replace("$", "")) * 100; 146 147 Extend.setActiveProduct('.helloextend-offer', { 148 referenceId: reference_id, 149 price: compositeProductPrice, 150 category: first_category 151 }); 152 159 153 }); 160 154 161 155 } else { 162 156 console.warn("helloextend-pdp-offers.js error: Unsupported product type: ", product_type); 163 supportedProductType = false;164 157 } 165 166 167 158 } 168 159 }); -
helloextend-protection/tags/1.2.0/js/helloextend-shipping-offers.js
r3348358 r3358723 1 1 (function ( $ ) { 2 2 'use strict'; 3 $(document).ready( 4 function ($) { 3 $(document).off('integration.extend.shipping').on('integration.extend.shipping', function () { 4 if(!ExtendWooCommerce || !ExtendShippingIntegration) { return; 5 } 5 6 6 if(!ExtendWooCommerce || !ExtendShippingIntegration) { return; 7 function initShippingOffers() 8 { 9 // Deconstructs ExtendProductIntegration variables 10 const { env, items, enable_helloextend_sp, ajax_url, update_order_review_nonce, helloextend_sp_add_sku } = ExtendShippingIntegration; 11 let items_array = eval(items); 12 13 // If Extend shipping protection offers are not enabled, hide Extend offer div 14 if(enable_helloextend_sp === '0') { 15 const extendShippingOffer = document.querySelector('.helloextend-sp-offer') 16 if (extendShippingOffer) { 17 extendShippingOffer.style.display = 'none'; 18 } 19 7 20 } 21 //const isShippingProtectionInCart = ExtendShippingIntegration.shippingProtectionInCart(items); 22 const isShippingProtectionInCart = false; 8 23 9 function initShippingOffers() 10 { 11 // Deconstructs ExtendProductIntegration variables 12 const { env, items, enable_helloextend_sp, ajax_url, update_order_review_nonce, helloextend_sp_add_sku } = ExtendShippingIntegration; 13 let items_array = eval(items); 24 //If Extend shipping protection is enabled, render offers 25 if (enable_helloextend_sp === '1') { 26 Extend.shippingProtection.render( 27 { 28 selector: '#helloextend-shipping-offer', 29 items: items_array, 30 // isShippingProtectionInCart: false, 31 onEnable: function (quote) { 32 // Update totals and trigger WooCommerce cart calculations 33 $.ajax( 34 { 35 type: 'POST', 36 url: ajax_url, 37 data: { 38 action: 'add_shipping_protection_fee', 39 fee_amount: quote.premium, 40 fee_label: 'Shipping Protection', 41 shipping_quote_id: quote.id 42 }, 43 success: function () { 44 $('body').trigger('update_checkout'); 14 45 15 // If Extend shipping protection offers are not enabled, hide Extend offer div 16 if(enable_helloextend_sp === '0') { 17 const extendShippingOffer = document.querySelector('.helloextend-sp-offer') 18 if (extendShippingOffer) { 19 extendShippingOffer.style.display = 'none'; 20 } 21 22 } 23 //const isShippingProtectionInCart = ExtendShippingIntegration.shippingProtectionInCart(items); 24 const isShippingProtectionInCart = false; 25 26 //If Extend shipping protection is enabled, render offers 27 if (enable_helloextend_sp === '1') { 28 Extend.shippingProtection.render( 29 { 30 selector: '#helloextend-shipping-offer', 31 items: items_array, 32 // isShippingProtectionInCart: false, 33 onEnable: function (quote) { 34 // Update totals and trigger WooCommerce cart calculations 35 $.ajax( 36 { 37 type: 'POST', 38 url: ajax_url, 39 data: { 40 action: 'add_shipping_protection_fee', 41 fee_amount: quote.premium, 42 fee_label: 'Shipping Protection', 43 shipping_quote_id: quote.id 44 }, 45 success: function () { 46 $('body').trigger('update_checkout'); 47 48 // Need to trigger again for SP line item settings to get correct total 49 if (helloextend_sp_add_sku) { 50 setTimeout(() => { 51 $('body').trigger('update_checkout'); 52 }, 50); 53 } 46 // Need to trigger again for SP line item settings to get correct total 47 if (helloextend_sp_add_sku) { 48 setTimeout(() => { 49 $('body').trigger('update_checkout'); 50 }, 50); 54 51 } 55 52 } 56 ); 57 }, 58 onDisable: function (quote) { 59 // Update totals and trigger WooCommerce cart calculations 60 $.ajax( 61 { 62 type: 'POST', 63 url: ajax_url, 64 data: { 65 action: 'remove_shipping_protection_fee', 66 }, 67 success: function () { 68 $('body').trigger('update_checkout'); 53 } 54 ); 55 }, 56 onDisable: function (quote) { 57 // Update totals and trigger WooCommerce cart calculations 58 $.ajax( 59 { 60 type: 'POST', 61 url: ajax_url, 62 data: { 63 action: 'remove_shipping_protection_fee', 64 }, 65 success: function () { 66 $('body').trigger('update_checkout'); 69 67 70 // Need to trigger again for SP line item settings to get correct total 71 if (helloextend_sp_add_sku) { 72 setTimeout(() => { 73 $('body').trigger('update_checkout'); 74 }, 50); 75 } 68 // Need to trigger again for SP line item settings to get correct total 69 if (helloextend_sp_add_sku) { 70 setTimeout(() => { 71 $('body').trigger('update_checkout'); 72 }, 50); 76 73 } 77 74 } 78 ); 79 }, 80 onUpdate: function (quote) { 75 } 76 ); 77 }, 78 onUpdate: function (quote) { 81 79 82 // Update totals and trigger WooCommerce cart calculations83 $.ajax(84 {85 type: 'POST',86 url: ajax_url,87 data: {88 action: 'add_shipping_protection_fee',89 fee_amount: quote.premium,90 fee_label: 'Shipping Protection',91 shipping_quote_id: quote.id92 },93 success: function () {94 $('body').trigger('update_checkout');80 // Update totals and trigger WooCommerce cart calculations 81 $.ajax( 82 { 83 type: 'POST', 84 url: ajax_url, 85 data: { 86 action: 'add_shipping_protection_fee', 87 fee_amount: quote.premium, 88 fee_label: 'Shipping Protection', 89 shipping_quote_id: quote.id 90 }, 91 success: function () { 92 $('body').trigger('update_checkout'); 95 93 96 // Need to trigger again for SP line item settings to get correct total 97 if (helloextend_sp_add_sku) { 98 setTimeout(() => { 99 $('body').trigger('update_checkout'); 100 }, 50); 101 } 94 // Need to trigger again for SP line item settings to get correct total 95 if (helloextend_sp_add_sku) { 96 setTimeout(() => { 97 $('body').trigger('update_checkout'); 98 }, 50); 102 99 } 103 100 } 104 );105 }101 } 102 ); 106 103 } 107 );108 }104 } 105 ); 109 106 } 107 } 110 108 111 initShippingOffers();109 initShippingOffers(); 112 110 113 } 114 ); 111 }); 115 112 116 113 function formatPrice(price) -
helloextend-protection/tags/1.2.0/readme.txt
r3352239 r3358723 6 6 Requires at least: 4.0 7 7 Tested up to: 6.8 8 Stable tag: 1. 1.38 Stable tag: 1.2.0 9 9 Requires PHP: 7.4 10 10 License: GPLv2 or later … … 79 79 80 80 == Changelog == 81 82 = 1.2.0 2025-09-09 = 83 * Feature - Customers now have the option to purchase a protection plan after their initial purchase. Contact Extend to learn more. 84 * Fix - Any protection plans on an order will be cancelled when the line items or entire order is refunded or cancelled 85 * Fix - Small bug fixes and improvements 86 81 87 = 1.1.3 2025-08-28 = 82 88 * Enhancement - Product images are now synced to Extend store -
helloextend-protection/trunk/helloextend-protection.php
r3352239 r3358723 17 17 * Plugin URI: https://docs.extend.com/docs/extend-protection-plugin-for-woocommerce 18 18 * Description: Extend Protection for Woocommerce. Allows WooCommerce merchants to offer product and shipping protection to their customers. 19 * Version: 1. 1.319 * Version: 1.2.0 20 20 * Author: Extend, Inc. 21 21 * Author URI: https://extend.com/ -
helloextend-protection/trunk/includes/class-helloextend-global.php
r3348358 r3358723 54 54 { 55 55 56 $this->helloextend_protection = $helloextend_protection;57 $this->version = $version;56 $this->helloextend_protection = $helloextend_protection; 57 $this->version = $version; 58 58 $this->hooks(); 59 59 } … … 121 121 // add sku to cart item and label it referenceId 122 122 $cart[$cart_item_key]['referenceId'] = $referenceId; 123 $cart[$cart_item_key]['product_name'] = $_woo_product->get_ title();123 $cart[$cart_item_key]['product_name'] = $_woo_product->get_name(); 124 124 } 125 125 … … 270 270 } 271 271 272 if ($helloextend_data['leadToken']) { 273 $helloextend_data['leadQuantity'] = $quantity; 274 } 275 272 276 WC()->cart->add_to_cart($warranty_product_id, $quantity, 0, 0, ['extendData' => $helloextend_data]); 273 277 } … … 346 350 $covered = wc_get_product($covered_id); 347 351 $sku = $cart_item['extendData']['planId']; 348 $covered_title = $covered->get_ title();352 $covered_title = $covered->get_name(); 349 353 350 354 $item->add_meta_data('Warranty', $title); … … 352 356 $item->add_meta_data('Plan Id', $sku); 353 357 $item->add_meta_data('Covered Product', $covered_title); 358 if (isset($cart_item['extendData']['leadToken'])) { 359 $item->add_meta_data('Lead Token', $cart_item['extendData']['leadToken']); 360 } 354 361 } 355 362 } … … 372 379 $covered = wc_get_product($covered_id); 373 380 $sku = $cart_item['extendData']['planId']; 374 $covered_title = $covered->get_ title();381 $covered_title = $covered->get_name(); 375 382 $data[] = [ 376 383 'key' => 'Product', … … 403 410 wp_enqueue_script('helloextend_global_script'); 404 411 wp_localize_script('helloextend_global_script', 'ExtendWooCommerce', compact('store_id', 'ajaxurl', 'environment')); 412 413 // Get the leadToken from URL parameters 414 $lead_token = $this->get_lead_token_from_url(); 415 if ($lead_token) { 416 // Sanitize the token for safe JavaScript output 417 $safe_lead_token = esc_js($lead_token); 418 419 // Output JavaScript to console 420 echo "<script type='text/javascript'>\n"; 421 echo "console.log('found leadToken: ', '" . $safe_lead_token . "');\n"; 422 echo "</script>\n"; 423 424 // next step: Run Post Purchase logic to handle lead Token 425 $this->helloextend_post_purchase($lead_token, $store_id, $environment, $ajaxurl); 426 } 405 427 } else { 406 428 HelloExtend_Protection_Logger::helloextend_log_error('Store Id missing or Extend Product Protection is disabled'); … … 509 531 return $categories[0]->name ?? 'Uncategorized'; 510 532 } 533 534 /** 535 * Get leadToken from URL parameters 536 * 537 * @return string|null The leadToken value or null if not found 538 */ 539 private function get_lead_token_from_url() { 540 // Check if leadToken exists in GET parameters 541 if (isset($_GET['leadToken']) && !empty($_GET['leadToken'])) { 542 // Sanitize the input 543 return sanitize_text_field($_GET['leadToken']); 544 } 545 546 return null; 547 } 548 549 /** 550 * Get the Post Purchase Logic if the lead token is passed 551 * 552 * @since 1.0.0 553 */ 554 private function helloextend_post_purchase($leadToken, $store_id, $environment, $ajaxurl) 555 { 556 $cart_url = wc_get_cart_url(); 557 wp_enqueue_script('helloextend_global_post_purchase_script'); 558 wp_localize_script('helloextend_global_post_purchase_script', 'ExtendWooCommerce', compact('store_id', 'leadToken', 'ajaxurl', 'environment', 'cart_url')); 559 } 511 560 } -
helloextend-protection/trunk/includes/class-helloextend-protection-cart-offer.php
r3348358 r3358723 48 48 49 49 protected string $warranty_product_id; 50 protected array $products = [];51 protected array $updates = [];52 50 private array $settings; 53 51 … … 77 75 } 78 76 77 private function is_item_helloextend($item) 78 { 79 $warranty_product_id = $this->settings['warranty_product_id']; 80 return $item['product_id'] == $warranty_product_id && isset($item['extendData']) && !empty($item['extendData']); 81 } 82 83 private function is_lead($item) 84 { 85 return $this->is_item_helloextend($item) && isset($item['extendData']['leadToken']) && isset($item['extendData']['leadQuantity']); 86 } 87 88 private function is_warranty($item) 89 { 90 return $this->is_item_helloextend($item) && !isset($item['extendData']['leadToken']) && !isset($item['extendData']['leadQuantity']); 91 } 92 93 private function get_product_id($line) 94 { 95 if ($this->is_item_helloextend($line)) { 96 return $line['extendData']['covered_product_id']; 97 } else { 98 return $line['variation_id'] > 0 ? $line['variation_id'] : $line['product_id']; 99 } 100 } 101 102 private function map_cart_items_with_warranties() 103 { 104 $cart_contents = WC()->cart->get_cart_contents(); 105 106 $products = array(); 107 108 foreach ( $cart_contents as $line ) { 109 110 $product_id = $this->get_product_id($line); 111 $id = $line['extendData']['leadToken'] ?? $product_id; 112 113 $product = $products[ $id ] ?? array( 114 'quantity' => 0, 115 'warranty_quantity' => 0, 116 'warranties' => array(), 117 ); 118 119 if ($this->is_warranty($line)) { 120 $product['warranty_quantity'] += $line['quantity']; 121 $product['warranties'][] = $line; 122 } else { 123 $product['quantity'] += $line['quantity']; 124 125 if (isset($line['extendData']) && isset($line['extendData']['leadQuantity'])) { 126 $product['leadQuantity'] = $line['extendData']['leadQuantity']; 127 $product['leadProductKey'] = $line['key']; 128 } 129 } 130 131 $products[ $id ] = $product; 132 } 133 134 return $products; 135 } 136 79 137 // get_cart_updates() 80 138 // goes through the cart and gets updates to products/plans for normalization 81 public function get_cart_updates() 82 { 83 84 $cart_contents = WC()->cart->get_cart_contents(); 85 86 foreach ( $cart_contents as $line ) { 87 88 // if we're on a warranty item 89 if (intval($line['product_id']) === intval($this->settings['warranty_product_id']) && isset($line['extendData']) && !empty($line['extendData']) ) { 90 // Grab reference id 91 $product_reference_id = $line['extendData']['covered_product_id']; 92 93 // If this product doesn't exist, create it with the warranty quantity and warranty added, else add to warranty quantity, and add warranty to warranty list 94 if (! isset($products[ $product_reference_id ]) ) { 95 $products[ $product_reference_id ] = [ 96 'quantity' => 0, 97 'warranty_quantity' => $line['quantity'], 98 'warranties' => [ $line ], 99 ]; 100 } else { 101 $products[ $product_reference_id ]['warranty_quantity'] += $line['quantity']; 102 array_push($products[ $product_reference_id ]['warranties'], $line); 103 } 104 // if we're on a non-warranty check if the product exists in list, if so add quantity, if not add to product list 105 } else { 106 $id = $line['variation_id'] > 0 ? $line['variation_id'] : $line['product_id']; 107 if (! isset($products[ $id ]) ) { 108 $products[ $id ] = [ 109 'quantity' => $line['quantity'], 110 'warranty_quantity' => 0, 111 'warranties' => [], 112 ]; 113 } else { 114 $products[ $id ]['quantity'] += $line['quantity']; 115 } 116 } 117 } 118 139 public function get_cart_updates($products) 140 { 119 141 $cart_balancing = $this->settings['helloextend_enable_cart_balancing'] == 1 ? true : false; 120 142 121 // if we have products, go through each and check for updates 122 if (isset($products) ) { 123 foreach ( $products as $product ) { 124 125 // if warranty quantity is greater than 0 and product quantity is 0 set warranty quantity to 0 126 if (intval($product['warranty_quantity']) > 0 && intval($product['quantity']) == 0 ) { 127 foreach ( $product['warranties'] as $warranty ) { 128 $updates[ $warranty['key'] ] = [ 'quantity' => 0 ]; 143 $updates = array(); 144 145 foreach ( $products as $product ) { 146 147 // If warranty item is coming from lead and the quantity in the cart does not match the lead quantity 148 if (isset($product['leadQuantity']) && isset($product['leadProductKey'])) { 149 if ($product['leadQuantity'] != $product['quantity']) { 150 $updates[$product['leadProductKey']] = $product['leadQuantity']; 151 } 152 153 continue; 154 } 155 156 // Remove warranties without products 157 if ($product['warranty_quantity'] > 0 && $product['quantity'] == 0 ) { 158 foreach ( $product['warranties'] as $warranty ) { 159 $updates[ $warranty['key'] ] = 0; 160 } 161 continue; 162 } 163 164 // grab difference of warranty quantity and product quantity 165 $quantity_diff = $product['warranty_quantity'] - $product['quantity']; 166 167 // No difference or warranties, no updates 168 if ($quantity_diff == 0 || $product['warranty_quantity'] == 0) { 169 continue; 170 } 171 172 // Too many warranties 173 if ($quantity_diff > 0 ) { 174 foreach ( $product['warranties'] as $warranty ) { 175 if ($quantity_diff == 0) { 176 break; 129 177 } 130 } else { 131 // grab difference of warranty_quantity and product quantity 132 $diff = $product['warranty_quantity'] - $product['quantity']; 133 134 // if there's a difference & that difference is greater than 0, we remove warranties till we reach the product quantity 135 if ($diff !== 0 ) { 136 if ($diff > 0 ) { 137 foreach ( $product['warranties'] as $warranty ) { 138 $new_quantity_diff = max([ 0, $diff - $warranty['quantity'] ]); 139 $removed_quantity = $diff - $new_quantity_diff; 140 $updates[ $warranty['key'] ] = [ 'quantity' => $warranty['quantity'] - $removed_quantity ]; 141 $diff = $new_quantity_diff; 142 } 143 } elseif ($cart_balancing && $diff < 0 ) { 144 foreach ( $product['warranties'] as $warranty ) { 145 $new_quantity_diff = max([ 0, $diff - $warranty['quantity'] ]); 146 $new_quantity = $warranty['quantity'] - $diff; 147 $updates[ $warranty['key'] ] = [ 'quantity' => $new_quantity ]; 148 $diff = $new_quantity_diff; 149 } 150 } 151 } 152 } 153 } 154 } 155 156 // if there's updates return updates 157 if (isset($updates) ) { 158 return $updates; 159 } 178 179 $new_quantity_diff = max([ 0, $quantity_diff - $warranty['quantity'] ]); 180 $removed_quantity = $quantity_diff - $new_quantity_diff; 181 $updates[ $warranty['key'] ] = $warranty['quantity'] - $removed_quantity; 182 $quantity_diff = $new_quantity_diff; 183 } 184 continue; 185 } 186 187 // Else, not enough warranties 188 if ($cart_balancing && $quantity_diff < 0 ) { 189 $warranty = $product['warranties'][0]; 190 $updates[$warranty['key']] = $warranty['quantity'] - $quantity_diff; 191 } 192 } 193 194 return $updates; 160 195 } 161 196 … … 165 200 { 166 201 167 $newUpdates = $this->get_cart_updates(); 168 169 if (isset($newUpdates) ) { 170 $cart = WC()->cart->get_cart_contents(); 171 foreach ( $cart as $line ) { 172 173 foreach ( $newUpdates as $key => $value ) { 174 if ($key == $line['key'] ) { 175 WC()->cart->set_quantity($key, $value['quantity'], true); 176 } 177 } 178 } 202 $products = $this->map_cart_items_with_warranties(); 203 204 $updates = $this->get_cart_updates($products); 205 206 foreach ( $updates as $key => $quantity_update ) { 207 WC()->cart->set_quantity($key, $quantity_update, true); 179 208 } 180 209 … … 192 221 if (! isset($cart_item['extendData']) ) { 193 222 $item_id = $cart_item['variation_id'] ? $cart_item['variation_id'] : $cart_item['product_id']; 194 $item_sku = $cart_item['data']->get_sku() ? $cart_item['data']->get_sku() : $item_id; 195 $referenceId = $item_id; 196 $categories = get_the_terms($item_id, 'product_cat'); 223 $parent_id = $cart_item['product_id']; 224 $categories = get_the_terms($parent_id, 'product_cat'); 197 225 $category = HelloExtend_Protection_Global::helloextend_get_first_valid_category($categories); 198 199 echo "<div id='offer_".esc_attr($item_id)."' class='cart-extend-offer' data-covered='".esc_attr($referenceId)."' data-category='".esc_attr($category)."'></div>"; 226 $price = (int) floatval($cart_item['data']->get_price() * 100); 227 228 echo "<div id='offer_".esc_attr($item_id)."' class='cart-extend-offer' data-covered='".esc_attr($item_id)."' data-category='".esc_attr($category)."' data-price='" . $price . "'></div>"; 200 229 } 201 230 } … … 208 237 $enable_helloextend = trim($this->settings['enable_helloextend']); 209 238 $helloextend_enable_cart_offers = $this->settings['helloextend_enable_cart_offers']; 210 $cart = WC()->cart;239 $cart = WC()->cart; 211 240 212 241 if ($helloextend_enable_cart_offers === '1' && $enable_helloextend === '1' ) { -
helloextend-protection/trunk/includes/class-helloextend-protection-orders.php
r3352239 r3358723 54 54 private array $settings; 55 55 56 private const TRANSACTION_STORE_PREFIX = 'STORE::'; 57 private const TRANSACTION_ORDER_PREFIX = '::ORDER::'; 58 private const TRANSACTION_PRODUCT_PREFIX = '::PRODUCT::'; 59 private const TRANSACTION_OLI_PREFIX = '::OLI::'; 60 56 61 /** 57 62 * Initialize the class and set its properties. … … 67 72 /* retrieve environment variables */ 68 73 $this->settings = HelloExtend_Protection_Global::helloextend_get_settings(); 69 74 $this->helloextend_product_protection_id = $this->settings['warranty_product_id']; 75 70 76 // Hook the callback function to the 'woocommerce_new_order' action 71 77 add_action('woocommerce_checkout_order_processed', [$this, 'create_update_order'], 10, 1); … … 76 82 // Hook the callback function to the order cancelled action 77 83 add_action('woocommerce_order_status_cancelled', [$this, 'cancel_order'], 10, 1); 78 } 79 80 public function get_product_image_url($product) 84 add_action('woocommerce_order_status_refunded', [$this, 'cancel_order'], 10, 1); 85 86 // Handle refunded orders 87 add_action('woocommerce_order_refunded', [$this, 'handle_contract_refund'], 10, 2); 88 } 89 90 private function get_product_image_url($product) 81 91 { 82 92 // Only accept valid WooCommerce product objects … … 127 137 } 128 138 139 private function is_item_helloextend($item) 140 { 141 $helloextend_meta = $item->get_meta('_helloextend_data'); 142 return $item->get_product_id() == $this->helloextend_product_protection_id && isset($helloextend_meta) && isset($helloextend_meta['planId']); 143 } 144 145 private function is_item_helloextend_no_lead($item) 146 { 147 $helloextend_meta = $item->get_meta('_helloextend_data'); 148 return $this->is_item_helloextend($item) && !isset($helloextend_meta['leadToken']); 149 } 150 151 private function is_item_helloextend_lead($item) 152 { 153 $helloextend_meta = $item->get_meta('_helloextend_data'); 154 return $this->is_item_helloextend($item) && isset($helloextend_meta['leadToken']); 155 } 156 157 private function get_price_in_cents($item_price) 158 { 159 return (int) floatval($item_price * 100); 160 } 161 162 private function get_purchased_leads($order) 163 { 164 $lead_line_items = array(); 165 foreach ($order->get_items() as $item) { 166 if ($this->is_item_helloextend_lead($item)) { 167 $helloextend_meta = $item->get_meta('_helloextend_data'); 168 169 $lead_line_items[] = array( 170 'leadToken' => $helloextend_meta['leadToken'], 171 'lineItemTransactionId' => self::TRANSACTION_STORE_PREFIX . $this->settings['store_id'] . self::TRANSACTION_ORDER_PREFIX . $order->get_id() . self::TRANSACTION_PRODUCT_PREFIX . $helloextend_meta['covered_product_id'] . self::TRANSACTION_OLI_PREFIX . $item->get_id(), 172 'plan' => array( 173 'id' => $helloextend_meta['planId'], 174 'purchasePrice' => $helloextend_meta['price'], 175 ), 176 'quantity' => $item->get_quantity() 177 ); 178 } 179 } 180 181 return empty($lead_line_items) ? null : $lead_line_items; 182 } 183 129 184 /** 130 185 * helloextend_get_plans_and_products($order, $fulfill_now = false) … … 143 198 $helloextend_meta_data = (array) $item->get_meta('_helloextend_data'); 144 199 145 // if item id is for extend-product-protection gra m$helloextend_meta_data and push it to the plans array146 if ($ helloextend_meta_data['planId']) {200 // if item id is for extend-product-protection grab $helloextend_meta_data and push it to the plans array 201 if ($this->is_item_helloextend_no_lead($item) && $helloextend_meta_data['planId']) { 147 202 $helloextend_plans[] = array( 148 203 'id' => $helloextend_meta_data['planId'], … … 153 208 } 154 209 210 $leads = $this->get_purchased_leads($order); 211 155 212 // Loop through the order items and add them to the line_items array 156 213 $helloextend_line_items = array(); … … 162 219 163 220 // Get the first product category 164 $product_category_ids = $product->get_category_ids(); 165 $cat_term = get_term_by('id', $product_category_ids[0], 'product_cat'); 166 $first_category = $cat_term->name; 221 if ($product->get_type() == 'variation') { 222 $first_category = HelloExtend_Protection_Global::helloextend_get_first_valid_category(get_the_terms($product->get_parent_id(), 'product_cat')); 223 } else { 224 $first_category = HelloExtend_Protection_Global::helloextend_get_first_valid_category(get_the_terms($product_id, 'product_cat')); 225 } 167 226 168 227 // if line_id matches any id in $helloextend_plans[], push the plan data into the covered product … … 175 234 } 176 235 177 // Get extend product id from settings178 $helloextend_product_protection_id = $this->settings['warranty_product_id'];179 180 236 // Add relevant data to the line_items array 181 237 // if product id for extend-product-protection, do not add it to helloextend_line_items array 182 if ($product_id != $ helloextend_product_protection_id) {238 if ($product_id != $this->helloextend_product_protection_id) { 183 239 184 240 $image_url = $this->get_product_image_url($product); 185 241 186 $helloextend_line_item s[]= array(242 $helloextend_line_item = array( 187 243 'lineItemTransactionId' => $product->get_id(), 188 244 'product' => array( … … 190 246 'title' => $product->get_name(), 191 247 'category' => $first_category, 192 'listPrice' => (int) floatval($product->get_regular_price() * 100),193 'purchasePrice' => (int) floatval($product->get_price() * 100),248 'listPrice' => $this->get_price_in_cents($product->get_regular_price() * 100), 249 'purchasePrice' => $this->get_price_in_cents($product->get_price() * 100), 194 250 'purchaseDate' => $order->get_data()['date_created']->getTimestamp() * 1000, 195 251 'imageUrl' => $image_url … … 201 257 // if $plan is not empty, add the plan to the current line item 202 258 if (!empty($plan)) { 203 $helloextend_line_items[count($helloextend_line_items) - 1]['plan'] = $plan; 204 } 205 } 206 } 259 $helloextend_line_item['plan'] = $plan; 260 } 261 262 $helloextend_line_items[] = $helloextend_line_item; 263 } 264 } 265 266 if ($leads) { 267 $helloextend_line_items = array_merge($helloextend_line_items, $leads); 268 } 269 207 270 return $helloextend_line_items; 208 271 } … … 441 504 442 505 if ( ! $order instanceof WC_Order ) { 443 HelloExtend_Protection_Logger::helloextend_log_error(444 'Cannot cancel Extend order – WooCommerce order ' . $order_id . ' not found.'445 );446 return;447 }506 HelloExtend_Protection_Logger::helloextend_log_error( 507 'Cannot cancel Extend order – WooCommerce order ' . $order_id . ' not found.' 508 ); 509 return; 510 } 448 511 449 512 // Get Token from Global function … … 526 589 } 527 590 } 591 592 public function handle_contract_refund(string $order_id, string $refund_id) 593 { 594 $order = wc_get_order($order_id); 595 596 if ( ! $order instanceof WC_Order ) { 597 HelloExtend_Protection_Logger::helloextend_log_error( 598 'Cannot refund Extend order - WooCommerce order ' . $order_id . ' not found.' 599 ); 600 return; 601 } 602 603 $refund = wc_get_order($refund_id); 604 605 if (!$refund instanceof WC_Order_Refund) { 606 HelloExtend_Protection_Logger::helloextend_log_error( 607 'Cannot refund Extend order - WooCommerce refund ' . $refund_id . ' not found.' 608 ); 609 return; 610 } 611 612 $refund_items = $refund->get_items(); 613 $refunded_contracts = []; 614 615 // Get contract IDs on this order and add them to contracts array 616 $contracts = get_post_meta($order->get_id(), '_product_protection_contracts', true); 617 foreach($refund_items as $refund_item) { 618 $refunded_item_id = $refund_item->get_meta('_refunded_item_id'); 619 $order_item = $order->get_item($refunded_item_id); 620 $helloextend_data = $order_item->get_meta('_helloextend_data'); 621 622 if ($refund_item->get_product_id() == $this->helloextend_product_protection_id && $helloextend_data) { 623 foreach($contracts as $covered_product => $contract_id) { 624 if ($helloextend_data['covered_product_id'] == $covered_product) { 625 626 $refunded_contracts[] = $contract_id; 627 break; 628 } 629 } 630 } 631 } 632 633 634 // Get Token from Global function 635 $token = HelloExtend_Protection_Global::helloextend_get_token(); 636 637 // If token exists, log successful token 638 if ($this->settings['enable_helloextend_debug'] == 1 && $token) { 639 HelloExtend_Protection_Logger::helloextend_log_debug('Access token created successfully'); 640 } 641 // If token does not exist, log error 642 if ($this->settings['enable_helloextend_debug'] == 1 && !$token) { 643 HelloExtend_Protection_Logger::helloextend_log_error('Error:Access token was not created, exiting order refund'); 644 return; 645 } 646 647 648 $cancellation_errors = []; 649 // Cancel the contract 650 // {{API_HOST}}/contracts/{{contractId}}/cancel 651 foreach ($refunded_contracts as $contract_id) { 652 $contract_cancel_endpoint = $this->settings['api_host'] . '/contracts/' . $contract_id . '/cancel'; 653 $contract_cancel_args = array( 654 'method' => 'POST', 655 'headers' => array( 656 'Content-Type' => 'application/json', 657 'Accept' => 'application/json; version=latest', 658 'X-Extend-Access-Token' => $token, 659 ), 660 ); 661 662 $contract_cancel_response = wp_remote_request( $contract_cancel_endpoint, $contract_cancel_args ); 663 664 if (is_wp_error($contract_cancel_response)) { 665 $error_message = $contract_cancel_response->get_error_message(); 666 $cancellation_errors[] = 'Cancel Contract Failed for ID ' . $contract_id . ' : POST request failed: ' . $error_message.', cannot cancel contract'; 667 } 668 669 $contract_cancel_response_code = wp_remote_retrieve_response_code( $contract_cancel_response ); 670 $data = json_decode(wp_remote_retrieve_body( $contract_cancel_response )); 671 if ($contract_cancel_response_code < 200 || $contract_cancel_response_code >= 300) { 672 $cancellation_errors[] = 'Contract cancel for ID ' . $contract_id . ' : POST request returned status ' . $contract_cancel_response_code . ' with body ' . $data; 673 } 674 675 if ($this->settings['enable_helloextend_debug']) { 676 HelloExtend_Protection_Logger::helloextend_log_debug( 677 'Contract ID ' . $contract_id . ' canceled successfully.' 678 ); 679 } 680 } 681 682 if (!empty($cancellation_errors)) { 683 HelloExtend_Protection_Logger::helloextend_log_error( 684 'Some contracts failed to cancel: ' . implode('; ', $cancellation_errors) 685 ); 686 } 687 688 if ($this->settings['enable_helloextend_debug'] == 1 && !empty($refunded_contracts)) { 689 HelloExtend_Protection_Logger::helloextend_log_debug( 690 'Contract IDs ' . join(", ", $refunded_contracts) . ' canceled succesfully.' 691 ); 692 } 693 } 528 694 } -
helloextend-protection/trunk/includes/class-helloextend-protection-shipping.php
r3348358 r3358723 119 119 if (! $product->is_virtual() ) { 120 120 $referenceId = $product->get_id(); 121 $categories = get_the_terms($cart_item['product_id'], 'product_cat'); 122 $category = HelloExtend_Protection_Global::helloextend_get_first_valid_category(is_wp_error($categories) ? [] : (array) $categories); 121 123 $items[] = array( 122 124 'referenceId' => $referenceId, 123 125 'quantity' => $cart_item['quantity'], 124 'category' => get_the_terms($product->get_id(), 'product_cat')[0]->name,126 'category' => $category, 125 127 'purchasePrice' => (int) floatval($product->get_price() * 100), 126 128 'productName' => $product->get_name(), -
helloextend-protection/trunk/includes/class-helloextend-protection.php
r3263092 r3358723 247 247 wp_register_script('helloextend_script', $this->sdk_url, array(), '1.0.0', true); 248 248 wp_register_script('helloextend_global_script', $this->url . '../js/helloextend-global.js', [ 'jquery', 'helloextend_script' ], '1.0.0', true); 249 wp_register_script('helloextend_global_post_purchase_script', $this->url . '../js/helloextend-post-purchase.js', [ 'jquery', 'helloextend_script' ], '1.0.0', true); 249 250 wp_register_script('helloextend_product_integration_script', $this->url . '../js/helloextend-pdp-offers.js', [ 'jquery', 'helloextend_global_script' ], '1.0.0', true); 250 251 wp_register_script('helloextend_cart_integration_script', $this->url . '../js/helloextend-cart-offers.js', [ 'jquery', 'helloextend_script' ], '1.0.0', true); -
helloextend-protection/trunk/js/helloextend-cart-offers.js
r3348358 r3358723 1 1 // This script is used to handle the rendering and functionality of Extend offers in a WooCommerce cart. 2 2 (($) => { 3 4 // If necessary objects (ExtendWooCommerce and ExtendCartIntegration) do not exist, stop the execution of the script.5 if (!ExtendWooCommerce || !ExtendCartIntegration) {6 return;7 }8 3 9 4 const SELECTORS = { … … 11 6 TITLE: '.product-name', 12 7 IMAGE: '.product-thumbnail', 13 PRICE: '.product-price',14 8 QUANTITY: 'input.qty', 15 9 EXTEND_OFFER: '.cart-extend-offer', … … 79 73 const quantity = $lineItemElement.find(SELECTORS.QUANTITY).val(); 80 74 81 const [ dollars, cents = '00' ] = $lineItemElement.find(SELECTORS.PRICE).text().trim().replace(/[$,]/g, '').split('.'); 82 const normalizedCents = cents.padEnd(2, '0'); 83 const price = `${dollars}${normalizedCents}`; 75 const price = $offer.data('price'); 84 76 85 77 renderExtendOffer($offer[0], { referenceId, category, price }, quantity); … … 89 81 90 82 // Wait until the document is fully loaded before running the script. 91 $(document).ready(() => { 83 $(document).off('integration.extend.cart').on('integration.extend.cart', () => { 84 // If necessary objects (ExtendWooCommerce and ExtendCartIntegration) do not exist, stop the execution of the script. 85 if (typeof Extend === 'undefined' 86 || typeof ExtendWooCommerce === 'undefined' 87 || typeof ExtendCartIntegration === 'undefined') { 88 if (ExtendWooCommerce.debugLogEnabled) 89 ExtendWooCommerce.extendAjaxLog('debug', 'One of Extend, ExtendWooCommerce, ExtendCartIntegration is not defined'); 90 return; 91 } 92 92 93 initCartOffers(); 93 94 }); -
helloextend-protection/trunk/js/helloextend-global.js
r3348358 r3358723 1 1 (function ( $ ) { 2 2 'use strict'; 3 $(document).ready( 4 function ($) { 5 if(!ExtendWooCommerce) { return; 6 } 3 $(document).ready(function ($) { 4 if(!ExtendWooCommerce) { return; 5 } 7 6 8 const { store_id: storeId, ajaxurl, environment, debug_log_enabled: debugLogEnabled } = ExtendWooCommerce;7 const { store_id: storeId, ajaxurl, environment, debug_log_enabled: debugLogEnabled } = ExtendWooCommerce; 9 8 10 Extend.config( 11 { 12 storeId, 13 environment 9 Extend.config({ 10 storeId, 11 environment 12 }); 13 14 async function addPlanToCart(opts) 15 { 16 return await jQuery.post( 17 ajaxurl, { 18 action: "add_to_cart_helloextend", 19 quantity: opts.quantity, 20 extendData: { 21 ...opts.plan, 22 leadToken: opts.leadToken 23 } 14 24 } 15 ); 25 ).promise() 26 } 16 27 17 window.ExtendWooCommerce = { 18 ...ExtendWooCommerce, 19 addPlanToCart, 20 getCart, 21 warrantyAlreadyInCart, 22 extendAjaxLog, 23 debugLogEnabled 24 } 25 26 async function addPlanToCart(opts) 27 { 28 return await jQuery.post( 28 async function getCart() 29 { 30 return JSON.parse( 31 await jQuery.post( 29 32 ajaxurl, { 30 action: "add_to_cart_helloextend", 31 quantity: opts.quantity, 32 extendData: opts.plan 33 action: "get_cart_helloextend" 33 34 } 34 35 ).promise() 36 ); 37 } 38 39 function warrantyAlreadyInCart(variantId, cart) 40 { 41 let cartContents = cart['cart_contents']; 42 if (!cartContents) { 43 cartContents = cart; 35 44 } 36 37 async function getCart() 38 { 39 return JSON.parse( 40 await jQuery.post( 41 ajaxurl, { 42 action: "get_cart_helloextend" 43 } 44 ).promise() 45 ); 46 } 47 48 function warrantyAlreadyInCart(variantId, cart) 49 { 50 let cartContents = cart['cart_contents']; 51 if (!cartContents) { 52 cartContents = cart; 53 } 54 const cartItems = Object.values(cartContents); 55 const extendWarranties = cartItems.filter( 56 function (lineItem) { 57 //filter through the customAttributes and grab the referenceId 58 let extendData = lineItem.extendData; 59 if (extendData && extendData['covered_product_id']) { 60 let referenceId = extendData['covered_product_id']; 61 return ( 45 const cartItems = Object.values(cartContents); 46 const extendWarranties = cartItems.filter( 47 function (lineItem) { 48 //filter through the customAttributes and grab the referenceId 49 let extendData = lineItem.extendData; 50 if (extendData && extendData['covered_product_id']) { 51 let referenceId = extendData['covered_product_id']; 52 return ( 62 53 extendData && 63 54 !extendData.leadToken && 64 55 referenceId && 65 56 referenceId.toString() === variantId.toString() 66 ); 67 } 57 ); 68 58 } 69 ); 70 return extendWarranties.length > 0; 71 } 59 } 60 ); 61 return extendWarranties.length > 0; 62 } 72 63 73 function extendAjaxLog(method, ...message) 74 { 64 function extendAjaxLog(method, ...message) { 75 65 76 message = message.join(' ');77 /* Now use an ajax call to write logs from js files... */78 $.ajax(79 {80 type: 'POST',81 url: ajaxurl,66 message = message.join(' '); 67 /* Now use an ajax call to write logs from js files... */ 68 $.ajax( 69 { 70 type: 'POST', 71 url: ajaxurl, 82 72 83 data: { 84 action: 'helloextend_logger_ajax_call', 85 message: message, 86 method: method, 87 }, 88 success: function (xhr, x, checkStatus) { 89 return null; 90 }, 91 error: function (e) { 92 console.error("helloextendAjaxLog error: ", e.statusText) 93 } 73 data: { 74 action: 'helloextend_logger_ajax_call', 75 message: message, 76 method: method, 77 }, 78 success: function (xhr, x, checkStatus) { 79 return null; 80 }, 81 error: function (e) { 82 console.error("helloextendAjaxLog error: ", e.statusText) 94 83 } 95 ); 96 } 84 } 85 ); 86 } 97 87 88 window.ExtendWooCommerce = { 89 ...ExtendWooCommerce, 90 addPlanToCart, 91 getCart, 92 warrantyAlreadyInCart, 93 extendAjaxLog, 94 debugLogEnabled 98 95 } 99 ) 96 97 $(document).trigger('integration.extend'); 98 }); 100 99 })(jQuery); 101 100 -
helloextend-protection/trunk/js/helloextend-pdp-offers.js
r3348358 r3358723 1 1 (function( $ ) { 2 2 'use strict'; 3 $(document). ready(function($) {3 $(document).off('integration.extend.pdp').on('integration.extend.pdp', function() { 4 4 5 5 if (!ExtendWooCommerce || !ExtendProductIntegration) return; … … 8 8 const { type: product_type, id: product_id, sku, first_category, price, helloextend_pdp_offers_enabled, helloextend_modal_offers_enabled, atc_button_selector } = ExtendProductIntegration; 9 9 10 const $atcButton = jQuery(atc_button_selector)10 const $atcButton = $(atc_button_selector); 11 11 12 const quantity = parseInt(document.querySelector('input[name="quantity"]').value || 1)12 const getQuantity = () => parseInt(($('input[name="quantity"]').val() || 1), 10); 13 13 14 let supportedProductType = true;15 14 let reference_id = product_id; 16 15 17 16 // If PDP offers are not enabled, hide Extend offer div 18 17 if (helloextend_pdp_offers_enabled === '0') { 19 const extendOffer = document.querySelector('.helloextend-offer') 20 extendOffer.style.display = 'none'; 18 $('.helloextend-offer').hide(); 21 19 } 22 20 23 function handleAddToCartLogic( variation_id) {21 function handleAddToCartLogic() { 24 22 25 $atcButton.o n('click', function extendHandler(e) {23 $atcButton.off('click.extend').on('click.extend', function extendHandler(e) { 26 24 e.preventDefault(); 27 25 e.stopImmediatePropagation(); … … 32 30 33 31 function triggerAddToCart() { 34 $atcButton.off('click ', extendHandler);32 $atcButton.off('click.extend', extendHandler); 35 33 $atcButton.trigger('click'); 36 $atcButton.on('click ', extendHandler);34 $atcButton.on('click.extend', extendHandler); 37 35 } 38 36 39 37 const component = Extend.buttons.instance('.helloextend-offer'); 40 38 41 39 /** get the users plan selection */ 42 40 const plan = component.getPlanSelection(); 43 const product = component.getActiveProduct();41 const referenceId = component.getActiveProduct().id; 44 42 45 43 if (plan) { 46 var planCopy = { ...plan, covered_product_id: variation_id }47 vardata = {48 quantity: quantity,44 let planCopy = { ...plan, covered_product_id: referenceId }; 45 let data = { 46 quantity: getQuantity(), 49 47 plan: planCopy, 50 48 price: (plan.price / 100).toFixed(2) 51 } 49 }; 50 52 51 ExtendWooCommerce.addPlanToCart(data) 53 52 .then(() => { 54 53 triggerAddToCart(); 55 }) 54 }); 56 55 } else { 57 56 if(helloextend_modal_offers_enabled === '1') { 58 57 Extend.modal.open({ 59 referenceId : variation_id,58 referenceId, 60 59 price: price, 61 60 category: first_category, 62 61 onClose: function(plan, product) { 63 62 if (plan && product) { 64 var planCopy = { ...plan, covered_product_id: variation_id }65 vardata = {66 quantity: quantity,63 let planCopy = { ...plan, covered_product_id: referenceId }; 64 let data = { 65 quantity: getQuantity(), 67 66 plan: planCopy, 68 67 price: (plan.price / 100).toFixed(2) 69 } 68 }; 69 70 70 ExtendWooCommerce.addPlanToCart(data) 71 71 .then(() => { 72 72 triggerAddToCart(); 73 }) 73 }); 74 74 } else { 75 triggerAddToCart() 75 triggerAddToCart(); 76 76 } 77 77 }, 78 78 }); 79 79 } else { 80 triggerAddToCart() 80 triggerAddToCart(); 81 81 } 82 82 } … … 97 97 98 98 // TODO: initalize cart offers 99 handleAddToCartLogic( reference_id);99 handleAddToCartLogic(); 100 100 101 101 } else if (product_type === 'variable') { 102 102 103 jQuery( ".single_variation_wrap" ).on( "show_variation", function ( event, variation ) {103 $( ".single_variation_wrap" ).on( "show_variation", function ( event, variation ) { 104 104 105 setTimeout(function(){106 105 let component = Extend.buttons.instance('.helloextend-offer'); 107 106 let variation_id = variation.variation_id; … … 109 108 110 109 if (component) { 111 112 110 if(variation_id) { 113 114 111 Extend.setActiveProduct('.helloextend-offer', 115 112 { … … 119 116 } 120 117 ); 121 122 123 118 } 124 119 } else { … … 130 125 } 131 126 132 handleAddToCartLogic(variation_id);127 }); 133 128 134 }, 1000); 135 }); 129 handleAddToCartLogic(); 136 130 } else if (product_type === 'composite') { 137 131 … … 145 139 146 140 // These two variables need to be settings in the plugin 147 let compositeProductOptionsSelector = '.dd-option' 148 let priceSelector = '.summary > .price > .woocommerce-Price-amount' 141 let compositeProductOptionsSelector = '.dd-option'; 142 let priceSelector = '.summary > .price > .woocommerce-Price-amount'; 149 143 150 jQuery(compositeProductOptionsSelector).on("click", function() {151 const compositeProductPrice = parseFloat( document.querySelector(priceSelector).textContent.replace("$", "")) * 100;152 if (compositeProductOptionsSelector && priceSelector) {153 Extend.setActiveProduct('.helloextend-offer', {154 referenceId: reference_id,155 price: compositeProductPrice,156 category: first_category157 });158 } 144 $(compositeProductOptionsSelector).on("click", function() { 145 const compositeProductPrice = parseFloat($(priceSelector).text().replace("$", "")) * 100; 146 147 Extend.setActiveProduct('.helloextend-offer', { 148 referenceId: reference_id, 149 price: compositeProductPrice, 150 category: first_category 151 }); 152 159 153 }); 160 154 161 155 } else { 162 156 console.warn("helloextend-pdp-offers.js error: Unsupported product type: ", product_type); 163 supportedProductType = false;164 157 } 165 166 167 158 } 168 159 }); -
helloextend-protection/trunk/js/helloextend-shipping-offers.js
r3348358 r3358723 1 1 (function ( $ ) { 2 2 'use strict'; 3 $(document).ready( 4 function ($) { 3 $(document).off('integration.extend.shipping').on('integration.extend.shipping', function () { 4 if(!ExtendWooCommerce || !ExtendShippingIntegration) { return; 5 } 5 6 6 if(!ExtendWooCommerce || !ExtendShippingIntegration) { return; 7 function initShippingOffers() 8 { 9 // Deconstructs ExtendProductIntegration variables 10 const { env, items, enable_helloextend_sp, ajax_url, update_order_review_nonce, helloextend_sp_add_sku } = ExtendShippingIntegration; 11 let items_array = eval(items); 12 13 // If Extend shipping protection offers are not enabled, hide Extend offer div 14 if(enable_helloextend_sp === '0') { 15 const extendShippingOffer = document.querySelector('.helloextend-sp-offer') 16 if (extendShippingOffer) { 17 extendShippingOffer.style.display = 'none'; 18 } 19 7 20 } 21 //const isShippingProtectionInCart = ExtendShippingIntegration.shippingProtectionInCart(items); 22 const isShippingProtectionInCart = false; 8 23 9 function initShippingOffers() 10 { 11 // Deconstructs ExtendProductIntegration variables 12 const { env, items, enable_helloextend_sp, ajax_url, update_order_review_nonce, helloextend_sp_add_sku } = ExtendShippingIntegration; 13 let items_array = eval(items); 24 //If Extend shipping protection is enabled, render offers 25 if (enable_helloextend_sp === '1') { 26 Extend.shippingProtection.render( 27 { 28 selector: '#helloextend-shipping-offer', 29 items: items_array, 30 // isShippingProtectionInCart: false, 31 onEnable: function (quote) { 32 // Update totals and trigger WooCommerce cart calculations 33 $.ajax( 34 { 35 type: 'POST', 36 url: ajax_url, 37 data: { 38 action: 'add_shipping_protection_fee', 39 fee_amount: quote.premium, 40 fee_label: 'Shipping Protection', 41 shipping_quote_id: quote.id 42 }, 43 success: function () { 44 $('body').trigger('update_checkout'); 14 45 15 // If Extend shipping protection offers are not enabled, hide Extend offer div 16 if(enable_helloextend_sp === '0') { 17 const extendShippingOffer = document.querySelector('.helloextend-sp-offer') 18 if (extendShippingOffer) { 19 extendShippingOffer.style.display = 'none'; 20 } 21 22 } 23 //const isShippingProtectionInCart = ExtendShippingIntegration.shippingProtectionInCart(items); 24 const isShippingProtectionInCart = false; 25 26 //If Extend shipping protection is enabled, render offers 27 if (enable_helloextend_sp === '1') { 28 Extend.shippingProtection.render( 29 { 30 selector: '#helloextend-shipping-offer', 31 items: items_array, 32 // isShippingProtectionInCart: false, 33 onEnable: function (quote) { 34 // Update totals and trigger WooCommerce cart calculations 35 $.ajax( 36 { 37 type: 'POST', 38 url: ajax_url, 39 data: { 40 action: 'add_shipping_protection_fee', 41 fee_amount: quote.premium, 42 fee_label: 'Shipping Protection', 43 shipping_quote_id: quote.id 44 }, 45 success: function () { 46 $('body').trigger('update_checkout'); 47 48 // Need to trigger again for SP line item settings to get correct total 49 if (helloextend_sp_add_sku) { 50 setTimeout(() => { 51 $('body').trigger('update_checkout'); 52 }, 50); 53 } 46 // Need to trigger again for SP line item settings to get correct total 47 if (helloextend_sp_add_sku) { 48 setTimeout(() => { 49 $('body').trigger('update_checkout'); 50 }, 50); 54 51 } 55 52 } 56 ); 57 }, 58 onDisable: function (quote) { 59 // Update totals and trigger WooCommerce cart calculations 60 $.ajax( 61 { 62 type: 'POST', 63 url: ajax_url, 64 data: { 65 action: 'remove_shipping_protection_fee', 66 }, 67 success: function () { 68 $('body').trigger('update_checkout'); 53 } 54 ); 55 }, 56 onDisable: function (quote) { 57 // Update totals and trigger WooCommerce cart calculations 58 $.ajax( 59 { 60 type: 'POST', 61 url: ajax_url, 62 data: { 63 action: 'remove_shipping_protection_fee', 64 }, 65 success: function () { 66 $('body').trigger('update_checkout'); 69 67 70 // Need to trigger again for SP line item settings to get correct total 71 if (helloextend_sp_add_sku) { 72 setTimeout(() => { 73 $('body').trigger('update_checkout'); 74 }, 50); 75 } 68 // Need to trigger again for SP line item settings to get correct total 69 if (helloextend_sp_add_sku) { 70 setTimeout(() => { 71 $('body').trigger('update_checkout'); 72 }, 50); 76 73 } 77 74 } 78 ); 79 }, 80 onUpdate: function (quote) { 75 } 76 ); 77 }, 78 onUpdate: function (quote) { 81 79 82 // Update totals and trigger WooCommerce cart calculations83 $.ajax(84 {85 type: 'POST',86 url: ajax_url,87 data: {88 action: 'add_shipping_protection_fee',89 fee_amount: quote.premium,90 fee_label: 'Shipping Protection',91 shipping_quote_id: quote.id92 },93 success: function () {94 $('body').trigger('update_checkout');80 // Update totals and trigger WooCommerce cart calculations 81 $.ajax( 82 { 83 type: 'POST', 84 url: ajax_url, 85 data: { 86 action: 'add_shipping_protection_fee', 87 fee_amount: quote.premium, 88 fee_label: 'Shipping Protection', 89 shipping_quote_id: quote.id 90 }, 91 success: function () { 92 $('body').trigger('update_checkout'); 95 93 96 // Need to trigger again for SP line item settings to get correct total 97 if (helloextend_sp_add_sku) { 98 setTimeout(() => { 99 $('body').trigger('update_checkout'); 100 }, 50); 101 } 94 // Need to trigger again for SP line item settings to get correct total 95 if (helloextend_sp_add_sku) { 96 setTimeout(() => { 97 $('body').trigger('update_checkout'); 98 }, 50); 102 99 } 103 100 } 104 );105 }101 } 102 ); 106 103 } 107 );108 }104 } 105 ); 109 106 } 107 } 110 108 111 initShippingOffers();109 initShippingOffers(); 112 110 113 } 114 ); 111 }); 115 112 116 113 function formatPrice(price) -
helloextend-protection/trunk/readme.txt
r3352239 r3358723 6 6 Requires at least: 4.0 7 7 Tested up to: 6.8 8 Stable tag: 1. 1.38 Stable tag: 1.2.0 9 9 Requires PHP: 7.4 10 10 License: GPLv2 or later … … 79 79 80 80 == Changelog == 81 82 = 1.2.0 2025-09-09 = 83 * Feature - Customers now have the option to purchase a protection plan after their initial purchase. Contact Extend to learn more. 84 * Fix - Any protection plans on an order will be cancelled when the line items or entire order is refunded or cancelled 85 * Fix - Small bug fixes and improvements 86 81 87 = 1.1.3 2025-08-28 = 82 88 * Enhancement - Product images are now synced to Extend store
Note: See TracChangeset
for help on using the changeset viewer.