| @@ -16,8 +16,9 @@ | ||
| 16 | 16 | use FluentCart\App\Models\Product; |
| 17 | 17 | use FluentCart\App\Models\ProductVariation; |
| 18 | 18 | use FluentCart\App\Models\ShippingMethod; |
| 19 | 19 | use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager; |
| 20 | +use FluentCart\App\Modules\Tax\TaxModule; | |
| 20 | 21 | use FluentCart\App\Services\Localization\LocalizationManager; |
| 21 | 22 | use FluentCart\App\Services\Renderer\AddressSelectRenderer; |
| 22 | 23 | use FluentCart\App\Services\Renderer\CartDrawerRenderer; |
| 23 | 24 | use FluentCart\App\Services\Renderer\CartRenderer; |
| @@ -60,8 +61,14 @@ | ||
| 60 | 61 | public function globalCheckoutRouteHandler() |
| 61 | 62 | { |
| 62 | 63 | nocache_headers(); |
| 63 | 64 | |
| 65 | + $nonce = isset($_SERVER['HTTP_X_WP_NONCE']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_X_WP_NONCE'])) : ''; | |
| 66 | + if (!wp_verify_nonce($nonce, 'fluentcart') && !wp_verify_nonce($nonce, 'wp_rest')) { | |
| 67 | + wp_send_json(['message' => __('Invalid nonce', 'fluent-cart')], 403); | |
| 68 | + return; | |
| 69 | + } | |
| 70 | + | |
| 64 | 71 | $startedAt = microtime(true); |
| 65 | 72 | |
| 66 | 73 | $action = App::request()->get('fc_checkout_action'); |
| 67 | 74 | $result = []; |
| @@ -190,8 +197,34 @@ | ||
| 190 | 197 | if (is_wp_error($result)) { |
| 191 | 198 | return $result; |
| 192 | 199 | } |
| 193 | 200 | |
| 201 | + $cartCouponCodes = is_array($cart->coupons) ? $cart->coupons : []; | |
| 202 | + $couponWasApplied = false; | |
| 203 | + foreach ($cartCouponCodes as $existingCode) { | |
| 204 | + if (strcasecmp((string) $existingCode, (string) $couponCode) === 0) { | |
| 205 | + $couponWasApplied = true; | |
| 206 | + break; | |
| 207 | + } | |
| 208 | + } | |
| 209 | + | |
| 210 | + if (!$couponWasApplied) { | |
| 211 | + $perCouponResults = is_array($result) ? Arr::get($result, 'coupon_results', []) : []; | |
| 212 | + $errorMessage = ''; | |
| 213 | + foreach ($perCouponResults as $resultCode => $couponResult) { | |
| 214 | + if (strcasecmp((string) $resultCode, (string) $couponCode) === 0) { | |
| 215 | + $errorMessage = Arr::get($couponResult, 'error', ''); | |
| 216 | + if ($errorMessage !== '') { | |
| 217 | + break; | |
| 218 | + } | |
| 219 | + } | |
| 220 | + } | |
| 221 | + if ($errorMessage === '') { | |
| 222 | + $errorMessage = __('No matching coupon found for this code.', 'fluent-cart'); | |
| 223 | + } | |
| 224 | + return new \WP_Error('coupon_not_applied', $errorMessage); | |
| 225 | + } | |
| 226 | + | |
| 194 | 227 | ob_start(); |
| 195 | 228 | (new CartSummaryRender($cart))->render($withWrapper = false); |
| 196 | 229 | $summary = ob_get_clean(); |
| 197 | 230 | |
| @@ -227,8 +260,14 @@ | ||
| 227 | 260 | 'content' => $modalCheckoutRender->getFragment('summary_group'), |
| 228 | 261 | 'type' => 'replace' |
| 229 | 262 | ]; |
| 230 | 263 | |
| 264 | + $fragments[] = [ | |
| 265 | + 'selector' => '[data-fct-modal-checkout-summary]', | |
| 266 | + 'content' => $modalCheckoutRender->getFragment('checkout_summary'), | |
| 267 | + 'type' => 'replace' | |
| 268 | + ]; | |
| 269 | + | |
| 231 | 270 | return [ |
| 232 | 271 | 'fragments' => $fragments, |
| 233 | 272 | 'cart' => $cart, |
| 234 | 273 | 'total' => $cartTotal, |
| @@ -289,9 +328,15 @@ | ||
| 289 | 328 | 'content' => $modalCheckoutRender->getFragment('summary_group'), |
| 290 | 329 | 'type' => 'replace' |
| 291 | 330 | ]; |
| 292 | 331 | |
| 332 | + $fragments[] = [ | |
| 333 | + 'selector' => '[data-fct-modal-checkout-summary]', | |
| 334 | + 'content' => $modalCheckoutRender->getFragment('checkout_summary'), | |
| 335 | + 'type' => 'replace' | |
| 336 | + ]; | |
| 293 | 337 | |
| 338 | + | |
| 294 | 339 | return [ |
| 295 | 340 | 'fragments' => $fragments, |
| 296 | 341 | 'total' => $cartTotal, |
| 297 | 342 | 'formatted_total' => Helper::toDecimal($cartTotal), |
| @@ -364,8 +409,14 @@ | ||
| 364 | 409 | $totalPrice = apply_filters('fluent_cart/cart/estimated_total', $totalPrice, [ |
| 365 | 410 | 'cart' => $cart |
| 366 | 411 | ]); |
| 367 | 412 | |
| 413 | + // Prorate credit and upgrade discount (plan upgrade) are post-tax adjustments on | |
| 414 | + // the payable total. | |
| 415 | + $totalPrice = max(0, $totalPrice | |
| 416 | + - (int) Arr::get($cart->checkout_data, 'prorate_credit.amount', 0) | |
| 417 | + - (int) Arr::get($cart->checkout_data, 'upgrade_discount.amount', 0)); | |
| 418 | + | |
| 368 | 419 | $fragments = []; |
| 369 | 420 | |
| 370 | 421 | $cartRender = (new CheckoutRenderer($cart)); |
| 371 | 422 | $modalCheckoutRender = (new ModalCheckoutRenderer($cart)); |
| @@ -427,8 +478,9 @@ | ||
| 427 | 478 | |
| 428 | 479 | $oldTaxTotal = Arr::get($cart->checkout_data, 'tax_data.tax_total', 0); |
| 429 | 480 | $oldShippingCharge = Arr::get($cart->checkout_data, 'shipping_data.shipping_charge', 0); |
| 430 | 481 | |
| 482 | + $previousFormData = Arr::get($cart->checkout_data, 'form_data', []); | |
| 431 | 483 | $checkoutData = Arr::wrap($cart->checkout_data); |
| 432 | 484 | Arr::set($checkoutData, 'form_data.' . $type . '_address_id', $addressId); |
| 433 | 485 | Arr::set($checkoutData, 'form_data.' . $type . '_country', $country); |
| 434 | 486 | Arr::set($checkoutData, 'form_data.' . $type . '_state', $state); |
| @@ -438,8 +490,14 @@ | ||
| 438 | 490 | Arr::set($checkoutData, 'form_data.shipping_country', $country); |
| 439 | 491 | Arr::set($checkoutData, 'form_data.shipping_state', $state); |
| 440 | 492 | } |
| 441 | 493 | |
| 494 | + $checkoutData = (new TaxModule())->maybeInvalidateVatValidationForCountryChange($checkoutData, [ | |
| 495 | + 'ship_to_different' => Arr::get($previousFormData, 'ship_to_different', 'no'), | |
| 496 | + 'billing_country' => Arr::get($previousFormData, 'billing_country', ''), | |
| 497 | + 'shipping_country' => Arr::get($previousFormData, 'shipping_country', ''), | |
| 498 | + ]); | |
| 499 | + | |
| 442 | 500 | $cart->checkout_data = $checkoutData; |
| 443 | 501 | |
| 444 | 502 | $cart->save(); |
| 445 | 503 | |
| @@ -559,8 +617,10 @@ | ||
| 559 | 617 | ob_start(); |
| 560 | 618 | |
| 561 | 619 | $selectedId = Arr::get($cart->checkout_data, 'shipping_data.shipping_method_id', ''); |
| 562 | 620 | |
| 621 | + $selectedId = CartHelper::resolveAutoSelectShippingMethod($cart, $availableShippingMethods ?: [], $selectedId); | |
| 622 | + | |
| 563 | 623 | if (!$availableShippingMethods || is_wp_error($availableShippingMethods)) { |
| 564 | 624 | (new ShippingMethodsRender($availableShippingMethods, $selectedId))->render(); |
| 565 | 625 | } else { |
| 566 | 626 | foreach ($availableShippingMethods as $method) { |
| @@ -581,8 +641,9 @@ | ||
| 581 | 641 | 'type' => 'replace' |
| 582 | 642 | ] |
| 583 | 643 | ], |
| 584 | 644 | 'country_code' => $data['country_code'], |
| 645 | + 'shipping_method_id' => $selectedId ?: null, | |
| 585 | 646 | ]; |
| 586 | 647 | } |
| 587 | 648 | |
| 588 | 649 | public function handleCartStatusAjax() |
| @@ -700,10 +761,10 @@ | ||
| 700 | 761 | return new \WP_Error('no_cart', __('No active cart found', 'fluent-cart')); |
| 701 | 762 | } |
| 702 | 763 | |
| 703 | 764 | $allData = App::request()->all(); |
| 704 | - $dataKey = (string) Arr::get($allData, 'data_key'); | |
| 705 | - $dataValue = (string) Arr::get($allData, 'data_value'); | |
| 765 | + $dataKey = sanitize_text_field((string) Arr::get($allData, 'data_key')); | |
| 766 | + $dataValue = sanitize_text_field((string) Arr::get($allData, 'data_value')); | |
| 706 | 767 | |
| 707 | 768 | if ($dataKey) { |
| 708 | 769 | $allData[$dataKey] = $dataValue; |
| 709 | 770 | } |
| @@ -725,9 +786,11 @@ | ||
| 725 | 786 | 'order_notes' => 'form_data.order_notes', |
| 726 | 787 | 'shipping_method_id' => 'shipping_data.shipping_method_id', |
| 727 | 788 | '_fct_pay_method' => 'form_data._fct_pay_method', |
| 728 | 789 | 'billing_company_name' => 'form_data.billing_company_name', |
| 729 | - 'fct_billing_tax_id' => 'tax_data.vat_number' | |
| 790 | + 'billing_legal_registration_id' => 'form_data.billing_legal_registration_id', | |
| 791 | + 'is_business' => 'form_data.is_business', | |
| 792 | + 'fct_billing_tax_id' => 'tax_data.vat_number', | |
| 730 | 793 | ]; |
| 731 | 794 | |
| 732 | 795 | $addressFieldKeys = [ |
| 733 | 796 | 'full_name', |
| @@ -766,8 +829,16 @@ | ||
| 766 | 829 | $normalizeData[$key] = $value; |
| 767 | 830 | } |
| 768 | 831 | } |
| 769 | 832 | |
| 833 | + // Force-check the explicit data_key field — bulk POST may have already stored it as a side-effect. | |
| 834 | + if ($dataKey && isset($validKeys[$dataKey]) && !isset($normalizeData[$dataKey])) { | |
| 835 | + $storedValue = (string) Arr::get($prevCheckoutData, $validKeys[$dataKey], ''); | |
| 836 | + if ($storedValue !== $dataValue) { | |
| 837 | + $normalizeData[$dataKey] = $dataValue; | |
| 838 | + } | |
| 839 | + } | |
| 840 | + | |
| 770 | 841 | if (!$normalizeData) { |
| 771 | 842 | return [ |
| 772 | 843 | 'message' => __('No changes detected', 'fluent-cart') |
| 773 | 844 | ]; |
| @@ -783,8 +854,9 @@ | ||
| 783 | 854 | foreach ($normalizeData as $normalizeKey => $normalizeValue) { |
| 784 | 855 | Arr::set($checkoutData, $validKeys[$normalizeKey], $normalizeValue); |
| 785 | 856 | } |
| 786 | 857 | |
| 858 | + $oldTaxTotal = (int) Arr::get($cart->checkout_data, 'tax_data.tax_total', 0); | |
| 787 | 859 | $fillData = [ |
| 788 | 860 | 'checkout_data' => $checkoutData, |
| 789 | 861 | 'cart_data' => $cart->cart_data, |
| 790 | 862 | 'hook_changes' => [ |
| @@ -815,8 +887,10 @@ | ||
| 815 | 887 | $cart->last_name = implode(' ', $nameParts); |
| 816 | 888 | } |
| 817 | 889 | |
| 818 | 890 | $cart->fill($fillData); |
| 891 | + $newTaxTotal = (int) Arr::get($cart->checkout_data, 'tax_data.tax_total', 0); | |
| 892 | + $taxAmountChanged = $oldTaxTotal !== $newTaxTotal; | |
| 819 | 893 | $cart->clearFeeCache(); |
| 820 | 894 | |
| 821 | 895 | $cart = CartHelper::getCart(); |
| 822 | 896 | $sanitizedUtmData = UtmHelper::getUtmDataOfRequest(); |
| @@ -844,30 +918,36 @@ | ||
| 844 | 918 | 'content' => $cartRender->getFragment('cart_summary_fragment'), |
| 845 | 919 | 'type' => 'replace' |
| 846 | 920 | ]; |
| 847 | 921 | |
| 922 | + if ($taxAmountChanged || !empty($hookChanges['shipping'])) { | |
| 923 | + // also update the payment methods | |
| 924 | + if ($enableModalCheckout === 'yes') { | |
| 925 | + $fragments[] = [ | |
| 926 | + 'selector' => '[data-fluent-cart-checkout-payment-methods]', | |
| 927 | + 'content' => $modalCheckoutRender->getFragment('payment_methods'), | |
| 928 | + 'type' => 'replace' | |
| 929 | + ]; | |
| 930 | + } else { | |
| 931 | + $fragments[] = [ | |
| 932 | + 'selector' => '[data-fluent-cart-checkout-payment-methods]', | |
| 933 | + 'content' => $cartRender->getFragment('payment_methods'), | |
| 934 | + 'type' => 'replace' | |
| 935 | + ]; | |
| 936 | + } | |
| 848 | 937 | |
| 849 | - // also update the payment methods | |
| 850 | - if ($enableModalCheckout === 'yes') { | |
| 851 | 938 | $fragments[] = [ |
| 852 | - 'selector' => '[data-fluent-cart-checkout-payment-methods]', | |
| 853 | - 'content' => $modalCheckoutRender->getFragment('payment_methods'), | |
| 854 | - 'type' => 'replace' | |
| 939 | + 'selector' => '[data-fct-modal-checkout-summary-group]', | |
| 940 | + 'content' => $modalCheckoutRender->getFragment('summary_group'), | |
| 941 | + 'type' => 'replace' | |
| 855 | 942 | ]; |
| 856 | - } else { | |
| 943 | + | |
| 857 | 944 | $fragments[] = [ |
| 858 | - 'selector' => '[data-fluent-cart-checkout-payment-methods]', | |
| 859 | - 'content' => $cartRender->getFragment('payment_methods'), | |
| 860 | - 'type' => 'replace' | |
| 945 | + 'selector' => '[data-fct-modal-checkout-summary]', | |
| 946 | + 'content' => $modalCheckoutRender->getFragment('checkout_summary'), | |
| 947 | + 'type' => 'replace' | |
| 861 | 948 | ]; |
| 862 | 949 | } |
| 863 | - | |
| 864 | - | |
| 865 | - $fragments[] = [ | |
| 866 | - 'selector' => '[data-fct-modal-checkout-summary-group]', | |
| 867 | - 'content' => $modalCheckoutRender->getFragment('summary_group'), | |
| 868 | - 'type' => 'replace' | |
| 869 | - ]; | |
| 870 | 950 | } |
| 871 | 951 | |
| 872 | 952 | // Re-render cart summary on payment method change (fees may depend on payment method) |
| 873 | 953 | if (isset($normalizeData['_fct_pay_method'])) { |
| @@ -890,8 +970,14 @@ | ||
| 890 | 970 | 'selector' => '[data-fct-modal-checkout-summary-group]', |
| 891 | 971 | 'content' => $modalCheckoutRender->getFragment('summary_group'), |
| 892 | 972 | 'type' => 'replace' |
| 893 | 973 | ]; |
| 974 | + | |
| 975 | + $fragments[] = [ | |
| 976 | + 'selector' => '[data-fct-modal-checkout-summary]', | |
| 977 | + 'content' => $modalCheckoutRender->getFragment('checkout_summary'), | |
| 978 | + 'type' => 'replace' | |
| 979 | + ]; | |
| 894 | 980 | } |
| 895 | 981 | } |
| 896 | 982 | |
| 897 | 983 | if (($dataKey === 'billing_address_id' || $dataKey === 'shipping_address_id') && $dataValue !== '') { |
| @@ -942,218 +1028,8 @@ | ||
| 942 | 1028 | 'cart' => $cart |
| 943 | 1029 | ]; |
| 944 | 1030 | } |
| 945 | 1031 | |
| 946 | - public function handleSaveCustomerDataAjax() | |
| 947 | - { | |
| 948 | - | |
| 949 | - $key = sanitize_text_field(App::request()->get('data_key')); | |
| 950 | - $value = sanitize_text_field(App::request()->get('data_value')); | |
| 951 | - | |
| 952 | - $cart = CartHelper::getCart(); | |
| 953 | - if (!$cart) { | |
| 954 | - return new \WP_Error('no_cart', __('No active cart found', 'fluent-cart')); | |
| 955 | - } | |
| 956 | - | |
| 957 | - $addressFieldKeys = [ | |
| 958 | - 'full_name', | |
| 959 | - 'country', | |
| 960 | - 'address_1', | |
| 961 | - 'address_2', | |
| 962 | - 'state', | |
| 963 | - 'city', | |
| 964 | - 'postcode' | |
| 965 | - ]; | |
| 966 | - | |
| 967 | - $validKeys = [ | |
| 968 | - 'ship_to_different' => 'form_data.ship_to_different', | |
| 969 | - 'billing_email' => 'form_data.billing_email', | |
| 970 | - 'billing_address_id' => 'form_data.billing_address_id', | |
| 971 | - 'shipping_address_id' => 'form_data.shipping_address_id', | |
| 972 | - 'billing_company' => 'form_data.billing_company', | |
| 973 | - 'order_notes' => 'form_data.order_notes', | |
| 974 | - 'shipping_method_id' => 'shipping_data.shipping_method_id', | |
| 975 | - '_fct_pay_method' => 'form_data._fct_pay_method', | |
| 976 | - ]; | |
| 977 | - | |
| 978 | - foreach ($addressFieldKeys as $addressFieldKey) { | |
| 979 | - $validKeys['billing_' . $addressFieldKey] = 'form_data.billing_' . $addressFieldKey; | |
| 980 | - $validKeys['shipping_' . $addressFieldKey] = 'form_data.shipping_' . $addressFieldKey; | |
| 981 | - } | |
| 982 | - | |
| 983 | - if (!isset($validKeys[$key])) { | |
| 984 | - return new \WP_Error('invalid_key', __('Invalid data key', 'fluent-cart')); | |
| 985 | - } | |
| 986 | - | |
| 987 | - $checkoutData = Arr::wrap($cart->checkout_data); | |
| 988 | - | |
| 989 | - $prevValue = Arr::get($checkoutData, $validKeys[$key], ''); | |
| 990 | - if ($prevValue === $value) { | |
| 991 | - return [ | |
| 992 | - 'message' => __('Updated', 'fluent-cart') | |
| 993 | - ]; | |
| 994 | - } | |
| 995 | - | |
| 996 | - $oldCheckoutData = $checkoutData; | |
| 997 | - Arr::set($checkoutData, $validKeys[$key], $value); | |
| 998 | - | |
| 999 | - $fillData = apply_filters('fluent_cart/checkout/before_patch_checkout_data', [ | |
| 1000 | - 'checkout_data' => $checkoutData, | |
| 1001 | - 'cart_data' => $cart->cart_data, | |
| 1002 | - 'hook_changes' => [ | |
| 1003 | - 'shipping' => false, | |
| 1004 | - 'tax' => false | |
| 1005 | - ] | |
| 1006 | - ], [ | |
| 1007 | - 'cart' => $cart, | |
| 1008 | - 'key' => $key, | |
| 1009 | - 'value' => $value, | |
| 1010 | - 'prev_value' => $prevValue, | |
| 1011 | - 'old_checkout_data' => $oldCheckoutData | |
| 1012 | - ]); | |
| 1013 | - | |
| 1014 | - $changes = Arr::get($fillData, 'hook_changes', []); | |
| 1015 | - unset($fillData['hook_changes']); | |
| 1016 | - | |
| 1017 | - | |
| 1018 | - $cart->fill($fillData); | |
| 1019 | - $cart->save(); | |
| 1020 | - $fragments = []; | |
| 1021 | - | |
| 1022 | - | |
| 1023 | - if (Arr::get($changes, 'shipping', false)) { | |
| 1024 | - $fragments[] = [ | |
| 1025 | - 'selector' => '[data-fluent-cart-checkout-page-shipping-methods-wrapper]', | |
| 1026 | - 'content' => (new CheckoutRenderer($cart))->getFragment('shipping_methods'), | |
| 1027 | - 'type' => 'replace' | |
| 1028 | - ]; | |
| 1029 | - } | |
| 1030 | - //(new CartSummaryRender($cart))->render(false); | |
| 1031 | - | |
| 1032 | - ob_start(); | |
| 1033 | - (new CartSummaryRender($cart))->render(false); | |
| 1034 | - $render = ob_get_clean(); | |
| 1035 | - if (array_filter($changes)) { | |
| 1036 | - $fragments[] = [ | |
| 1037 | - 'selector' => '[data-fluent-cart-checkout-page-cart-items-wrapper]', | |
| 1038 | - //'content' => (new CheckoutRender($cart))->getFragment('cart_summary_fragment'), | |
| 1039 | - 'content' => $render, | |
| 1040 | - 'type' => 'replace' | |
| 1041 | - ]; | |
| 1042 | - } | |
| 1043 | - | |
| 1044 | - return [ | |
| 1045 | - 'fragments' => $fragments, | |
| 1046 | - 'message' => __('Data saved successfully', 'fluent-cart'), | |
| 1047 | - 'tax_total_Changes' => Arr::get($changes, 'tax', false), | |
| 1048 | - 'shipping_charge_changes' => Arr::get($changes, 'shipping', false), | |
| 1049 | - 'notify' => false, | |
| 1050 | - 'cart' => $cart | |
| 1051 | - ]; | |
| 1052 | - | |
| 1053 | - // Shipping Module | |
| 1054 | - | |
| 1055 | - add_action('fluent_cart/checkout/customer_data_saved', function ($data) { | |
| 1056 | - // Recalculate tax if needed | |
| 1057 | - | |
| 1058 | - do_action('fluent_cart/checkout/tax_data_changed', $data); | |
| 1059 | - | |
| 1060 | - }); | |
| 1061 | - | |
| 1062 | - do_action('fluent_cart/checkout/customer_data_saved', [ | |
| 1063 | - 'cart' => $cart, | |
| 1064 | - 'key' => $key, | |
| 1065 | - 'value' => $value, | |
| 1066 | - 'old_value' => $prevValue, | |
| 1067 | - 'old_data' => $oldCheckoutData | |
| 1068 | - ]); | |
| 1069 | - | |
| 1070 | - $didTaxChanges = did_action('fluent_cart/checkout/tax_data_changed'); | |
| 1071 | - $didShippingChanges = did_action('fluent_cart/checkout/shipping_data_changed'); | |
| 1072 | - | |
| 1073 | - | |
| 1074 | - if ($cart) { | |
| 1075 | - $checkoutRender = new CheckoutRenderer($cart); | |
| 1076 | - $shippingChanged = false; | |
| 1077 | - // we have to decide for which keys we will update shipping option | |
| 1078 | - $fragments = []; | |
| 1079 | - if ($cart->requireShipping()) { | |
| 1080 | - $watchingKeys = ['ship_to_different', 'billing_address', 'billing_country', 'billing_state']; | |
| 1081 | - if (Arr::get($cart->checkout_data, 'form_data.ship_to_different', null) === 'yes') { | |
| 1082 | - $watchingKeys = ['ship_to_different', 'shipping_address', 'shipping_country', 'shipping_state']; | |
| 1083 | - } | |
| 1084 | - | |
| 1085 | - if (in_array($key, $watchingKeys)) { | |
| 1086 | - $fragments[] = [ | |
| 1087 | - 'selector' => '[data-fluent-cart-checkout-page-shipping-methods-wrapper]', | |
| 1088 | - 'content' => $checkoutRender->getFragment('shipping_methods'), | |
| 1089 | - 'type' => 'replace' | |
| 1090 | - ]; | |
| 1091 | - } | |
| 1092 | - | |
| 1093 | - $shippingChanged = true; | |
| 1094 | - } | |
| 1095 | - | |
| 1096 | - $oldTaxTotal = Arr::get($cart->checkout_data, 'tax_data.tax_total', 0); | |
| 1097 | - $oldShippingCharge = Arr::get($cart->checkout_data, 'shipping_data.shipping_charge', 0); | |
| 1098 | - | |
| 1099 | - $countryCode = Arr::get($cart->checkout_data, 'form_data.shipping_country'); | |
| 1100 | - | |
| 1101 | - if (!$countryCode) { | |
| 1102 | - $countryCode = Arr::get($cart->checkout_data, 'form_data.billing_country'); | |
| 1103 | - } | |
| 1104 | - | |
| 1105 | - if (Arr::get($cart->checkout_data, 'form_data.ship_to_different', 'no') === 'no') { | |
| 1106 | - $countryCode = Arr::get($cart->checkout_data, 'form_data.billing_country'); | |
| 1107 | - } | |
| 1108 | - | |
| 1109 | - $data = [ | |
| 1110 | - 'country_code' => $countryCode, | |
| 1111 | - 'timezone' => '' | |
| 1112 | - ]; | |
| 1113 | - | |
| 1114 | - $list = $this->getShippingMethodsListView($data); | |
| 1115 | - | |
| 1116 | - if ($list === false) { | |
| 1117 | - $cart->checkout_data = array_merge($cart->checkout_data, [ | |
| 1118 | - 'shipping_data' => [ | |
| 1119 | - 'shipping_method_id' => null, | |
| 1120 | - 'shipping_charge' => 0 | |
| 1121 | - ] | |
| 1122 | - ]); | |
| 1123 | - $cart->save(); | |
| 1124 | - } | |
| 1125 | - | |
| 1126 | - do_action('fluent_cart/checkout/form_data_changed', [ | |
| 1127 | - 'cart' => $cart | |
| 1128 | - ]); | |
| 1129 | - | |
| 1130 | - $fragments[] = [ | |
| 1131 | - 'selector' => '[data-fluent-cart-checkout-page-cart-items-wrapper]', | |
| 1132 | - 'content' => $checkoutRender->getFragment('cart_summary_fragment'), | |
| 1133 | - 'type' => 'replace' | |
| 1134 | - ]; | |
| 1135 | - | |
| 1136 | - $newTaxTotal = Arr::get($cart->checkout_data, 'tax_data.tax_total', 0); | |
| 1137 | - $newShippingCharge = Arr::get($cart->checkout_data, 'shipping_data.shipping_charge', 0); | |
| 1138 | - | |
| 1139 | - $checkoutData = [ | |
| 1140 | - 'fragments' => $fragments, | |
| 1141 | - 'message' => __('Data saved successfully', 'fluent-cart'), | |
| 1142 | - 'tax_total_Changes' => $oldTaxTotal != $newTaxTotal, | |
| 1143 | - 'shipping_charge_changes' => $oldShippingCharge != $newShippingCharge, | |
| 1144 | - 'notify' => false | |
| 1145 | - ]; | |
| 1146 | - | |
| 1147 | - return apply_filters('fluent_cart/checkout/checkout_data_changed', $checkoutData, ['cart' => $cart]); | |
| 1148 | - } | |
| 1149 | - | |
| 1150 | - return [ | |
| 1151 | - 'message' => __('Failed to save data', 'fluent-cart'), | |
| 1152 | - 'notify' => false | |
| 1153 | - ]; | |
| 1154 | - } | |
| 1155 | - | |
| 1156 | 1032 | public function handleOrderBumpRequest() |
| 1157 | 1033 | { |
| 1158 | 1034 | $requestData = App::request()->all(); |
| 1159 | 1035 | |
| @@ -1162,9 +1038,9 @@ | ||
| 1162 | 1038 | return new \WP_Error('no_cart', __('No active cart found', 'fluent-cart')); |
| 1163 | 1039 | } |
| 1164 | 1040 | |
| 1165 | 1041 | $checkoutData = $cart->checkout_data; |
| 1166 | - if (!empty($checkoutData['upgrade_data']) || !empty($checkoutData['is_locked'])) { | |
| 1042 | + if (!$cart->acceptsAdditionalItems()) { | |
| 1167 | 1043 | return new \WP_Error('invalid_request', __('This cart is locked or already has an upgrade applied.', 'fluent-cart')); |
| 1168 | 1044 | } |
| 1169 | 1045 | |
| 1170 | 1046 | $upgradeFromVariationId = (int) Arr::get($requestData, 'upgrade_form', 0); |
| @@ -1179,8 +1055,14 @@ | ||
| 1179 | 1055 | 'request_data' => $requestData |
| 1180 | 1056 | ]); |
| 1181 | 1057 | } |
| 1182 | 1058 | |
| 1059 | + // A locked cart may still take bumps through the accepts_additional_items | |
| 1060 | + // filter, but must never let an item it pinned be swapped out. | |
| 1061 | + if (Arr::get($checkoutData, 'is_locked') === 'yes') { | |
| 1062 | + return new \WP_Error('invalid_request', __('This cart is locked and cannot be modified.', 'fluent-cart')); | |
| 1063 | + } | |
| 1064 | + | |
| 1183 | 1065 | if (!$upgradeFromVariationId || !$targetVariationId) { |
| 1184 | 1066 | return new \WP_Error('invalid_request', __('Invalid upgrade request.', 'fluent-cart')); |
| 1185 | 1067 | } |
| 1186 | 1068 | |
| @@ -1259,10 +1141,12 @@ | ||
| 1259 | 1141 | $sanitizedData[$dataKey] = ''; |
| 1260 | 1142 | } else { |
| 1261 | 1143 | $sanitizedData[$dataKey] = sanitize_email($dataValue ?? ''); |
| 1262 | 1144 | } |
| 1263 | - } else if (in_array($dataKey, ['billing_company_name', 'shipping_company_name'])) { | |
| 1145 | + } else if (in_array($dataKey, ['billing_company_name', 'shipping_company_name', 'billing_legal_registration_id'])) { | |
| 1264 | 1146 | $sanitizedData[$dataKey] = sanitize_text_field($dataValue); |
| 1147 | + } else if ($dataKey === 'is_business') { | |
| 1148 | + $sanitizedData[$dataKey] = $dataValue === 'yes' ? 'yes' : 'no'; | |
| 1265 | 1149 | } else if ($dataKey === 'billing_state' && !empty($dataValue)) { |
| 1266 | 1150 | $billingCountry = Arr::get($allData, 'billing_country'); |
| 1267 | 1151 | |
| 1268 | 1152 | $states = LocalizationManager::getInstance()->statesOptions($billingCountry); |