| @@ -351,9 +351,21 @@ | ||
| 351 | 351 | ]); |
| 352 | 352 | } |
| 353 | 353 | |
| 354 | 354 | if (!$variation) { |
| 355 | - return $cart->removeItem($itemId); | |
| 355 | + // An item already in the cart whose variation row has since | |
| 356 | + // disappeared (product/variation deleted) is dropped gracefully. | |
| 357 | + // An id that was never in the cart and resolves to nothing is a | |
| 358 | + // client error — silently answering "Cart updated successfully" | |
| 359 | + // hid typos and probing as a 200 no-op. | |
| 360 | + if ($existingItem !== null) { | |
| 361 | + return $cart->removeItem($itemId); | |
| 362 | + } | |
| 363 | + | |
| 364 | + return new WP_Error( | |
| 365 | + 'invalid_item', | |
| 366 | + __('Invalid item.', 'fluent-cart') | |
| 367 | + ); | |
| 356 | 368 | } |
| 357 | 369 | |
| 358 | 370 | $soldIndividually = $isCustom |
| 359 | 371 | ? !empty($variation->sold_individually) |
| @@ -392,9 +404,13 @@ | ||
| 392 | 404 | } |
| 393 | 405 | |
| 394 | 406 | $utmData = static::prepareUtmData($data); |
| 395 | 407 | if ($utmData) { |
| 396 | - $cart->utm_data = array_merge(is_array($cart->utm_data) ? $cart->utm_data : [], $utmData); | |
| 408 | + // Replaced, not merged. A cart row is reused across visits, so merging | |
| 409 | + // key by key accumulated a union of every touch that ever reached it and | |
| 410 | + // the column stopped describing any single one. The browser has already | |
| 411 | + // resolved which touch this is, so its block is the answer. | |
| 412 | + $cart->utm_data = $utmData; | |
| 397 | 413 | $cart->save(); |
| 398 | 414 | } |
| 399 | 415 | |
| 400 | 416 | return $cart; |