| @@ -16,8 +16,64 @@ | ||
| 16 | 16 | use FluentCart\Framework\Support\Arr; |
| 17 | 17 | |
| 18 | 18 | class CartHelper |
| 19 | 19 | { |
| 20 | + const MAX_QUANTITY = 100000; | |
| 21 | + | |
| 22 | + const QUANTITY_HARD_LIMIT = 10000000; | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * The ceiling is what keeps `unit_price * quantity` inside PHP's integer range — | |
| 26 | + * without one the product overflows to a float and casts back to a wrapped total. | |
| 27 | + * Cart updates send a signed delta, so those allow the negative side too. | |
| 28 | + */ | |
| 29 | + public static function validateQuantity($quantity, $isDelta = false) | |
| 30 | + { | |
| 31 | + if (!is_numeric($quantity)) { | |
| 32 | + return static::invalidQuantityError(); | |
| 33 | + } | |
| 34 | + | |
| 35 | + $max = static::maxQuantity(); | |
| 36 | + $value = (float)$quantity; | |
| 37 | + $min = $isDelta ? -$max : 1; | |
| 38 | + | |
| 39 | + if ($value != floor($value) || $value < $min || $value > $max) { | |
| 40 | + return static::invalidQuantityError(); | |
| 41 | + } | |
| 42 | + | |
| 43 | + return null; | |
| 44 | + } | |
| 45 | + | |
| 46 | + public static function maxQuantity() | |
| 47 | + { | |
| 48 | + /** | |
| 49 | + * Filter the highest quantity a single cart line accepts. | |
| 50 | + * | |
| 51 | + * Return a whole number of one or more. The value is a safety ceiling, not just | |
| 52 | + * a storefront preference: it is what keeps a line's price multiplication inside | |
| 53 | + * PHP's integer range. A value that cannot serve that purpose is ignored — a | |
| 54 | + * non-numeric or fractional value falls back to the default, and anything above | |
| 55 | + * QUANTITY_HARD_LIMIT is capped there. | |
| 56 | + * | |
| 57 | + * @param int $maxQuantity Highest accepted quantity for one cart line. | |
| 58 | + */ | |
| 59 | + $max = apply_filters('fluent_cart/cart/max_quantity', self::MAX_QUANTITY); | |
| 60 | + | |
| 61 | + if (!is_numeric($max) || (float)$max != floor((float)$max) || $max < 1) { | |
| 62 | + return self::MAX_QUANTITY; | |
| 63 | + } | |
| 64 | + | |
| 65 | + return (int)min($max, self::QUANTITY_HARD_LIMIT); | |
| 66 | + } | |
| 67 | + | |
| 68 | + private static function invalidQuantityError() | |
| 69 | + { | |
| 70 | + /* translators: %d: the highest quantity a single cart line accepts. */ | |
| 71 | + $message = __('Please enter a product quantity between 1 and %d.', 'fluent-cart'); | |
| 72 | + | |
| 73 | + return new \WP_Error('invalid_cart_quantity', sprintf($message, static::maxQuantity())); | |
| 74 | + } | |
| 75 | + | |
| 20 | 76 | public static function getCart($hash = null, $create = false) |
| 21 | 77 | { |
| 22 | 78 | return CartResource::get([ |
| 23 | 79 | 'hash' => $hash ?? App::request()->get(Helper::INSTANT_CHECKOUT_URL_PARAM), |
| @@ -790,9 +846,10 @@ | ||
| 790 | 846 | { |
| 791 | 847 | if (is_user_logged_in()) { |
| 792 | 848 | $wpUser = wp_get_current_user(); |
| 793 | 849 | $cart->user_id = get_current_user_id(); |
| 794 | - $customer = Customer::query()->where('email', wp_get_current_user()->user_email)->first(); | |
| 850 | + // The cart belongs to the account's linked customer, not to whichever record holds its email. | |
| 851 | + $customer = Customer::query()->where('user_id', $wpUser->ID)->orderBy('id', 'ASC')->first(); | |
| 795 | 852 | if ($customer) { |
| 796 | 853 | $cart->customer_id = $customer->id; |
| 797 | 854 | } |
| 798 | 855 | $cart->email = $wpUser->user_email; |