| @@ -285,8 +285,15 @@ | ||
| 285 | 285 | if (Arr::get($discount, 'value', 0) > 0) { |
| 286 | 286 | static::distributeManualDiscount($items, Helper::toCent(Arr::get($discount, 'value', 0))); |
| 287 | 287 | } |
| 288 | 288 | |
| 289 | + $couponCheck = CouponResource::validateOrderCoupons($items, (array) Arr::get($data, 'applied_coupon', []), Arr::get($customer, 'email', '')); | |
| 290 | + if (is_wp_error($couponCheck)) { | |
| 291 | + return $couponCheck; | |
| 292 | + } | |
| 293 | + $items = $couponCheck['items']; | |
| 294 | + $data['applied_coupon'] = $couponCheck['applied_coupons']; | |
| 295 | + | |
| 289 | 296 | // admin order processor |
| 290 | 297 | $adminOrderProcessor = new AdminOrderProcessor($items, [ |
| 291 | 298 | 'customer_id' => $customer->id, |
| 292 | 299 | 'payment_method' => $paymentMethod, |
| @@ -1360,8 +1367,35 @@ | ||
| 1360 | 1367 | } |
| 1361 | 1368 | $subscription->save(); |
| 1362 | 1369 | } |
| 1363 | 1370 | |
| 1371 | + /** | |
| 1372 | + * Whether the submitted coupon and item discounts match the calculated ones, within a cent of rounding. | |
| 1373 | + * | |
| 1374 | + * @param array $submittedItems | |
| 1375 | + * @param array $submittedCoupons Applied-coupon map keyed by coupon code. | |
| 1376 | + * @param array $couponCheck Result of CouponResource::validateOrderCoupons(). | |
| 1377 | + * @return bool | |
| 1378 | + */ | |
| 1379 | + private static function couponDiscountsMatch(array $submittedItems, array $submittedCoupons, array $couponCheck): bool | |
| 1380 | + { | |
| 1381 | + foreach ($couponCheck['applied_coupons'] as $code => $calculated) { | |
| 1382 | + $submitted = isset($submittedCoupons[$code]['discount']) ? (float) $submittedCoupons[$code]['discount'] : 0; | |
| 1383 | + if (abs($submitted - (float) $calculated['discount']) > 1) { | |
| 1384 | + return false; | |
| 1385 | + } | |
| 1386 | + } | |
| 1387 | + | |
| 1388 | + foreach ($couponCheck['items'] as $index => $calculatedItem) { | |
| 1389 | + $submitted = (float) Arr::get($submittedItems, $index . '.discount_total', 0); | |
| 1390 | + if (abs($submitted - (float) Arr::get($calculatedItem, 'discount_total', 0)) > 1) { | |
| 1391 | + return false; | |
| 1392 | + } | |
| 1393 | + } | |
| 1394 | + | |
| 1395 | + return true; | |
| 1396 | + } | |
| 1397 | + | |
| 1364 | 1398 | private static function distributeManualDiscount(&$items, $manualDiscountTotal) |
| 1365 | 1399 | { |
| 1366 | 1400 | $totalSubtotal = array_reduce($items, function ($carry, $item) { |
| 1367 | 1401 | return $carry + ((int)Arr::get($item, 'unit_price', 0) * (int)Arr::get($item, 'quantity', 1)); |
| @@ -1608,8 +1642,27 @@ | ||
| 1608 | 1642 | $appliedCoupons = Arr::get($orderData, 'applied_coupon'); |
| 1609 | 1643 | $discount = $data['discount']; |
| 1610 | 1644 | $shipping = $data['shipping']; |
| 1611 | 1645 | |
| 1646 | + if (!empty($appliedCoupons)) { | |
| 1647 | + $submittedItems = Arr::except((array) Arr::get($orderData, 'order_items', []), ['*']); | |
| 1648 | + $couponCheck = CouponResource::validateOrderCoupons( | |
| 1649 | + $submittedItems, | |
| 1650 | + (array) $appliedCoupons, | |
| 1651 | + $order->customer ? $order->customer->email : '' | |
| 1652 | + ); | |
| 1653 | + if (is_wp_error($couponCheck)) { | |
| 1654 | + return $couponCheck; | |
| 1655 | + } | |
| 1656 | + // Update saves the submitted items and totals, so they must already carry the calculated discounts. | |
| 1657 | + if (!static::couponDiscountsMatch($submittedItems, (array) $appliedCoupons, $couponCheck)) { | |
| 1658 | + return static::makeErrorResponse([ | |
| 1659 | + ['code' => 'coupon_discount_changed', 'message' => __('Coupon discounts have changed. Please re-apply the coupons and save again.', 'fluent-cart')] | |
| 1660 | + ], 422); | |
| 1661 | + } | |
| 1662 | + $appliedCoupons = $couponCheck['applied_coupons']; | |
| 1663 | + } | |
| 1664 | + | |
| 1612 | 1665 | $orderId = $order->id; |
| 1613 | 1666 | |
| 1614 | 1667 | /** |
| 1615 | 1668 | * First delete the deleted items |
| @@ -2499,8 +2552,12 @@ | ||
| 2499 | 2552 | foreach ($keysToInclude as $key) { |
| 2500 | 2553 | $address->{$key} = $addressData[$key]; |
| 2501 | 2554 | } |
| 2502 | 2555 | |
| 2556 | + if (array_key_exists('meta', $addressData)) { | |
| 2557 | + $address->meta = $addressData['meta']; | |
| 2558 | + } | |
| 2559 | + | |
| 2503 | 2560 | if ($address->save()) { |
| 2504 | 2561 | return $address; |
| 2505 | 2562 | } |
| 2506 | 2563 | return static::makeErrorResponse([ |
| @@ -2509,9 +2566,9 @@ | ||
| 2509 | 2566 | } |
| 2510 | 2567 | |
| 2511 | 2568 | private static function createOrderAddress(array $address, $orderId) |
| 2512 | 2569 | { |
| 2513 | - $keysToInclude = ['order_id', 'type', 'name', 'address_1', 'address_2', 'city', 'state', 'postcode', 'country']; | |
| 2570 | + $keysToInclude = ['order_id', 'type', 'name', 'address_1', 'address_2', 'city', 'state', 'postcode', 'country', 'meta']; | |
| 2514 | 2571 | $address = Arr::only($address, $keysToInclude); |
| 2515 | 2572 | $address['order_id'] = $orderId; |
| 2516 | 2573 | |
| 2517 | 2574 | if (!empty($address)) { |