PluginProbe
Route ‑ Shipping Protection / 2.4.19
Route ‑ Shipping Protection v2.4.19
2.4.19 2.4.18 2.4.17 2.4.16 2.4.15 2.4.14 2.4.13 2.4.12 2.4.11 2.4.10 2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.2.4 2.2.5 2.2.6 2.2.8 2.2.9 2.3.0 2.3.1 All 153 releases
← All changes | public/class-routeapp-public.php +91 -28 2.4.14 → 2.4.19 View file →
@@ -35,8 +35,9 @@
35 35 const ROUTE_PROTECTION_SKU = 'ROUTEINS';
36 36 const ROUTE_FEE_ITEM_META = '_routeapp_protection_fee';
37 37 const ROUTE_FEE_SKU_META = '_routeapp_protection_sku';
38 38 const ALLOWED_QUOTE_TYPE = 'paid_by_customer';
39 + const PICKUP_SHIPPING_METHOD_IDS = array( 'local_pickup', 'pickup_location' );
39 40
40 41 const AMPLITUDE_API_KEY = [
41 42 'production' => '6d4f9158886e0c6d78e3bd97663ba920',
42 43 'stage' => '99a869d62986f6415610c6741057bc92',
@@ -127,11 +128,8 @@
127 128 //CHECKOUT HOOK
128 129 $checkout_hook = $this->routeapp_get_checkout_hook();
129 130 add_action($checkout_hook, array($this, 'checkout_route_insurance'), 1, 0);
130 131
131 -
132 - add_action('woocommerce_checkout_before_order_review', array($this, 'checkout_route_insurance_fee'), 20, 1);
133 -
134 132 add_action('woocommerce_cart_calculate_fees', array($this, 'checkout_route_insurance_fee'), 20, 1);
135 133 add_action('wp_ajax_woo_get_ajax_data', array($this, 'checkout_route_insurance_set_session'));
136 134 add_action('wp_ajax_nopriv_woo_get_ajax_data', array($this, 'checkout_route_insurance_set_session'));
137 135
@@ -431,20 +429,24 @@
431 429 }
432 430 }
433 431
434 432 /**
435 - * Add route insurance fee in checkout and cart
433 + * Add route insurance fee in checkout and cart.
434 + * Only runs during woocommerce_cart_calculate_fees. The old
435 + * woocommerce_checkout_before_order_review binding is gone — it fired
436 + * after totals were already calculated and re-added the fee after opt-out.
437 + *
436 438 * @since 1.0.0
437 439 * @param Object $cart_or_checkout
438 - * MSS-6261:The issue: checkout_route_insurance_fee is bound to hooks that pass different parameter types:
439 - * woocommerce_checkout_before_order_review -> WC_Checkout
440 - * woocommerce_cart_calculate_fees -> WC_Cart
441 - * At line 388, it assumes a WC_Cart and calls get_cart_hash(), which fails when a WC_Checkout is passed.
442 - * Fixing by detecting the object type and handling both cases.
443 - */
440 + */
444 441 public function checkout_route_insurance_fee( $cart_or_checkout ) {
445 442 if ( (is_admin() && ! defined( 'DOING_AJAX' )) ) return;
446 443
444 + // Widget AJAX / fragment hooks must not add fees (re-adds after opt-out, races coupons).
445 + if ( function_exists( 'doing_action' ) && ! doing_action( 'woocommerce_cart_calculate_fees' ) ) {
446 + return;
447 + }
448 +
447 449 // Determine if we received a WC_Cart or WC_Checkout object
448 450 $is_checkout_object = is_a( $cart_or_checkout, 'WC_Checkout' );
449 451 $cart = $is_checkout_object ? WC()->cart : $cart_or_checkout;
450 452
@@ -475,10 +477,20 @@
475 477 $checkbox = $route_insurance_quote->payment_responsible->ToggleState;
476 478 WC()->session->set('checkbox_checked', $checkbox);
477 479 }
478 480
481 + // First checkout paint still defaults ON when ToggleState is missing
482 + // (pre-existing behavior). Do not default ON during checkout AJAX —
483 + // that re-added the fee after opt-out when the session was empty.
484 + if ( $this->routeapp_should_default_fee_opt_in( $checkbox ) ) {
485 + $checkbox = true;
486 + if ( WC()->session ) {
487 + WC()->session->set( 'checkbox_checked', true );
488 + }
489 + }
490 +
479 491 Route_Setup::set_setup_check_widget(Route_Setup::SETUP_CHECK_WIDGET_PHP);
480 - if ( (is_null($checkbox) && is_checkout() || ($checkbox === TRUE || $checkbox === 1)) && $route_insurance_amount > 0 && $this->routeapp_is_shipping_method_allowed()) {
492 + if ( ($checkbox === TRUE || $checkbox === 1) && $route_insurance_amount > 0 && $this->routeapp_is_shipping_method_allowed()) {
481 493 $cart->add_fee( $this->routeapp_get_insurance_label(),
482 494 $route_insurance_amount,
483 495 $this->routeapp_get_fee_taxable(),
484 496 $this->routeapp_get_taxable_class());
@@ -485,8 +497,35 @@
485 497 }
486 498 }
487 499
488 500 /**
501 + * WooCommerce / CheckoutWC totals refresh (update_order_review, etc.).
502 + *
503 + * @return bool
504 + */
505 + public function routeapp_is_checkout_ajax_refresh() {
506 + if ( ! empty( $_REQUEST['wc-ajax'] ) ) {
507 + return true;
508 + }
509 +
510 + return defined( 'DOING_AJAX' ) && DOING_AJAX;
511 + }
512 +
513 + /**
514 + * Keep the pre-existing first-load default (fee ON) when there is no
515 + * session choice and no ToggleState. Skip that default on AJAX refreshes.
516 + *
517 + * @param mixed $checkbox Session checkbox value.
518 + * @return bool
519 + */
520 + public function routeapp_should_default_fee_opt_in( $checkbox ) {
521 + return is_null( $checkbox )
522 + && function_exists( 'is_checkout' )
523 + && is_checkout()
524 + && ! $this->routeapp_is_checkout_ajax_refresh();
525 + }
526 +
527 + /**
489 528 * Create checkout
490 529 * @since 1.0.0
491 530 *
492 531 */
@@ -631,8 +670,16 @@
631 670 'merchantId' => $this->routeapp_api_client->get_merchant_id(),
632 671 )
633 672 );
634 673
674 + wp_localize_script(
675 + $this->plugin_name,
676 + 'routeappWidgetConfig',
677 + array(
678 + 'excludedShippingMethods' => array_values( (array) $this->routeapp_get_excluded_shipping_methods() ),
679 + )
680 + );
681 +
635 682 wp_enqueue_style(
636 683 $this->plugin_name . '-widget',
637 684 plugin_dir_url( __FILE__ ) . 'css/routeapp-widget.css',
638 685 array(),
@@ -694,17 +741,11 @@
694 741 '<input type="hidden" value="' . $this->routeapp_api_client->get_merchant_id() . '" class="routeapp-merchant-id">'.
695 742 '<input type="hidden" value="' . $custom_env . '" class="routeapp-env">'.
696 743 '<input type="hidden" value="' . $checkbox .'" class="routeapp-checkbox-cookie">';
697 744
698 - //check if shipping method is allowed
699 - $cart = WC()->cart;
700 - $shipping_method = $cart->calculate_shipping();
701 - if (isset($shipping_method[0]->method_id)) {
702 - $shipping_method = $shipping_method[0]->method_id;
703 - $show_widget = $this->routeapp_is_shipping_method_allowed($shipping_method);
704 - if (!$show_widget) {
705 - $return .= '<input type="hidden" value="true" class="routeapp-invalid-shipping-method">';
706 - }
745 + $chosen_methods = ( WC()->session ) ? WC()->session->get( 'chosen_shipping_methods' ) : array();
746 + if ( ! empty( $chosen_methods[0] ) && ! $this->routeapp_is_shipping_method_allowed( $chosen_methods[0] ) ) {
747 + $return .= '<input type="hidden" value="true" class="routeapp-invalid-shipping-method">';
707 748 }
708 749 return $return . '<div id="RouteWidget" class="route-div"></div></div>' ;
709 750 }
710 751
@@ -1310,10 +1351,9 @@
1310 1351 } else {
1311 1352 return false;
1312 1353 }
1313 1354 }
1314 - $excludedShippingMethods = $this->routeapp_get_excluded_shipping_methods();
1315 - if (is_array($excludedShippingMethods) && in_array($shipping_method, $excludedShippingMethods)) {
1355 + if ($this->routeapp_is_shipping_method_excluded($shipping_method)) {
1316 1356 return false;
1317 1357 }
1318 1358 } else {
1319 1359 if (WC()->session->get('shipping_for_package_0')) {
@@ -1320,10 +1360,9 @@
1320 1360 foreach( WC()->session->get('shipping_for_package_0')['rates'] as $method_id => $rate ){
1321 1361 if( WC()->session->get('chosen_shipping_methods')[0] == $method_id ){
1322 1362 $shippingMethod = explode(':', $rate->id);
1323 1363 $shippingMethod = $shippingMethod[0];
1324 - $excludedShippingMethods = $this->routeapp_get_excluded_shipping_methods();
1325 - if (in_array($shippingMethod, $excludedShippingMethods)) {
1364 + if ($this->routeapp_is_shipping_method_excluded($shippingMethod)) {
1326 1365 return false;
1327 1366 }
1328 1367 }
1329 1368 }
@@ -1331,8 +1370,30 @@
1331 1370 }
1332 1371 return true;
1333 1372 }
1334 1373
1374 + /**
1375 + * Whether a shipping method is configured as in-store pickup (excluded from protection).
1376 + * WooCommerce Blocks local pickup uses pickup_location; classic checkout uses local_pickup.
1377 + *
1378 + * @param string $shipping_method Method id, optionally with instance suffix (e.g. pickup_location:0).
1379 + * @return bool
1380 + */
1381 + public function routeapp_is_shipping_method_excluded($shipping_method) {
1382 + $shipping_method = explode(':', (string) $shipping_method)[0];
1383 + $excluded = $this->routeapp_get_excluded_shipping_methods();
1384 + if (!is_array($excluded) || empty($excluded) || $shipping_method === '') {
1385 + return false;
1386 + }
1387 + if (in_array($shipping_method, $excluded, true)) {
1388 + return true;
1389 + }
1390 + if (in_array($shipping_method, self::PICKUP_SHIPPING_METHOD_IDS, true)) {
1391 + return (bool) array_intersect(self::PICKUP_SHIPPING_METHOD_IDS, $excluded);
1392 + }
1393 + return false;
1394 + }
1395 +
1335 1396 public function routeapp_get_insurance_label() {
1336 1397 return __(self::ROUTE_LABEL, 'routeapp');
1337 1398 }
1338 1399
@@ -1578,8 +1639,11 @@
1578 1639 return rest_ensure_response( [] );
1579 1640 }
1580 1641
1581 1642 $shipping_method = $_POST['shipping_method'];
1643 + if ($shipping_method === 'false' || $shipping_method === '') {
1644 + $shipping_method = false;
1645 + }
1582 1646
1583 1647 //check first if shipping method is allowed
1584 1648 if ($shipping_method) {
1585 1649 $show_widget = $this->routeapp_is_shipping_method_allowed($shipping_method);
@@ -1589,11 +1653,10 @@
1589 1653 }
1590 1654 }
1591 1655
1592 1656 $cart = WC()->cart;
1593 - $this->checkout_route_insurance_fee($cart);
1594 1657 $response= [
1595 - 'routeapp-subtotal' => round($this->get_cart_subtotal_with_only_shippable_items($cart), 2),
1658 + 'routeapp-subtotal' => $cart ? round($this->get_cart_subtotal_with_only_shippable_items($cart), 2) : 0,
1596 1659 ];
1597 1660
1598 1661 wp_send_json( $response );
1599 1662 }
@@ -2302,9 +2365,9 @@
2302 2365 if ( empty( $method_id ) ) {
2303 2366 continue;
2304 2367 }
2305 2368 $method_id = explode( ':', $method_id )[0];
2306 - if ( in_array( $method_id, $excluded_methods, true ) ) {
2369 + if ( $this->routeapp_is_shipping_method_excluded( $method_id ) ) {
2307 2370 return 1;
2308 2371 }
2309 2372 }
2310 2373 return 0;
@@ -2324,9 +2387,9 @@
2324 2387 if ( ! isset( $packages['rates'][ $chosen_id ] ) ) {
2325 2388 return 0;
2326 2389 }
2327 2390 $method_id = explode( ':', $packages['rates'][ $chosen_id ]->id )[0];
2328 - if ( in_array( $method_id, $excluded_methods, true ) ) {
2391 + if ( $this->routeapp_is_shipping_method_excluded( $method_id ) ) {
2329 2392 return 1;
2330 2393 }
2331 2394
2332 2395 return 0;