Plugin Directory

Changeset 3358723


Ignore:
Timestamp:
09/09/2025 03:30:55 PM (6 months ago)
Author:
helloextend
Message:

Updated to version 1.2 with new features and fixes

Location:
helloextend-protection
Files:
2 added
19 edited
4 copied

Legend:

Unmodified
Added
Removed
  • helloextend-protection/tags/1.2.0/helloextend-protection.php

    r3352239 r3358723  
    1717 * Plugin URI:        https://docs.extend.com/docs/extend-protection-plugin-for-woocommerce
    1818 * Description:       Extend Protection for Woocommerce. Allows WooCommerce merchants to offer product and shipping protection to their customers.
    19  * Version:           1.1.3
     19 * Version:           1.2.0
    2020 * Author:            Extend, Inc.
    2121 * Author URI:        https://extend.com/
  • helloextend-protection/tags/1.2.0/includes/class-helloextend-global.php

    r3348358 r3358723  
    5454    {
    5555
    56         $this->helloextend_protection = $helloextend_protection;
    57         $this->version           = $version;
     56        $this->helloextend_protection   = $helloextend_protection;
     57        $this->version                  = $version;
    5858        $this->hooks();
    5959    }
     
    121121            // add sku to cart item and label it referenceId
    122122            $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();
    124124        }
    125125
     
    270270        }
    271271
     272        if ($helloextend_data['leadToken']) {
     273            $helloextend_data['leadQuantity'] = $quantity;
     274        }
     275
    272276        WC()->cart->add_to_cart($warranty_product_id, $quantity, 0, 0, ['extendData' => $helloextend_data]);
    273277    }
     
    346350            $covered       = wc_get_product($covered_id);
    347351            $sku           = $cart_item['extendData']['planId'];
    348             $covered_title = $covered->get_title();
     352            $covered_title = $covered->get_name();
    349353
    350354            $item->add_meta_data('Warranty', $title);
     
    352356            $item->add_meta_data('Plan Id', $sku);
    353357            $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            }
    354361        }
    355362    }
     
    372379            $covered       = wc_get_product($covered_id);
    373380            $sku           = $cart_item['extendData']['planId'];
    374             $covered_title = $covered->get_title();
     381            $covered_title = $covered->get_name();
    375382            $data[]        = [
    376383                'key'   => 'Product',
     
    403410            wp_enqueue_script('helloextend_global_script');
    404411            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            }
    405427        } else {
    406428            HelloExtend_Protection_Logger::helloextend_log_error('Store Id missing or Extend Product Protection is disabled');
     
    509531        return $categories[0]->name ?? 'Uncategorized';
    510532    }
     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    }
    511560}
  • helloextend-protection/tags/1.2.0/includes/class-helloextend-protection-cart-offer.php

    r3348358 r3358723  
    4848
    4949    protected string $warranty_product_id;
    50     protected array $products = [];
    51     protected array $updates  = [];
    5250    private array $settings;
    5351
     
    7775    }
    7876
     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
    79137    // get_cart_updates()
    80138    // 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    {
    119141        $cart_balancing = $this->settings['helloextend_enable_cart_balancing'] == 1 ? true : false;
    120142
    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;
    129177                    }
    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;
    160195    }
    161196
     
    165200    {
    166201
    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);
    179208        }
    180209
     
    192221        if (! isset($cart_item['extendData']) ) {
    193222            $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');
    197225            $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>";
    200229        }
    201230    }
     
    208237        $enable_helloextend             = trim($this->settings['enable_helloextend']);
    209238        $helloextend_enable_cart_offers = $this->settings['helloextend_enable_cart_offers'];
    210         $cart                      = WC()->cart;
     239        $cart                           = WC()->cart;
    211240
    212241        if ($helloextend_enable_cart_offers === '1' && $enable_helloextend === '1' ) {
  • helloextend-protection/tags/1.2.0/includes/class-helloextend-protection-orders.php

    r3352239 r3358723  
    5454    private array $settings;
    5555
     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
    5661    /**
    5762     * Initialize the class and set its properties.
     
    6772        /* retrieve environment variables */
    6873        $this->settings = HelloExtend_Protection_Global::helloextend_get_settings();
    69 
     74        $this->helloextend_product_protection_id = $this->settings['warranty_product_id'];
     75 
    7076        // Hook the callback function to the 'woocommerce_new_order' action
    7177        add_action('woocommerce_checkout_order_processed', [$this, 'create_update_order'], 10, 1);
     
    7682        // Hook the callback function to the order cancelled action
    7783        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)
    8191    {
    8292        // Only accept valid WooCommerce product objects
     
    127137    }
    128138
     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
    129184    /**
    130185     * helloextend_get_plans_and_products($order, $fulfill_now = false)
     
    143198            $helloextend_meta_data = (array) $item->get_meta('_helloextend_data');
    144199
    145             // if  item id is for extend-product-protection gram $helloextend_meta_data and push it to the plans array
    146             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']) {
    147202                $helloextend_plans[] = array(
    148203                    'id'                 => $helloextend_meta_data['planId'],
     
    153208        }
    154209
     210        $leads = $this->get_purchased_leads($order);
     211
    155212        // Loop through the order items and add them to the line_items array
    156213        $helloextend_line_items = array();
     
    162219
    163220            // 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            }
    167226
    168227            // if line_id matches any id in $helloextend_plans[], push the plan data into the covered product
     
    175234            }
    176235
    177             // Get extend product id from settings
    178             $helloextend_product_protection_id = $this->settings['warranty_product_id'];
    179 
    180236            // Add relevant data to the line_items array
    181237            // 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) {
    183239               
    184240                $image_url = $this->get_product_image_url($product);
    185241
    186                 $helloextend_line_items[] = array(
     242                $helloextend_line_item = array(
    187243                    'lineItemTransactionId' => $product->get_id(),
    188244                    'product'               => array(
     
    190246                        'title'         => $product->get_name(),
    191247                        '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),
    194250                        'purchaseDate'  => $order->get_data()['date_created']->getTimestamp() * 1000,
    195251                        'imageUrl'      => $image_url
     
    201257                // if $plan is not empty, add the plan to the current line item
    202258                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
    207270        return $helloextend_line_items;
    208271    }
     
    441504
    442505        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        }
    448511
    449512        // Get Token from Global function
     
    526589        }
    527590    }
     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    }
    528694}
  • helloextend-protection/tags/1.2.0/includes/class-helloextend-protection-shipping.php

    r3348358 r3358723  
    119119            if (! $product->is_virtual() ) {
    120120                $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);
    121123                $items[]     = array(
    122124                 'referenceId'   => $referenceId,
    123125                 'quantity'      => $cart_item['quantity'],
    124                  'category'      => get_the_terms($product->get_id(), 'product_cat')[0]->name,
     126                 'category'      => $category,
    125127                 'purchasePrice' => (int) floatval($product->get_price() * 100),
    126128                 'productName'   => $product->get_name(),
  • helloextend-protection/tags/1.2.0/includes/class-helloextend-protection.php

    r3263092 r3358723  
    247247        wp_register_script('helloextend_script', $this->sdk_url, array(), '1.0.0', true);
    248248        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);
    249250        wp_register_script('helloextend_product_integration_script', $this->url . '../js/helloextend-pdp-offers.js', [ 'jquery', 'helloextend_global_script' ], '1.0.0', true);
    250251        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  
    11// This script is used to handle the rendering and functionality of Extend offers in a WooCommerce cart.
    22(($) => {
    3 
    4     // If necessary objects (ExtendWooCommerce and ExtendCartIntegration) do not exist, stop the execution of the script.
    5     if (!ExtendWooCommerce || !ExtendCartIntegration) {
    6         return;
    7     }
    83
    94    const SELECTORS = {
     
    116        TITLE: '.product-name',
    127        IMAGE: '.product-thumbnail',
    13         PRICE: '.product-price',
    148        QUANTITY: 'input.qty',
    159        EXTEND_OFFER: '.cart-extend-offer',
     
    7973                const quantity = $lineItemElement.find(SELECTORS.QUANTITY).val();
    8074               
    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');
    8476               
    8577                renderExtendOffer($offer[0], { referenceId, category, price }, quantity);
     
    8981   
    9082    // 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       
    9293        initCartOffers();
    9394    });
  • helloextend-protection/tags/1.2.0/js/helloextend-global.js

    r3348358 r3358723  
    11(function ( $ ) {
    22    'use strict';
    3     $(document).ready(
    4         function ($) {
    5             if(!ExtendWooCommerce) { return;
    6             }
     3    $(document).ready(function ($) {
     4        if(!ExtendWooCommerce) { return;
     5        }
    76
    8             const { store_id: storeId, ajaxurl, environment, debug_log_enabled: debugLogEnabled } = ExtendWooCommerce;
     7        const { store_id: storeId, ajaxurl, environment, debug_log_enabled: debugLogEnabled } = ExtendWooCommerce;
    98
    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                    }
    1424                }
    15             );
     25            ).promise()
     26        }
    1627
    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(
    2932                    ajaxurl, {
    30                         action: "add_to_cart_helloextend",
    31                         quantity: opts.quantity,
    32                         extendData: opts.plan
     33                        action: "get_cart_helloextend"
    3334                    }
    3435                ).promise()
     36            );
     37        }
     38
     39        function warrantyAlreadyInCart(variantId, cart)
     40        {
     41            let cartContents = cart['cart_contents'];
     42            if (!cartContents) {
     43                cartContents = cart;
    3544            }
    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 (
    6253                            extendData &&
    6354                            !extendData.leadToken &&
    6455                            referenceId &&
    6556                            referenceId.toString() === variantId.toString()
    66                             );
    67                         }
     57                        );
    6858                    }
    69                 );
    70                 return extendWarranties.length > 0;
    71             }
     59                }
     60            );
     61            return extendWarranties.length > 0;
     62        }
    7263
    73             function extendAjaxLog(method, ...message)
    74             {
     64        function extendAjaxLog(method, ...message) {
    7565
    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,
    8272
    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)
    9483                    }
    95                 );
    96             }
     84                }
     85            );
     86        }
    9787
     88        window.ExtendWooCommerce = {
     89            ...ExtendWooCommerce,
     90            addPlanToCart,
     91            getCart,
     92            warrantyAlreadyInCart,
     93            extendAjaxLog,
     94            debugLogEnabled
    9895        }
    99     )
     96
     97        $(document).trigger('integration.extend');
     98    });
    10099})(jQuery);
    101100
  • helloextend-protection/tags/1.2.0/js/helloextend-pdp-offers.js

    r3348358 r3358723  
    11(function( $ ) {
    22    'use strict';
    3     $(document).ready(function($) {
     3    $(document).off('integration.extend.pdp').on('integration.extend.pdp', function() {
    44
    55        if (!ExtendWooCommerce || !ExtendProductIntegration) return;
     
    88        const { type: product_type, id: product_id, sku, first_category, price, helloextend_pdp_offers_enabled, helloextend_modal_offers_enabled, atc_button_selector } = ExtendProductIntegration;
    99
    10         const $atcButton = jQuery(atc_button_selector)
     10        const $atcButton = $(atc_button_selector);
    1111
    12         const quantity = parseInt(document.querySelector('input[name="quantity"]').value || 1)
     12        const getQuantity = () => parseInt(($('input[name="quantity"]').val() || 1), 10);
    1313
    14         let supportedProductType = true;
    1514        let reference_id = product_id;
    1615
    1716        // If PDP offers are not enabled, hide Extend offer div
    1817        if (helloextend_pdp_offers_enabled === '0') {
    19             const extendOffer = document.querySelector('.helloextend-offer')
    20             extendOffer.style.display = 'none';
     18            $('.helloextend-offer').hide();
    2119        }
    2220
    23         function handleAddToCartLogic(variation_id)  {
     21        function handleAddToCartLogic()  {
    2422
    25             $atcButton.on('click', function extendHandler(e) {
     23            $atcButton.off('click.extend').on('click.extend', function extendHandler(e) {
    2624                e.preventDefault();
    2725                e.stopImmediatePropagation();
     
    3230
    3331                function triggerAddToCart() {
    34                     $atcButton.off('click', extendHandler);
     32                    $atcButton.off('click.extend', extendHandler);
    3533                    $atcButton.trigger('click');
    36                     $atcButton.on('click', extendHandler);
     34                    $atcButton.on('click.extend', extendHandler);
    3735                }
    3836
    3937                const component = Extend.buttons.instance('.helloextend-offer');
    40 
     38               
    4139                /** get the users plan selection */
    4240                const plan = component.getPlanSelection();
    43                 const product = component.getActiveProduct();
     41                const referenceId = component.getActiveProduct().id;
    4442
    4543                if (plan) {
    46                     var planCopy = { ...plan, covered_product_id: variation_id }
    47                     var data = {
    48                         quantity: quantity,
     44                    let planCopy = { ...plan, covered_product_id: referenceId };
     45                    let data = {
     46                        quantity: getQuantity(),
    4947                        plan: planCopy,
    5048                        price: (plan.price / 100).toFixed(2)
    51                     }
     49                    };
     50
    5251                    ExtendWooCommerce.addPlanToCart(data)
    5352                        .then(() => {
    5453                            triggerAddToCart();
    55                         })
     54                        });
    5655                } else {
    5756                    if(helloextend_modal_offers_enabled === '1') {
    5857                        Extend.modal.open({
    59                             referenceId: variation_id,
     58                            referenceId,
    6059                            price: price,
    6160                            category: first_category,
    6261                            onClose: function(plan, product) {
    6362                                if (plan && product) {
    64                                     var planCopy = { ...plan, covered_product_id: variation_id }
    65                                     var data = {
    66                                         quantity: quantity,
     63                                    let planCopy = { ...plan, covered_product_id: referenceId };
     64                                    let data = {
     65                                        quantity: getQuantity(),
    6766                                        plan: planCopy,
    6867                                        price: (plan.price / 100).toFixed(2)
    69                                     }
     68                                    };
     69
    7070                                    ExtendWooCommerce.addPlanToCart(data)
    7171                                        .then(() => {
    7272                                            triggerAddToCart();
    73                                         })
     73                                        });
    7474                                } else {
    75                                     triggerAddToCart()
     75                                    triggerAddToCart();
    7676                                }
    7777                            },
    7878                        });
    7979                    } else {
    80                         triggerAddToCart()
     80                        triggerAddToCart();
    8181                    }
    8282                }
     
    9797
    9898                // TODO: initalize cart offers
    99                 handleAddToCartLogic(reference_id);
     99                handleAddToCartLogic();
    100100
    101101            } else if (product_type === 'variable') {
    102102
    103                 jQuery( ".single_variation_wrap" ).on( "show_variation", function ( event, variation )  {
     103                $( ".single_variation_wrap" ).on( "show_variation", function ( event, variation )  {
    104104
    105                     setTimeout(function(){
    106105                        let component = Extend.buttons.instance('.helloextend-offer');
    107106                        let variation_id = variation.variation_id;
     
    109108
    110109                        if (component) {
    111 
    112110                            if(variation_id) {
    113 
    114111                                Extend.setActiveProduct('.helloextend-offer',
    115112                                    {
     
    119116                                    }
    120117                                );
    121 
    122 
    123118                            }
    124119                        } else {
     
    130125                        }
    131126
    132                         handleAddToCartLogic(variation_id);
     127                    });
    133128
    134                     }, 1000);
    135                 });
     129                    handleAddToCartLogic();
    136130            } else if (product_type === 'composite') {
    137131
     
    145139
    146140                // 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';
    149143
    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_category
    157                         });
    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
    159153                });
    160154
    161155            } else {
    162156                console.warn("helloextend-pdp-offers.js error: Unsupported product type: ", product_type);
    163                 supportedProductType = false;
    164157            }
    165 
    166 
    167158        }
    168159    });
  • helloextend-protection/tags/1.2.0/js/helloextend-shipping-offers.js

    r3348358 r3358723  
    11(function ( $ ) {
    22    'use strict';
    3     $(document).ready(
    4         function ($) {
     3    $(document).off('integration.extend.shipping').on('integration.extend.shipping', function () {
     4        if(!ExtendWooCommerce || !ExtendShippingIntegration) { return;
     5        }
    56
    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
    720            }
     21            //const isShippingProtectionInCart = ExtendShippingIntegration.shippingProtectionInCart(items);
     22            const isShippingProtectionInCart = false;
    823
    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');
    1445
    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);
    5451                                        }
    5552                                    }
    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');
    6967
    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);
    7673                                        }
    7774                                    }
    78                                 );
    79                             },
    80                             onUpdate: function (quote) {
     75                                }
     76                            );
     77                        },
     78                        onUpdate: function (quote) {
    8179
    82                                 // Update totals and trigger WooCommerce cart calculations
    83                                 $.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.id
    92                                         },
    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');
    9593
    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);
    10299                                        }
    103100                                    }
    104                                 );
    105                             }
     101                                }
     102                            );
    106103                        }
    107                     );
    108                 }
     104                    }
     105                );
    109106            }
     107        }
    110108
    111             initShippingOffers();
     109        initShippingOffers();
    112110
    113         }
    114     );
     111    });
    115112
    116113    function formatPrice(price)
  • helloextend-protection/tags/1.2.0/readme.txt

    r3352239 r3358723  
    66Requires at least: 4.0
    77Tested up to: 6.8
    8 Stable tag: 1.1.3
     8Stable tag: 1.2.0
    99Requires PHP: 7.4
    1010License: GPLv2 or later
     
    7979
    8080== 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
    8187= 1.1.3 2025-08-28 =
    8288 * Enhancement - Product images are now synced to Extend store
  • helloextend-protection/trunk/helloextend-protection.php

    r3352239 r3358723  
    1717 * Plugin URI:        https://docs.extend.com/docs/extend-protection-plugin-for-woocommerce
    1818 * Description:       Extend Protection for Woocommerce. Allows WooCommerce merchants to offer product and shipping protection to their customers.
    19  * Version:           1.1.3
     19 * Version:           1.2.0
    2020 * Author:            Extend, Inc.
    2121 * Author URI:        https://extend.com/
  • helloextend-protection/trunk/includes/class-helloextend-global.php

    r3348358 r3358723  
    5454    {
    5555
    56         $this->helloextend_protection = $helloextend_protection;
    57         $this->version           = $version;
     56        $this->helloextend_protection   = $helloextend_protection;
     57        $this->version                  = $version;
    5858        $this->hooks();
    5959    }
     
    121121            // add sku to cart item and label it referenceId
    122122            $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();
    124124        }
    125125
     
    270270        }
    271271
     272        if ($helloextend_data['leadToken']) {
     273            $helloextend_data['leadQuantity'] = $quantity;
     274        }
     275
    272276        WC()->cart->add_to_cart($warranty_product_id, $quantity, 0, 0, ['extendData' => $helloextend_data]);
    273277    }
     
    346350            $covered       = wc_get_product($covered_id);
    347351            $sku           = $cart_item['extendData']['planId'];
    348             $covered_title = $covered->get_title();
     352            $covered_title = $covered->get_name();
    349353
    350354            $item->add_meta_data('Warranty', $title);
     
    352356            $item->add_meta_data('Plan Id', $sku);
    353357            $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            }
    354361        }
    355362    }
     
    372379            $covered       = wc_get_product($covered_id);
    373380            $sku           = $cart_item['extendData']['planId'];
    374             $covered_title = $covered->get_title();
     381            $covered_title = $covered->get_name();
    375382            $data[]        = [
    376383                'key'   => 'Product',
     
    403410            wp_enqueue_script('helloextend_global_script');
    404411            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            }
    405427        } else {
    406428            HelloExtend_Protection_Logger::helloextend_log_error('Store Id missing or Extend Product Protection is disabled');
     
    509531        return $categories[0]->name ?? 'Uncategorized';
    510532    }
     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    }
    511560}
  • helloextend-protection/trunk/includes/class-helloextend-protection-cart-offer.php

    r3348358 r3358723  
    4848
    4949    protected string $warranty_product_id;
    50     protected array $products = [];
    51     protected array $updates  = [];
    5250    private array $settings;
    5351
     
    7775    }
    7876
     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
    79137    // get_cart_updates()
    80138    // 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    {
    119141        $cart_balancing = $this->settings['helloextend_enable_cart_balancing'] == 1 ? true : false;
    120142
    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;
    129177                    }
    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;
    160195    }
    161196
     
    165200    {
    166201
    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);
    179208        }
    180209
     
    192221        if (! isset($cart_item['extendData']) ) {
    193222            $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');
    197225            $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>";
    200229        }
    201230    }
     
    208237        $enable_helloextend             = trim($this->settings['enable_helloextend']);
    209238        $helloextend_enable_cart_offers = $this->settings['helloextend_enable_cart_offers'];
    210         $cart                      = WC()->cart;
     239        $cart                           = WC()->cart;
    211240
    212241        if ($helloextend_enable_cart_offers === '1' && $enable_helloextend === '1' ) {
  • helloextend-protection/trunk/includes/class-helloextend-protection-orders.php

    r3352239 r3358723  
    5454    private array $settings;
    5555
     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
    5661    /**
    5762     * Initialize the class and set its properties.
     
    6772        /* retrieve environment variables */
    6873        $this->settings = HelloExtend_Protection_Global::helloextend_get_settings();
    69 
     74        $this->helloextend_product_protection_id = $this->settings['warranty_product_id'];
     75 
    7076        // Hook the callback function to the 'woocommerce_new_order' action
    7177        add_action('woocommerce_checkout_order_processed', [$this, 'create_update_order'], 10, 1);
     
    7682        // Hook the callback function to the order cancelled action
    7783        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)
    8191    {
    8292        // Only accept valid WooCommerce product objects
     
    127137    }
    128138
     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
    129184    /**
    130185     * helloextend_get_plans_and_products($order, $fulfill_now = false)
     
    143198            $helloextend_meta_data = (array) $item->get_meta('_helloextend_data');
    144199
    145             // if  item id is for extend-product-protection gram $helloextend_meta_data and push it to the plans array
    146             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']) {
    147202                $helloextend_plans[] = array(
    148203                    'id'                 => $helloextend_meta_data['planId'],
     
    153208        }
    154209
     210        $leads = $this->get_purchased_leads($order);
     211
    155212        // Loop through the order items and add them to the line_items array
    156213        $helloextend_line_items = array();
     
    162219
    163220            // 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            }
    167226
    168227            // if line_id matches any id in $helloextend_plans[], push the plan data into the covered product
     
    175234            }
    176235
    177             // Get extend product id from settings
    178             $helloextend_product_protection_id = $this->settings['warranty_product_id'];
    179 
    180236            // Add relevant data to the line_items array
    181237            // 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) {
    183239               
    184240                $image_url = $this->get_product_image_url($product);
    185241
    186                 $helloextend_line_items[] = array(
     242                $helloextend_line_item = array(
    187243                    'lineItemTransactionId' => $product->get_id(),
    188244                    'product'               => array(
     
    190246                        'title'         => $product->get_name(),
    191247                        '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),
    194250                        'purchaseDate'  => $order->get_data()['date_created']->getTimestamp() * 1000,
    195251                        'imageUrl'      => $image_url
     
    201257                // if $plan is not empty, add the plan to the current line item
    202258                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
    207270        return $helloextend_line_items;
    208271    }
     
    441504
    442505        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        }
    448511
    449512        // Get Token from Global function
     
    526589        }
    527590    }
     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    }
    528694}
  • helloextend-protection/trunk/includes/class-helloextend-protection-shipping.php

    r3348358 r3358723  
    119119            if (! $product->is_virtual() ) {
    120120                $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);
    121123                $items[]     = array(
    122124                 'referenceId'   => $referenceId,
    123125                 'quantity'      => $cart_item['quantity'],
    124                  'category'      => get_the_terms($product->get_id(), 'product_cat')[0]->name,
     126                 'category'      => $category,
    125127                 'purchasePrice' => (int) floatval($product->get_price() * 100),
    126128                 'productName'   => $product->get_name(),
  • helloextend-protection/trunk/includes/class-helloextend-protection.php

    r3263092 r3358723  
    247247        wp_register_script('helloextend_script', $this->sdk_url, array(), '1.0.0', true);
    248248        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);
    249250        wp_register_script('helloextend_product_integration_script', $this->url . '../js/helloextend-pdp-offers.js', [ 'jquery', 'helloextend_global_script' ], '1.0.0', true);
    250251        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  
    11// This script is used to handle the rendering and functionality of Extend offers in a WooCommerce cart.
    22(($) => {
    3 
    4     // If necessary objects (ExtendWooCommerce and ExtendCartIntegration) do not exist, stop the execution of the script.
    5     if (!ExtendWooCommerce || !ExtendCartIntegration) {
    6         return;
    7     }
    83
    94    const SELECTORS = {
     
    116        TITLE: '.product-name',
    127        IMAGE: '.product-thumbnail',
    13         PRICE: '.product-price',
    148        QUANTITY: 'input.qty',
    159        EXTEND_OFFER: '.cart-extend-offer',
     
    7973                const quantity = $lineItemElement.find(SELECTORS.QUANTITY).val();
    8074               
    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');
    8476               
    8577                renderExtendOffer($offer[0], { referenceId, category, price }, quantity);
     
    8981   
    9082    // 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       
    9293        initCartOffers();
    9394    });
  • helloextend-protection/trunk/js/helloextend-global.js

    r3348358 r3358723  
    11(function ( $ ) {
    22    'use strict';
    3     $(document).ready(
    4         function ($) {
    5             if(!ExtendWooCommerce) { return;
    6             }
     3    $(document).ready(function ($) {
     4        if(!ExtendWooCommerce) { return;
     5        }
    76
    8             const { store_id: storeId, ajaxurl, environment, debug_log_enabled: debugLogEnabled } = ExtendWooCommerce;
     7        const { store_id: storeId, ajaxurl, environment, debug_log_enabled: debugLogEnabled } = ExtendWooCommerce;
    98
    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                    }
    1424                }
    15             );
     25            ).promise()
     26        }
    1627
    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(
    2932                    ajaxurl, {
    30                         action: "add_to_cart_helloextend",
    31                         quantity: opts.quantity,
    32                         extendData: opts.plan
     33                        action: "get_cart_helloextend"
    3334                    }
    3435                ).promise()
     36            );
     37        }
     38
     39        function warrantyAlreadyInCart(variantId, cart)
     40        {
     41            let cartContents = cart['cart_contents'];
     42            if (!cartContents) {
     43                cartContents = cart;
    3544            }
    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 (
    6253                            extendData &&
    6354                            !extendData.leadToken &&
    6455                            referenceId &&
    6556                            referenceId.toString() === variantId.toString()
    66                             );
    67                         }
     57                        );
    6858                    }
    69                 );
    70                 return extendWarranties.length > 0;
    71             }
     59                }
     60            );
     61            return extendWarranties.length > 0;
     62        }
    7263
    73             function extendAjaxLog(method, ...message)
    74             {
     64        function extendAjaxLog(method, ...message) {
    7565
    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,
    8272
    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)
    9483                    }
    95                 );
    96             }
     84                }
     85            );
     86        }
    9787
     88        window.ExtendWooCommerce = {
     89            ...ExtendWooCommerce,
     90            addPlanToCart,
     91            getCart,
     92            warrantyAlreadyInCart,
     93            extendAjaxLog,
     94            debugLogEnabled
    9895        }
    99     )
     96
     97        $(document).trigger('integration.extend');
     98    });
    10099})(jQuery);
    101100
  • helloextend-protection/trunk/js/helloextend-pdp-offers.js

    r3348358 r3358723  
    11(function( $ ) {
    22    'use strict';
    3     $(document).ready(function($) {
     3    $(document).off('integration.extend.pdp').on('integration.extend.pdp', function() {
    44
    55        if (!ExtendWooCommerce || !ExtendProductIntegration) return;
     
    88        const { type: product_type, id: product_id, sku, first_category, price, helloextend_pdp_offers_enabled, helloextend_modal_offers_enabled, atc_button_selector } = ExtendProductIntegration;
    99
    10         const $atcButton = jQuery(atc_button_selector)
     10        const $atcButton = $(atc_button_selector);
    1111
    12         const quantity = parseInt(document.querySelector('input[name="quantity"]').value || 1)
     12        const getQuantity = () => parseInt(($('input[name="quantity"]').val() || 1), 10);
    1313
    14         let supportedProductType = true;
    1514        let reference_id = product_id;
    1615
    1716        // If PDP offers are not enabled, hide Extend offer div
    1817        if (helloextend_pdp_offers_enabled === '0') {
    19             const extendOffer = document.querySelector('.helloextend-offer')
    20             extendOffer.style.display = 'none';
     18            $('.helloextend-offer').hide();
    2119        }
    2220
    23         function handleAddToCartLogic(variation_id)  {
     21        function handleAddToCartLogic()  {
    2422
    25             $atcButton.on('click', function extendHandler(e) {
     23            $atcButton.off('click.extend').on('click.extend', function extendHandler(e) {
    2624                e.preventDefault();
    2725                e.stopImmediatePropagation();
     
    3230
    3331                function triggerAddToCart() {
    34                     $atcButton.off('click', extendHandler);
     32                    $atcButton.off('click.extend', extendHandler);
    3533                    $atcButton.trigger('click');
    36                     $atcButton.on('click', extendHandler);
     34                    $atcButton.on('click.extend', extendHandler);
    3735                }
    3836
    3937                const component = Extend.buttons.instance('.helloextend-offer');
    40 
     38               
    4139                /** get the users plan selection */
    4240                const plan = component.getPlanSelection();
    43                 const product = component.getActiveProduct();
     41                const referenceId = component.getActiveProduct().id;
    4442
    4543                if (plan) {
    46                     var planCopy = { ...plan, covered_product_id: variation_id }
    47                     var data = {
    48                         quantity: quantity,
     44                    let planCopy = { ...plan, covered_product_id: referenceId };
     45                    let data = {
     46                        quantity: getQuantity(),
    4947                        plan: planCopy,
    5048                        price: (plan.price / 100).toFixed(2)
    51                     }
     49                    };
     50
    5251                    ExtendWooCommerce.addPlanToCart(data)
    5352                        .then(() => {
    5453                            triggerAddToCart();
    55                         })
     54                        });
    5655                } else {
    5756                    if(helloextend_modal_offers_enabled === '1') {
    5857                        Extend.modal.open({
    59                             referenceId: variation_id,
     58                            referenceId,
    6059                            price: price,
    6160                            category: first_category,
    6261                            onClose: function(plan, product) {
    6362                                if (plan && product) {
    64                                     var planCopy = { ...plan, covered_product_id: variation_id }
    65                                     var data = {
    66                                         quantity: quantity,
     63                                    let planCopy = { ...plan, covered_product_id: referenceId };
     64                                    let data = {
     65                                        quantity: getQuantity(),
    6766                                        plan: planCopy,
    6867                                        price: (plan.price / 100).toFixed(2)
    69                                     }
     68                                    };
     69
    7070                                    ExtendWooCommerce.addPlanToCart(data)
    7171                                        .then(() => {
    7272                                            triggerAddToCart();
    73                                         })
     73                                        });
    7474                                } else {
    75                                     triggerAddToCart()
     75                                    triggerAddToCart();
    7676                                }
    7777                            },
    7878                        });
    7979                    } else {
    80                         triggerAddToCart()
     80                        triggerAddToCart();
    8181                    }
    8282                }
     
    9797
    9898                // TODO: initalize cart offers
    99                 handleAddToCartLogic(reference_id);
     99                handleAddToCartLogic();
    100100
    101101            } else if (product_type === 'variable') {
    102102
    103                 jQuery( ".single_variation_wrap" ).on( "show_variation", function ( event, variation )  {
     103                $( ".single_variation_wrap" ).on( "show_variation", function ( event, variation )  {
    104104
    105                     setTimeout(function(){
    106105                        let component = Extend.buttons.instance('.helloextend-offer');
    107106                        let variation_id = variation.variation_id;
     
    109108
    110109                        if (component) {
    111 
    112110                            if(variation_id) {
    113 
    114111                                Extend.setActiveProduct('.helloextend-offer',
    115112                                    {
     
    119116                                    }
    120117                                );
    121 
    122 
    123118                            }
    124119                        } else {
     
    130125                        }
    131126
    132                         handleAddToCartLogic(variation_id);
     127                    });
    133128
    134                     }, 1000);
    135                 });
     129                    handleAddToCartLogic();
    136130            } else if (product_type === 'composite') {
    137131
     
    145139
    146140                // 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';
    149143
    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_category
    157                         });
    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
    159153                });
    160154
    161155            } else {
    162156                console.warn("helloextend-pdp-offers.js error: Unsupported product type: ", product_type);
    163                 supportedProductType = false;
    164157            }
    165 
    166 
    167158        }
    168159    });
  • helloextend-protection/trunk/js/helloextend-shipping-offers.js

    r3348358 r3358723  
    11(function ( $ ) {
    22    'use strict';
    3     $(document).ready(
    4         function ($) {
     3    $(document).off('integration.extend.shipping').on('integration.extend.shipping', function () {
     4        if(!ExtendWooCommerce || !ExtendShippingIntegration) { return;
     5        }
    56
    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
    720            }
     21            //const isShippingProtectionInCart = ExtendShippingIntegration.shippingProtectionInCart(items);
     22            const isShippingProtectionInCart = false;
    823
    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');
    1445
    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);
    5451                                        }
    5552                                    }
    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');
    6967
    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);
    7673                                        }
    7774                                    }
    78                                 );
    79                             },
    80                             onUpdate: function (quote) {
     75                                }
     76                            );
     77                        },
     78                        onUpdate: function (quote) {
    8179
    82                                 // Update totals and trigger WooCommerce cart calculations
    83                                 $.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.id
    92                                         },
    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');
    9593
    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);
    10299                                        }
    103100                                    }
    104                                 );
    105                             }
     101                                }
     102                            );
    106103                        }
    107                     );
    108                 }
     104                    }
     105                );
    109106            }
     107        }
    110108
    111             initShippingOffers();
     109        initShippingOffers();
    112110
    113         }
    114     );
     111    });
    115112
    116113    function formatPrice(price)
  • helloextend-protection/trunk/readme.txt

    r3352239 r3358723  
    66Requires at least: 4.0
    77Tested up to: 6.8
    8 Stable tag: 1.1.3
     8Stable tag: 1.2.0
    99Requires PHP: 7.4
    1010License: GPLv2 or later
     
    7979
    8080== 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
    8187= 1.1.3 2025-08-28 =
    8288 * Enhancement - Product images are now synced to Extend store
Note: See TracChangeset for help on using the changeset viewer.